1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker * Copyright (c) 2017, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker *
4*77c1e3ccSAndroid Build Coastguard Worker * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker */
11*77c1e3ccSAndroid Build Coastguard Worker
12*77c1e3ccSAndroid Build Coastguard Worker #include <math.h>
13*77c1e3ccSAndroid Build Coastguard Worker #include <stdio.h>
14*77c1e3ccSAndroid Build Coastguard Worker #include <stdlib.h>
15*77c1e3ccSAndroid Build Coastguard Worker #include <string.h>
16*77c1e3ccSAndroid Build Coastguard Worker
17*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/aom_dsp_common.h"
18*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/mathutils.h"
19*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/noise_model.h"
20*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/noise_util.h"
21*77c1e3ccSAndroid Build Coastguard Worker #include "aom_mem/aom_mem.h"
22*77c1e3ccSAndroid Build Coastguard Worker #include "aom_ports/mem.h"
23*77c1e3ccSAndroid Build Coastguard Worker #include "aom_scale/yv12config.h"
24*77c1e3ccSAndroid Build Coastguard Worker
25*77c1e3ccSAndroid Build Coastguard Worker #define kLowPolyNumParams 3
26*77c1e3ccSAndroid Build Coastguard Worker
27*77c1e3ccSAndroid Build Coastguard Worker static const int kMaxLag = 4;
28*77c1e3ccSAndroid Build Coastguard Worker
29*77c1e3ccSAndroid Build Coastguard Worker // Defines a function that can be used to obtain the mean of a block for the
30*77c1e3ccSAndroid Build Coastguard Worker // provided data type (uint8_t, or uint16_t)
31*77c1e3ccSAndroid Build Coastguard Worker #define GET_BLOCK_MEAN(INT_TYPE, suffix) \
32*77c1e3ccSAndroid Build Coastguard Worker static double get_block_mean_##suffix(const INT_TYPE *data, int w, int h, \
33*77c1e3ccSAndroid Build Coastguard Worker int stride, int x_o, int y_o, \
34*77c1e3ccSAndroid Build Coastguard Worker int block_size) { \
35*77c1e3ccSAndroid Build Coastguard Worker const int max_h = AOMMIN(h - y_o, block_size); \
36*77c1e3ccSAndroid Build Coastguard Worker const int max_w = AOMMIN(w - x_o, block_size); \
37*77c1e3ccSAndroid Build Coastguard Worker double block_mean = 0; \
38*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < max_h; ++y) { \
39*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < max_w; ++x) { \
40*77c1e3ccSAndroid Build Coastguard Worker block_mean += data[(y_o + y) * stride + x_o + x]; \
41*77c1e3ccSAndroid Build Coastguard Worker } \
42*77c1e3ccSAndroid Build Coastguard Worker } \
43*77c1e3ccSAndroid Build Coastguard Worker return block_mean / (max_w * max_h); \
44*77c1e3ccSAndroid Build Coastguard Worker }
45*77c1e3ccSAndroid Build Coastguard Worker
GET_BLOCK_MEAN(uint8_t,lowbd)46*77c1e3ccSAndroid Build Coastguard Worker GET_BLOCK_MEAN(uint8_t, lowbd)
47*77c1e3ccSAndroid Build Coastguard Worker GET_BLOCK_MEAN(uint16_t, highbd)
48*77c1e3ccSAndroid Build Coastguard Worker
49*77c1e3ccSAndroid Build Coastguard Worker static inline double get_block_mean(const uint8_t *data, int w, int h,
50*77c1e3ccSAndroid Build Coastguard Worker int stride, int x_o, int y_o,
51*77c1e3ccSAndroid Build Coastguard Worker int block_size, int use_highbd) {
52*77c1e3ccSAndroid Build Coastguard Worker if (use_highbd)
53*77c1e3ccSAndroid Build Coastguard Worker return get_block_mean_highbd((const uint16_t *)data, w, h, stride, x_o, y_o,
54*77c1e3ccSAndroid Build Coastguard Worker block_size);
55*77c1e3ccSAndroid Build Coastguard Worker return get_block_mean_lowbd(data, w, h, stride, x_o, y_o, block_size);
56*77c1e3ccSAndroid Build Coastguard Worker }
57*77c1e3ccSAndroid Build Coastguard Worker
58*77c1e3ccSAndroid Build Coastguard Worker // Defines a function that can be used to obtain the variance of a block
59*77c1e3ccSAndroid Build Coastguard Worker // for the provided data type (uint8_t, or uint16_t)
60*77c1e3ccSAndroid Build Coastguard Worker #define GET_NOISE_VAR(INT_TYPE, suffix) \
61*77c1e3ccSAndroid Build Coastguard Worker static double get_noise_var_##suffix( \
62*77c1e3ccSAndroid Build Coastguard Worker const INT_TYPE *data, const INT_TYPE *denoised, int stride, int w, \
63*77c1e3ccSAndroid Build Coastguard Worker int h, int x_o, int y_o, int block_size_x, int block_size_y) { \
64*77c1e3ccSAndroid Build Coastguard Worker const int max_h = AOMMIN(h - y_o, block_size_y); \
65*77c1e3ccSAndroid Build Coastguard Worker const int max_w = AOMMIN(w - x_o, block_size_x); \
66*77c1e3ccSAndroid Build Coastguard Worker double noise_var = 0; \
67*77c1e3ccSAndroid Build Coastguard Worker double noise_mean = 0; \
68*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < max_h; ++y) { \
69*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < max_w; ++x) { \
70*77c1e3ccSAndroid Build Coastguard Worker double noise = (double)data[(y_o + y) * stride + x_o + x] - \
71*77c1e3ccSAndroid Build Coastguard Worker denoised[(y_o + y) * stride + x_o + x]; \
72*77c1e3ccSAndroid Build Coastguard Worker noise_mean += noise; \
73*77c1e3ccSAndroid Build Coastguard Worker noise_var += noise * noise; \
74*77c1e3ccSAndroid Build Coastguard Worker } \
75*77c1e3ccSAndroid Build Coastguard Worker } \
76*77c1e3ccSAndroid Build Coastguard Worker noise_mean /= (max_w * max_h); \
77*77c1e3ccSAndroid Build Coastguard Worker return noise_var / (max_w * max_h) - noise_mean * noise_mean; \
78*77c1e3ccSAndroid Build Coastguard Worker }
79*77c1e3ccSAndroid Build Coastguard Worker
GET_NOISE_VAR(uint8_t,lowbd)80*77c1e3ccSAndroid Build Coastguard Worker GET_NOISE_VAR(uint8_t, lowbd)
81*77c1e3ccSAndroid Build Coastguard Worker GET_NOISE_VAR(uint16_t, highbd)
82*77c1e3ccSAndroid Build Coastguard Worker
83*77c1e3ccSAndroid Build Coastguard Worker static inline double get_noise_var(const uint8_t *data, const uint8_t *denoised,
84*77c1e3ccSAndroid Build Coastguard Worker int w, int h, int stride, int x_o, int y_o,
85*77c1e3ccSAndroid Build Coastguard Worker int block_size_x, int block_size_y,
86*77c1e3ccSAndroid Build Coastguard Worker int use_highbd) {
87*77c1e3ccSAndroid Build Coastguard Worker if (use_highbd)
88*77c1e3ccSAndroid Build Coastguard Worker return get_noise_var_highbd((const uint16_t *)data,
89*77c1e3ccSAndroid Build Coastguard Worker (const uint16_t *)denoised, w, h, stride, x_o,
90*77c1e3ccSAndroid Build Coastguard Worker y_o, block_size_x, block_size_y);
91*77c1e3ccSAndroid Build Coastguard Worker return get_noise_var_lowbd(data, denoised, w, h, stride, x_o, y_o,
92*77c1e3ccSAndroid Build Coastguard Worker block_size_x, block_size_y);
93*77c1e3ccSAndroid Build Coastguard Worker }
94*77c1e3ccSAndroid Build Coastguard Worker
equation_system_clear(aom_equation_system_t * eqns)95*77c1e3ccSAndroid Build Coastguard Worker static void equation_system_clear(aom_equation_system_t *eqns) {
96*77c1e3ccSAndroid Build Coastguard Worker const int n = eqns->n;
97*77c1e3ccSAndroid Build Coastguard Worker memset(eqns->A, 0, sizeof(*eqns->A) * n * n);
98*77c1e3ccSAndroid Build Coastguard Worker memset(eqns->x, 0, sizeof(*eqns->x) * n);
99*77c1e3ccSAndroid Build Coastguard Worker memset(eqns->b, 0, sizeof(*eqns->b) * n);
100*77c1e3ccSAndroid Build Coastguard Worker }
101*77c1e3ccSAndroid Build Coastguard Worker
equation_system_copy(aom_equation_system_t * dst,const aom_equation_system_t * src)102*77c1e3ccSAndroid Build Coastguard Worker static void equation_system_copy(aom_equation_system_t *dst,
103*77c1e3ccSAndroid Build Coastguard Worker const aom_equation_system_t *src) {
104*77c1e3ccSAndroid Build Coastguard Worker const int n = dst->n;
105*77c1e3ccSAndroid Build Coastguard Worker memcpy(dst->A, src->A, sizeof(*dst->A) * n * n);
106*77c1e3ccSAndroid Build Coastguard Worker memcpy(dst->x, src->x, sizeof(*dst->x) * n);
107*77c1e3ccSAndroid Build Coastguard Worker memcpy(dst->b, src->b, sizeof(*dst->b) * n);
108*77c1e3ccSAndroid Build Coastguard Worker }
109*77c1e3ccSAndroid Build Coastguard Worker
equation_system_init(aom_equation_system_t * eqns,int n)110*77c1e3ccSAndroid Build Coastguard Worker static int equation_system_init(aom_equation_system_t *eqns, int n) {
111*77c1e3ccSAndroid Build Coastguard Worker eqns->A = (double *)aom_malloc(sizeof(*eqns->A) * n * n);
112*77c1e3ccSAndroid Build Coastguard Worker eqns->b = (double *)aom_malloc(sizeof(*eqns->b) * n);
113*77c1e3ccSAndroid Build Coastguard Worker eqns->x = (double *)aom_malloc(sizeof(*eqns->x) * n);
114*77c1e3ccSAndroid Build Coastguard Worker eqns->n = n;
115*77c1e3ccSAndroid Build Coastguard Worker if (!eqns->A || !eqns->b || !eqns->x) {
116*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to allocate system of equations of size %d\n", n);
117*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->A);
118*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->b);
119*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->x);
120*77c1e3ccSAndroid Build Coastguard Worker memset(eqns, 0, sizeof(*eqns));
121*77c1e3ccSAndroid Build Coastguard Worker return 0;
122*77c1e3ccSAndroid Build Coastguard Worker }
123*77c1e3ccSAndroid Build Coastguard Worker equation_system_clear(eqns);
124*77c1e3ccSAndroid Build Coastguard Worker return 1;
125*77c1e3ccSAndroid Build Coastguard Worker }
126*77c1e3ccSAndroid Build Coastguard Worker
equation_system_solve(aom_equation_system_t * eqns)127*77c1e3ccSAndroid Build Coastguard Worker static int equation_system_solve(aom_equation_system_t *eqns) {
128*77c1e3ccSAndroid Build Coastguard Worker const int n = eqns->n;
129*77c1e3ccSAndroid Build Coastguard Worker double *b = (double *)aom_malloc(sizeof(*b) * n);
130*77c1e3ccSAndroid Build Coastguard Worker double *A = (double *)aom_malloc(sizeof(*A) * n * n);
131*77c1e3ccSAndroid Build Coastguard Worker int ret = 0;
132*77c1e3ccSAndroid Build Coastguard Worker if (A == NULL || b == NULL) {
133*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate temp values of size %dx%d\n", n, n);
134*77c1e3ccSAndroid Build Coastguard Worker aom_free(b);
135*77c1e3ccSAndroid Build Coastguard Worker aom_free(A);
136*77c1e3ccSAndroid Build Coastguard Worker return 0;
137*77c1e3ccSAndroid Build Coastguard Worker }
138*77c1e3ccSAndroid Build Coastguard Worker memcpy(A, eqns->A, sizeof(*eqns->A) * n * n);
139*77c1e3ccSAndroid Build Coastguard Worker memcpy(b, eqns->b, sizeof(*eqns->b) * n);
140*77c1e3ccSAndroid Build Coastguard Worker ret = linsolve(n, A, eqns->n, b, eqns->x);
141*77c1e3ccSAndroid Build Coastguard Worker aom_free(b);
142*77c1e3ccSAndroid Build Coastguard Worker aom_free(A);
143*77c1e3ccSAndroid Build Coastguard Worker
144*77c1e3ccSAndroid Build Coastguard Worker if (ret == 0) {
145*77c1e3ccSAndroid Build Coastguard Worker return 0;
146*77c1e3ccSAndroid Build Coastguard Worker }
147*77c1e3ccSAndroid Build Coastguard Worker return 1;
148*77c1e3ccSAndroid Build Coastguard Worker }
149*77c1e3ccSAndroid Build Coastguard Worker
equation_system_add(aom_equation_system_t * dest,aom_equation_system_t * src)150*77c1e3ccSAndroid Build Coastguard Worker static void equation_system_add(aom_equation_system_t *dest,
151*77c1e3ccSAndroid Build Coastguard Worker aom_equation_system_t *src) {
152*77c1e3ccSAndroid Build Coastguard Worker const int n = dest->n;
153*77c1e3ccSAndroid Build Coastguard Worker int i, j;
154*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < n; ++i) {
155*77c1e3ccSAndroid Build Coastguard Worker for (j = 0; j < n; ++j) {
156*77c1e3ccSAndroid Build Coastguard Worker dest->A[i * n + j] += src->A[i * n + j];
157*77c1e3ccSAndroid Build Coastguard Worker }
158*77c1e3ccSAndroid Build Coastguard Worker dest->b[i] += src->b[i];
159*77c1e3ccSAndroid Build Coastguard Worker }
160*77c1e3ccSAndroid Build Coastguard Worker }
161*77c1e3ccSAndroid Build Coastguard Worker
equation_system_free(aom_equation_system_t * eqns)162*77c1e3ccSAndroid Build Coastguard Worker static void equation_system_free(aom_equation_system_t *eqns) {
163*77c1e3ccSAndroid Build Coastguard Worker if (!eqns) return;
164*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->A);
165*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->b);
166*77c1e3ccSAndroid Build Coastguard Worker aom_free(eqns->x);
167*77c1e3ccSAndroid Build Coastguard Worker memset(eqns, 0, sizeof(*eqns));
168*77c1e3ccSAndroid Build Coastguard Worker }
169*77c1e3ccSAndroid Build Coastguard Worker
noise_strength_solver_clear(aom_noise_strength_solver_t * solver)170*77c1e3ccSAndroid Build Coastguard Worker static void noise_strength_solver_clear(aom_noise_strength_solver_t *solver) {
171*77c1e3ccSAndroid Build Coastguard Worker equation_system_clear(&solver->eqns);
172*77c1e3ccSAndroid Build Coastguard Worker solver->num_equations = 0;
173*77c1e3ccSAndroid Build Coastguard Worker solver->total = 0;
174*77c1e3ccSAndroid Build Coastguard Worker }
175*77c1e3ccSAndroid Build Coastguard Worker
noise_strength_solver_add(aom_noise_strength_solver_t * dest,aom_noise_strength_solver_t * src)176*77c1e3ccSAndroid Build Coastguard Worker static void noise_strength_solver_add(aom_noise_strength_solver_t *dest,
177*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t *src) {
178*77c1e3ccSAndroid Build Coastguard Worker equation_system_add(&dest->eqns, &src->eqns);
179*77c1e3ccSAndroid Build Coastguard Worker dest->num_equations += src->num_equations;
180*77c1e3ccSAndroid Build Coastguard Worker dest->total += src->total;
181*77c1e3ccSAndroid Build Coastguard Worker }
182*77c1e3ccSAndroid Build Coastguard Worker
183*77c1e3ccSAndroid Build Coastguard Worker // Return the number of coefficients required for the given parameters
num_coeffs(const aom_noise_model_params_t params)184*77c1e3ccSAndroid Build Coastguard Worker static int num_coeffs(const aom_noise_model_params_t params) {
185*77c1e3ccSAndroid Build Coastguard Worker const int n = 2 * params.lag + 1;
186*77c1e3ccSAndroid Build Coastguard Worker switch (params.shape) {
187*77c1e3ccSAndroid Build Coastguard Worker case AOM_NOISE_SHAPE_DIAMOND: return params.lag * (params.lag + 1);
188*77c1e3ccSAndroid Build Coastguard Worker case AOM_NOISE_SHAPE_SQUARE: return (n * n) / 2;
189*77c1e3ccSAndroid Build Coastguard Worker }
190*77c1e3ccSAndroid Build Coastguard Worker return 0;
191*77c1e3ccSAndroid Build Coastguard Worker }
192*77c1e3ccSAndroid Build Coastguard Worker
noise_state_init(aom_noise_state_t * state,int n,int bit_depth)193*77c1e3ccSAndroid Build Coastguard Worker static int noise_state_init(aom_noise_state_t *state, int n, int bit_depth) {
194*77c1e3ccSAndroid Build Coastguard Worker const int kNumBins = 20;
195*77c1e3ccSAndroid Build Coastguard Worker if (!equation_system_init(&state->eqns, n)) {
196*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed initialization noise state with size %d\n", n);
197*77c1e3ccSAndroid Build Coastguard Worker return 0;
198*77c1e3ccSAndroid Build Coastguard Worker }
199*77c1e3ccSAndroid Build Coastguard Worker state->ar_gain = 1.0;
200*77c1e3ccSAndroid Build Coastguard Worker state->num_observations = 0;
201*77c1e3ccSAndroid Build Coastguard Worker return aom_noise_strength_solver_init(&state->strength_solver, kNumBins,
202*77c1e3ccSAndroid Build Coastguard Worker bit_depth);
203*77c1e3ccSAndroid Build Coastguard Worker }
204*77c1e3ccSAndroid Build Coastguard Worker
set_chroma_coefficient_fallback_soln(aom_equation_system_t * eqns)205*77c1e3ccSAndroid Build Coastguard Worker static void set_chroma_coefficient_fallback_soln(aom_equation_system_t *eqns) {
206*77c1e3ccSAndroid Build Coastguard Worker const double kTolerance = 1e-6;
207*77c1e3ccSAndroid Build Coastguard Worker const int last = eqns->n - 1;
208*77c1e3ccSAndroid Build Coastguard Worker // Set all of the AR coefficients to zero, but try to solve for correlation
209*77c1e3ccSAndroid Build Coastguard Worker // with the luma channel
210*77c1e3ccSAndroid Build Coastguard Worker memset(eqns->x, 0, sizeof(*eqns->x) * eqns->n);
211*77c1e3ccSAndroid Build Coastguard Worker if (fabs(eqns->A[last * eqns->n + last]) > kTolerance) {
212*77c1e3ccSAndroid Build Coastguard Worker eqns->x[last] = eqns->b[last] / eqns->A[last * eqns->n + last];
213*77c1e3ccSAndroid Build Coastguard Worker }
214*77c1e3ccSAndroid Build Coastguard Worker }
215*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_lut_init(aom_noise_strength_lut_t * lut,int num_points)216*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_strength_lut_init(aom_noise_strength_lut_t *lut, int num_points) {
217*77c1e3ccSAndroid Build Coastguard Worker if (!lut) return 0;
218*77c1e3ccSAndroid Build Coastguard Worker if (num_points <= 0) return 0;
219*77c1e3ccSAndroid Build Coastguard Worker lut->num_points = 0;
220*77c1e3ccSAndroid Build Coastguard Worker lut->points = (double(*)[2])aom_malloc(num_points * sizeof(*lut->points));
221*77c1e3ccSAndroid Build Coastguard Worker if (!lut->points) return 0;
222*77c1e3ccSAndroid Build Coastguard Worker lut->num_points = num_points;
223*77c1e3ccSAndroid Build Coastguard Worker memset(lut->points, 0, sizeof(*lut->points) * num_points);
224*77c1e3ccSAndroid Build Coastguard Worker return 1;
225*77c1e3ccSAndroid Build Coastguard Worker }
226*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_lut_free(aom_noise_strength_lut_t * lut)227*77c1e3ccSAndroid Build Coastguard Worker void aom_noise_strength_lut_free(aom_noise_strength_lut_t *lut) {
228*77c1e3ccSAndroid Build Coastguard Worker if (!lut) return;
229*77c1e3ccSAndroid Build Coastguard Worker aom_free(lut->points);
230*77c1e3ccSAndroid Build Coastguard Worker memset(lut, 0, sizeof(*lut));
231*77c1e3ccSAndroid Build Coastguard Worker }
232*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_lut_eval(const aom_noise_strength_lut_t * lut,double x)233*77c1e3ccSAndroid Build Coastguard Worker double aom_noise_strength_lut_eval(const aom_noise_strength_lut_t *lut,
234*77c1e3ccSAndroid Build Coastguard Worker double x) {
235*77c1e3ccSAndroid Build Coastguard Worker int i = 0;
236*77c1e3ccSAndroid Build Coastguard Worker // Constant extrapolation for x < x_0.
237*77c1e3ccSAndroid Build Coastguard Worker if (x < lut->points[0][0]) return lut->points[0][1];
238*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < lut->num_points - 1; ++i) {
239*77c1e3ccSAndroid Build Coastguard Worker if (x >= lut->points[i][0] && x <= lut->points[i + 1][0]) {
240*77c1e3ccSAndroid Build Coastguard Worker const double a =
241*77c1e3ccSAndroid Build Coastguard Worker (x - lut->points[i][0]) / (lut->points[i + 1][0] - lut->points[i][0]);
242*77c1e3ccSAndroid Build Coastguard Worker return lut->points[i + 1][1] * a + lut->points[i][1] * (1.0 - a);
243*77c1e3ccSAndroid Build Coastguard Worker }
244*77c1e3ccSAndroid Build Coastguard Worker }
245*77c1e3ccSAndroid Build Coastguard Worker // Constant extrapolation for x > x_{n-1}
246*77c1e3ccSAndroid Build Coastguard Worker return lut->points[lut->num_points - 1][1];
247*77c1e3ccSAndroid Build Coastguard Worker }
248*77c1e3ccSAndroid Build Coastguard Worker
noise_strength_solver_get_bin_index(const aom_noise_strength_solver_t * solver,double value)249*77c1e3ccSAndroid Build Coastguard Worker static double noise_strength_solver_get_bin_index(
250*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *solver, double value) {
251*77c1e3ccSAndroid Build Coastguard Worker const double val =
252*77c1e3ccSAndroid Build Coastguard Worker fclamp(value, solver->min_intensity, solver->max_intensity);
253*77c1e3ccSAndroid Build Coastguard Worker const double range = solver->max_intensity - solver->min_intensity;
254*77c1e3ccSAndroid Build Coastguard Worker return (solver->num_bins - 1) * (val - solver->min_intensity) / range;
255*77c1e3ccSAndroid Build Coastguard Worker }
256*77c1e3ccSAndroid Build Coastguard Worker
noise_strength_solver_get_value(const aom_noise_strength_solver_t * solver,double x)257*77c1e3ccSAndroid Build Coastguard Worker static double noise_strength_solver_get_value(
258*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *solver, double x) {
259*77c1e3ccSAndroid Build Coastguard Worker const double bin = noise_strength_solver_get_bin_index(solver, x);
260*77c1e3ccSAndroid Build Coastguard Worker const int bin_i0 = (int)floor(bin);
261*77c1e3ccSAndroid Build Coastguard Worker const int bin_i1 = AOMMIN(solver->num_bins - 1, bin_i0 + 1);
262*77c1e3ccSAndroid Build Coastguard Worker const double a = bin - bin_i0;
263*77c1e3ccSAndroid Build Coastguard Worker return (1.0 - a) * solver->eqns.x[bin_i0] + a * solver->eqns.x[bin_i1];
264*77c1e3ccSAndroid Build Coastguard Worker }
265*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_add_measurement(aom_noise_strength_solver_t * solver,double block_mean,double noise_std)266*77c1e3ccSAndroid Build Coastguard Worker void aom_noise_strength_solver_add_measurement(
267*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t *solver, double block_mean, double noise_std) {
268*77c1e3ccSAndroid Build Coastguard Worker const double bin = noise_strength_solver_get_bin_index(solver, block_mean);
269*77c1e3ccSAndroid Build Coastguard Worker const int bin_i0 = (int)floor(bin);
270*77c1e3ccSAndroid Build Coastguard Worker const int bin_i1 = AOMMIN(solver->num_bins - 1, bin_i0 + 1);
271*77c1e3ccSAndroid Build Coastguard Worker const double a = bin - bin_i0;
272*77c1e3ccSAndroid Build Coastguard Worker const int n = solver->num_bins;
273*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A[bin_i0 * n + bin_i0] += (1.0 - a) * (1.0 - a);
274*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A[bin_i1 * n + bin_i0] += a * (1.0 - a);
275*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A[bin_i1 * n + bin_i1] += a * a;
276*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A[bin_i0 * n + bin_i1] += a * (1.0 - a);
277*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.b[bin_i0] += (1.0 - a) * noise_std;
278*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.b[bin_i1] += a * noise_std;
279*77c1e3ccSAndroid Build Coastguard Worker solver->total += noise_std;
280*77c1e3ccSAndroid Build Coastguard Worker solver->num_equations++;
281*77c1e3ccSAndroid Build Coastguard Worker }
282*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_solve(aom_noise_strength_solver_t * solver)283*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_strength_solver_solve(aom_noise_strength_solver_t *solver) {
284*77c1e3ccSAndroid Build Coastguard Worker // Add regularization proportional to the number of constraints
285*77c1e3ccSAndroid Build Coastguard Worker const int n = solver->num_bins;
286*77c1e3ccSAndroid Build Coastguard Worker const double kAlpha = 2.0 * (double)(solver->num_equations) / n;
287*77c1e3ccSAndroid Build Coastguard Worker int result = 0;
288*77c1e3ccSAndroid Build Coastguard Worker double mean = 0;
289*77c1e3ccSAndroid Build Coastguard Worker
290*77c1e3ccSAndroid Build Coastguard Worker // Do this in a non-destructive manner so it is not confusing to the caller
291*77c1e3ccSAndroid Build Coastguard Worker double *old_A = solver->eqns.A;
292*77c1e3ccSAndroid Build Coastguard Worker double *A = (double *)aom_malloc(sizeof(*A) * n * n);
293*77c1e3ccSAndroid Build Coastguard Worker if (!A) {
294*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate copy of A\n");
295*77c1e3ccSAndroid Build Coastguard Worker return 0;
296*77c1e3ccSAndroid Build Coastguard Worker }
297*77c1e3ccSAndroid Build Coastguard Worker memcpy(A, old_A, sizeof(*A) * n * n);
298*77c1e3ccSAndroid Build Coastguard Worker
299*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
300*77c1e3ccSAndroid Build Coastguard Worker const int i_lo = AOMMAX(0, i - 1);
301*77c1e3ccSAndroid Build Coastguard Worker const int i_hi = AOMMIN(n - 1, i + 1);
302*77c1e3ccSAndroid Build Coastguard Worker A[i * n + i_lo] -= kAlpha;
303*77c1e3ccSAndroid Build Coastguard Worker A[i * n + i] += 2 * kAlpha;
304*77c1e3ccSAndroid Build Coastguard Worker A[i * n + i_hi] -= kAlpha;
305*77c1e3ccSAndroid Build Coastguard Worker }
306*77c1e3ccSAndroid Build Coastguard Worker
307*77c1e3ccSAndroid Build Coastguard Worker // Small regularization to give average noise strength
308*77c1e3ccSAndroid Build Coastguard Worker mean = solver->total / solver->num_equations;
309*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
310*77c1e3ccSAndroid Build Coastguard Worker A[i * n + i] += 1.0 / 8192.;
311*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.b[i] += mean / 8192.;
312*77c1e3ccSAndroid Build Coastguard Worker }
313*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A = A;
314*77c1e3ccSAndroid Build Coastguard Worker result = equation_system_solve(&solver->eqns);
315*77c1e3ccSAndroid Build Coastguard Worker solver->eqns.A = old_A;
316*77c1e3ccSAndroid Build Coastguard Worker
317*77c1e3ccSAndroid Build Coastguard Worker aom_free(A);
318*77c1e3ccSAndroid Build Coastguard Worker return result;
319*77c1e3ccSAndroid Build Coastguard Worker }
320*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_init(aom_noise_strength_solver_t * solver,int num_bins,int bit_depth)321*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_strength_solver_init(aom_noise_strength_solver_t *solver,
322*77c1e3ccSAndroid Build Coastguard Worker int num_bins, int bit_depth) {
323*77c1e3ccSAndroid Build Coastguard Worker if (!solver) return 0;
324*77c1e3ccSAndroid Build Coastguard Worker memset(solver, 0, sizeof(*solver));
325*77c1e3ccSAndroid Build Coastguard Worker solver->num_bins = num_bins;
326*77c1e3ccSAndroid Build Coastguard Worker solver->min_intensity = 0;
327*77c1e3ccSAndroid Build Coastguard Worker solver->max_intensity = (1 << bit_depth) - 1;
328*77c1e3ccSAndroid Build Coastguard Worker solver->total = 0;
329*77c1e3ccSAndroid Build Coastguard Worker solver->num_equations = 0;
330*77c1e3ccSAndroid Build Coastguard Worker return equation_system_init(&solver->eqns, num_bins);
331*77c1e3ccSAndroid Build Coastguard Worker }
332*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_free(aom_noise_strength_solver_t * solver)333*77c1e3ccSAndroid Build Coastguard Worker void aom_noise_strength_solver_free(aom_noise_strength_solver_t *solver) {
334*77c1e3ccSAndroid Build Coastguard Worker if (!solver) return;
335*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&solver->eqns);
336*77c1e3ccSAndroid Build Coastguard Worker }
337*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_get_center(const aom_noise_strength_solver_t * solver,int i)338*77c1e3ccSAndroid Build Coastguard Worker double aom_noise_strength_solver_get_center(
339*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *solver, int i) {
340*77c1e3ccSAndroid Build Coastguard Worker const double range = solver->max_intensity - solver->min_intensity;
341*77c1e3ccSAndroid Build Coastguard Worker const int n = solver->num_bins;
342*77c1e3ccSAndroid Build Coastguard Worker return ((double)i) / (n - 1) * range + solver->min_intensity;
343*77c1e3ccSAndroid Build Coastguard Worker }
344*77c1e3ccSAndroid Build Coastguard Worker
345*77c1e3ccSAndroid Build Coastguard Worker // Computes the residual if a point were to be removed from the lut. This is
346*77c1e3ccSAndroid Build Coastguard Worker // calculated as the area between the output of the solver and the line segment
347*77c1e3ccSAndroid Build Coastguard Worker // that would be formed between [x_{i - 1}, x_{i + 1}).
update_piecewise_linear_residual(const aom_noise_strength_solver_t * solver,const aom_noise_strength_lut_t * lut,double * residual,int start,int end)348*77c1e3ccSAndroid Build Coastguard Worker static void update_piecewise_linear_residual(
349*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *solver,
350*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_lut_t *lut, double *residual, int start, int end) {
351*77c1e3ccSAndroid Build Coastguard Worker const double dx = 255. / solver->num_bins;
352*77c1e3ccSAndroid Build Coastguard Worker for (int i = AOMMAX(start, 1); i < AOMMIN(end, lut->num_points - 1); ++i) {
353*77c1e3ccSAndroid Build Coastguard Worker const int lower = AOMMAX(0, (int)floor(noise_strength_solver_get_bin_index(
354*77c1e3ccSAndroid Build Coastguard Worker solver, lut->points[i - 1][0])));
355*77c1e3ccSAndroid Build Coastguard Worker const int upper = AOMMIN(solver->num_bins - 1,
356*77c1e3ccSAndroid Build Coastguard Worker (int)ceil(noise_strength_solver_get_bin_index(
357*77c1e3ccSAndroid Build Coastguard Worker solver, lut->points[i + 1][0])));
358*77c1e3ccSAndroid Build Coastguard Worker double r = 0;
359*77c1e3ccSAndroid Build Coastguard Worker for (int j = lower; j <= upper; ++j) {
360*77c1e3ccSAndroid Build Coastguard Worker const double x = aom_noise_strength_solver_get_center(solver, j);
361*77c1e3ccSAndroid Build Coastguard Worker if (x < lut->points[i - 1][0]) continue;
362*77c1e3ccSAndroid Build Coastguard Worker if (x >= lut->points[i + 1][0]) continue;
363*77c1e3ccSAndroid Build Coastguard Worker const double y = solver->eqns.x[j];
364*77c1e3ccSAndroid Build Coastguard Worker const double a = (x - lut->points[i - 1][0]) /
365*77c1e3ccSAndroid Build Coastguard Worker (lut->points[i + 1][0] - lut->points[i - 1][0]);
366*77c1e3ccSAndroid Build Coastguard Worker const double estimate_y =
367*77c1e3ccSAndroid Build Coastguard Worker lut->points[i - 1][1] * (1.0 - a) + lut->points[i + 1][1] * a;
368*77c1e3ccSAndroid Build Coastguard Worker r += fabs(y - estimate_y);
369*77c1e3ccSAndroid Build Coastguard Worker }
370*77c1e3ccSAndroid Build Coastguard Worker residual[i] = r * dx;
371*77c1e3ccSAndroid Build Coastguard Worker }
372*77c1e3ccSAndroid Build Coastguard Worker }
373*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_strength_solver_fit_piecewise(const aom_noise_strength_solver_t * solver,int max_output_points,aom_noise_strength_lut_t * lut)374*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_strength_solver_fit_piecewise(
375*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *solver, int max_output_points,
376*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t *lut) {
377*77c1e3ccSAndroid Build Coastguard Worker // The tolerance is normalized to be give consistent results between
378*77c1e3ccSAndroid Build Coastguard Worker // different bit-depths.
379*77c1e3ccSAndroid Build Coastguard Worker const double kTolerance = solver->max_intensity * 0.00625 / 255.0;
380*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_lut_init(lut, solver->num_bins)) {
381*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to init lut\n");
382*77c1e3ccSAndroid Build Coastguard Worker return 0;
383*77c1e3ccSAndroid Build Coastguard Worker }
384*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < solver->num_bins; ++i) {
385*77c1e3ccSAndroid Build Coastguard Worker lut->points[i][0] = aom_noise_strength_solver_get_center(solver, i);
386*77c1e3ccSAndroid Build Coastguard Worker lut->points[i][1] = solver->eqns.x[i];
387*77c1e3ccSAndroid Build Coastguard Worker }
388*77c1e3ccSAndroid Build Coastguard Worker if (max_output_points < 0) {
389*77c1e3ccSAndroid Build Coastguard Worker max_output_points = solver->num_bins;
390*77c1e3ccSAndroid Build Coastguard Worker }
391*77c1e3ccSAndroid Build Coastguard Worker
392*77c1e3ccSAndroid Build Coastguard Worker double *residual = (double *)aom_malloc(solver->num_bins * sizeof(*residual));
393*77c1e3ccSAndroid Build Coastguard Worker if (!residual) {
394*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(lut);
395*77c1e3ccSAndroid Build Coastguard Worker return 0;
396*77c1e3ccSAndroid Build Coastguard Worker }
397*77c1e3ccSAndroid Build Coastguard Worker memset(residual, 0, sizeof(*residual) * solver->num_bins);
398*77c1e3ccSAndroid Build Coastguard Worker
399*77c1e3ccSAndroid Build Coastguard Worker update_piecewise_linear_residual(solver, lut, residual, 0, solver->num_bins);
400*77c1e3ccSAndroid Build Coastguard Worker
401*77c1e3ccSAndroid Build Coastguard Worker // Greedily remove points if there are too many or if it doesn't hurt local
402*77c1e3ccSAndroid Build Coastguard Worker // approximation (never remove the end points)
403*77c1e3ccSAndroid Build Coastguard Worker while (lut->num_points > 2) {
404*77c1e3ccSAndroid Build Coastguard Worker int min_index = 1;
405*77c1e3ccSAndroid Build Coastguard Worker for (int j = 1; j < lut->num_points - 1; ++j) {
406*77c1e3ccSAndroid Build Coastguard Worker if (residual[j] < residual[min_index]) {
407*77c1e3ccSAndroid Build Coastguard Worker min_index = j;
408*77c1e3ccSAndroid Build Coastguard Worker }
409*77c1e3ccSAndroid Build Coastguard Worker }
410*77c1e3ccSAndroid Build Coastguard Worker const double dx =
411*77c1e3ccSAndroid Build Coastguard Worker lut->points[min_index + 1][0] - lut->points[min_index - 1][0];
412*77c1e3ccSAndroid Build Coastguard Worker const double avg_residual = residual[min_index] / dx;
413*77c1e3ccSAndroid Build Coastguard Worker if (lut->num_points <= max_output_points && avg_residual > kTolerance) {
414*77c1e3ccSAndroid Build Coastguard Worker break;
415*77c1e3ccSAndroid Build Coastguard Worker }
416*77c1e3ccSAndroid Build Coastguard Worker
417*77c1e3ccSAndroid Build Coastguard Worker const int num_remaining = lut->num_points - min_index - 1;
418*77c1e3ccSAndroid Build Coastguard Worker memmove(lut->points + min_index, lut->points + min_index + 1,
419*77c1e3ccSAndroid Build Coastguard Worker sizeof(lut->points[0]) * num_remaining);
420*77c1e3ccSAndroid Build Coastguard Worker lut->num_points--;
421*77c1e3ccSAndroid Build Coastguard Worker
422*77c1e3ccSAndroid Build Coastguard Worker update_piecewise_linear_residual(solver, lut, residual, min_index - 1,
423*77c1e3ccSAndroid Build Coastguard Worker min_index + 1);
424*77c1e3ccSAndroid Build Coastguard Worker }
425*77c1e3ccSAndroid Build Coastguard Worker aom_free(residual);
426*77c1e3ccSAndroid Build Coastguard Worker return 1;
427*77c1e3ccSAndroid Build Coastguard Worker }
428*77c1e3ccSAndroid Build Coastguard Worker
aom_flat_block_finder_init(aom_flat_block_finder_t * block_finder,int block_size,int bit_depth,int use_highbd)429*77c1e3ccSAndroid Build Coastguard Worker int aom_flat_block_finder_init(aom_flat_block_finder_t *block_finder,
430*77c1e3ccSAndroid Build Coastguard Worker int block_size, int bit_depth, int use_highbd) {
431*77c1e3ccSAndroid Build Coastguard Worker const int n = block_size * block_size;
432*77c1e3ccSAndroid Build Coastguard Worker aom_equation_system_t eqns;
433*77c1e3ccSAndroid Build Coastguard Worker double *AtA_inv = 0;
434*77c1e3ccSAndroid Build Coastguard Worker double *A = 0;
435*77c1e3ccSAndroid Build Coastguard Worker int x = 0, y = 0, i = 0, j = 0;
436*77c1e3ccSAndroid Build Coastguard Worker block_finder->A = NULL;
437*77c1e3ccSAndroid Build Coastguard Worker block_finder->AtA_inv = NULL;
438*77c1e3ccSAndroid Build Coastguard Worker
439*77c1e3ccSAndroid Build Coastguard Worker if (!equation_system_init(&eqns, kLowPolyNumParams)) {
440*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to init equation system for block_size=%d\n",
441*77c1e3ccSAndroid Build Coastguard Worker block_size);
442*77c1e3ccSAndroid Build Coastguard Worker return 0;
443*77c1e3ccSAndroid Build Coastguard Worker }
444*77c1e3ccSAndroid Build Coastguard Worker
445*77c1e3ccSAndroid Build Coastguard Worker AtA_inv = (double *)aom_malloc(kLowPolyNumParams * kLowPolyNumParams *
446*77c1e3ccSAndroid Build Coastguard Worker sizeof(*AtA_inv));
447*77c1e3ccSAndroid Build Coastguard Worker A = (double *)aom_malloc(kLowPolyNumParams * n * sizeof(*A));
448*77c1e3ccSAndroid Build Coastguard Worker if (AtA_inv == NULL || A == NULL) {
449*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to alloc A or AtA_inv for block_size=%d\n",
450*77c1e3ccSAndroid Build Coastguard Worker block_size);
451*77c1e3ccSAndroid Build Coastguard Worker aom_free(AtA_inv);
452*77c1e3ccSAndroid Build Coastguard Worker aom_free(A);
453*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&eqns);
454*77c1e3ccSAndroid Build Coastguard Worker return 0;
455*77c1e3ccSAndroid Build Coastguard Worker }
456*77c1e3ccSAndroid Build Coastguard Worker
457*77c1e3ccSAndroid Build Coastguard Worker block_finder->A = A;
458*77c1e3ccSAndroid Build Coastguard Worker block_finder->AtA_inv = AtA_inv;
459*77c1e3ccSAndroid Build Coastguard Worker block_finder->block_size = block_size;
460*77c1e3ccSAndroid Build Coastguard Worker block_finder->normalization = (1 << bit_depth) - 1;
461*77c1e3ccSAndroid Build Coastguard Worker block_finder->use_highbd = use_highbd;
462*77c1e3ccSAndroid Build Coastguard Worker
463*77c1e3ccSAndroid Build Coastguard Worker for (y = 0; y < block_size; ++y) {
464*77c1e3ccSAndroid Build Coastguard Worker const double yd = ((double)y - block_size / 2.) / (block_size / 2.);
465*77c1e3ccSAndroid Build Coastguard Worker for (x = 0; x < block_size; ++x) {
466*77c1e3ccSAndroid Build Coastguard Worker const double xd = ((double)x - block_size / 2.) / (block_size / 2.);
467*77c1e3ccSAndroid Build Coastguard Worker const double coords[3] = { yd, xd, 1 };
468*77c1e3ccSAndroid Build Coastguard Worker const int row = y * block_size + x;
469*77c1e3ccSAndroid Build Coastguard Worker A[kLowPolyNumParams * row + 0] = yd;
470*77c1e3ccSAndroid Build Coastguard Worker A[kLowPolyNumParams * row + 1] = xd;
471*77c1e3ccSAndroid Build Coastguard Worker A[kLowPolyNumParams * row + 2] = 1;
472*77c1e3ccSAndroid Build Coastguard Worker
473*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < kLowPolyNumParams; ++i) {
474*77c1e3ccSAndroid Build Coastguard Worker for (j = 0; j < kLowPolyNumParams; ++j) {
475*77c1e3ccSAndroid Build Coastguard Worker eqns.A[kLowPolyNumParams * i + j] += coords[i] * coords[j];
476*77c1e3ccSAndroid Build Coastguard Worker }
477*77c1e3ccSAndroid Build Coastguard Worker }
478*77c1e3ccSAndroid Build Coastguard Worker }
479*77c1e3ccSAndroid Build Coastguard Worker }
480*77c1e3ccSAndroid Build Coastguard Worker
481*77c1e3ccSAndroid Build Coastguard Worker // Lazy inverse using existing equation solver.
482*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < kLowPolyNumParams; ++i) {
483*77c1e3ccSAndroid Build Coastguard Worker memset(eqns.b, 0, sizeof(*eqns.b) * kLowPolyNumParams);
484*77c1e3ccSAndroid Build Coastguard Worker eqns.b[i] = 1;
485*77c1e3ccSAndroid Build Coastguard Worker equation_system_solve(&eqns);
486*77c1e3ccSAndroid Build Coastguard Worker
487*77c1e3ccSAndroid Build Coastguard Worker for (j = 0; j < kLowPolyNumParams; ++j) {
488*77c1e3ccSAndroid Build Coastguard Worker AtA_inv[j * kLowPolyNumParams + i] = eqns.x[j];
489*77c1e3ccSAndroid Build Coastguard Worker }
490*77c1e3ccSAndroid Build Coastguard Worker }
491*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&eqns);
492*77c1e3ccSAndroid Build Coastguard Worker return 1;
493*77c1e3ccSAndroid Build Coastguard Worker }
494*77c1e3ccSAndroid Build Coastguard Worker
aom_flat_block_finder_free(aom_flat_block_finder_t * block_finder)495*77c1e3ccSAndroid Build Coastguard Worker void aom_flat_block_finder_free(aom_flat_block_finder_t *block_finder) {
496*77c1e3ccSAndroid Build Coastguard Worker if (!block_finder) return;
497*77c1e3ccSAndroid Build Coastguard Worker aom_free(block_finder->A);
498*77c1e3ccSAndroid Build Coastguard Worker aom_free(block_finder->AtA_inv);
499*77c1e3ccSAndroid Build Coastguard Worker memset(block_finder, 0, sizeof(*block_finder));
500*77c1e3ccSAndroid Build Coastguard Worker }
501*77c1e3ccSAndroid Build Coastguard Worker
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)502*77c1e3ccSAndroid Build Coastguard Worker void aom_flat_block_finder_extract_block(
503*77c1e3ccSAndroid Build Coastguard Worker const aom_flat_block_finder_t *block_finder, const uint8_t *const data,
504*77c1e3ccSAndroid Build Coastguard Worker int w, int h, int stride, int offsx, int offsy, double *plane,
505*77c1e3ccSAndroid Build Coastguard Worker double *block) {
506*77c1e3ccSAndroid Build Coastguard Worker const int block_size = block_finder->block_size;
507*77c1e3ccSAndroid Build Coastguard Worker const int n = block_size * block_size;
508*77c1e3ccSAndroid Build Coastguard Worker const double *A = block_finder->A;
509*77c1e3ccSAndroid Build Coastguard Worker const double *AtA_inv = block_finder->AtA_inv;
510*77c1e3ccSAndroid Build Coastguard Worker double plane_coords[kLowPolyNumParams];
511*77c1e3ccSAndroid Build Coastguard Worker double AtA_inv_b[kLowPolyNumParams];
512*77c1e3ccSAndroid Build Coastguard Worker int xi, yi, i;
513*77c1e3ccSAndroid Build Coastguard Worker
514*77c1e3ccSAndroid Build Coastguard Worker if (block_finder->use_highbd) {
515*77c1e3ccSAndroid Build Coastguard Worker const uint16_t *const data16 = (const uint16_t *const)data;
516*77c1e3ccSAndroid Build Coastguard Worker for (yi = 0; yi < block_size; ++yi) {
517*77c1e3ccSAndroid Build Coastguard Worker const int y = clamp(offsy + yi, 0, h - 1);
518*77c1e3ccSAndroid Build Coastguard Worker for (xi = 0; xi < block_size; ++xi) {
519*77c1e3ccSAndroid Build Coastguard Worker const int x = clamp(offsx + xi, 0, w - 1);
520*77c1e3ccSAndroid Build Coastguard Worker block[yi * block_size + xi] =
521*77c1e3ccSAndroid Build Coastguard Worker ((double)data16[y * stride + x]) / block_finder->normalization;
522*77c1e3ccSAndroid Build Coastguard Worker }
523*77c1e3ccSAndroid Build Coastguard Worker }
524*77c1e3ccSAndroid Build Coastguard Worker } else {
525*77c1e3ccSAndroid Build Coastguard Worker for (yi = 0; yi < block_size; ++yi) {
526*77c1e3ccSAndroid Build Coastguard Worker const int y = clamp(offsy + yi, 0, h - 1);
527*77c1e3ccSAndroid Build Coastguard Worker for (xi = 0; xi < block_size; ++xi) {
528*77c1e3ccSAndroid Build Coastguard Worker const int x = clamp(offsx + xi, 0, w - 1);
529*77c1e3ccSAndroid Build Coastguard Worker block[yi * block_size + xi] =
530*77c1e3ccSAndroid Build Coastguard Worker ((double)data[y * stride + x]) / block_finder->normalization;
531*77c1e3ccSAndroid Build Coastguard Worker }
532*77c1e3ccSAndroid Build Coastguard Worker }
533*77c1e3ccSAndroid Build Coastguard Worker }
534*77c1e3ccSAndroid Build Coastguard Worker multiply_mat(block, A, AtA_inv_b, 1, n, kLowPolyNumParams);
535*77c1e3ccSAndroid Build Coastguard Worker multiply_mat(AtA_inv, AtA_inv_b, plane_coords, kLowPolyNumParams,
536*77c1e3ccSAndroid Build Coastguard Worker kLowPolyNumParams, 1);
537*77c1e3ccSAndroid Build Coastguard Worker multiply_mat(A, plane_coords, plane, n, kLowPolyNumParams, 1);
538*77c1e3ccSAndroid Build Coastguard Worker
539*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < n; ++i) {
540*77c1e3ccSAndroid Build Coastguard Worker block[i] -= plane[i];
541*77c1e3ccSAndroid Build Coastguard Worker }
542*77c1e3ccSAndroid Build Coastguard Worker }
543*77c1e3ccSAndroid Build Coastguard Worker
544*77c1e3ccSAndroid Build Coastguard Worker typedef struct {
545*77c1e3ccSAndroid Build Coastguard Worker int index;
546*77c1e3ccSAndroid Build Coastguard Worker float score;
547*77c1e3ccSAndroid Build Coastguard Worker } index_and_score_t;
548*77c1e3ccSAndroid Build Coastguard Worker
compare_scores(const void * a,const void * b)549*77c1e3ccSAndroid Build Coastguard Worker static int compare_scores(const void *a, const void *b) {
550*77c1e3ccSAndroid Build Coastguard Worker const float diff =
551*77c1e3ccSAndroid Build Coastguard Worker ((index_and_score_t *)a)->score - ((index_and_score_t *)b)->score;
552*77c1e3ccSAndroid Build Coastguard Worker if (diff < 0)
553*77c1e3ccSAndroid Build Coastguard Worker return -1;
554*77c1e3ccSAndroid Build Coastguard Worker else if (diff > 0)
555*77c1e3ccSAndroid Build Coastguard Worker return 1;
556*77c1e3ccSAndroid Build Coastguard Worker return 0;
557*77c1e3ccSAndroid Build Coastguard Worker }
558*77c1e3ccSAndroid Build Coastguard Worker
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)559*77c1e3ccSAndroid Build Coastguard Worker int aom_flat_block_finder_run(const aom_flat_block_finder_t *block_finder,
560*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data, int w, int h,
561*77c1e3ccSAndroid Build Coastguard Worker int stride, uint8_t *flat_blocks) {
562*77c1e3ccSAndroid Build Coastguard Worker // The gradient-based features used in this code are based on:
563*77c1e3ccSAndroid Build Coastguard Worker // A. Kokaram, D. Kelly, H. Denman and A. Crawford, "Measuring noise
564*77c1e3ccSAndroid Build Coastguard Worker // correlation for improved video denoising," 2012 19th, ICIP.
565*77c1e3ccSAndroid Build Coastguard Worker // The thresholds are more lenient to allow for correct grain modeling
566*77c1e3ccSAndroid Build Coastguard Worker // if extreme cases.
567*77c1e3ccSAndroid Build Coastguard Worker const int block_size = block_finder->block_size;
568*77c1e3ccSAndroid Build Coastguard Worker const int n = block_size * block_size;
569*77c1e3ccSAndroid Build Coastguard Worker const double kTraceThreshold = 0.15 / (32 * 32);
570*77c1e3ccSAndroid Build Coastguard Worker const double kRatioThreshold = 1.25;
571*77c1e3ccSAndroid Build Coastguard Worker const double kNormThreshold = 0.08 / (32 * 32);
572*77c1e3ccSAndroid Build Coastguard Worker const double kVarThreshold = 0.005 / (double)n;
573*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_w = (w + block_size - 1) / block_size;
574*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_h = (h + block_size - 1) / block_size;
575*77c1e3ccSAndroid Build Coastguard Worker int num_flat = 0;
576*77c1e3ccSAndroid Build Coastguard Worker double *plane = (double *)aom_malloc(n * sizeof(*plane));
577*77c1e3ccSAndroid Build Coastguard Worker double *block = (double *)aom_malloc(n * sizeof(*block));
578*77c1e3ccSAndroid Build Coastguard Worker index_and_score_t *scores = (index_and_score_t *)aom_malloc(
579*77c1e3ccSAndroid Build Coastguard Worker num_blocks_w * num_blocks_h * sizeof(*scores));
580*77c1e3ccSAndroid Build Coastguard Worker if (plane == NULL || block == NULL || scores == NULL) {
581*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to allocate memory for block of size %d\n", n);
582*77c1e3ccSAndroid Build Coastguard Worker aom_free(plane);
583*77c1e3ccSAndroid Build Coastguard Worker aom_free(block);
584*77c1e3ccSAndroid Build Coastguard Worker aom_free(scores);
585*77c1e3ccSAndroid Build Coastguard Worker return -1;
586*77c1e3ccSAndroid Build Coastguard Worker }
587*77c1e3ccSAndroid Build Coastguard Worker
588*77c1e3ccSAndroid Build Coastguard Worker #ifdef NOISE_MODEL_LOG_SCORE
589*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "score = [");
590*77c1e3ccSAndroid Build Coastguard Worker #endif
591*77c1e3ccSAndroid Build Coastguard Worker for (int by = 0; by < num_blocks_h; ++by) {
592*77c1e3ccSAndroid Build Coastguard Worker for (int bx = 0; bx < num_blocks_w; ++bx) {
593*77c1e3ccSAndroid Build Coastguard Worker // Compute gradient covariance matrix.
594*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_extract_block(block_finder, data, w, h, stride,
595*77c1e3ccSAndroid Build Coastguard Worker bx * block_size, by * block_size,
596*77c1e3ccSAndroid Build Coastguard Worker plane, block);
597*77c1e3ccSAndroid Build Coastguard Worker double Gxx = 0, Gxy = 0, Gyy = 0;
598*77c1e3ccSAndroid Build Coastguard Worker double mean = 0;
599*77c1e3ccSAndroid Build Coastguard Worker double var = 0;
600*77c1e3ccSAndroid Build Coastguard Worker
601*77c1e3ccSAndroid Build Coastguard Worker for (int yi = 1; yi < block_size - 1; ++yi) {
602*77c1e3ccSAndroid Build Coastguard Worker for (int xi = 1; xi < block_size - 1; ++xi) {
603*77c1e3ccSAndroid Build Coastguard Worker const double gx = (block[yi * block_size + xi + 1] -
604*77c1e3ccSAndroid Build Coastguard Worker block[yi * block_size + xi - 1]) /
605*77c1e3ccSAndroid Build Coastguard Worker 2;
606*77c1e3ccSAndroid Build Coastguard Worker const double gy = (block[yi * block_size + xi + block_size] -
607*77c1e3ccSAndroid Build Coastguard Worker block[yi * block_size + xi - block_size]) /
608*77c1e3ccSAndroid Build Coastguard Worker 2;
609*77c1e3ccSAndroid Build Coastguard Worker Gxx += gx * gx;
610*77c1e3ccSAndroid Build Coastguard Worker Gxy += gx * gy;
611*77c1e3ccSAndroid Build Coastguard Worker Gyy += gy * gy;
612*77c1e3ccSAndroid Build Coastguard Worker
613*77c1e3ccSAndroid Build Coastguard Worker const double value = block[yi * block_size + xi];
614*77c1e3ccSAndroid Build Coastguard Worker mean += value;
615*77c1e3ccSAndroid Build Coastguard Worker var += value * value;
616*77c1e3ccSAndroid Build Coastguard Worker }
617*77c1e3ccSAndroid Build Coastguard Worker }
618*77c1e3ccSAndroid Build Coastguard Worker mean /= (block_size - 2) * (block_size - 2);
619*77c1e3ccSAndroid Build Coastguard Worker
620*77c1e3ccSAndroid Build Coastguard Worker // Normalize gradients by block_size.
621*77c1e3ccSAndroid Build Coastguard Worker Gxx /= ((block_size - 2) * (block_size - 2));
622*77c1e3ccSAndroid Build Coastguard Worker Gxy /= ((block_size - 2) * (block_size - 2));
623*77c1e3ccSAndroid Build Coastguard Worker Gyy /= ((block_size - 2) * (block_size - 2));
624*77c1e3ccSAndroid Build Coastguard Worker var = var / ((block_size - 2) * (block_size - 2)) - mean * mean;
625*77c1e3ccSAndroid Build Coastguard Worker
626*77c1e3ccSAndroid Build Coastguard Worker {
627*77c1e3ccSAndroid Build Coastguard Worker const double trace = Gxx + Gyy;
628*77c1e3ccSAndroid Build Coastguard Worker const double det = Gxx * Gyy - Gxy * Gxy;
629*77c1e3ccSAndroid Build Coastguard Worker const double e1 = (trace + sqrt(trace * trace - 4 * det)) / 2.;
630*77c1e3ccSAndroid Build Coastguard Worker const double e2 = (trace - sqrt(trace * trace - 4 * det)) / 2.;
631*77c1e3ccSAndroid Build Coastguard Worker const double norm = e1; // Spectral norm
632*77c1e3ccSAndroid Build Coastguard Worker const double ratio = (e1 / AOMMAX(e2, 1e-6));
633*77c1e3ccSAndroid Build Coastguard Worker const int is_flat = (trace < kTraceThreshold) &&
634*77c1e3ccSAndroid Build Coastguard Worker (ratio < kRatioThreshold) &&
635*77c1e3ccSAndroid Build Coastguard Worker (norm < kNormThreshold) && (var > kVarThreshold);
636*77c1e3ccSAndroid Build Coastguard Worker // The following weights are used to combine the above features to give
637*77c1e3ccSAndroid Build Coastguard Worker // a sigmoid score for flatness. If the input was normalized to [0,100]
638*77c1e3ccSAndroid Build Coastguard Worker // the magnitude of these values would be close to 1 (e.g., weights
639*77c1e3ccSAndroid Build Coastguard Worker // corresponding to variance would be a factor of 10000x smaller).
640*77c1e3ccSAndroid Build Coastguard Worker // The weights are given in the following order:
641*77c1e3ccSAndroid Build Coastguard Worker // [{var}, {ratio}, {trace}, {norm}, offset]
642*77c1e3ccSAndroid Build Coastguard Worker // with one of the most discriminative being simply the variance.
643*77c1e3ccSAndroid Build Coastguard Worker const double weights[5] = { -6682, -0.2056, 13087, -12434, 2.5694 };
644*77c1e3ccSAndroid Build Coastguard Worker double sum_weights = weights[0] * var + weights[1] * ratio +
645*77c1e3ccSAndroid Build Coastguard Worker weights[2] * trace + weights[3] * norm +
646*77c1e3ccSAndroid Build Coastguard Worker weights[4];
647*77c1e3ccSAndroid Build Coastguard Worker // clamp the value to [-25.0, 100.0] to prevent overflow
648*77c1e3ccSAndroid Build Coastguard Worker sum_weights = fclamp(sum_weights, -25.0, 100.0);
649*77c1e3ccSAndroid Build Coastguard Worker const float score = (float)(1.0 / (1 + exp(-sum_weights)));
650*77c1e3ccSAndroid Build Coastguard Worker flat_blocks[by * num_blocks_w + bx] = is_flat ? 255 : 0;
651*77c1e3ccSAndroid Build Coastguard Worker scores[by * num_blocks_w + bx].score = var > kVarThreshold ? score : 0;
652*77c1e3ccSAndroid Build Coastguard Worker scores[by * num_blocks_w + bx].index = by * num_blocks_w + bx;
653*77c1e3ccSAndroid Build Coastguard Worker #ifdef NOISE_MODEL_LOG_SCORE
654*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "%g %g %g %g %g %d ", score, var, ratio, trace, norm,
655*77c1e3ccSAndroid Build Coastguard Worker is_flat);
656*77c1e3ccSAndroid Build Coastguard Worker #endif
657*77c1e3ccSAndroid Build Coastguard Worker num_flat += is_flat;
658*77c1e3ccSAndroid Build Coastguard Worker }
659*77c1e3ccSAndroid Build Coastguard Worker }
660*77c1e3ccSAndroid Build Coastguard Worker #ifdef NOISE_MODEL_LOG_SCORE
661*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "\n");
662*77c1e3ccSAndroid Build Coastguard Worker #endif
663*77c1e3ccSAndroid Build Coastguard Worker }
664*77c1e3ccSAndroid Build Coastguard Worker #ifdef NOISE_MODEL_LOG_SCORE
665*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "];\n");
666*77c1e3ccSAndroid Build Coastguard Worker #endif
667*77c1e3ccSAndroid Build Coastguard Worker // Find the top-scored blocks (most likely to be flat) and set the flat blocks
668*77c1e3ccSAndroid Build Coastguard Worker // be the union of the thresholded results and the top 10th percentile of the
669*77c1e3ccSAndroid Build Coastguard Worker // scored results.
670*77c1e3ccSAndroid Build Coastguard Worker qsort(scores, num_blocks_w * num_blocks_h, sizeof(*scores), &compare_scores);
671*77c1e3ccSAndroid Build Coastguard Worker const int top_nth_percentile = num_blocks_w * num_blocks_h * 90 / 100;
672*77c1e3ccSAndroid Build Coastguard Worker const float score_threshold = scores[top_nth_percentile].score;
673*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_blocks_w * num_blocks_h; ++i) {
674*77c1e3ccSAndroid Build Coastguard Worker if (scores[i].score >= score_threshold) {
675*77c1e3ccSAndroid Build Coastguard Worker num_flat += flat_blocks[scores[i].index] == 0;
676*77c1e3ccSAndroid Build Coastguard Worker flat_blocks[scores[i].index] |= 1;
677*77c1e3ccSAndroid Build Coastguard Worker }
678*77c1e3ccSAndroid Build Coastguard Worker }
679*77c1e3ccSAndroid Build Coastguard Worker aom_free(block);
680*77c1e3ccSAndroid Build Coastguard Worker aom_free(plane);
681*77c1e3ccSAndroid Build Coastguard Worker aom_free(scores);
682*77c1e3ccSAndroid Build Coastguard Worker return num_flat;
683*77c1e3ccSAndroid Build Coastguard Worker }
684*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_model_init(aom_noise_model_t * model,const aom_noise_model_params_t params)685*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_model_init(aom_noise_model_t *model,
686*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_model_params_t params) {
687*77c1e3ccSAndroid Build Coastguard Worker const int n = num_coeffs(params);
688*77c1e3ccSAndroid Build Coastguard Worker const int lag = params.lag;
689*77c1e3ccSAndroid Build Coastguard Worker const int bit_depth = params.bit_depth;
690*77c1e3ccSAndroid Build Coastguard Worker int x = 0, y = 0, i = 0, c = 0;
691*77c1e3ccSAndroid Build Coastguard Worker
692*77c1e3ccSAndroid Build Coastguard Worker memset(model, 0, sizeof(*model));
693*77c1e3ccSAndroid Build Coastguard Worker if (params.lag < 1) {
694*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Invalid noise param: lag = %d must be >= 1\n", params.lag);
695*77c1e3ccSAndroid Build Coastguard Worker return 0;
696*77c1e3ccSAndroid Build Coastguard Worker }
697*77c1e3ccSAndroid Build Coastguard Worker if (params.lag > kMaxLag) {
698*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Invalid noise param: lag = %d must be <= %d\n", params.lag,
699*77c1e3ccSAndroid Build Coastguard Worker kMaxLag);
700*77c1e3ccSAndroid Build Coastguard Worker return 0;
701*77c1e3ccSAndroid Build Coastguard Worker }
702*77c1e3ccSAndroid Build Coastguard Worker if (!(params.bit_depth == 8 || params.bit_depth == 10 ||
703*77c1e3ccSAndroid Build Coastguard Worker params.bit_depth == 12)) {
704*77c1e3ccSAndroid Build Coastguard Worker return 0;
705*77c1e3ccSAndroid Build Coastguard Worker }
706*77c1e3ccSAndroid Build Coastguard Worker
707*77c1e3ccSAndroid Build Coastguard Worker memcpy(&model->params, ¶ms, sizeof(params));
708*77c1e3ccSAndroid Build Coastguard Worker for (c = 0; c < 3; ++c) {
709*77c1e3ccSAndroid Build Coastguard Worker if (!noise_state_init(&model->combined_state[c], n + (c > 0), bit_depth)) {
710*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to allocate noise state for channel %d\n", c);
711*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(model);
712*77c1e3ccSAndroid Build Coastguard Worker return 0;
713*77c1e3ccSAndroid Build Coastguard Worker }
714*77c1e3ccSAndroid Build Coastguard Worker if (!noise_state_init(&model->latest_state[c], n + (c > 0), bit_depth)) {
715*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Failed to allocate noise state for channel %d\n", c);
716*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(model);
717*77c1e3ccSAndroid Build Coastguard Worker return 0;
718*77c1e3ccSAndroid Build Coastguard Worker }
719*77c1e3ccSAndroid Build Coastguard Worker }
720*77c1e3ccSAndroid Build Coastguard Worker model->n = n;
721*77c1e3ccSAndroid Build Coastguard Worker model->coords = (int(*)[2])aom_malloc(sizeof(*model->coords) * n);
722*77c1e3ccSAndroid Build Coastguard Worker if (!model->coords) {
723*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(model);
724*77c1e3ccSAndroid Build Coastguard Worker return 0;
725*77c1e3ccSAndroid Build Coastguard Worker }
726*77c1e3ccSAndroid Build Coastguard Worker
727*77c1e3ccSAndroid Build Coastguard Worker for (y = -lag; y <= 0; ++y) {
728*77c1e3ccSAndroid Build Coastguard Worker const int max_x = y == 0 ? -1 : lag;
729*77c1e3ccSAndroid Build Coastguard Worker for (x = -lag; x <= max_x; ++x) {
730*77c1e3ccSAndroid Build Coastguard Worker switch (params.shape) {
731*77c1e3ccSAndroid Build Coastguard Worker case AOM_NOISE_SHAPE_DIAMOND:
732*77c1e3ccSAndroid Build Coastguard Worker if (abs(x) <= y + lag) {
733*77c1e3ccSAndroid Build Coastguard Worker model->coords[i][0] = x;
734*77c1e3ccSAndroid Build Coastguard Worker model->coords[i][1] = y;
735*77c1e3ccSAndroid Build Coastguard Worker ++i;
736*77c1e3ccSAndroid Build Coastguard Worker }
737*77c1e3ccSAndroid Build Coastguard Worker break;
738*77c1e3ccSAndroid Build Coastguard Worker case AOM_NOISE_SHAPE_SQUARE:
739*77c1e3ccSAndroid Build Coastguard Worker model->coords[i][0] = x;
740*77c1e3ccSAndroid Build Coastguard Worker model->coords[i][1] = y;
741*77c1e3ccSAndroid Build Coastguard Worker ++i;
742*77c1e3ccSAndroid Build Coastguard Worker break;
743*77c1e3ccSAndroid Build Coastguard Worker default:
744*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Invalid shape\n");
745*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(model);
746*77c1e3ccSAndroid Build Coastguard Worker return 0;
747*77c1e3ccSAndroid Build Coastguard Worker }
748*77c1e3ccSAndroid Build Coastguard Worker }
749*77c1e3ccSAndroid Build Coastguard Worker }
750*77c1e3ccSAndroid Build Coastguard Worker assert(i == n);
751*77c1e3ccSAndroid Build Coastguard Worker return 1;
752*77c1e3ccSAndroid Build Coastguard Worker }
753*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_model_free(aom_noise_model_t * model)754*77c1e3ccSAndroid Build Coastguard Worker void aom_noise_model_free(aom_noise_model_t *model) {
755*77c1e3ccSAndroid Build Coastguard Worker int c = 0;
756*77c1e3ccSAndroid Build Coastguard Worker if (!model) return;
757*77c1e3ccSAndroid Build Coastguard Worker
758*77c1e3ccSAndroid Build Coastguard Worker aom_free(model->coords);
759*77c1e3ccSAndroid Build Coastguard Worker for (c = 0; c < 3; ++c) {
760*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&model->latest_state[c].eqns);
761*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&model->combined_state[c].eqns);
762*77c1e3ccSAndroid Build Coastguard Worker
763*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&model->latest_state[c].strength_solver.eqns);
764*77c1e3ccSAndroid Build Coastguard Worker equation_system_free(&model->combined_state[c].strength_solver.eqns);
765*77c1e3ccSAndroid Build Coastguard Worker }
766*77c1e3ccSAndroid Build Coastguard Worker memset(model, 0, sizeof(*model));
767*77c1e3ccSAndroid Build Coastguard Worker }
768*77c1e3ccSAndroid Build Coastguard Worker
769*77c1e3ccSAndroid Build Coastguard Worker // Extracts the neighborhood defined by coords around point (x, y) from
770*77c1e3ccSAndroid Build Coastguard Worker // the difference between the data and denoised images. Also extracts the
771*77c1e3ccSAndroid Build Coastguard Worker // entry (possibly downsampled) for (x, y) in the alt_data (e.g., luma).
772*77c1e3ccSAndroid Build Coastguard Worker #define EXTRACT_AR_ROW(INT_TYPE, suffix) \
773*77c1e3ccSAndroid Build Coastguard Worker static double extract_ar_row_##suffix( \
774*77c1e3ccSAndroid Build Coastguard Worker int(*coords)[2], int num_coords, const INT_TYPE *const data, \
775*77c1e3ccSAndroid Build Coastguard Worker const INT_TYPE *const denoised, int stride, int sub_log2[2], \
776*77c1e3ccSAndroid Build Coastguard Worker const INT_TYPE *const alt_data, const INT_TYPE *const alt_denoised, \
777*77c1e3ccSAndroid Build Coastguard Worker int alt_stride, int x, int y, double *buffer) { \
778*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_coords; ++i) { \
779*77c1e3ccSAndroid Build Coastguard Worker const int x_i = x + coords[i][0], y_i = y + coords[i][1]; \
780*77c1e3ccSAndroid Build Coastguard Worker buffer[i] = \
781*77c1e3ccSAndroid Build Coastguard Worker (double)data[y_i * stride + x_i] - denoised[y_i * stride + x_i]; \
782*77c1e3ccSAndroid Build Coastguard Worker } \
783*77c1e3ccSAndroid Build Coastguard Worker const double val = \
784*77c1e3ccSAndroid Build Coastguard Worker (double)data[y * stride + x] - denoised[y * stride + x]; \
785*77c1e3ccSAndroid Build Coastguard Worker \
786*77c1e3ccSAndroid Build Coastguard Worker if (alt_data && alt_denoised) { \
787*77c1e3ccSAndroid Build Coastguard Worker double avg_data = 0, avg_denoised = 0; \
788*77c1e3ccSAndroid Build Coastguard Worker int num_samples = 0; \
789*77c1e3ccSAndroid Build Coastguard Worker for (int dy_i = 0; dy_i < (1 << sub_log2[1]); dy_i++) { \
790*77c1e3ccSAndroid Build Coastguard Worker const int y_up = (y << sub_log2[1]) + dy_i; \
791*77c1e3ccSAndroid Build Coastguard Worker for (int dx_i = 0; dx_i < (1 << sub_log2[0]); dx_i++) { \
792*77c1e3ccSAndroid Build Coastguard Worker const int x_up = (x << sub_log2[0]) + dx_i; \
793*77c1e3ccSAndroid Build Coastguard Worker avg_data += alt_data[y_up * alt_stride + x_up]; \
794*77c1e3ccSAndroid Build Coastguard Worker avg_denoised += alt_denoised[y_up * alt_stride + x_up]; \
795*77c1e3ccSAndroid Build Coastguard Worker num_samples++; \
796*77c1e3ccSAndroid Build Coastguard Worker } \
797*77c1e3ccSAndroid Build Coastguard Worker } \
798*77c1e3ccSAndroid Build Coastguard Worker buffer[num_coords] = (avg_data - avg_denoised) / num_samples; \
799*77c1e3ccSAndroid Build Coastguard Worker } \
800*77c1e3ccSAndroid Build Coastguard Worker return val; \
801*77c1e3ccSAndroid Build Coastguard Worker }
802*77c1e3ccSAndroid Build Coastguard Worker
EXTRACT_AR_ROW(uint8_t,lowbd)803*77c1e3ccSAndroid Build Coastguard Worker EXTRACT_AR_ROW(uint8_t, lowbd)
804*77c1e3ccSAndroid Build Coastguard Worker EXTRACT_AR_ROW(uint16_t, highbd)
805*77c1e3ccSAndroid Build Coastguard Worker
806*77c1e3ccSAndroid Build Coastguard Worker static int add_block_observations(
807*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t *noise_model, int c, const uint8_t *const data,
808*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const denoised, int w, int h, int stride, int sub_log2[2],
809*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const alt_data, const uint8_t *const alt_denoised,
810*77c1e3ccSAndroid Build Coastguard Worker int alt_stride, const uint8_t *const flat_blocks, int block_size,
811*77c1e3ccSAndroid Build Coastguard Worker int num_blocks_w, int num_blocks_h) {
812*77c1e3ccSAndroid Build Coastguard Worker const int lag = noise_model->params.lag;
813*77c1e3ccSAndroid Build Coastguard Worker const int num_coords = noise_model->n;
814*77c1e3ccSAndroid Build Coastguard Worker const double normalization = (1 << noise_model->params.bit_depth) - 1;
815*77c1e3ccSAndroid Build Coastguard Worker double *A = noise_model->latest_state[c].eqns.A;
816*77c1e3ccSAndroid Build Coastguard Worker double *b = noise_model->latest_state[c].eqns.b;
817*77c1e3ccSAndroid Build Coastguard Worker double *buffer = (double *)aom_malloc(sizeof(*buffer) * (num_coords + 1));
818*77c1e3ccSAndroid Build Coastguard Worker const int n = noise_model->latest_state[c].eqns.n;
819*77c1e3ccSAndroid Build Coastguard Worker
820*77c1e3ccSAndroid Build Coastguard Worker if (!buffer) {
821*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate buffer of size %d\n", num_coords + 1);
822*77c1e3ccSAndroid Build Coastguard Worker return 0;
823*77c1e3ccSAndroid Build Coastguard Worker }
824*77c1e3ccSAndroid Build Coastguard Worker for (int by = 0; by < num_blocks_h; ++by) {
825*77c1e3ccSAndroid Build Coastguard Worker const int y_o = by * (block_size >> sub_log2[1]);
826*77c1e3ccSAndroid Build Coastguard Worker for (int bx = 0; bx < num_blocks_w; ++bx) {
827*77c1e3ccSAndroid Build Coastguard Worker const int x_o = bx * (block_size >> sub_log2[0]);
828*77c1e3ccSAndroid Build Coastguard Worker if (!flat_blocks[by * num_blocks_w + bx]) {
829*77c1e3ccSAndroid Build Coastguard Worker continue;
830*77c1e3ccSAndroid Build Coastguard Worker }
831*77c1e3ccSAndroid Build Coastguard Worker int y_start =
832*77c1e3ccSAndroid Build Coastguard Worker (by > 0 && flat_blocks[(by - 1) * num_blocks_w + bx]) ? 0 : lag;
833*77c1e3ccSAndroid Build Coastguard Worker int x_start =
834*77c1e3ccSAndroid Build Coastguard Worker (bx > 0 && flat_blocks[by * num_blocks_w + bx - 1]) ? 0 : lag;
835*77c1e3ccSAndroid Build Coastguard Worker int y_end = AOMMIN((h >> sub_log2[1]) - by * (block_size >> sub_log2[1]),
836*77c1e3ccSAndroid Build Coastguard Worker block_size >> sub_log2[1]);
837*77c1e3ccSAndroid Build Coastguard Worker int x_end = AOMMIN(
838*77c1e3ccSAndroid Build Coastguard Worker (w >> sub_log2[0]) - bx * (block_size >> sub_log2[0]) - lag,
839*77c1e3ccSAndroid Build Coastguard Worker (bx + 1 < num_blocks_w && flat_blocks[by * num_blocks_w + bx + 1])
840*77c1e3ccSAndroid Build Coastguard Worker ? (block_size >> sub_log2[0])
841*77c1e3ccSAndroid Build Coastguard Worker : ((block_size >> sub_log2[0]) - lag));
842*77c1e3ccSAndroid Build Coastguard Worker for (int y = y_start; y < y_end; ++y) {
843*77c1e3ccSAndroid Build Coastguard Worker for (int x = x_start; x < x_end; ++x) {
844*77c1e3ccSAndroid Build Coastguard Worker const double val =
845*77c1e3ccSAndroid Build Coastguard Worker noise_model->params.use_highbd
846*77c1e3ccSAndroid Build Coastguard Worker ? extract_ar_row_highbd(noise_model->coords, num_coords,
847*77c1e3ccSAndroid Build Coastguard Worker (const uint16_t *const)data,
848*77c1e3ccSAndroid Build Coastguard Worker (const uint16_t *const)denoised,
849*77c1e3ccSAndroid Build Coastguard Worker stride, sub_log2,
850*77c1e3ccSAndroid Build Coastguard Worker (const uint16_t *const)alt_data,
851*77c1e3ccSAndroid Build Coastguard Worker (const uint16_t *const)alt_denoised,
852*77c1e3ccSAndroid Build Coastguard Worker alt_stride, x + x_o, y + y_o, buffer)
853*77c1e3ccSAndroid Build Coastguard Worker : extract_ar_row_lowbd(noise_model->coords, num_coords, data,
854*77c1e3ccSAndroid Build Coastguard Worker denoised, stride, sub_log2, alt_data,
855*77c1e3ccSAndroid Build Coastguard Worker alt_denoised, alt_stride, x + x_o,
856*77c1e3ccSAndroid Build Coastguard Worker y + y_o, buffer);
857*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
858*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < n; ++j) {
859*77c1e3ccSAndroid Build Coastguard Worker A[i * n + j] +=
860*77c1e3ccSAndroid Build Coastguard Worker (buffer[i] * buffer[j]) / (normalization * normalization);
861*77c1e3ccSAndroid Build Coastguard Worker }
862*77c1e3ccSAndroid Build Coastguard Worker b[i] += (buffer[i] * val) / (normalization * normalization);
863*77c1e3ccSAndroid Build Coastguard Worker }
864*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[c].num_observations++;
865*77c1e3ccSAndroid Build Coastguard Worker }
866*77c1e3ccSAndroid Build Coastguard Worker }
867*77c1e3ccSAndroid Build Coastguard Worker }
868*77c1e3ccSAndroid Build Coastguard Worker }
869*77c1e3ccSAndroid Build Coastguard Worker aom_free(buffer);
870*77c1e3ccSAndroid Build Coastguard Worker return 1;
871*77c1e3ccSAndroid Build Coastguard Worker }
872*77c1e3ccSAndroid Build Coastguard Worker
add_noise_std_observations(aom_noise_model_t * noise_model,int c,const double * coeffs,const uint8_t * const data,const uint8_t * const denoised,int w,int h,int stride,int sub_log2[2],const uint8_t * const alt_data,int alt_stride,const uint8_t * const flat_blocks,int block_size,int num_blocks_w,int num_blocks_h)873*77c1e3ccSAndroid Build Coastguard Worker static void add_noise_std_observations(
874*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t *noise_model, int c, const double *coeffs,
875*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data, const uint8_t *const denoised, int w, int h,
876*77c1e3ccSAndroid Build Coastguard Worker int stride, int sub_log2[2], const uint8_t *const alt_data, int alt_stride,
877*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const flat_blocks, int block_size, int num_blocks_w,
878*77c1e3ccSAndroid Build Coastguard Worker int num_blocks_h) {
879*77c1e3ccSAndroid Build Coastguard Worker const int num_coords = noise_model->n;
880*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t *noise_strength_solver =
881*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[c].strength_solver;
882*77c1e3ccSAndroid Build Coastguard Worker
883*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_strength_solver_t *noise_strength_luma =
884*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[0].strength_solver;
885*77c1e3ccSAndroid Build Coastguard Worker const double luma_gain = noise_model->latest_state[0].ar_gain;
886*77c1e3ccSAndroid Build Coastguard Worker const double noise_gain = noise_model->latest_state[c].ar_gain;
887*77c1e3ccSAndroid Build Coastguard Worker for (int by = 0; by < num_blocks_h; ++by) {
888*77c1e3ccSAndroid Build Coastguard Worker const int y_o = by * (block_size >> sub_log2[1]);
889*77c1e3ccSAndroid Build Coastguard Worker for (int bx = 0; bx < num_blocks_w; ++bx) {
890*77c1e3ccSAndroid Build Coastguard Worker const int x_o = bx * (block_size >> sub_log2[0]);
891*77c1e3ccSAndroid Build Coastguard Worker if (!flat_blocks[by * num_blocks_w + bx]) {
892*77c1e3ccSAndroid Build Coastguard Worker continue;
893*77c1e3ccSAndroid Build Coastguard Worker }
894*77c1e3ccSAndroid Build Coastguard Worker const int num_samples_h =
895*77c1e3ccSAndroid Build Coastguard Worker AOMMIN((h >> sub_log2[1]) - by * (block_size >> sub_log2[1]),
896*77c1e3ccSAndroid Build Coastguard Worker block_size >> sub_log2[1]);
897*77c1e3ccSAndroid Build Coastguard Worker const int num_samples_w =
898*77c1e3ccSAndroid Build Coastguard Worker AOMMIN((w >> sub_log2[0]) - bx * (block_size >> sub_log2[0]),
899*77c1e3ccSAndroid Build Coastguard Worker (block_size >> sub_log2[0]));
900*77c1e3ccSAndroid Build Coastguard Worker // Make sure that we have a reasonable amount of samples to consider the
901*77c1e3ccSAndroid Build Coastguard Worker // block
902*77c1e3ccSAndroid Build Coastguard Worker if (num_samples_w * num_samples_h > block_size) {
903*77c1e3ccSAndroid Build Coastguard Worker const double block_mean = get_block_mean(
904*77c1e3ccSAndroid Build Coastguard Worker alt_data ? alt_data : data, w, h, alt_data ? alt_stride : stride,
905*77c1e3ccSAndroid Build Coastguard Worker x_o << sub_log2[0], y_o << sub_log2[1], block_size,
906*77c1e3ccSAndroid Build Coastguard Worker noise_model->params.use_highbd);
907*77c1e3ccSAndroid Build Coastguard Worker const double noise_var = get_noise_var(
908*77c1e3ccSAndroid Build Coastguard Worker data, denoised, stride, w >> sub_log2[0], h >> sub_log2[1], x_o,
909*77c1e3ccSAndroid Build Coastguard Worker y_o, block_size >> sub_log2[0], block_size >> sub_log2[1],
910*77c1e3ccSAndroid Build Coastguard Worker noise_model->params.use_highbd);
911*77c1e3ccSAndroid Build Coastguard Worker // We want to remove the part of the noise that came from being
912*77c1e3ccSAndroid Build Coastguard Worker // correlated with luma. Note that the noise solver for luma must
913*77c1e3ccSAndroid Build Coastguard Worker // have already been run.
914*77c1e3ccSAndroid Build Coastguard Worker const double luma_strength =
915*77c1e3ccSAndroid Build Coastguard Worker c > 0 ? luma_gain * noise_strength_solver_get_value(
916*77c1e3ccSAndroid Build Coastguard Worker noise_strength_luma, block_mean)
917*77c1e3ccSAndroid Build Coastguard Worker : 0;
918*77c1e3ccSAndroid Build Coastguard Worker const double corr = c > 0 ? coeffs[num_coords] : 0;
919*77c1e3ccSAndroid Build Coastguard Worker // Chroma noise:
920*77c1e3ccSAndroid Build Coastguard Worker // N(0, noise_var) = N(0, uncorr_var) + corr * N(0, luma_strength^2)
921*77c1e3ccSAndroid Build Coastguard Worker // The uncorrelated component:
922*77c1e3ccSAndroid Build Coastguard Worker // uncorr_var = noise_var - (corr * luma_strength)^2
923*77c1e3ccSAndroid Build Coastguard Worker // But don't allow fully correlated noise (hence the max), since the
924*77c1e3ccSAndroid Build Coastguard Worker // synthesis cannot model it.
925*77c1e3ccSAndroid Build Coastguard Worker const double uncorr_std = sqrt(
926*77c1e3ccSAndroid Build Coastguard Worker AOMMAX(noise_var / 16, noise_var - pow(corr * luma_strength, 2)));
927*77c1e3ccSAndroid Build Coastguard Worker // After we've removed correlation with luma, undo the gain that will
928*77c1e3ccSAndroid Build Coastguard Worker // come from running the IIR filter.
929*77c1e3ccSAndroid Build Coastguard Worker const double adjusted_strength = uncorr_std / noise_gain;
930*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_add_measurement(
931*77c1e3ccSAndroid Build Coastguard Worker noise_strength_solver, block_mean, adjusted_strength);
932*77c1e3ccSAndroid Build Coastguard Worker }
933*77c1e3ccSAndroid Build Coastguard Worker }
934*77c1e3ccSAndroid Build Coastguard Worker }
935*77c1e3ccSAndroid Build Coastguard Worker }
936*77c1e3ccSAndroid Build Coastguard Worker
937*77c1e3ccSAndroid Build Coastguard Worker // Return true if the noise estimate appears to be different from the combined
938*77c1e3ccSAndroid Build Coastguard Worker // (multi-frame) estimate. The difference is measured by checking whether the
939*77c1e3ccSAndroid Build Coastguard Worker // AR coefficients have diverged (using a threshold on normalized cross
940*77c1e3ccSAndroid Build Coastguard Worker // correlation), or whether the noise strength has changed.
is_noise_model_different(aom_noise_model_t * const noise_model)941*77c1e3ccSAndroid Build Coastguard Worker static int is_noise_model_different(aom_noise_model_t *const noise_model) {
942*77c1e3ccSAndroid Build Coastguard Worker // These thresholds are kind of arbitrary and will likely need further tuning
943*77c1e3ccSAndroid Build Coastguard Worker // (or exported as parameters). The threshold on noise strength is a weighted
944*77c1e3ccSAndroid Build Coastguard Worker // difference between the noise strength histograms
945*77c1e3ccSAndroid Build Coastguard Worker const double kCoeffThreshold = 0.9;
946*77c1e3ccSAndroid Build Coastguard Worker const double kStrengthThreshold =
947*77c1e3ccSAndroid Build Coastguard Worker 0.005 * (1 << (noise_model->params.bit_depth - 8));
948*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 1; ++c) {
949*77c1e3ccSAndroid Build Coastguard Worker const double corr =
950*77c1e3ccSAndroid Build Coastguard Worker aom_normalized_cross_correlation(noise_model->latest_state[c].eqns.x,
951*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[c].eqns.x,
952*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[c].eqns.n);
953*77c1e3ccSAndroid Build Coastguard Worker if (corr < kCoeffThreshold) return 1;
954*77c1e3ccSAndroid Build Coastguard Worker
955*77c1e3ccSAndroid Build Coastguard Worker const double dx =
956*77c1e3ccSAndroid Build Coastguard Worker 1.0 / noise_model->latest_state[c].strength_solver.num_bins;
957*77c1e3ccSAndroid Build Coastguard Worker
958*77c1e3ccSAndroid Build Coastguard Worker const aom_equation_system_t *latest_eqns =
959*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[c].strength_solver.eqns;
960*77c1e3ccSAndroid Build Coastguard Worker const aom_equation_system_t *combined_eqns =
961*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[c].strength_solver.eqns;
962*77c1e3ccSAndroid Build Coastguard Worker double diff = 0;
963*77c1e3ccSAndroid Build Coastguard Worker double total_weight = 0;
964*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < latest_eqns->n; ++j) {
965*77c1e3ccSAndroid Build Coastguard Worker double weight = 0;
966*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < latest_eqns->n; ++i) {
967*77c1e3ccSAndroid Build Coastguard Worker weight += latest_eqns->A[i * latest_eqns->n + j];
968*77c1e3ccSAndroid Build Coastguard Worker }
969*77c1e3ccSAndroid Build Coastguard Worker weight = sqrt(weight);
970*77c1e3ccSAndroid Build Coastguard Worker diff += weight * fabs(latest_eqns->x[j] - combined_eqns->x[j]);
971*77c1e3ccSAndroid Build Coastguard Worker total_weight += weight;
972*77c1e3ccSAndroid Build Coastguard Worker }
973*77c1e3ccSAndroid Build Coastguard Worker if (diff * dx / total_weight > kStrengthThreshold) return 1;
974*77c1e3ccSAndroid Build Coastguard Worker }
975*77c1e3ccSAndroid Build Coastguard Worker return 0;
976*77c1e3ccSAndroid Build Coastguard Worker }
977*77c1e3ccSAndroid Build Coastguard Worker
ar_equation_system_solve(aom_noise_state_t * state,int is_chroma)978*77c1e3ccSAndroid Build Coastguard Worker static int ar_equation_system_solve(aom_noise_state_t *state, int is_chroma) {
979*77c1e3ccSAndroid Build Coastguard Worker const int ret = equation_system_solve(&state->eqns);
980*77c1e3ccSAndroid Build Coastguard Worker state->ar_gain = 1.0;
981*77c1e3ccSAndroid Build Coastguard Worker if (!ret) return ret;
982*77c1e3ccSAndroid Build Coastguard Worker
983*77c1e3ccSAndroid Build Coastguard Worker // Update the AR gain from the equation system as it will be used to fit
984*77c1e3ccSAndroid Build Coastguard Worker // the noise strength as a function of intensity. In the Yule-Walker
985*77c1e3ccSAndroid Build Coastguard Worker // equations, the diagonal should be the variance of the correlated noise.
986*77c1e3ccSAndroid Build Coastguard Worker // In the case of the least squares estimate, there will be some variability
987*77c1e3ccSAndroid Build Coastguard Worker // in the diagonal. So use the mean of the diagonal as the estimate of
988*77c1e3ccSAndroid Build Coastguard Worker // overall variance (this works for least squares or Yule-Walker formulation).
989*77c1e3ccSAndroid Build Coastguard Worker double var = 0;
990*77c1e3ccSAndroid Build Coastguard Worker const int n = state->eqns.n;
991*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < (state->eqns.n - is_chroma); ++i) {
992*77c1e3ccSAndroid Build Coastguard Worker var += state->eqns.A[i * n + i] / state->num_observations;
993*77c1e3ccSAndroid Build Coastguard Worker }
994*77c1e3ccSAndroid Build Coastguard Worker var /= (n - is_chroma);
995*77c1e3ccSAndroid Build Coastguard Worker
996*77c1e3ccSAndroid Build Coastguard Worker // Keep track of E(Y^2) = <b, x> + E(X^2)
997*77c1e3ccSAndroid Build Coastguard Worker // In the case that we are using chroma and have an estimate of correlation
998*77c1e3ccSAndroid Build Coastguard Worker // with luma we adjust that estimate slightly to remove the correlated bits by
999*77c1e3ccSAndroid Build Coastguard Worker // subtracting out the last column of a scaled by our correlation estimate
1000*77c1e3ccSAndroid Build Coastguard Worker // from b. E(y^2) = <b - A(:, end)*x(end), x>
1001*77c1e3ccSAndroid Build Coastguard Worker double sum_covar = 0;
1002*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < state->eqns.n - is_chroma; ++i) {
1003*77c1e3ccSAndroid Build Coastguard Worker double bi = state->eqns.b[i];
1004*77c1e3ccSAndroid Build Coastguard Worker if (is_chroma) {
1005*77c1e3ccSAndroid Build Coastguard Worker bi -= state->eqns.A[i * n + (n - 1)] * state->eqns.x[n - 1];
1006*77c1e3ccSAndroid Build Coastguard Worker }
1007*77c1e3ccSAndroid Build Coastguard Worker sum_covar += (bi * state->eqns.x[i]) / state->num_observations;
1008*77c1e3ccSAndroid Build Coastguard Worker }
1009*77c1e3ccSAndroid Build Coastguard Worker // Now, get an estimate of the variance of uncorrelated noise signal and use
1010*77c1e3ccSAndroid Build Coastguard Worker // it to determine the gain of the AR filter.
1011*77c1e3ccSAndroid Build Coastguard Worker const double noise_var = AOMMAX(var - sum_covar, 1e-6);
1012*77c1e3ccSAndroid Build Coastguard Worker state->ar_gain = AOMMAX(1, sqrt(AOMMAX(var / noise_var, 1e-6)));
1013*77c1e3ccSAndroid Build Coastguard Worker return ret;
1014*77c1e3ccSAndroid Build Coastguard Worker }
1015*77c1e3ccSAndroid Build Coastguard Worker
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 stride[3],int chroma_sub_log2[2],const uint8_t * const flat_blocks,int block_size)1016*77c1e3ccSAndroid Build Coastguard Worker aom_noise_status_t aom_noise_model_update(
1017*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t *const noise_model, const uint8_t *const data[3],
1018*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const denoised[3], int w, int h, int stride[3],
1019*77c1e3ccSAndroid Build Coastguard Worker int chroma_sub_log2[2], const uint8_t *const flat_blocks, int block_size) {
1020*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_w = (w + block_size - 1) / block_size;
1021*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_h = (h + block_size - 1) / block_size;
1022*77c1e3ccSAndroid Build Coastguard Worker int y_model_different = 0;
1023*77c1e3ccSAndroid Build Coastguard Worker int num_blocks = 0;
1024*77c1e3ccSAndroid Build Coastguard Worker int i = 0, channel = 0;
1025*77c1e3ccSAndroid Build Coastguard Worker
1026*77c1e3ccSAndroid Build Coastguard Worker if (block_size <= 1) {
1027*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "block_size = %d must be > 1\n", block_size);
1028*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INVALID_ARGUMENT;
1029*77c1e3ccSAndroid Build Coastguard Worker }
1030*77c1e3ccSAndroid Build Coastguard Worker
1031*77c1e3ccSAndroid Build Coastguard Worker if (block_size < noise_model->params.lag * 2 + 1) {
1032*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "block_size = %d must be >= %d\n", block_size,
1033*77c1e3ccSAndroid Build Coastguard Worker noise_model->params.lag * 2 + 1);
1034*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INVALID_ARGUMENT;
1035*77c1e3ccSAndroid Build Coastguard Worker }
1036*77c1e3ccSAndroid Build Coastguard Worker
1037*77c1e3ccSAndroid Build Coastguard Worker // Clear the latest equation system
1038*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < 3; ++i) {
1039*77c1e3ccSAndroid Build Coastguard Worker equation_system_clear(&noise_model->latest_state[i].eqns);
1040*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[i].num_observations = 0;
1041*77c1e3ccSAndroid Build Coastguard Worker noise_strength_solver_clear(&noise_model->latest_state[i].strength_solver);
1042*77c1e3ccSAndroid Build Coastguard Worker }
1043*77c1e3ccSAndroid Build Coastguard Worker
1044*77c1e3ccSAndroid Build Coastguard Worker // Check that we have enough flat blocks
1045*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < num_blocks_h * num_blocks_w; ++i) {
1046*77c1e3ccSAndroid Build Coastguard Worker if (flat_blocks[i]) {
1047*77c1e3ccSAndroid Build Coastguard Worker num_blocks++;
1048*77c1e3ccSAndroid Build Coastguard Worker }
1049*77c1e3ccSAndroid Build Coastguard Worker }
1050*77c1e3ccSAndroid Build Coastguard Worker
1051*77c1e3ccSAndroid Build Coastguard Worker if (num_blocks <= 1) {
1052*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Not enough flat blocks to update noise estimate\n");
1053*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INSUFFICIENT_FLAT_BLOCKS;
1054*77c1e3ccSAndroid Build Coastguard Worker }
1055*77c1e3ccSAndroid Build Coastguard Worker
1056*77c1e3ccSAndroid Build Coastguard Worker for (channel = 0; channel < 3; ++channel) {
1057*77c1e3ccSAndroid Build Coastguard Worker int no_subsampling[2] = { 0, 0 };
1058*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *alt_data = channel > 0 ? data[0] : 0;
1059*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *alt_denoised = channel > 0 ? denoised[0] : 0;
1060*77c1e3ccSAndroid Build Coastguard Worker int *sub = channel > 0 ? chroma_sub_log2 : no_subsampling;
1061*77c1e3ccSAndroid Build Coastguard Worker const int is_chroma = channel != 0;
1062*77c1e3ccSAndroid Build Coastguard Worker if (!data[channel] || !denoised[channel]) break;
1063*77c1e3ccSAndroid Build Coastguard Worker if (!add_block_observations(noise_model, channel, data[channel],
1064*77c1e3ccSAndroid Build Coastguard Worker denoised[channel], w, h, stride[channel], sub,
1065*77c1e3ccSAndroid Build Coastguard Worker alt_data, alt_denoised, stride[0], flat_blocks,
1066*77c1e3ccSAndroid Build Coastguard Worker block_size, num_blocks_w, num_blocks_h)) {
1067*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Adding block observation failed\n");
1068*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INTERNAL_ERROR;
1069*77c1e3ccSAndroid Build Coastguard Worker }
1070*77c1e3ccSAndroid Build Coastguard Worker
1071*77c1e3ccSAndroid Build Coastguard Worker if (!ar_equation_system_solve(&noise_model->latest_state[channel],
1072*77c1e3ccSAndroid Build Coastguard Worker is_chroma)) {
1073*77c1e3ccSAndroid Build Coastguard Worker if (is_chroma) {
1074*77c1e3ccSAndroid Build Coastguard Worker set_chroma_coefficient_fallback_soln(
1075*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[channel].eqns);
1076*77c1e3ccSAndroid Build Coastguard Worker } else {
1077*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Solving latest noise equation system failed %d!\n",
1078*77c1e3ccSAndroid Build Coastguard Worker channel);
1079*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INTERNAL_ERROR;
1080*77c1e3ccSAndroid Build Coastguard Worker }
1081*77c1e3ccSAndroid Build Coastguard Worker }
1082*77c1e3ccSAndroid Build Coastguard Worker
1083*77c1e3ccSAndroid Build Coastguard Worker add_noise_std_observations(
1084*77c1e3ccSAndroid Build Coastguard Worker noise_model, channel, noise_model->latest_state[channel].eqns.x,
1085*77c1e3ccSAndroid Build Coastguard Worker data[channel], denoised[channel], w, h, stride[channel], sub, alt_data,
1086*77c1e3ccSAndroid Build Coastguard Worker stride[0], flat_blocks, block_size, num_blocks_w, num_blocks_h);
1087*77c1e3ccSAndroid Build Coastguard Worker
1088*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_solver_solve(
1089*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[channel].strength_solver)) {
1090*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Solving latest noise strength failed!\n");
1091*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INTERNAL_ERROR;
1092*77c1e3ccSAndroid Build Coastguard Worker }
1093*77c1e3ccSAndroid Build Coastguard Worker
1094*77c1e3ccSAndroid Build Coastguard Worker // Check noise characteristics and return if error.
1095*77c1e3ccSAndroid Build Coastguard Worker if (channel == 0 &&
1096*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[channel].strength_solver.num_equations >
1097*77c1e3ccSAndroid Build Coastguard Worker 0 &&
1098*77c1e3ccSAndroid Build Coastguard Worker is_noise_model_different(noise_model)) {
1099*77c1e3ccSAndroid Build Coastguard Worker y_model_different = 1;
1100*77c1e3ccSAndroid Build Coastguard Worker }
1101*77c1e3ccSAndroid Build Coastguard Worker
1102*77c1e3ccSAndroid Build Coastguard Worker // Don't update the combined stats if the y model is different.
1103*77c1e3ccSAndroid Build Coastguard Worker if (y_model_different) continue;
1104*77c1e3ccSAndroid Build Coastguard Worker
1105*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[channel].num_observations +=
1106*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[channel].num_observations;
1107*77c1e3ccSAndroid Build Coastguard Worker equation_system_add(&noise_model->combined_state[channel].eqns,
1108*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[channel].eqns);
1109*77c1e3ccSAndroid Build Coastguard Worker if (!ar_equation_system_solve(&noise_model->combined_state[channel],
1110*77c1e3ccSAndroid Build Coastguard Worker is_chroma)) {
1111*77c1e3ccSAndroid Build Coastguard Worker if (is_chroma) {
1112*77c1e3ccSAndroid Build Coastguard Worker set_chroma_coefficient_fallback_soln(
1113*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[channel].eqns);
1114*77c1e3ccSAndroid Build Coastguard Worker } else {
1115*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Solving combined noise equation system failed %d!\n",
1116*77c1e3ccSAndroid Build Coastguard Worker channel);
1117*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INTERNAL_ERROR;
1118*77c1e3ccSAndroid Build Coastguard Worker }
1119*77c1e3ccSAndroid Build Coastguard Worker }
1120*77c1e3ccSAndroid Build Coastguard Worker
1121*77c1e3ccSAndroid Build Coastguard Worker noise_strength_solver_add(
1122*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[channel].strength_solver,
1123*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[channel].strength_solver);
1124*77c1e3ccSAndroid Build Coastguard Worker
1125*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_solver_solve(
1126*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[channel].strength_solver)) {
1127*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Solving combined noise strength failed!\n");
1128*77c1e3ccSAndroid Build Coastguard Worker return AOM_NOISE_STATUS_INTERNAL_ERROR;
1129*77c1e3ccSAndroid Build Coastguard Worker }
1130*77c1e3ccSAndroid Build Coastguard Worker }
1131*77c1e3ccSAndroid Build Coastguard Worker
1132*77c1e3ccSAndroid Build Coastguard Worker return y_model_different ? AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE
1133*77c1e3ccSAndroid Build Coastguard Worker : AOM_NOISE_STATUS_OK;
1134*77c1e3ccSAndroid Build Coastguard Worker }
1135*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_model_save_latest(aom_noise_model_t * noise_model)1136*77c1e3ccSAndroid Build Coastguard Worker void aom_noise_model_save_latest(aom_noise_model_t *noise_model) {
1137*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; c++) {
1138*77c1e3ccSAndroid Build Coastguard Worker equation_system_copy(&noise_model->combined_state[c].eqns,
1139*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[c].eqns);
1140*77c1e3ccSAndroid Build Coastguard Worker equation_system_copy(&noise_model->combined_state[c].strength_solver.eqns,
1141*77c1e3ccSAndroid Build Coastguard Worker &noise_model->latest_state[c].strength_solver.eqns);
1142*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[c].strength_solver.num_equations =
1143*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[c].strength_solver.num_equations;
1144*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[c].num_observations =
1145*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[c].num_observations;
1146*77c1e3ccSAndroid Build Coastguard Worker noise_model->combined_state[c].ar_gain =
1147*77c1e3ccSAndroid Build Coastguard Worker noise_model->latest_state[c].ar_gain;
1148*77c1e3ccSAndroid Build Coastguard Worker }
1149*77c1e3ccSAndroid Build Coastguard Worker }
1150*77c1e3ccSAndroid Build Coastguard Worker
aom_noise_model_get_grain_parameters(aom_noise_model_t * const noise_model,aom_film_grain_t * film_grain)1151*77c1e3ccSAndroid Build Coastguard Worker int aom_noise_model_get_grain_parameters(aom_noise_model_t *const noise_model,
1152*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t *film_grain) {
1153*77c1e3ccSAndroid Build Coastguard Worker if (noise_model->params.lag > 3) {
1154*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "params.lag = %d > 3\n", noise_model->params.lag);
1155*77c1e3ccSAndroid Build Coastguard Worker return 0;
1156*77c1e3ccSAndroid Build Coastguard Worker }
1157*77c1e3ccSAndroid Build Coastguard Worker uint16_t random_seed = film_grain->random_seed;
1158*77c1e3ccSAndroid Build Coastguard Worker memset(film_grain, 0, sizeof(*film_grain));
1159*77c1e3ccSAndroid Build Coastguard Worker film_grain->random_seed = random_seed;
1160*77c1e3ccSAndroid Build Coastguard Worker
1161*77c1e3ccSAndroid Build Coastguard Worker film_grain->apply_grain = 1;
1162*77c1e3ccSAndroid Build Coastguard Worker film_grain->update_parameters = 1;
1163*77c1e3ccSAndroid Build Coastguard Worker
1164*77c1e3ccSAndroid Build Coastguard Worker film_grain->ar_coeff_lag = noise_model->params.lag;
1165*77c1e3ccSAndroid Build Coastguard Worker
1166*77c1e3ccSAndroid Build Coastguard Worker // Convert the scaling functions to 8 bit values
1167*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_t scaling_points[3];
1168*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_solver_fit_piecewise(
1169*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[0].strength_solver, 14,
1170*77c1e3ccSAndroid Build Coastguard Worker scaling_points + 0)) {
1171*77c1e3ccSAndroid Build Coastguard Worker return 0;
1172*77c1e3ccSAndroid Build Coastguard Worker }
1173*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_solver_fit_piecewise(
1174*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[1].strength_solver, 10,
1175*77c1e3ccSAndroid Build Coastguard Worker scaling_points + 1)) {
1176*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 0);
1177*77c1e3ccSAndroid Build Coastguard Worker return 0;
1178*77c1e3ccSAndroid Build Coastguard Worker }
1179*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_strength_solver_fit_piecewise(
1180*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[2].strength_solver, 10,
1181*77c1e3ccSAndroid Build Coastguard Worker scaling_points + 2)) {
1182*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 0);
1183*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 1);
1184*77c1e3ccSAndroid Build Coastguard Worker return 0;
1185*77c1e3ccSAndroid Build Coastguard Worker }
1186*77c1e3ccSAndroid Build Coastguard Worker
1187*77c1e3ccSAndroid Build Coastguard Worker // Both the domain and the range of the scaling functions in the film_grain
1188*77c1e3ccSAndroid Build Coastguard Worker // are normalized to 8-bit (e.g., they are implicitly scaled during grain
1189*77c1e3ccSAndroid Build Coastguard Worker // synthesis).
1190*77c1e3ccSAndroid Build Coastguard Worker const double strength_divisor = 1 << (noise_model->params.bit_depth - 8);
1191*77c1e3ccSAndroid Build Coastguard Worker double max_scaling_value = 1e-4;
1192*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
1193*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < scaling_points[c].num_points; ++i) {
1194*77c1e3ccSAndroid Build Coastguard Worker scaling_points[c].points[i][0] =
1195*77c1e3ccSAndroid Build Coastguard Worker AOMMIN(255, scaling_points[c].points[i][0] / strength_divisor);
1196*77c1e3ccSAndroid Build Coastguard Worker scaling_points[c].points[i][1] =
1197*77c1e3ccSAndroid Build Coastguard Worker AOMMIN(255, scaling_points[c].points[i][1] / strength_divisor);
1198*77c1e3ccSAndroid Build Coastguard Worker max_scaling_value =
1199*77c1e3ccSAndroid Build Coastguard Worker AOMMAX(scaling_points[c].points[i][1], max_scaling_value);
1200*77c1e3ccSAndroid Build Coastguard Worker }
1201*77c1e3ccSAndroid Build Coastguard Worker }
1202*77c1e3ccSAndroid Build Coastguard Worker
1203*77c1e3ccSAndroid Build Coastguard Worker // Scaling_shift values are in the range [8,11]
1204*77c1e3ccSAndroid Build Coastguard Worker const int max_scaling_value_log2 =
1205*77c1e3ccSAndroid Build Coastguard Worker clamp((int)floor(log2(max_scaling_value) + 1), 2, 5);
1206*77c1e3ccSAndroid Build Coastguard Worker film_grain->scaling_shift = 5 + (8 - max_scaling_value_log2);
1207*77c1e3ccSAndroid Build Coastguard Worker
1208*77c1e3ccSAndroid Build Coastguard Worker const double scale_factor = 1 << (8 - max_scaling_value_log2);
1209*77c1e3ccSAndroid Build Coastguard Worker film_grain->num_y_points = scaling_points[0].num_points;
1210*77c1e3ccSAndroid Build Coastguard Worker film_grain->num_cb_points = scaling_points[1].num_points;
1211*77c1e3ccSAndroid Build Coastguard Worker film_grain->num_cr_points = scaling_points[2].num_points;
1212*77c1e3ccSAndroid Build Coastguard Worker
1213*77c1e3ccSAndroid Build Coastguard Worker int(*film_grain_scaling[3])[2] = {
1214*77c1e3ccSAndroid Build Coastguard Worker film_grain->scaling_points_y,
1215*77c1e3ccSAndroid Build Coastguard Worker film_grain->scaling_points_cb,
1216*77c1e3ccSAndroid Build Coastguard Worker film_grain->scaling_points_cr,
1217*77c1e3ccSAndroid Build Coastguard Worker };
1218*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; c++) {
1219*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < scaling_points[c].num_points; ++i) {
1220*77c1e3ccSAndroid Build Coastguard Worker film_grain_scaling[c][i][0] = (int)(scaling_points[c].points[i][0] + 0.5);
1221*77c1e3ccSAndroid Build Coastguard Worker film_grain_scaling[c][i][1] = clamp(
1222*77c1e3ccSAndroid Build Coastguard Worker (int)(scale_factor * scaling_points[c].points[i][1] + 0.5), 0, 255);
1223*77c1e3ccSAndroid Build Coastguard Worker }
1224*77c1e3ccSAndroid Build Coastguard Worker }
1225*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 0);
1226*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 1);
1227*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_lut_free(scaling_points + 2);
1228*77c1e3ccSAndroid Build Coastguard Worker
1229*77c1e3ccSAndroid Build Coastguard Worker // Convert the ar_coeffs into 8-bit values
1230*77c1e3ccSAndroid Build Coastguard Worker const int n_coeff = noise_model->combined_state[0].eqns.n;
1231*77c1e3ccSAndroid Build Coastguard Worker double max_coeff = 1e-4, min_coeff = -1e-4;
1232*77c1e3ccSAndroid Build Coastguard Worker double y_corr[2] = { 0, 0 };
1233*77c1e3ccSAndroid Build Coastguard Worker double avg_luma_strength = 0;
1234*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; c++) {
1235*77c1e3ccSAndroid Build Coastguard Worker aom_equation_system_t *eqns = &noise_model->combined_state[c].eqns;
1236*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n_coeff; ++i) {
1237*77c1e3ccSAndroid Build Coastguard Worker max_coeff = AOMMAX(max_coeff, eqns->x[i]);
1238*77c1e3ccSAndroid Build Coastguard Worker min_coeff = AOMMIN(min_coeff, eqns->x[i]);
1239*77c1e3ccSAndroid Build Coastguard Worker }
1240*77c1e3ccSAndroid Build Coastguard Worker // Since the correlation between luma/chroma was computed in an already
1241*77c1e3ccSAndroid Build Coastguard Worker // scaled space, we adjust it in the un-scaled space.
1242*77c1e3ccSAndroid Build Coastguard Worker aom_noise_strength_solver_t *solver =
1243*77c1e3ccSAndroid Build Coastguard Worker &noise_model->combined_state[c].strength_solver;
1244*77c1e3ccSAndroid Build Coastguard Worker // Compute a weighted average of the strength for the channel.
1245*77c1e3ccSAndroid Build Coastguard Worker double average_strength = 0, total_weight = 0;
1246*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < solver->eqns.n; ++i) {
1247*77c1e3ccSAndroid Build Coastguard Worker double w = 0;
1248*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < solver->eqns.n; ++j) {
1249*77c1e3ccSAndroid Build Coastguard Worker w += solver->eqns.A[i * solver->eqns.n + j];
1250*77c1e3ccSAndroid Build Coastguard Worker }
1251*77c1e3ccSAndroid Build Coastguard Worker w = sqrt(w);
1252*77c1e3ccSAndroid Build Coastguard Worker average_strength += solver->eqns.x[i] * w;
1253*77c1e3ccSAndroid Build Coastguard Worker total_weight += w;
1254*77c1e3ccSAndroid Build Coastguard Worker }
1255*77c1e3ccSAndroid Build Coastguard Worker if (total_weight == 0)
1256*77c1e3ccSAndroid Build Coastguard Worker average_strength = 1;
1257*77c1e3ccSAndroid Build Coastguard Worker else
1258*77c1e3ccSAndroid Build Coastguard Worker average_strength /= total_weight;
1259*77c1e3ccSAndroid Build Coastguard Worker if (c == 0) {
1260*77c1e3ccSAndroid Build Coastguard Worker avg_luma_strength = average_strength;
1261*77c1e3ccSAndroid Build Coastguard Worker } else {
1262*77c1e3ccSAndroid Build Coastguard Worker y_corr[c - 1] = avg_luma_strength * eqns->x[n_coeff] / average_strength;
1263*77c1e3ccSAndroid Build Coastguard Worker max_coeff = AOMMAX(max_coeff, y_corr[c - 1]);
1264*77c1e3ccSAndroid Build Coastguard Worker min_coeff = AOMMIN(min_coeff, y_corr[c - 1]);
1265*77c1e3ccSAndroid Build Coastguard Worker }
1266*77c1e3ccSAndroid Build Coastguard Worker }
1267*77c1e3ccSAndroid Build Coastguard Worker // Shift value: AR coeffs range (values 6-9)
1268*77c1e3ccSAndroid Build Coastguard Worker // 6: [-2, 2), 7: [-1, 1), 8: [-0.5, 0.5), 9: [-0.25, 0.25)
1269*77c1e3ccSAndroid Build Coastguard Worker film_grain->ar_coeff_shift =
1270*77c1e3ccSAndroid Build Coastguard Worker clamp(7 - (int)AOMMAX(1 + floor(log2(max_coeff)), ceil(log2(-min_coeff))),
1271*77c1e3ccSAndroid Build Coastguard Worker 6, 9);
1272*77c1e3ccSAndroid Build Coastguard Worker double scale_ar_coeff = 1 << film_grain->ar_coeff_shift;
1273*77c1e3ccSAndroid Build Coastguard Worker int *ar_coeffs[3] = {
1274*77c1e3ccSAndroid Build Coastguard Worker film_grain->ar_coeffs_y,
1275*77c1e3ccSAndroid Build Coastguard Worker film_grain->ar_coeffs_cb,
1276*77c1e3ccSAndroid Build Coastguard Worker film_grain->ar_coeffs_cr,
1277*77c1e3ccSAndroid Build Coastguard Worker };
1278*77c1e3ccSAndroid Build Coastguard Worker for (int c = 0; c < 3; ++c) {
1279*77c1e3ccSAndroid Build Coastguard Worker aom_equation_system_t *eqns = &noise_model->combined_state[c].eqns;
1280*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n_coeff; ++i) {
1281*77c1e3ccSAndroid Build Coastguard Worker ar_coeffs[c][i] =
1282*77c1e3ccSAndroid Build Coastguard Worker clamp((int)round(scale_ar_coeff * eqns->x[i]), -128, 127);
1283*77c1e3ccSAndroid Build Coastguard Worker }
1284*77c1e3ccSAndroid Build Coastguard Worker if (c > 0) {
1285*77c1e3ccSAndroid Build Coastguard Worker ar_coeffs[c][n_coeff] =
1286*77c1e3ccSAndroid Build Coastguard Worker clamp((int)round(scale_ar_coeff * y_corr[c - 1]), -128, 127);
1287*77c1e3ccSAndroid Build Coastguard Worker }
1288*77c1e3ccSAndroid Build Coastguard Worker }
1289*77c1e3ccSAndroid Build Coastguard Worker
1290*77c1e3ccSAndroid Build Coastguard Worker // At the moment, the noise modeling code assumes that the chroma scaling
1291*77c1e3ccSAndroid Build Coastguard Worker // functions are a function of luma.
1292*77c1e3ccSAndroid Build Coastguard Worker film_grain->cb_mult = 128; // 8 bits
1293*77c1e3ccSAndroid Build Coastguard Worker film_grain->cb_luma_mult = 192; // 8 bits
1294*77c1e3ccSAndroid Build Coastguard Worker film_grain->cb_offset = 256; // 9 bits
1295*77c1e3ccSAndroid Build Coastguard Worker
1296*77c1e3ccSAndroid Build Coastguard Worker film_grain->cr_mult = 128; // 8 bits
1297*77c1e3ccSAndroid Build Coastguard Worker film_grain->cr_luma_mult = 192; // 8 bits
1298*77c1e3ccSAndroid Build Coastguard Worker film_grain->cr_offset = 256; // 9 bits
1299*77c1e3ccSAndroid Build Coastguard Worker
1300*77c1e3ccSAndroid Build Coastguard Worker film_grain->chroma_scaling_from_luma = 0;
1301*77c1e3ccSAndroid Build Coastguard Worker film_grain->grain_scale_shift = 0;
1302*77c1e3ccSAndroid Build Coastguard Worker film_grain->overlap_flag = 1;
1303*77c1e3ccSAndroid Build Coastguard Worker return 1;
1304*77c1e3ccSAndroid Build Coastguard Worker }
1305*77c1e3ccSAndroid Build Coastguard Worker
pointwise_multiply(const float * a,float * b,int n)1306*77c1e3ccSAndroid Build Coastguard Worker static void pointwise_multiply(const float *a, float *b, int n) {
1307*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < n; ++i) {
1308*77c1e3ccSAndroid Build Coastguard Worker b[i] *= a[i];
1309*77c1e3ccSAndroid Build Coastguard Worker }
1310*77c1e3ccSAndroid Build Coastguard Worker }
1311*77c1e3ccSAndroid Build Coastguard Worker
get_half_cos_window(int block_size)1312*77c1e3ccSAndroid Build Coastguard Worker static float *get_half_cos_window(int block_size) {
1313*77c1e3ccSAndroid Build Coastguard Worker float *window_function =
1314*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_malloc(block_size * block_size * sizeof(*window_function));
1315*77c1e3ccSAndroid Build Coastguard Worker if (!window_function) return NULL;
1316*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < block_size; ++y) {
1317*77c1e3ccSAndroid Build Coastguard Worker const double cos_yd = cos((.5 + y) * PI / block_size - PI / 2);
1318*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < block_size; ++x) {
1319*77c1e3ccSAndroid Build Coastguard Worker const double cos_xd = cos((.5 + x) * PI / block_size - PI / 2);
1320*77c1e3ccSAndroid Build Coastguard Worker window_function[y * block_size + x] = (float)(cos_yd * cos_xd);
1321*77c1e3ccSAndroid Build Coastguard Worker }
1322*77c1e3ccSAndroid Build Coastguard Worker }
1323*77c1e3ccSAndroid Build Coastguard Worker return window_function;
1324*77c1e3ccSAndroid Build Coastguard Worker }
1325*77c1e3ccSAndroid Build Coastguard Worker
1326*77c1e3ccSAndroid Build Coastguard Worker #define DITHER_AND_QUANTIZE(INT_TYPE, suffix) \
1327*77c1e3ccSAndroid Build Coastguard Worker static void dither_and_quantize_##suffix( \
1328*77c1e3ccSAndroid Build Coastguard Worker float *result, int result_stride, INT_TYPE *denoised, int w, int h, \
1329*77c1e3ccSAndroid Build Coastguard Worker int stride, int chroma_sub_w, int chroma_sub_h, int block_size, \
1330*77c1e3ccSAndroid Build Coastguard Worker float block_normalization) { \
1331*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < (h >> chroma_sub_h); ++y) { \
1332*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < (w >> chroma_sub_w); ++x) { \
1333*77c1e3ccSAndroid Build Coastguard Worker const int result_idx = \
1334*77c1e3ccSAndroid Build Coastguard Worker (y + (block_size >> chroma_sub_h)) * result_stride + x + \
1335*77c1e3ccSAndroid Build Coastguard Worker (block_size >> chroma_sub_w); \
1336*77c1e3ccSAndroid Build Coastguard Worker INT_TYPE new_val = (INT_TYPE)AOMMIN( \
1337*77c1e3ccSAndroid Build Coastguard Worker AOMMAX(result[result_idx] * block_normalization + 0.5f, 0), \
1338*77c1e3ccSAndroid Build Coastguard Worker block_normalization); \
1339*77c1e3ccSAndroid Build Coastguard Worker const float err = \
1340*77c1e3ccSAndroid Build Coastguard Worker -(((float)new_val) / block_normalization - result[result_idx]); \
1341*77c1e3ccSAndroid Build Coastguard Worker denoised[y * stride + x] = new_val; \
1342*77c1e3ccSAndroid Build Coastguard Worker if (x + 1 < (w >> chroma_sub_w)) { \
1343*77c1e3ccSAndroid Build Coastguard Worker result[result_idx + 1] += err * 7.0f / 16.0f; \
1344*77c1e3ccSAndroid Build Coastguard Worker } \
1345*77c1e3ccSAndroid Build Coastguard Worker if (y + 1 < (h >> chroma_sub_h)) { \
1346*77c1e3ccSAndroid Build Coastguard Worker if (x > 0) { \
1347*77c1e3ccSAndroid Build Coastguard Worker result[result_idx + result_stride - 1] += err * 3.0f / 16.0f; \
1348*77c1e3ccSAndroid Build Coastguard Worker } \
1349*77c1e3ccSAndroid Build Coastguard Worker result[result_idx + result_stride] += err * 5.0f / 16.0f; \
1350*77c1e3ccSAndroid Build Coastguard Worker if (x + 1 < (w >> chroma_sub_w)) { \
1351*77c1e3ccSAndroid Build Coastguard Worker result[result_idx + result_stride + 1] += err * 1.0f / 16.0f; \
1352*77c1e3ccSAndroid Build Coastguard Worker } \
1353*77c1e3ccSAndroid Build Coastguard Worker } \
1354*77c1e3ccSAndroid Build Coastguard Worker } \
1355*77c1e3ccSAndroid Build Coastguard Worker } \
1356*77c1e3ccSAndroid Build Coastguard Worker }
1357*77c1e3ccSAndroid Build Coastguard Worker
DITHER_AND_QUANTIZE(uint8_t,lowbd)1358*77c1e3ccSAndroid Build Coastguard Worker DITHER_AND_QUANTIZE(uint8_t, lowbd)
1359*77c1e3ccSAndroid Build Coastguard Worker DITHER_AND_QUANTIZE(uint16_t, highbd)
1360*77c1e3ccSAndroid Build Coastguard Worker
1361*77c1e3ccSAndroid Build Coastguard Worker int aom_wiener_denoise_2d(const uint8_t *const data[3], uint8_t *denoised[3],
1362*77c1e3ccSAndroid Build Coastguard Worker int w, int h, int stride[3], int chroma_sub[2],
1363*77c1e3ccSAndroid Build Coastguard Worker float *noise_psd[3], int block_size, int bit_depth,
1364*77c1e3ccSAndroid Build Coastguard Worker int use_highbd) {
1365*77c1e3ccSAndroid Build Coastguard Worker float *plane = NULL, *block = NULL, *window_full = NULL,
1366*77c1e3ccSAndroid Build Coastguard Worker *window_chroma = NULL;
1367*77c1e3ccSAndroid Build Coastguard Worker double *block_d = NULL, *plane_d = NULL;
1368*77c1e3ccSAndroid Build Coastguard Worker struct aom_noise_tx_t *tx_full = NULL;
1369*77c1e3ccSAndroid Build Coastguard Worker struct aom_noise_tx_t *tx_chroma = NULL;
1370*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_w = (w + block_size - 1) / block_size;
1371*77c1e3ccSAndroid Build Coastguard Worker const int num_blocks_h = (h + block_size - 1) / block_size;
1372*77c1e3ccSAndroid Build Coastguard Worker const int result_stride = (num_blocks_w + 2) * block_size;
1373*77c1e3ccSAndroid Build Coastguard Worker const int result_height = (num_blocks_h + 2) * block_size;
1374*77c1e3ccSAndroid Build Coastguard Worker float *result = NULL;
1375*77c1e3ccSAndroid Build Coastguard Worker int init_success = 1;
1376*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t block_finder_full;
1377*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t block_finder_chroma;
1378*77c1e3ccSAndroid Build Coastguard Worker const float kBlockNormalization = (float)((1 << bit_depth) - 1);
1379*77c1e3ccSAndroid Build Coastguard Worker if (chroma_sub[0] != chroma_sub[1]) {
1380*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr,
1381*77c1e3ccSAndroid Build Coastguard Worker "aom_wiener_denoise_2d doesn't handle different chroma "
1382*77c1e3ccSAndroid Build Coastguard Worker "subsampling\n");
1383*77c1e3ccSAndroid Build Coastguard Worker return 0;
1384*77c1e3ccSAndroid Build Coastguard Worker }
1385*77c1e3ccSAndroid Build Coastguard Worker init_success &= aom_flat_block_finder_init(&block_finder_full, block_size,
1386*77c1e3ccSAndroid Build Coastguard Worker bit_depth, use_highbd);
1387*77c1e3ccSAndroid Build Coastguard Worker result = (float *)aom_malloc((num_blocks_h + 2) * block_size * result_stride *
1388*77c1e3ccSAndroid Build Coastguard Worker sizeof(*result));
1389*77c1e3ccSAndroid Build Coastguard Worker plane = (float *)aom_malloc(block_size * block_size * sizeof(*plane));
1390*77c1e3ccSAndroid Build Coastguard Worker block =
1391*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_memalign(32, 2 * block_size * block_size * sizeof(*block));
1392*77c1e3ccSAndroid Build Coastguard Worker block_d = (double *)aom_malloc(block_size * block_size * sizeof(*block_d));
1393*77c1e3ccSAndroid Build Coastguard Worker plane_d = (double *)aom_malloc(block_size * block_size * sizeof(*plane_d));
1394*77c1e3ccSAndroid Build Coastguard Worker window_full = get_half_cos_window(block_size);
1395*77c1e3ccSAndroid Build Coastguard Worker tx_full = aom_noise_tx_malloc(block_size);
1396*77c1e3ccSAndroid Build Coastguard Worker
1397*77c1e3ccSAndroid Build Coastguard Worker if (chroma_sub[0] != 0) {
1398*77c1e3ccSAndroid Build Coastguard Worker init_success &= aom_flat_block_finder_init(&block_finder_chroma,
1399*77c1e3ccSAndroid Build Coastguard Worker block_size >> chroma_sub[0],
1400*77c1e3ccSAndroid Build Coastguard Worker bit_depth, use_highbd);
1401*77c1e3ccSAndroid Build Coastguard Worker window_chroma = get_half_cos_window(block_size >> chroma_sub[0]);
1402*77c1e3ccSAndroid Build Coastguard Worker tx_chroma = aom_noise_tx_malloc(block_size >> chroma_sub[0]);
1403*77c1e3ccSAndroid Build Coastguard Worker } else {
1404*77c1e3ccSAndroid Build Coastguard Worker window_chroma = window_full;
1405*77c1e3ccSAndroid Build Coastguard Worker tx_chroma = tx_full;
1406*77c1e3ccSAndroid Build Coastguard Worker }
1407*77c1e3ccSAndroid Build Coastguard Worker
1408*77c1e3ccSAndroid Build Coastguard Worker init_success &= (tx_full != NULL) && (tx_chroma != NULL) && (plane != NULL) &&
1409*77c1e3ccSAndroid Build Coastguard Worker (plane_d != NULL) && (block != NULL) && (block_d != NULL) &&
1410*77c1e3ccSAndroid Build Coastguard Worker (window_full != NULL) && (window_chroma != NULL) &&
1411*77c1e3ccSAndroid Build Coastguard Worker (result != NULL);
1412*77c1e3ccSAndroid Build Coastguard Worker for (int c = init_success ? 0 : 3; c < 3; ++c) {
1413*77c1e3ccSAndroid Build Coastguard Worker float *window_function = c == 0 ? window_full : window_chroma;
1414*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t *block_finder = &block_finder_full;
1415*77c1e3ccSAndroid Build Coastguard Worker const int chroma_sub_h = c > 0 ? chroma_sub[1] : 0;
1416*77c1e3ccSAndroid Build Coastguard Worker const int chroma_sub_w = c > 0 ? chroma_sub[0] : 0;
1417*77c1e3ccSAndroid Build Coastguard Worker struct aom_noise_tx_t *tx =
1418*77c1e3ccSAndroid Build Coastguard Worker (c > 0 && chroma_sub[0] > 0) ? tx_chroma : tx_full;
1419*77c1e3ccSAndroid Build Coastguard Worker if (!data[c] || !denoised[c]) continue;
1420*77c1e3ccSAndroid Build Coastguard Worker if (c > 0 && chroma_sub[0] != 0) {
1421*77c1e3ccSAndroid Build Coastguard Worker block_finder = &block_finder_chroma;
1422*77c1e3ccSAndroid Build Coastguard Worker }
1423*77c1e3ccSAndroid Build Coastguard Worker memset(result, 0, sizeof(*result) * result_stride * result_height);
1424*77c1e3ccSAndroid Build Coastguard Worker // Do overlapped block processing (half overlapped). The block rows can
1425*77c1e3ccSAndroid Build Coastguard Worker // easily be done in parallel
1426*77c1e3ccSAndroid Build Coastguard Worker for (int offsy = 0; offsy < (block_size >> chroma_sub_h);
1427*77c1e3ccSAndroid Build Coastguard Worker offsy += (block_size >> chroma_sub_h) / 2) {
1428*77c1e3ccSAndroid Build Coastguard Worker for (int offsx = 0; offsx < (block_size >> chroma_sub_w);
1429*77c1e3ccSAndroid Build Coastguard Worker offsx += (block_size >> chroma_sub_w) / 2) {
1430*77c1e3ccSAndroid Build Coastguard Worker // Pad the boundary when processing each block-set.
1431*77c1e3ccSAndroid Build Coastguard Worker for (int by = -1; by < num_blocks_h; ++by) {
1432*77c1e3ccSAndroid Build Coastguard Worker for (int bx = -1; bx < num_blocks_w; ++bx) {
1433*77c1e3ccSAndroid Build Coastguard Worker const int pixels_per_block =
1434*77c1e3ccSAndroid Build Coastguard Worker (block_size >> chroma_sub_w) * (block_size >> chroma_sub_h);
1435*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_extract_block(
1436*77c1e3ccSAndroid Build Coastguard Worker block_finder, data[c], w >> chroma_sub_w, h >> chroma_sub_h,
1437*77c1e3ccSAndroid Build Coastguard Worker stride[c], bx * (block_size >> chroma_sub_w) + offsx,
1438*77c1e3ccSAndroid Build Coastguard Worker by * (block_size >> chroma_sub_h) + offsy, plane_d, block_d);
1439*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < pixels_per_block; ++j) {
1440*77c1e3ccSAndroid Build Coastguard Worker block[j] = (float)block_d[j];
1441*77c1e3ccSAndroid Build Coastguard Worker plane[j] = (float)plane_d[j];
1442*77c1e3ccSAndroid Build Coastguard Worker }
1443*77c1e3ccSAndroid Build Coastguard Worker pointwise_multiply(window_function, block, pixels_per_block);
1444*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_forward(tx, block);
1445*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_filter(tx, noise_psd[c]);
1446*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_inverse(tx, block);
1447*77c1e3ccSAndroid Build Coastguard Worker
1448*77c1e3ccSAndroid Build Coastguard Worker // Apply window function to the plane approximation (we will apply
1449*77c1e3ccSAndroid Build Coastguard Worker // it to the sum of plane + block when composing the results).
1450*77c1e3ccSAndroid Build Coastguard Worker pointwise_multiply(window_function, plane, pixels_per_block);
1451*77c1e3ccSAndroid Build Coastguard Worker
1452*77c1e3ccSAndroid Build Coastguard Worker for (int y = 0; y < (block_size >> chroma_sub_h); ++y) {
1453*77c1e3ccSAndroid Build Coastguard Worker const int y_result =
1454*77c1e3ccSAndroid Build Coastguard Worker y + (by + 1) * (block_size >> chroma_sub_h) + offsy;
1455*77c1e3ccSAndroid Build Coastguard Worker for (int x = 0; x < (block_size >> chroma_sub_w); ++x) {
1456*77c1e3ccSAndroid Build Coastguard Worker const int x_result =
1457*77c1e3ccSAndroid Build Coastguard Worker x + (bx + 1) * (block_size >> chroma_sub_w) + offsx;
1458*77c1e3ccSAndroid Build Coastguard Worker result[y_result * result_stride + x_result] +=
1459*77c1e3ccSAndroid Build Coastguard Worker (block[y * (block_size >> chroma_sub_w) + x] +
1460*77c1e3ccSAndroid Build Coastguard Worker plane[y * (block_size >> chroma_sub_w) + x]) *
1461*77c1e3ccSAndroid Build Coastguard Worker window_function[y * (block_size >> chroma_sub_w) + x];
1462*77c1e3ccSAndroid Build Coastguard Worker }
1463*77c1e3ccSAndroid Build Coastguard Worker }
1464*77c1e3ccSAndroid Build Coastguard Worker }
1465*77c1e3ccSAndroid Build Coastguard Worker }
1466*77c1e3ccSAndroid Build Coastguard Worker }
1467*77c1e3ccSAndroid Build Coastguard Worker }
1468*77c1e3ccSAndroid Build Coastguard Worker if (use_highbd) {
1469*77c1e3ccSAndroid Build Coastguard Worker dither_and_quantize_highbd(result, result_stride, (uint16_t *)denoised[c],
1470*77c1e3ccSAndroid Build Coastguard Worker w, h, stride[c], chroma_sub_w, chroma_sub_h,
1471*77c1e3ccSAndroid Build Coastguard Worker block_size, kBlockNormalization);
1472*77c1e3ccSAndroid Build Coastguard Worker } else {
1473*77c1e3ccSAndroid Build Coastguard Worker dither_and_quantize_lowbd(result, result_stride, denoised[c], w, h,
1474*77c1e3ccSAndroid Build Coastguard Worker stride[c], chroma_sub_w, chroma_sub_h,
1475*77c1e3ccSAndroid Build Coastguard Worker block_size, kBlockNormalization);
1476*77c1e3ccSAndroid Build Coastguard Worker }
1477*77c1e3ccSAndroid Build Coastguard Worker }
1478*77c1e3ccSAndroid Build Coastguard Worker aom_free(result);
1479*77c1e3ccSAndroid Build Coastguard Worker aom_free(plane);
1480*77c1e3ccSAndroid Build Coastguard Worker aom_free(block);
1481*77c1e3ccSAndroid Build Coastguard Worker aom_free(plane_d);
1482*77c1e3ccSAndroid Build Coastguard Worker aom_free(block_d);
1483*77c1e3ccSAndroid Build Coastguard Worker aom_free(window_full);
1484*77c1e3ccSAndroid Build Coastguard Worker
1485*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_free(tx_full);
1486*77c1e3ccSAndroid Build Coastguard Worker
1487*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&block_finder_full);
1488*77c1e3ccSAndroid Build Coastguard Worker if (chroma_sub[0] != 0) {
1489*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&block_finder_chroma);
1490*77c1e3ccSAndroid Build Coastguard Worker aom_free(window_chroma);
1491*77c1e3ccSAndroid Build Coastguard Worker aom_noise_tx_free(tx_chroma);
1492*77c1e3ccSAndroid Build Coastguard Worker }
1493*77c1e3ccSAndroid Build Coastguard Worker return init_success;
1494*77c1e3ccSAndroid Build Coastguard Worker }
1495*77c1e3ccSAndroid Build Coastguard Worker
1496*77c1e3ccSAndroid Build Coastguard Worker struct aom_denoise_and_model_t {
1497*77c1e3ccSAndroid Build Coastguard Worker int block_size;
1498*77c1e3ccSAndroid Build Coastguard Worker int bit_depth;
1499*77c1e3ccSAndroid Build Coastguard Worker float noise_level;
1500*77c1e3ccSAndroid Build Coastguard Worker
1501*77c1e3ccSAndroid Build Coastguard Worker // Size of current denoised buffer and flat_block buffer
1502*77c1e3ccSAndroid Build Coastguard Worker int width;
1503*77c1e3ccSAndroid Build Coastguard Worker int height;
1504*77c1e3ccSAndroid Build Coastguard Worker int y_stride;
1505*77c1e3ccSAndroid Build Coastguard Worker int uv_stride;
1506*77c1e3ccSAndroid Build Coastguard Worker int num_blocks_w;
1507*77c1e3ccSAndroid Build Coastguard Worker int num_blocks_h;
1508*77c1e3ccSAndroid Build Coastguard Worker
1509*77c1e3ccSAndroid Build Coastguard Worker // Buffers for image and noise_psd allocated on the fly
1510*77c1e3ccSAndroid Build Coastguard Worker float *noise_psd[3];
1511*77c1e3ccSAndroid Build Coastguard Worker uint8_t *denoised[3];
1512*77c1e3ccSAndroid Build Coastguard Worker uint8_t *flat_blocks;
1513*77c1e3ccSAndroid Build Coastguard Worker
1514*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_t flat_block_finder;
1515*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_t noise_model;
1516*77c1e3ccSAndroid Build Coastguard Worker };
1517*77c1e3ccSAndroid Build Coastguard Worker
aom_denoise_and_model_alloc(int bit_depth,int block_size,float noise_level)1518*77c1e3ccSAndroid Build Coastguard Worker struct aom_denoise_and_model_t *aom_denoise_and_model_alloc(int bit_depth,
1519*77c1e3ccSAndroid Build Coastguard Worker int block_size,
1520*77c1e3ccSAndroid Build Coastguard Worker float noise_level) {
1521*77c1e3ccSAndroid Build Coastguard Worker struct aom_denoise_and_model_t *ctx =
1522*77c1e3ccSAndroid Build Coastguard Worker (struct aom_denoise_and_model_t *)aom_malloc(
1523*77c1e3ccSAndroid Build Coastguard Worker sizeof(struct aom_denoise_and_model_t));
1524*77c1e3ccSAndroid Build Coastguard Worker if (!ctx) {
1525*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate denoise_and_model struct\n");
1526*77c1e3ccSAndroid Build Coastguard Worker return NULL;
1527*77c1e3ccSAndroid Build Coastguard Worker }
1528*77c1e3ccSAndroid Build Coastguard Worker memset(ctx, 0, sizeof(*ctx));
1529*77c1e3ccSAndroid Build Coastguard Worker
1530*77c1e3ccSAndroid Build Coastguard Worker ctx->block_size = block_size;
1531*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_level = noise_level;
1532*77c1e3ccSAndroid Build Coastguard Worker ctx->bit_depth = bit_depth;
1533*77c1e3ccSAndroid Build Coastguard Worker
1534*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_psd[0] =
1535*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_malloc(sizeof(*ctx->noise_psd[0]) * block_size * block_size);
1536*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_psd[1] =
1537*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_malloc(sizeof(*ctx->noise_psd[1]) * block_size * block_size);
1538*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_psd[2] =
1539*77c1e3ccSAndroid Build Coastguard Worker (float *)aom_malloc(sizeof(*ctx->noise_psd[2]) * block_size * block_size);
1540*77c1e3ccSAndroid Build Coastguard Worker if (!ctx->noise_psd[0] || !ctx->noise_psd[1] || !ctx->noise_psd[2]) {
1541*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate noise PSD buffers\n");
1542*77c1e3ccSAndroid Build Coastguard Worker aom_denoise_and_model_free(ctx);
1543*77c1e3ccSAndroid Build Coastguard Worker return NULL;
1544*77c1e3ccSAndroid Build Coastguard Worker }
1545*77c1e3ccSAndroid Build Coastguard Worker return ctx;
1546*77c1e3ccSAndroid Build Coastguard Worker }
1547*77c1e3ccSAndroid Build Coastguard Worker
aom_denoise_and_model_free(struct aom_denoise_and_model_t * ctx)1548*77c1e3ccSAndroid Build Coastguard Worker void aom_denoise_and_model_free(struct aom_denoise_and_model_t *ctx) {
1549*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx->flat_blocks);
1550*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < 3; ++i) {
1551*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx->denoised[i]);
1552*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx->noise_psd[i]);
1553*77c1e3ccSAndroid Build Coastguard Worker }
1554*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&ctx->noise_model);
1555*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&ctx->flat_block_finder);
1556*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx);
1557*77c1e3ccSAndroid Build Coastguard Worker }
1558*77c1e3ccSAndroid Build Coastguard Worker
denoise_and_model_realloc_if_necessary(struct aom_denoise_and_model_t * ctx,const YV12_BUFFER_CONFIG * sd)1559*77c1e3ccSAndroid Build Coastguard Worker static int denoise_and_model_realloc_if_necessary(
1560*77c1e3ccSAndroid Build Coastguard Worker struct aom_denoise_and_model_t *ctx, const YV12_BUFFER_CONFIG *sd) {
1561*77c1e3ccSAndroid Build Coastguard Worker if (ctx->width == sd->y_width && ctx->height == sd->y_height &&
1562*77c1e3ccSAndroid Build Coastguard Worker ctx->y_stride == sd->y_stride && ctx->uv_stride == sd->uv_stride)
1563*77c1e3ccSAndroid Build Coastguard Worker return 1;
1564*77c1e3ccSAndroid Build Coastguard Worker const int use_highbd = (sd->flags & YV12_FLAG_HIGHBITDEPTH) != 0;
1565*77c1e3ccSAndroid Build Coastguard Worker const int block_size = ctx->block_size;
1566*77c1e3ccSAndroid Build Coastguard Worker
1567*77c1e3ccSAndroid Build Coastguard Worker ctx->width = sd->y_width;
1568*77c1e3ccSAndroid Build Coastguard Worker ctx->height = sd->y_height;
1569*77c1e3ccSAndroid Build Coastguard Worker ctx->y_stride = sd->y_stride;
1570*77c1e3ccSAndroid Build Coastguard Worker ctx->uv_stride = sd->uv_stride;
1571*77c1e3ccSAndroid Build Coastguard Worker
1572*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < 3; ++i) {
1573*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx->denoised[i]);
1574*77c1e3ccSAndroid Build Coastguard Worker ctx->denoised[i] = NULL;
1575*77c1e3ccSAndroid Build Coastguard Worker }
1576*77c1e3ccSAndroid Build Coastguard Worker aom_free(ctx->flat_blocks);
1577*77c1e3ccSAndroid Build Coastguard Worker ctx->flat_blocks = NULL;
1578*77c1e3ccSAndroid Build Coastguard Worker
1579*77c1e3ccSAndroid Build Coastguard Worker ctx->denoised[0] =
1580*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)aom_malloc((sd->y_stride * sd->y_height) << use_highbd);
1581*77c1e3ccSAndroid Build Coastguard Worker ctx->denoised[1] =
1582*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)aom_malloc((sd->uv_stride * sd->uv_height) << use_highbd);
1583*77c1e3ccSAndroid Build Coastguard Worker ctx->denoised[2] =
1584*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)aom_malloc((sd->uv_stride * sd->uv_height) << use_highbd);
1585*77c1e3ccSAndroid Build Coastguard Worker if (!ctx->denoised[0] || !ctx->denoised[1] || !ctx->denoised[2]) {
1586*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate denoise buffers\n");
1587*77c1e3ccSAndroid Build Coastguard Worker return 0;
1588*77c1e3ccSAndroid Build Coastguard Worker }
1589*77c1e3ccSAndroid Build Coastguard Worker ctx->num_blocks_w = (sd->y_width + ctx->block_size - 1) / ctx->block_size;
1590*77c1e3ccSAndroid Build Coastguard Worker ctx->num_blocks_h = (sd->y_height + ctx->block_size - 1) / ctx->block_size;
1591*77c1e3ccSAndroid Build Coastguard Worker ctx->flat_blocks =
1592*77c1e3ccSAndroid Build Coastguard Worker (uint8_t *)aom_malloc(ctx->num_blocks_w * ctx->num_blocks_h);
1593*77c1e3ccSAndroid Build Coastguard Worker if (!ctx->flat_blocks) {
1594*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to allocate flat_blocks buffer\n");
1595*77c1e3ccSAndroid Build Coastguard Worker return 0;
1596*77c1e3ccSAndroid Build Coastguard Worker }
1597*77c1e3ccSAndroid Build Coastguard Worker
1598*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_free(&ctx->flat_block_finder);
1599*77c1e3ccSAndroid Build Coastguard Worker if (!aom_flat_block_finder_init(&ctx->flat_block_finder, ctx->block_size,
1600*77c1e3ccSAndroid Build Coastguard Worker ctx->bit_depth, use_highbd)) {
1601*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to init flat block finder\n");
1602*77c1e3ccSAndroid Build Coastguard Worker return 0;
1603*77c1e3ccSAndroid Build Coastguard Worker }
1604*77c1e3ccSAndroid Build Coastguard Worker
1605*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_model_params_t params = { AOM_NOISE_SHAPE_SQUARE, 3,
1606*77c1e3ccSAndroid Build Coastguard Worker ctx->bit_depth, use_highbd };
1607*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_free(&ctx->noise_model);
1608*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_model_init(&ctx->noise_model, params)) {
1609*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to init noise model\n");
1610*77c1e3ccSAndroid Build Coastguard Worker return 0;
1611*77c1e3ccSAndroid Build Coastguard Worker }
1612*77c1e3ccSAndroid Build Coastguard Worker
1613*77c1e3ccSAndroid Build Coastguard Worker // Simply use a flat PSD (although we could use the flat blocks to estimate
1614*77c1e3ccSAndroid Build Coastguard Worker // PSD) those to estimate an actual noise PSD)
1615*77c1e3ccSAndroid Build Coastguard Worker const float y_noise_level =
1616*77c1e3ccSAndroid Build Coastguard Worker aom_noise_psd_get_default_value(ctx->block_size, ctx->noise_level);
1617*77c1e3ccSAndroid Build Coastguard Worker const float uv_noise_level = aom_noise_psd_get_default_value(
1618*77c1e3ccSAndroid Build Coastguard Worker ctx->block_size >> sd->subsampling_x, ctx->noise_level);
1619*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < block_size * block_size; ++i) {
1620*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_psd[0][i] = y_noise_level;
1621*77c1e3ccSAndroid Build Coastguard Worker ctx->noise_psd[1][i] = ctx->noise_psd[2][i] = uv_noise_level;
1622*77c1e3ccSAndroid Build Coastguard Worker }
1623*77c1e3ccSAndroid Build Coastguard Worker return 1;
1624*77c1e3ccSAndroid Build Coastguard Worker }
1625*77c1e3ccSAndroid Build Coastguard Worker
1626*77c1e3ccSAndroid Build Coastguard Worker // TODO(aomedia:3151): Handle a monochrome image (sd->u_buffer and sd->v_buffer
1627*77c1e3ccSAndroid Build Coastguard Worker // are null pointers) correctly.
aom_denoise_and_model_run(struct aom_denoise_and_model_t * ctx,const YV12_BUFFER_CONFIG * sd,aom_film_grain_t * film_grain,int apply_denoise)1628*77c1e3ccSAndroid Build Coastguard Worker int aom_denoise_and_model_run(struct aom_denoise_and_model_t *ctx,
1629*77c1e3ccSAndroid Build Coastguard Worker const YV12_BUFFER_CONFIG *sd,
1630*77c1e3ccSAndroid Build Coastguard Worker aom_film_grain_t *film_grain, int apply_denoise) {
1631*77c1e3ccSAndroid Build Coastguard Worker const int block_size = ctx->block_size;
1632*77c1e3ccSAndroid Build Coastguard Worker const int use_highbd = (sd->flags & YV12_FLAG_HIGHBITDEPTH) != 0;
1633*77c1e3ccSAndroid Build Coastguard Worker uint8_t *raw_data[3] = {
1634*77c1e3ccSAndroid Build Coastguard Worker use_highbd ? (uint8_t *)CONVERT_TO_SHORTPTR(sd->y_buffer) : sd->y_buffer,
1635*77c1e3ccSAndroid Build Coastguard Worker use_highbd ? (uint8_t *)CONVERT_TO_SHORTPTR(sd->u_buffer) : sd->u_buffer,
1636*77c1e3ccSAndroid Build Coastguard Worker use_highbd ? (uint8_t *)CONVERT_TO_SHORTPTR(sd->v_buffer) : sd->v_buffer,
1637*77c1e3ccSAndroid Build Coastguard Worker };
1638*77c1e3ccSAndroid Build Coastguard Worker const uint8_t *const data[3] = { raw_data[0], raw_data[1], raw_data[2] };
1639*77c1e3ccSAndroid Build Coastguard Worker int strides[3] = { sd->y_stride, sd->uv_stride, sd->uv_stride };
1640*77c1e3ccSAndroid Build Coastguard Worker int chroma_sub_log2[2] = { sd->subsampling_x, sd->subsampling_y };
1641*77c1e3ccSAndroid Build Coastguard Worker
1642*77c1e3ccSAndroid Build Coastguard Worker if (!denoise_and_model_realloc_if_necessary(ctx, sd)) {
1643*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to realloc buffers\n");
1644*77c1e3ccSAndroid Build Coastguard Worker return 0;
1645*77c1e3ccSAndroid Build Coastguard Worker }
1646*77c1e3ccSAndroid Build Coastguard Worker
1647*77c1e3ccSAndroid Build Coastguard Worker aom_flat_block_finder_run(&ctx->flat_block_finder, data[0], sd->y_width,
1648*77c1e3ccSAndroid Build Coastguard Worker sd->y_height, strides[0], ctx->flat_blocks);
1649*77c1e3ccSAndroid Build Coastguard Worker
1650*77c1e3ccSAndroid Build Coastguard Worker if (!aom_wiener_denoise_2d(data, ctx->denoised, sd->y_width, sd->y_height,
1651*77c1e3ccSAndroid Build Coastguard Worker strides, chroma_sub_log2, ctx->noise_psd,
1652*77c1e3ccSAndroid Build Coastguard Worker block_size, ctx->bit_depth, use_highbd)) {
1653*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to denoise image\n");
1654*77c1e3ccSAndroid Build Coastguard Worker return 0;
1655*77c1e3ccSAndroid Build Coastguard Worker }
1656*77c1e3ccSAndroid Build Coastguard Worker
1657*77c1e3ccSAndroid Build Coastguard Worker const aom_noise_status_t status = aom_noise_model_update(
1658*77c1e3ccSAndroid Build Coastguard Worker &ctx->noise_model, data, (const uint8_t *const *)ctx->denoised,
1659*77c1e3ccSAndroid Build Coastguard Worker sd->y_width, sd->y_height, strides, chroma_sub_log2, ctx->flat_blocks,
1660*77c1e3ccSAndroid Build Coastguard Worker block_size);
1661*77c1e3ccSAndroid Build Coastguard Worker int have_noise_estimate = 0;
1662*77c1e3ccSAndroid Build Coastguard Worker if (status == AOM_NOISE_STATUS_OK) {
1663*77c1e3ccSAndroid Build Coastguard Worker have_noise_estimate = 1;
1664*77c1e3ccSAndroid Build Coastguard Worker } else if (status == AOM_NOISE_STATUS_DIFFERENT_NOISE_TYPE) {
1665*77c1e3ccSAndroid Build Coastguard Worker aom_noise_model_save_latest(&ctx->noise_model);
1666*77c1e3ccSAndroid Build Coastguard Worker have_noise_estimate = 1;
1667*77c1e3ccSAndroid Build Coastguard Worker } else {
1668*77c1e3ccSAndroid Build Coastguard Worker // Unable to update noise model; proceed if we have a previous estimate.
1669*77c1e3ccSAndroid Build Coastguard Worker have_noise_estimate =
1670*77c1e3ccSAndroid Build Coastguard Worker (ctx->noise_model.combined_state[0].strength_solver.num_equations > 0);
1671*77c1e3ccSAndroid Build Coastguard Worker }
1672*77c1e3ccSAndroid Build Coastguard Worker
1673*77c1e3ccSAndroid Build Coastguard Worker film_grain->apply_grain = 0;
1674*77c1e3ccSAndroid Build Coastguard Worker if (have_noise_estimate) {
1675*77c1e3ccSAndroid Build Coastguard Worker if (!aom_noise_model_get_grain_parameters(&ctx->noise_model, film_grain)) {
1676*77c1e3ccSAndroid Build Coastguard Worker fprintf(stderr, "Unable to get grain parameters.\n");
1677*77c1e3ccSAndroid Build Coastguard Worker return 0;
1678*77c1e3ccSAndroid Build Coastguard Worker }
1679*77c1e3ccSAndroid Build Coastguard Worker if (!film_grain->random_seed) {
1680*77c1e3ccSAndroid Build Coastguard Worker film_grain->random_seed = 7391;
1681*77c1e3ccSAndroid Build Coastguard Worker }
1682*77c1e3ccSAndroid Build Coastguard Worker if (apply_denoise) {
1683*77c1e3ccSAndroid Build Coastguard Worker memcpy(raw_data[0], ctx->denoised[0],
1684*77c1e3ccSAndroid Build Coastguard Worker (strides[0] * sd->y_height) << use_highbd);
1685*77c1e3ccSAndroid Build Coastguard Worker if (!sd->monochrome) {
1686*77c1e3ccSAndroid Build Coastguard Worker memcpy(raw_data[1], ctx->denoised[1],
1687*77c1e3ccSAndroid Build Coastguard Worker (strides[1] * sd->uv_height) << use_highbd);
1688*77c1e3ccSAndroid Build Coastguard Worker memcpy(raw_data[2], ctx->denoised[2],
1689*77c1e3ccSAndroid Build Coastguard Worker (strides[2] * sd->uv_height) << use_highbd);
1690*77c1e3ccSAndroid Build Coastguard Worker }
1691*77c1e3ccSAndroid Build Coastguard Worker }
1692*77c1e3ccSAndroid Build Coastguard Worker }
1693*77c1e3ccSAndroid Build Coastguard Worker return 1;
1694*77c1e3ccSAndroid Build Coastguard Worker }
1695