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
69 /* If motion_magnitude is small, making the denoiser more aggressive by
70 * increasing the adjustment for each level. */
71 if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD)
72 {
73 for (i = 0; i < 3; i++)
74 adj_val[i] += 1;
75 }
76
77 for (r = 0; r < 16; ++r)
78 {
79 for (c = 0; c < 16; ++c)
80 {
81 int diff = 0;
82 int adjustment = 0;
83 int absdiff = 0;
84
85 diff = mc_running_avg_y[c] - sig[c];
86 absdiff = abs(diff);
87
88 /* When |diff| < 4, use pixel value from last denoised raw. */
89 if (absdiff <= 3)
90 {
91 running_avg_y[c] = mc_running_avg_y[c];
92 sum_diff += diff;
93 }
94 else
95 {
96 if (absdiff >= 4 && absdiff <= 7)
97 adjustment = adj_val[0];
98 else if (absdiff >= 8 && absdiff <= 15)
99 adjustment = adj_val[1];
100 else
101 adjustment = adj_val[2];
102
103 if (diff > 0)
104 {
105 if ((sig[c] + adjustment) > 255)
106 running_avg_y[c] = 255;
107 else
108 running_avg_y[c] = sig[c] + adjustment;
109
110 sum_diff += adjustment;
111 }
112 else
113 {
114 if ((sig[c] - adjustment) < 0)
115 running_avg_y[c] = 0;
116 else
117 running_avg_y[c] = sig[c] - adjustment;
118
119 sum_diff -= adjustment;
120 }
121 }
122 }
123
124 /* Update pointers for next iteration. */
125 sig += sig_stride;
126 mc_running_avg_y += mc_avg_y_stride;
127 running_avg_y += avg_y_stride;
128 }
129
130 if (abs(sum_diff) > SUM_DIFF_THRESHOLD)
131 return COPY_BLOCK;
132
133 vp8_copy_mem16x16(running_avg->y_buffer + y_offset, avg_y_stride,
134 signal->thismb, sig_stride);
135 return FILTER_BLOCK;
136 }
137
vp8_denoiser_allocate(VP8_DENOISER * denoiser,int width,int height)138 int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height)
139 {
140 int i;
141 assert(denoiser);
142
143 for (i = 0; i < MAX_REF_FRAMES; i++)
144 {
145 denoiser->yv12_running_avg[i].flags = 0;
146
147 if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_running_avg[i]), width,
148 height, VP8BORDERINPIXELS)
149 < 0)
150 {
151 vp8_denoiser_free(denoiser);
152 return 1;
153 }
154 vpx_memset(denoiser->yv12_running_avg[i].buffer_alloc, 0,
155 denoiser->yv12_running_avg[i].frame_size);
156
157 }
158 denoiser->yv12_mc_running_avg.flags = 0;
159
160 if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_mc_running_avg), width,
161 height, VP8BORDERINPIXELS) < 0)
162 {
163 vp8_denoiser_free(denoiser);
164 return 1;
165 }
166
167 vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0,
168 denoiser->yv12_mc_running_avg.frame_size);
169 return 0;
170 }
171
vp8_denoiser_free(VP8_DENOISER * denoiser)172 void vp8_denoiser_free(VP8_DENOISER *denoiser)
173 {
174 int i;
175 assert(denoiser);
176
177 for (i = 0; i < MAX_REF_FRAMES ; i++)
178 {
179 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg[i]);
180 }
181 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg);
182 }
183
184
vp8_denoiser_denoise_mb(VP8_DENOISER * denoiser,MACROBLOCK * x,unsigned int best_sse,unsigned int zero_mv_sse,int recon_yoffset,int recon_uvoffset)185 void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser,
186 MACROBLOCK *x,
187 unsigned int best_sse,
188 unsigned int zero_mv_sse,
189 int recon_yoffset,
190 int recon_uvoffset)
191 {
192 int mv_row;
193 int mv_col;
194 unsigned int motion_magnitude2;
195
196 MV_REFERENCE_FRAME frame = x->best_reference_frame;
197 MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame;
198
199 enum vp8_denoiser_decision decision = FILTER_BLOCK;
200
201 if (zero_frame)
202 {
203 YV12_BUFFER_CONFIG *src = &denoiser->yv12_running_avg[frame];
204 YV12_BUFFER_CONFIG *dst = &denoiser->yv12_mc_running_avg;
205 YV12_BUFFER_CONFIG saved_pre,saved_dst;
206 MB_MODE_INFO saved_mbmi;
207 MACROBLOCKD *filter_xd = &x->e_mbd;
208 MB_MODE_INFO *mbmi = &filter_xd->mode_info_context->mbmi;
209 int sse_diff = zero_mv_sse - best_sse;
210
211 saved_mbmi = *mbmi;
212
213 /* Use the best MV for the compensation. */
214 mbmi->ref_frame = x->best_reference_frame;
215 mbmi->mode = x->best_sse_inter_mode;
216 mbmi->mv = x->best_sse_mv;
217 mbmi->need_to_clamp_mvs = x->need_to_clamp_best_mvs;
218 mv_col = x->best_sse_mv.as_mv.col;
219 mv_row = x->best_sse_mv.as_mv.row;
220
221 if (frame == INTRA_FRAME ||
222 ((unsigned int)(mv_row *mv_row + mv_col *mv_col)
223 <= NOISE_MOTION_THRESHOLD &&
224 sse_diff < (int)SSE_DIFF_THRESHOLD))
225 {
226 /*
227 * Handle intra blocks as referring to last frame with zero motion
228 * and let the absolute pixel difference affect the filter factor.
229 * Also consider small amount of motion as being random walk due
230 * to noise, if it doesn't mean that we get a much bigger error.
231 * Note that any changes to the mode info only affects the
232 * denoising.
233 */
234 mbmi->ref_frame =
235 x->best_zeromv_reference_frame;
236
237 src = &denoiser->yv12_running_avg[zero_frame];
238
239 mbmi->mode = ZEROMV;
240 mbmi->mv.as_int = 0;
241 x->best_sse_inter_mode = ZEROMV;
242 x->best_sse_mv.as_int = 0;
243 best_sse = zero_mv_sse;
244 }
245
246 saved_pre = filter_xd->pre;
247 saved_dst = filter_xd->dst;
248
249 /* Compensate the running average. */
250 filter_xd->pre.y_buffer = src->y_buffer + recon_yoffset;
251 filter_xd->pre.u_buffer = src->u_buffer + recon_uvoffset;
252 filter_xd->pre.v_buffer = src->v_buffer + recon_uvoffset;
253 /* Write the compensated running average to the destination buffer. */
254 filter_xd->dst.y_buffer = dst->y_buffer + recon_yoffset;
255 filter_xd->dst.u_buffer = dst->u_buffer + recon_uvoffset;
256 filter_xd->dst.v_buffer = dst->v_buffer + recon_uvoffset;
257
258 if (!x->skip)
259 {
260 vp8_build_inter_predictors_mb(filter_xd);
261 }
262 else
263 {
264 vp8_build_inter16x16_predictors_mb(filter_xd,
265 filter_xd->dst.y_buffer,
266 filter_xd->dst.u_buffer,
267 filter_xd->dst.v_buffer,
268 filter_xd->dst.y_stride,
269 filter_xd->dst.uv_stride);
270 }
271 filter_xd->pre = saved_pre;
272 filter_xd->dst = saved_dst;
273 *mbmi = saved_mbmi;
274
275 }
276
277 mv_row = x->best_sse_mv.as_mv.row;
278 mv_col = x->best_sse_mv.as_mv.col;
279 motion_magnitude2 = mv_row * mv_row + mv_col * mv_col;
280 if (best_sse > SSE_THRESHOLD || motion_magnitude2
281 > 8 * NOISE_MOTION_THRESHOLD)
282 {
283 decision = COPY_BLOCK;
284 }
285
286 if (decision == FILTER_BLOCK)
287 {
288 /* Filter. */
289 decision = vp8_denoiser_filter(&denoiser->yv12_mc_running_avg,
290 &denoiser->yv12_running_avg[INTRA_FRAME],
291 x,
292 motion_magnitude2,
293 recon_yoffset, recon_uvoffset);
294 }
295 if (decision == COPY_BLOCK)
296 {
297 /* No filtering of this block; it differs too much from the predictor,
298 * or the motion vector magnitude is considered too big.
299 */
300 vp8_copy_mem16x16(
301 x->thismb, 16,
302 denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset,
303 denoiser->yv12_running_avg[INTRA_FRAME].y_stride);
304 }
305 }
306