|
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 |
|
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 |
|
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 |
|
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 |
|
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 } |