1 /*
2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "denoising.h"
12
13 #include "vp8/common/reconinter.h"
14 #include "vpx/vpx_integer.h"
15 #include "vpx_mem/vpx_mem.h"
16 #include "vp8_rtcd.h"
17
18 static const unsigned int NOISE_MOTION_THRESHOLD = 25 * 25;
19 /* SSE_DIFF_THRESHOLD is selected as ~95% confidence assuming
20 * var(noise) ~= 100.
21 */
22 static const unsigned int SSE_DIFF_THRESHOLD = 16 * 16 * 20;
23 static const unsigned int SSE_THRESHOLD = 16 * 16 * 40;
24
25 /*
26 * The filter function was modified to reduce the computational complexity.
27 * Step 1:
28 * Instead of applying tap coefficients for each pixel, we calculated the
29 * pixel adjustments vs. pixel diff value ahead of time.
30 * adjustment = filtered_value - current_raw
31 * = (filter_coefficient * diff + 128) >> 8
32 * where
33 * filter_coefficient = (255 << 8) / (256 + ((absdiff * 330) >> 3));
34 * filter_coefficient += filter_coefficient /
35 * (3 + motion_magnitude_adjustment);
36 * filter_coefficient is clamped to 0 ~ 255.
37 *
38 * Step 2:
39 * The adjustment vs. diff curve becomes flat very quick when diff increases.
40 * This allowed us to use only several levels to approximate the curve without
41 * changing the filtering algorithm too much.
42 * The adjustments were further corrected by checking the motion magnitude.
43 * The levels used are:
44 * diff adjustment w/o motion correction adjustment w/ motion correction
45 * [-255, -16] -6 -7
46 * [-15, -8] -4 -5
47 * [-7, -4] -3 -4
48 * [-3, 3] diff diff
49 * [4, 7] 3 4
50 * [8, 15] 4 5
51 * [16, 255] 6 7
52 */
53
vp8_denoiser_filter_c(YV12_BUFFER_CONFIG * mc_running_avg,YV12_BUFFER_CONFIG * running_avg,MACROBLOCK * signal,unsigned int motion_magnitude,int y_offset,int uv_offset)54 int vp8_denoiser_filter_c(YV12_BUFFER_CONFIG *mc_running_avg,
55 YV12_BUFFER_CONFIG *running_avg, MACROBLOCK *signal,
56 unsigned int motion_magnitude, int y_offset,
57 int uv_offset)
58 {
59 unsigned char *sig = signal->thismb;
60 int sig_stride = 16;
61 unsigned char *mc_running_avg_y = mc_running_avg->y_buffer + y_offset;
62 int mc_avg_y_stride = mc_running_avg->y_stride;
63 unsigned char *running_avg_y = running_avg->y_buffer + y_offset;
64 int avg_y_stride = running_avg->y_stride;
65 int r, c, i;
66 int sum_diff = 0;
67 int adj_val[3] = {3, 4, 6};
68 (void)uv_offset;
69
70 /* If motion_magnitude is small, making the denoiser more aggressive by
71 * increasing the adjustment for each level. */
72 if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD)
73 {
74 for (i = 0; i < 3; i++)
75 adj_val[i] += 1;
76 }
77
78 for (r = 0; r < 16; ++r)
79 {
80 for (c = 0; c < 16; ++c)
81 {
82 int diff = 0;
83 int adjustment = 0;
84 int absdiff = 0;
85
86 diff = mc_running_avg_y[c] - sig[c];
87 absdiff = abs(diff);
88
89 /* When |diff| < 4, use pixel value from last denoised raw. */
90 if (absdiff <= 3)
91 {
92 running_avg_y[c] = mc_running_avg_y[c];
93 sum_diff += diff;
94 }
95 else
96 {
97 if (absdiff >= 4 && absdiff <= 7)
98 adjustment = adj_val[0];
99 else if (absdiff >= 8 && absdiff <= 15)
100 adjustment = adj_val[1];
101 else
102 adjustment = adj_val[2];
103
104 if (diff > 0)
105 {
106 if ((sig[c] + adjustment) > 255)
107 running_avg_y[c] = 255;
108 else
109 running_avg_y[c] = sig[c] + adjustment;
110
111 sum_diff += adjustment;
112 }
113 else
114 {
115 if ((sig[c] - adjustment) < 0)
116 running_avg_y[c] = 0;
117 else
118 running_avg_y[c] = sig[c] - adjustment;
119
120 sum_diff -= adjustment;
121 }
122 }
123 }
124
125 /* Update pointers for next iteration. */
126 sig += sig_stride;
127 mc_running_avg_y += mc_avg_y_stride;
128 running_avg_y += avg_y_stride;
129 }
130
131 if (abs(sum_diff) > SUM_DIFF_THRESHOLD)
132 return COPY_BLOCK;
133
134 vp8_copy_mem16x16(running_avg->y_buffer + y_offset, avg_y_stride,
135 signal->thismb, sig_stride);
136 return FILTER_BLOCK;
137 }
138
vp8_denoiser_allocate(VP8_DENOISER * denoiser,int width,int height)139 int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height)
140 {
141 int i;
142 assert(denoiser);
143
144 for (i = 0; i < MAX_REF_FRAMES; i++)
145 {
146 denoiser->yv12_running_avg[i].flags = 0;
147
148 if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_running_avg[i]), width,
149 height, VP8BORDERINPIXELS)
150 < 0)
151 {
152 vp8_denoiser_free(denoiser);
153 return 1;
154 }
155 vpx_memset(denoiser->yv12_running_avg[i].buffer_alloc, 0,
156 denoiser->yv12_running_avg[i].frame_size);
157
158 }
159 denoiser->yv12_mc_running_avg.flags = 0;
160
161 if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_mc_running_avg), width,
162 height, VP8BORDERINPIXELS) < 0)
163 {
164 vp8_denoiser_free(denoiser);
165 return 1;
166 }
167
168 vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0,
169 denoiser->yv12_mc_running_avg.frame_size);
170 return 0;
171 }
172
vp8_denoiser_free(VP8_DENOISER * denoiser)173 void vp8_denoiser_free(VP8_DENOISER *denoiser)
174 {
175 int i;
176 assert(denoiser);
177
178 for (i = 0; i < MAX_REF_FRAMES ; i++)
179 {
180 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg[i]);
181 }
182 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg);
183 }
184
185
vp8_denoiser_denoise_mb(VP8_DENOISER * denoiser,MACROBLOCK * x,unsigned int best_sse,unsigned int zero_mv_sse,int recon_yoffset,int recon_uvoffset)186 void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser,
187 MACROBLOCK *x,
188 unsigned int best_sse,
189 unsigned int zero_mv_sse,
190 int recon_yoffset,
191 int recon_uvoffset)
192 {
193 int mv_row;
194 int mv_col;
195 unsigned int motion_magnitude2;
196
197 MV_REFERENCE_FRAME frame = x->best_reference_frame;
198 MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame;
199
200 enum vp8_denoiser_decision decision = FILTER_BLOCK;
201
202 if (zero_frame)
203 {
204 YV12_BUFFER_CONFIG *src = &denoiser->yv12_running_avg[frame];
205 YV12_BUFFER_CONFIG *dst = &denoiser->yv12_mc_running_avg;
206 YV12_BUFFER_CONFIG saved_pre,saved_dst;
207 MB_MODE_INFO saved_mbmi;
208 MACROBLOCKD *filter_xd = &x->e_mbd;
209 MB_MODE_INFO *mbmi = &filter_xd->mode_info_context->mbmi;
210 int sse_diff = zero_mv_sse - best_sse;
211
212 saved_mbmi = *mbmi;
213
214 /* Use the best MV for the compensation. */
215 mbmi->ref_frame = x->best_reference_frame;
216 mbmi->mode = x->best_sse_inter_mode;
217 mbmi->mv = x->best_sse_mv;
218 mbmi->need_to_clamp_mvs = x->need_to_clamp_best_mvs;
219 mv_col = x->best_sse_mv.as_mv.col;
220 mv_row = x->best_sse_mv.as_mv.row;
221
222 if (frame == INTRA_FRAME ||
223 ((unsigned int)(mv_row *mv_row + mv_col *mv_col)
224 <= NOISE_MOTION_THRESHOLD &&
225 sse_diff < (int)SSE_DIFF_THRESHOLD))
226 {
227 /*
228 * Handle intra blocks as referring to last frame with zero motion
229 * and let the absolute pixel difference affect the filter factor.
230 * Also consider small amount of motion as being random walk due
231 * to noise, if it doesn't mean that we get a much bigger error.
232 * Note that any changes to the mode info only affects the
233 * denoising.
234 */
235 mbmi->ref_frame =
236 x->best_zeromv_reference_frame;
237
238 src = &denoiser->yv12_running_avg[zero_frame];
239
240 mbmi->mode = ZEROMV;
241 mbmi->mv.as_int = 0;
242 x->best_sse_inter_mode = ZEROMV;
243 x->best_sse_mv.as_int = 0;
244 best_sse = zero_mv_sse;
245 }
246
247 saved_pre = filter_xd->pre;
248 saved_dst = filter_xd->dst;
249
250 /* Compensate the running average. */
251 filter_xd->pre.y_buffer = src->y_buffer + recon_yoffset;
252 filter_xd->pre.u_buffer = src->u_buffer + recon_uvoffset;
253 filter_xd->pre.v_buffer = src->v_buffer + recon_uvoffset;
254 /* Write the compensated running average to the destination buffer. */
255 filter_xd->dst.y_buffer = dst->y_buffer + recon_yoffset;
256 filter_xd->dst.u_buffer = dst->u_buffer + recon_uvoffset;
257 filter_xd->dst.v_buffer = dst->v_buffer + recon_uvoffset;
258
259 if (!x->skip)
260 {
261 vp8_build_inter_predictors_mb(filter_xd);
262 }
263 else
264 {
265 vp8_build_inter16x16_predictors_mb(filter_xd,
266 filter_xd->dst.y_buffer,
267 filter_xd->dst.u_buffer,
268 filter_xd->dst.v_buffer,
269 filter_xd->dst.y_stride,
270 filter_xd->dst.uv_stride);
271 }
272 filter_xd->pre = saved_pre;
273 filter_xd->dst = saved_dst;
274 *mbmi = saved_mbmi;
275
276 }
277
278 mv_row = x->best_sse_mv.as_mv.row;
279 mv_col = x->best_sse_mv.as_mv.col;
280 motion_magnitude2 = mv_row * mv_row + mv_col * mv_col;
281 if (best_sse > SSE_THRESHOLD || motion_magnitude2
282 > 8 * NOISE_MOTION_THRESHOLD)
283 {
284 decision = COPY_BLOCK;
285 }
286
287 if (decision == FILTER_BLOCK)
288 {
289 /* Filter. */
290 decision = vp8_denoiser_filter(&denoiser->yv12_mc_running_avg,
291 &denoiser->yv12_running_avg[INTRA_FRAME],
292 x,
293 motion_magnitude2,
294 recon_yoffset, recon_uvoffset);
295 }
296 if (decision == COPY_BLOCK)
297 {
298 /* No filtering of this block; it differs too much from the predictor,
299 * or the motion vector magnitude is considered too big.
300 */
301 vp8_copy_mem16x16(
302 x->thismb, 16,
303 denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
304 denoiser->yv12_running_avg[INTRA_FRAME].y_stride);
305 }
306 }
307