Commit ed25a610 authored by Neil Birkbeck's avatar Neil Birkbeck Committed by Yaowu Xu

Add utilities to aom_dsp for modeling correlated noise.

The auto-regressive model allows for different window shapes
and different lag sizes.

Although most likely to be used as a reference for modeling
noise in AV1, the model is currently parameterized more generally
than AV1 needs.

I will add an example (hopefully with a denoiser) in future
commits.

Change-Id: I1ba1067543601c2c01db4970d42766bb35da77f0
parent 22a51d9e
......@@ -302,6 +302,10 @@ if (CONFIG_AV1_ENCODER)
"${AOM_ROOT}/aom_dsp/bitwriter.h"
"${AOM_ROOT}/aom_dsp/bitwriter_buffer.c"
"${AOM_ROOT}/aom_dsp/bitwriter_buffer.h"
"${AOM_ROOT}/aom_dsp/noise_util.h"
"${AOM_ROOT}/aom_dsp/noise_util.c"
"${AOM_ROOT}/aom_dsp/noise_model.c"
"${AOM_ROOT}/aom_dsp/noise_model.c"
"${AOM_ROOT}/aom_dsp/psnr.c"
"${AOM_ROOT}/aom_dsp/psnr.h"
"${AOM_ROOT}/aom_dsp/sad.c"
......
This diff is collapsed.
/*
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#ifndef AOM_DSP_NOISE_MODEL_H_
#define AOM_DSP_NOISE_MODEL_H_
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include <stdint.h>
/*!\brief Wrapper of data required to represent linear system of eqns and soln.
*/
typedef struct {
double *A;
double *b;
double *x;
int n;
} aom_equation_system_t;
/*!\brief Representation of a piecewise linear curve
*
* Holds n points as (x, y) pairs, that store the curve.
*/
typedef struct {
double (*points)[2];
int num_points;
} aom_noise_strength_lut_t;
/*!\brief Init the noise strength lut with the given number of points*/
int aom_noise_strength_lut_init(aom_noise_strength_lut_t *lut, int num_points);
/*!\brief Frees the noise strength lut. */
void aom_noise_strength_lut_free(aom_noise_strength_lut_t *lut);
/*!\brief Evaluate the lut at the point x.
*
* \param[in] lut The lut data.
* \param[in] x The coordinate to evaluate the lut.
*/
double aom_noise_strength_lut_eval(const aom_noise_strength_lut_t *lut,
double x);
/*!\brief Helper struct to model noise strength as a function of intensity.
*
* Internally, this structure holds a representation of a linear system
* of equations that models noise strength (standard deviation) as a
* function of intensity. The mapping is initially stored using a
* piecewise representation with evenly spaced bins that cover the entire
* domain from [min_intensity, max_intensity]. Each observation (x,y) gives a
* constraint of the form:
* y_{i} (1 - a) + y_{i+1} a = y
* where y_{i} is the value of bin i and x_{i} <= x <= x_{i+1} and
* a = x/(x_{i+1} - x{i}). The equation system holds the corresponding
* normal equations.
*
* As there may be missing data, the solution is regularized to get a
* complete set of values for the bins. A reduced representation after
* solving can be obtained by getting the corresponding noise_strength_lut_t.
*/
typedef struct {
aom_equation_system_t eqns;
double min_intensity;
double max_intensity;
int num_bins;
int num_equations;
double total;
} aom_noise_strength_solver_t;
/*!\brief Initializes the noise solver with the given number of bins.
*
* Returns 0 if initialization fails.
*
* \param[in] solver The noise solver to be initialized.
* \param[in] num_bins Number of bins to use in the internal representation.
*/
int aom_noise_strength_solver_init(aom_noise_strength_solver_t *solver,
int num_bins);
void aom_noise_strength_solver_free(aom_noise_strength_solver_t *solver);
/*!\brief Gets the x coordinate of bin i.
*
* \param[in] i The bin whose coordinate to query.
*/
double aom_noise_strength_solver_get_center(
const aom_noise_strength_solver_t *solver, int i);
/*!\brief Add an observation of the block mean intensity to its noise strength.
*
* \param[in] block_mean The average block intensity,
* \param[in] noise_std The observed noise strength.
*/
void aom_noise_strength_solver_add_measurement(
aom_noise_strength_solver_t *solver, double block_mean, double noise_std);
/*!\brief Solves the current set of equations for the noise strength. */
int aom_noise_strength_solver_solve(aom_noise_strength_solver_t *solver);
/*!\brief Fits a reduced piecewise linear lut to the internal solution
*
* \param[out] lut The output piecewise linear lut.
*/
int aom_noise_strength_solver_fit_piecewise(
const aom_noise_strength_solver_t *solver, aom_noise_strength_lut_t *lut);
/*!\brief Helper for holding precomputed data for finding flat blocks.
*
* Internally a block is modeled with a low-order polynomial model. A
* planar model would be a bunch of equations like:
* <[y_i x_i 1], [a_1, a_2, a_3]> = b_i
* for each point in the block. The system matrix A with row i as [y_i x_i 1]
* is maintained as is the inverse, inv(A'*A), so that the plane parameters
* can be fit for each block.
*/
typedef struct {
double *AtA_inv;
double *A;
int num_params; // The number of parameters used for internal low-order model
int block_size; // The block size the finder was initialized with
} aom_flat_block_finder_t;
/*!\brief Init the block_finder with the given block size */
int aom_flat_block_finder_init(aom_flat_block_finder_t *block_finder,
int block_size);
void aom_flat_block_finder_free(aom_flat_block_finder_t *block_finder);
/*!\brief Helper to extract a block and low order "planar" model. */
void aom_flat_block_finder_extract_block(
const aom_flat_block_finder_t *block_finder, const uint8_t *const data,
int w, int h, int stride, int offsx, int offsy, double *plane,
double *block);
int aom_flat_block_finder_run(const aom_flat_block_finder_t *block_finder,
const uint8_t *const data, int w, int h,
int stride, uint8_t *flat_blocks);
// The noise shape indicates the allowed coefficients in the AR model.
typedef enum {
AOM_NOISE_SHAPE_DIAMOND = 0,
AOM_NOISE_SHAPE_SQUARE = 1
} aom_noise_shape;
// The parameters of the noise model include the shape type and the lag.
typedef struct {
aom_noise_shape shape;
int lag;
} aom_noise_model_params_t;
/*!\brief State of a noise model estimate for a single channel.
*
* This contains a system of equations that can be used to solve
* for the auto-regressive coefficients as well as a noise strength
* solver that can be used to model noise strength as a function of
* intensity.
*/
typedef struct {
aom_equation_system_t eqns;
aom_noise_strength_solver_t strength_solver;
} aom_noise_state_t;
/*!\brief Complete model of noise for a planar video
*
* This includes a noise model for the latest frame and an aggregated
* estimate over all previous frames that had similar parameters.
*/
typedef struct {
aom_noise_model_params_t params;
aom_noise_state_t combined_state[3]; // Combined state per channel
aom_noise_state_t latest_state[3]; // Latest state per channel
int (*coords)[2]; // Offsets (x,y) of the coefficient samples
int n; // Number of parameters (size of coords)
} aom_noise_model_t;
/*!\brief Result of a noise model update. */
typedef enum {
AOM_NOISE_STATUS_OK = 0,
AOM_NOISE_STATUS_INVALID_ARGUMENT,
AOM_NOISE_STATUS_INSUFFICIENT_FLAT_BLOCKS,
AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE,
AOM_NOISE_STATUS_INTERNAL_ERROR,
} aom_noise_status_t;
/*!\brief Initializes a noise model with the given parameters.
*
* Returns 0 on failure.
*/
int aom_noise_model_init(aom_noise_model_t *model,
const aom_noise_model_params_t params);
void aom_noise_model_free(aom_noise_model_t *model);
/*!\brief Updates the noise model with a new frame observation.
*
* Updates the noise model with measurements from the given input frame and a
* denoised variant of it. Noise is sampled from flat blocks using the flat
* block map.
*
* Returns a noise_status indicating if the update was successful. If the
* Update was successful, the combined_state is updated with measurements from
* the provided frame. If status is OK or DIFFERENT_NOISE_TYPE, the latest noise
* state will be updated with measurements from the provided frame.
*
* \param[in,out] noise_model The noise model to be updated
* \param[in] data Raw frame data
* \param[in] denoised Denoised frame data.
* \param[in] w Frame width
* \param[in] h Frame height
* \param[in] strides Stride of the planes
* \param[in] chroma_sub_log2 Chroma subsampling for planes != 0.
* \param[in] flat_blocks A map to blocks that have been determined flat
* \param[in] block_size The size of blocks.
*/
aom_noise_status_t aom_noise_model_update(
aom_noise_model_t *const noise_model, const uint8_t *const data[3],
const uint8_t *const denoised[3], int w, int h, int strides[3],
int chroma_sub_log2[2], const uint8_t *const flat_blocks, int block_size);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // AOM_DSP_NOISE_MODEL_H_
/*
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "aom_dsp/noise_util.h"
#include "aom_mem/aom_mem.h"
// Return normally distrbuted values with standard deviation of sigma.
double aom_randn(double sigma) {
while (1) {
const double u = 2.0 * ((double)rand()) / RAND_MAX - 1.0;
const double v = 2.0 * ((double)rand()) / RAND_MAX - 1.0;
const double s = u * u + v * v;
if (s > 0 && s < 1) {
return sigma * (u * sqrt(-2.0 * log(s) / s));
}
}
return 0;
}
double aom_normalized_cross_correlation(const double *a, const double *b,
int n) {
double c = 0;
double a_len = 0;
double b_len = 0;
for (int i = 0; i < n; ++i) {
a_len += a[i] * a[i];
b_len += b[i] * b[i];
c += a[i] * b[i];
}
return c / (sqrt(a_len) * sqrt(b_len));
}
void aom_noise_synth(int lag, int n, const int (*coords)[2],
const double *coeffs, double *data, int w, int h) {
const int pad_size = 3 * lag;
const int padded_w = w + pad_size;
const int padded_h = h + pad_size;
int x = 0, y = 0;
double *padded = (double *)aom_malloc(padded_w * padded_h * sizeof(*padded));
for (y = 0; y < padded_h; ++y) {
for (x = 0; x < padded_w; ++x) {
padded[y * padded_w + x] = aom_randn(1.0);
}
}
for (y = lag; y < padded_h; ++y) {
for (x = lag; x < padded_w; ++x) {
double sum = 0;
int i = 0;
for (i = 0; i < n; ++i) {
const int dx = coords[i][0];
const int dy = coords[i][1];
sum += padded[(y + dy) * padded_w + (x + dx)] * coeffs[i];
}
padded[y * padded_w + x] += sum;
}
}
// Copy over the padded rows to the output
for (y = 0; y < h; ++y) {
memcpy(data + y * w, padded + y * padded_w, sizeof(*data) * w);
}
aom_free(padded);
}
int aom_noise_data_validate(const double *data, int w, int h) {
const double kVarianceThreshold = 2;
const double kMeanThreshold = 2;
int x = 0, y = 0;
int ret_value = 1;
double var = 0, mean = 0;
double *mean_x, *mean_y, *var_x, *var_y;
// Check that noise variance is not increasing in x or y
// and that the data is zero mean.
mean_x = (double *)aom_malloc(sizeof(*mean_x) * w);
var_x = (double *)aom_malloc(sizeof(*var_x) * w);
mean_y = (double *)aom_malloc(sizeof(*mean_x) * h);
var_y = (double *)aom_malloc(sizeof(*var_y) * h);
memset(mean_x, 0, sizeof(*mean_x) * w);
memset(var_x, 0, sizeof(*var_x) * w);
memset(mean_y, 0, sizeof(*mean_y) * h);
memset(var_y, 0, sizeof(*var_y) * h);
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
const double d = data[y * w + x];
var_x[x] += d * d;
var_y[y] += d * d;
mean_x[x] += d;
mean_y[y] += d;
var += d * d;
mean += d;
}
}
mean /= (w * h);
var = var / (w * h) - mean * mean;
for (y = 0; y < h; ++y) {
mean_y[y] /= h;
var_y[y] = var_y[y] / h - mean_y[y] * mean_y[y];
if (fabs(var_y[y] - var) >= kVarianceThreshold) {
fprintf(stderr, "Variance distance too large %f %f\n", var_y[y], var);
ret_value = 0;
break;
}
if (fabs(mean_y[y] - mean) >= kMeanThreshold) {
fprintf(stderr, "Mean distance too large %f %f\n", mean_y[y], mean);
ret_value = 0;
break;
}
}
for (x = 0; x < w; ++x) {
mean_x[x] /= w;
var_x[x] = var_x[x] / w - mean_x[x] * mean_x[x];
if (fabs(var_x[x] - var) >= kVarianceThreshold) {
fprintf(stderr, "Variance distance too large %f %f\n", var_x[x], var);
ret_value = 0;
break;
}
if (fabs(mean_x[x] - mean) >= kMeanThreshold) {
fprintf(stderr, "Mean distance too large %f %f\n", mean_x[x], mean);
ret_value = 0;
break;
}
}
aom_free(mean_x);
aom_free(mean_y);
aom_free(var_x);
aom_free(var_y);
return ret_value;
}
/*
* Copyright (c) 2017, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#ifndef AOM_DSP_NOISE_UTIL_H_
#define AOM_DSP_NOISE_UTIL_H_
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Computes normalized cross correlation of two vectors a and b of length n.
double aom_normalized_cross_correlation(const double *a, const double *b,
int n);
// Synthesizes noise using the auto-regressive filter of the given lag,
// with the provided n coefficients sampled at the given coords.
void aom_noise_synth(int lag, int n, const int (*coords)[2],
const double *coeffs, double *data, int w, int h);
// Validates the correlated noise in the data buffer of size (w, h).
int aom_noise_data_validate(const double *data, int w, int h);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // AOM_DSP_NOISE_UTIL_H_
This diff is collapsed.
......@@ -225,6 +225,7 @@ if (CONFIG_AV1_ENCODER)
"${AOM_ROOT}/test/masked_sad_test.cc"
"${AOM_ROOT}/test/masked_variance_test.cc"
"${AOM_ROOT}/test/minmax_test.cc"
"${AOM_ROOT}/test/noise_model_test.cc"
"${AOM_ROOT}/test/subtract_test.cc"
"${AOM_ROOT}/test/sum_squares_test.cc"
"${AOM_ROOT}/test/variance_test.cc")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment