/src/mozilla-central/media/libvpx/libvpx/vp8/encoder/denoising.c
Line | Count | Source (jump to first uncovered line) |
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 <limits.h> |
12 | | |
13 | | #include "denoising.h" |
14 | | |
15 | | #include "vp8/common/reconinter.h" |
16 | | #include "vpx/vpx_integer.h" |
17 | | #include "vpx_mem/vpx_mem.h" |
18 | | #include "vp8_rtcd.h" |
19 | | |
20 | | static const unsigned int NOISE_MOTION_THRESHOLD = 25 * 25; |
21 | | /* SSE_DIFF_THRESHOLD is selected as ~95% confidence assuming |
22 | | * var(noise) ~= 100. |
23 | | */ |
24 | | static const unsigned int SSE_DIFF_THRESHOLD = 16 * 16 * 20; |
25 | | static const unsigned int SSE_THRESHOLD = 16 * 16 * 40; |
26 | | static const unsigned int SSE_THRESHOLD_HIGH = 16 * 16 * 80; |
27 | | |
28 | | /* |
29 | | * The filter function was modified to reduce the computational complexity. |
30 | | * Step 1: |
31 | | * Instead of applying tap coefficients for each pixel, we calculated the |
32 | | * pixel adjustments vs. pixel diff value ahead of time. |
33 | | * adjustment = filtered_value - current_raw |
34 | | * = (filter_coefficient * diff + 128) >> 8 |
35 | | * where |
36 | | * filter_coefficient = (255 << 8) / (256 + ((absdiff * 330) >> 3)); |
37 | | * filter_coefficient += filter_coefficient / |
38 | | * (3 + motion_magnitude_adjustment); |
39 | | * filter_coefficient is clamped to 0 ~ 255. |
40 | | * |
41 | | * Step 2: |
42 | | * The adjustment vs. diff curve becomes flat very quick when diff increases. |
43 | | * This allowed us to use only several levels to approximate the curve without |
44 | | * changing the filtering algorithm too much. |
45 | | * The adjustments were further corrected by checking the motion magnitude. |
46 | | * The levels used are: |
47 | | * diff adjustment w/o motion correction adjustment w/ motion correction |
48 | | * [-255, -16] -6 -7 |
49 | | * [-15, -8] -4 -5 |
50 | | * [-7, -4] -3 -4 |
51 | | * [-3, 3] diff diff |
52 | | * [4, 7] 3 4 |
53 | | * [8, 15] 4 5 |
54 | | * [16, 255] 6 7 |
55 | | */ |
56 | | |
57 | | int vp8_denoiser_filter_c(unsigned char *mc_running_avg_y, int mc_avg_y_stride, |
58 | | unsigned char *running_avg_y, int avg_y_stride, |
59 | | unsigned char *sig, int sig_stride, |
60 | | unsigned int motion_magnitude, |
61 | 0 | int increase_denoising) { |
62 | 0 | unsigned char *running_avg_y_start = running_avg_y; |
63 | 0 | unsigned char *sig_start = sig; |
64 | 0 | int sum_diff_thresh; |
65 | 0 | int r, c; |
66 | 0 | int sum_diff = 0; |
67 | 0 | int adj_val[3] = { 3, 4, 6 }; |
68 | 0 | int shift_inc1 = 0; |
69 | 0 | int shift_inc2 = 1; |
70 | 0 | int col_sum[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
71 | 0 | /* If motion_magnitude is small, making the denoiser more aggressive by |
72 | 0 | * increasing the adjustment for each level. Add another increment for |
73 | 0 | * blocks that are labeled for increase denoising. */ |
74 | 0 | if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD) { |
75 | 0 | if (increase_denoising) { |
76 | 0 | shift_inc1 = 1; |
77 | 0 | shift_inc2 = 2; |
78 | 0 | } |
79 | 0 | adj_val[0] += shift_inc2; |
80 | 0 | adj_val[1] += shift_inc2; |
81 | 0 | adj_val[2] += shift_inc2; |
82 | 0 | } |
83 | 0 |
|
84 | 0 | for (r = 0; r < 16; ++r) { |
85 | 0 | for (c = 0; c < 16; ++c) { |
86 | 0 | int diff = 0; |
87 | 0 | int adjustment = 0; |
88 | 0 | int absdiff = 0; |
89 | 0 |
|
90 | 0 | diff = mc_running_avg_y[c] - sig[c]; |
91 | 0 | absdiff = abs(diff); |
92 | 0 |
|
93 | 0 | // When |diff| <= |3 + shift_inc1|, use pixel value from |
94 | 0 | // last denoised raw. |
95 | 0 | if (absdiff <= 3 + shift_inc1) { |
96 | 0 | running_avg_y[c] = mc_running_avg_y[c]; |
97 | 0 | col_sum[c] += diff; |
98 | 0 | } else { |
99 | 0 | if (absdiff >= 4 + shift_inc1 && absdiff <= 7) { |
100 | 0 | adjustment = adj_val[0]; |
101 | 0 | } else if (absdiff >= 8 && absdiff <= 15) { |
102 | 0 | adjustment = adj_val[1]; |
103 | 0 | } else { |
104 | 0 | adjustment = adj_val[2]; |
105 | 0 | } |
106 | 0 |
|
107 | 0 | if (diff > 0) { |
108 | 0 | if ((sig[c] + adjustment) > 255) { |
109 | 0 | running_avg_y[c] = 255; |
110 | 0 | } else { |
111 | 0 | running_avg_y[c] = sig[c] + adjustment; |
112 | 0 | } |
113 | 0 |
|
114 | 0 | col_sum[c] += adjustment; |
115 | 0 | } else { |
116 | 0 | if ((sig[c] - adjustment) < 0) { |
117 | 0 | running_avg_y[c] = 0; |
118 | 0 | } else { |
119 | 0 | running_avg_y[c] = sig[c] - adjustment; |
120 | 0 | } |
121 | 0 |
|
122 | 0 | col_sum[c] -= adjustment; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | } |
126 | 0 |
|
127 | 0 | /* Update pointers for next iteration. */ |
128 | 0 | sig += sig_stride; |
129 | 0 | mc_running_avg_y += mc_avg_y_stride; |
130 | 0 | running_avg_y += avg_y_stride; |
131 | 0 | } |
132 | 0 |
|
133 | 0 | for (c = 0; c < 16; ++c) { |
134 | 0 | // Below we clip the value in the same way which SSE code use. |
135 | 0 | // When adopting aggressive denoiser, the adj_val for each pixel |
136 | 0 | // could be at most 8 (this is current max adjustment of the map). |
137 | 0 | // In SSE code, we calculate the sum of adj_val for |
138 | 0 | // the columns, so the sum could be upto 128(16 rows). However, |
139 | 0 | // the range of the value is -128 ~ 127 in SSE code, that's why |
140 | 0 | // we do this change in C code. |
141 | 0 | // We don't do this for UV denoiser, since there are only 8 rows, |
142 | 0 | // and max adjustments <= 8, so the sum of the columns will not |
143 | 0 | // exceed 64. |
144 | 0 | if (col_sum[c] >= 128) { |
145 | 0 | col_sum[c] = 127; |
146 | 0 | } |
147 | 0 | sum_diff += col_sum[c]; |
148 | 0 | } |
149 | 0 |
|
150 | 0 | sum_diff_thresh = SUM_DIFF_THRESHOLD; |
151 | 0 | if (increase_denoising) sum_diff_thresh = SUM_DIFF_THRESHOLD_HIGH; |
152 | 0 | if (abs(sum_diff) > sum_diff_thresh) { |
153 | 0 | // Before returning to copy the block (i.e., apply no denoising), check |
154 | 0 | // if we can still apply some (weaker) temporal filtering to this block, |
155 | 0 | // that would otherwise not be denoised at all. Simplest is to apply |
156 | 0 | // an additional adjustment to running_avg_y to bring it closer to sig. |
157 | 0 | // The adjustment is capped by a maximum delta, and chosen such that |
158 | 0 | // in most cases the resulting sum_diff will be within the |
159 | 0 | // accceptable range given by sum_diff_thresh. |
160 | 0 |
|
161 | 0 | // The delta is set by the excess of absolute pixel diff over threshold. |
162 | 0 | int delta = ((abs(sum_diff) - sum_diff_thresh) >> 8) + 1; |
163 | 0 | // Only apply the adjustment for max delta up to 3. |
164 | 0 | if (delta < 4) { |
165 | 0 | sig -= sig_stride * 16; |
166 | 0 | mc_running_avg_y -= mc_avg_y_stride * 16; |
167 | 0 | running_avg_y -= avg_y_stride * 16; |
168 | 0 | for (r = 0; r < 16; ++r) { |
169 | 0 | for (c = 0; c < 16; ++c) { |
170 | 0 | int diff = mc_running_avg_y[c] - sig[c]; |
171 | 0 | int adjustment = abs(diff); |
172 | 0 | if (adjustment > delta) adjustment = delta; |
173 | 0 | if (diff > 0) { |
174 | 0 | // Bring denoised signal down. |
175 | 0 | if (running_avg_y[c] - adjustment < 0) { |
176 | 0 | running_avg_y[c] = 0; |
177 | 0 | } else { |
178 | 0 | running_avg_y[c] = running_avg_y[c] - adjustment; |
179 | 0 | } |
180 | 0 | col_sum[c] -= adjustment; |
181 | 0 | } else if (diff < 0) { |
182 | 0 | // Bring denoised signal up. |
183 | 0 | if (running_avg_y[c] + adjustment > 255) { |
184 | 0 | running_avg_y[c] = 255; |
185 | 0 | } else { |
186 | 0 | running_avg_y[c] = running_avg_y[c] + adjustment; |
187 | 0 | } |
188 | 0 | col_sum[c] += adjustment; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | // TODO(marpan): Check here if abs(sum_diff) has gone below the |
192 | 0 | // threshold sum_diff_thresh, and if so, we can exit the row loop. |
193 | 0 | sig += sig_stride; |
194 | 0 | mc_running_avg_y += mc_avg_y_stride; |
195 | 0 | running_avg_y += avg_y_stride; |
196 | 0 | } |
197 | 0 |
|
198 | 0 | sum_diff = 0; |
199 | 0 | for (c = 0; c < 16; ++c) { |
200 | 0 | if (col_sum[c] >= 128) { |
201 | 0 | col_sum[c] = 127; |
202 | 0 | } |
203 | 0 | sum_diff += col_sum[c]; |
204 | 0 | } |
205 | 0 |
|
206 | 0 | if (abs(sum_diff) > sum_diff_thresh) return COPY_BLOCK; |
207 | 0 | } else { |
208 | 0 | return COPY_BLOCK; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | |
212 | 0 | vp8_copy_mem16x16(running_avg_y_start, avg_y_stride, sig_start, sig_stride); |
213 | 0 | return FILTER_BLOCK; |
214 | 0 | } |
215 | | |
216 | | int vp8_denoiser_filter_uv_c(unsigned char *mc_running_avg_uv, |
217 | | int mc_avg_uv_stride, |
218 | | unsigned char *running_avg_uv, int avg_uv_stride, |
219 | | unsigned char *sig, int sig_stride, |
220 | | unsigned int motion_magnitude, |
221 | 0 | int increase_denoising) { |
222 | 0 | unsigned char *running_avg_uv_start = running_avg_uv; |
223 | 0 | unsigned char *sig_start = sig; |
224 | 0 | int sum_diff_thresh; |
225 | 0 | int r, c; |
226 | 0 | int sum_diff = 0; |
227 | 0 | int sum_block = 0; |
228 | 0 | int adj_val[3] = { 3, 4, 6 }; |
229 | 0 | int shift_inc1 = 0; |
230 | 0 | int shift_inc2 = 1; |
231 | 0 | /* If motion_magnitude is small, making the denoiser more aggressive by |
232 | 0 | * increasing the adjustment for each level. Add another increment for |
233 | 0 | * blocks that are labeled for increase denoising. */ |
234 | 0 | if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD_UV) { |
235 | 0 | if (increase_denoising) { |
236 | 0 | shift_inc1 = 1; |
237 | 0 | shift_inc2 = 2; |
238 | 0 | } |
239 | 0 | adj_val[0] += shift_inc2; |
240 | 0 | adj_val[1] += shift_inc2; |
241 | 0 | adj_val[2] += shift_inc2; |
242 | 0 | } |
243 | 0 |
|
244 | 0 | // Avoid denoising color signal if its close to average level. |
245 | 0 | for (r = 0; r < 8; ++r) { |
246 | 0 | for (c = 0; c < 8; ++c) { |
247 | 0 | sum_block += sig[c]; |
248 | 0 | } |
249 | 0 | sig += sig_stride; |
250 | 0 | } |
251 | 0 | if (abs(sum_block - (128 * 8 * 8)) < SUM_DIFF_FROM_AVG_THRESH_UV) { |
252 | 0 | return COPY_BLOCK; |
253 | 0 | } |
254 | 0 | |
255 | 0 | sig -= sig_stride * 8; |
256 | 0 | for (r = 0; r < 8; ++r) { |
257 | 0 | for (c = 0; c < 8; ++c) { |
258 | 0 | int diff = 0; |
259 | 0 | int adjustment = 0; |
260 | 0 | int absdiff = 0; |
261 | 0 |
|
262 | 0 | diff = mc_running_avg_uv[c] - sig[c]; |
263 | 0 | absdiff = abs(diff); |
264 | 0 |
|
265 | 0 | // When |diff| <= |3 + shift_inc1|, use pixel value from |
266 | 0 | // last denoised raw. |
267 | 0 | if (absdiff <= 3 + shift_inc1) { |
268 | 0 | running_avg_uv[c] = mc_running_avg_uv[c]; |
269 | 0 | sum_diff += diff; |
270 | 0 | } else { |
271 | 0 | if (absdiff >= 4 && absdiff <= 7) { |
272 | 0 | adjustment = adj_val[0]; |
273 | 0 | } else if (absdiff >= 8 && absdiff <= 15) { |
274 | 0 | adjustment = adj_val[1]; |
275 | 0 | } else { |
276 | 0 | adjustment = adj_val[2]; |
277 | 0 | } |
278 | 0 | if (diff > 0) { |
279 | 0 | if ((sig[c] + adjustment) > 255) { |
280 | 0 | running_avg_uv[c] = 255; |
281 | 0 | } else { |
282 | 0 | running_avg_uv[c] = sig[c] + adjustment; |
283 | 0 | } |
284 | 0 | sum_diff += adjustment; |
285 | 0 | } else { |
286 | 0 | if ((sig[c] - adjustment) < 0) { |
287 | 0 | running_avg_uv[c] = 0; |
288 | 0 | } else { |
289 | 0 | running_avg_uv[c] = sig[c] - adjustment; |
290 | 0 | } |
291 | 0 | sum_diff -= adjustment; |
292 | 0 | } |
293 | 0 | } |
294 | 0 | } |
295 | 0 | /* Update pointers for next iteration. */ |
296 | 0 | sig += sig_stride; |
297 | 0 | mc_running_avg_uv += mc_avg_uv_stride; |
298 | 0 | running_avg_uv += avg_uv_stride; |
299 | 0 | } |
300 | 0 |
|
301 | 0 | sum_diff_thresh = SUM_DIFF_THRESHOLD_UV; |
302 | 0 | if (increase_denoising) sum_diff_thresh = SUM_DIFF_THRESHOLD_HIGH_UV; |
303 | 0 | if (abs(sum_diff) > sum_diff_thresh) { |
304 | 0 | // Before returning to copy the block (i.e., apply no denoising), check |
305 | 0 | // if we can still apply some (weaker) temporal filtering to this block, |
306 | 0 | // that would otherwise not be denoised at all. Simplest is to apply |
307 | 0 | // an additional adjustment to running_avg_y to bring it closer to sig. |
308 | 0 | // The adjustment is capped by a maximum delta, and chosen such that |
309 | 0 | // in most cases the resulting sum_diff will be within the |
310 | 0 | // accceptable range given by sum_diff_thresh. |
311 | 0 |
|
312 | 0 | // The delta is set by the excess of absolute pixel diff over threshold. |
313 | 0 | int delta = ((abs(sum_diff) - sum_diff_thresh) >> 8) + 1; |
314 | 0 | // Only apply the adjustment for max delta up to 3. |
315 | 0 | if (delta < 4) { |
316 | 0 | sig -= sig_stride * 8; |
317 | 0 | mc_running_avg_uv -= mc_avg_uv_stride * 8; |
318 | 0 | running_avg_uv -= avg_uv_stride * 8; |
319 | 0 | for (r = 0; r < 8; ++r) { |
320 | 0 | for (c = 0; c < 8; ++c) { |
321 | 0 | int diff = mc_running_avg_uv[c] - sig[c]; |
322 | 0 | int adjustment = abs(diff); |
323 | 0 | if (adjustment > delta) adjustment = delta; |
324 | 0 | if (diff > 0) { |
325 | 0 | // Bring denoised signal down. |
326 | 0 | if (running_avg_uv[c] - adjustment < 0) { |
327 | 0 | running_avg_uv[c] = 0; |
328 | 0 | } else { |
329 | 0 | running_avg_uv[c] = running_avg_uv[c] - adjustment; |
330 | 0 | } |
331 | 0 | sum_diff -= adjustment; |
332 | 0 | } else if (diff < 0) { |
333 | 0 | // Bring denoised signal up. |
334 | 0 | if (running_avg_uv[c] + adjustment > 255) { |
335 | 0 | running_avg_uv[c] = 255; |
336 | 0 | } else { |
337 | 0 | running_avg_uv[c] = running_avg_uv[c] + adjustment; |
338 | 0 | } |
339 | 0 | sum_diff += adjustment; |
340 | 0 | } |
341 | 0 | } |
342 | 0 | // TODO(marpan): Check here if abs(sum_diff) has gone below the |
343 | 0 | // threshold sum_diff_thresh, and if so, we can exit the row loop. |
344 | 0 | sig += sig_stride; |
345 | 0 | mc_running_avg_uv += mc_avg_uv_stride; |
346 | 0 | running_avg_uv += avg_uv_stride; |
347 | 0 | } |
348 | 0 | if (abs(sum_diff) > sum_diff_thresh) return COPY_BLOCK; |
349 | 0 | } else { |
350 | 0 | return COPY_BLOCK; |
351 | 0 | } |
352 | 0 | } |
353 | 0 | |
354 | 0 | vp8_copy_mem8x8(running_avg_uv_start, avg_uv_stride, sig_start, sig_stride); |
355 | 0 | return FILTER_BLOCK; |
356 | 0 | } |
357 | | |
358 | 0 | void vp8_denoiser_set_parameters(VP8_DENOISER *denoiser, int mode) { |
359 | 0 | assert(mode > 0); // Denoiser is allocated only if mode > 0. |
360 | 0 | if (mode == 1) { |
361 | 0 | denoiser->denoiser_mode = kDenoiserOnYOnly; |
362 | 0 | } else if (mode == 2) { |
363 | 0 | denoiser->denoiser_mode = kDenoiserOnYUV; |
364 | 0 | } else if (mode == 3) { |
365 | 0 | denoiser->denoiser_mode = kDenoiserOnYUVAggressive; |
366 | 0 | } else { |
367 | 0 | denoiser->denoiser_mode = kDenoiserOnYUV; |
368 | 0 | } |
369 | 0 | if (denoiser->denoiser_mode != kDenoiserOnYUVAggressive) { |
370 | 0 | denoiser->denoise_pars.scale_sse_thresh = 1; |
371 | 0 | denoiser->denoise_pars.scale_motion_thresh = 8; |
372 | 0 | denoiser->denoise_pars.scale_increase_filter = 0; |
373 | 0 | denoiser->denoise_pars.denoise_mv_bias = 95; |
374 | 0 | denoiser->denoise_pars.pickmode_mv_bias = 100; |
375 | 0 | denoiser->denoise_pars.qp_thresh = 0; |
376 | 0 | denoiser->denoise_pars.consec_zerolast = UINT_MAX; |
377 | 0 | denoiser->denoise_pars.spatial_blur = 0; |
378 | 0 | } else { |
379 | 0 | denoiser->denoise_pars.scale_sse_thresh = 2; |
380 | 0 | denoiser->denoise_pars.scale_motion_thresh = 16; |
381 | 0 | denoiser->denoise_pars.scale_increase_filter = 1; |
382 | 0 | denoiser->denoise_pars.denoise_mv_bias = 60; |
383 | 0 | denoiser->denoise_pars.pickmode_mv_bias = 75; |
384 | 0 | denoiser->denoise_pars.qp_thresh = 80; |
385 | 0 | denoiser->denoise_pars.consec_zerolast = 15; |
386 | 0 | denoiser->denoise_pars.spatial_blur = 0; |
387 | 0 | } |
388 | 0 | } |
389 | | |
390 | | int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height, |
391 | 0 | int num_mb_rows, int num_mb_cols, int mode) { |
392 | 0 | int i; |
393 | 0 | assert(denoiser); |
394 | 0 | denoiser->num_mb_cols = num_mb_cols; |
395 | 0 |
|
396 | 0 | for (i = 0; i < MAX_REF_FRAMES; ++i) { |
397 | 0 | denoiser->yv12_running_avg[i].flags = 0; |
398 | 0 |
|
399 | 0 | if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_running_avg[i]), width, |
400 | 0 | height, VP8BORDERINPIXELS) < 0) { |
401 | 0 | vp8_denoiser_free(denoiser); |
402 | 0 | return 1; |
403 | 0 | } |
404 | 0 | memset(denoiser->yv12_running_avg[i].buffer_alloc, 0, |
405 | 0 | denoiser->yv12_running_avg[i].frame_size); |
406 | 0 | } |
407 | 0 | denoiser->yv12_mc_running_avg.flags = 0; |
408 | 0 |
|
409 | 0 | if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_mc_running_avg), width, |
410 | 0 | height, VP8BORDERINPIXELS) < 0) { |
411 | 0 | vp8_denoiser_free(denoiser); |
412 | 0 | return 1; |
413 | 0 | } |
414 | 0 | |
415 | 0 | memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0, |
416 | 0 | denoiser->yv12_mc_running_avg.frame_size); |
417 | 0 |
|
418 | 0 | if (vp8_yv12_alloc_frame_buffer(&denoiser->yv12_last_source, width, height, |
419 | 0 | VP8BORDERINPIXELS) < 0) { |
420 | 0 | vp8_denoiser_free(denoiser); |
421 | 0 | return 1; |
422 | 0 | } |
423 | 0 | memset(denoiser->yv12_last_source.buffer_alloc, 0, |
424 | 0 | denoiser->yv12_last_source.frame_size); |
425 | 0 |
|
426 | 0 | denoiser->denoise_state = vpx_calloc((num_mb_rows * num_mb_cols), 1); |
427 | 0 | if (!denoiser->denoise_state) { |
428 | 0 | vp8_denoiser_free(denoiser); |
429 | 0 | return 1; |
430 | 0 | } |
431 | 0 | memset(denoiser->denoise_state, 0, (num_mb_rows * num_mb_cols)); |
432 | 0 | vp8_denoiser_set_parameters(denoiser, mode); |
433 | 0 | denoiser->nmse_source_diff = 0; |
434 | 0 | denoiser->nmse_source_diff_count = 0; |
435 | 0 | denoiser->qp_avg = 0; |
436 | 0 | // QP threshold below which we can go up to aggressive mode. |
437 | 0 | denoiser->qp_threshold_up = 80; |
438 | 0 | // QP threshold above which we can go back down to normal mode. |
439 | 0 | // For now keep this second threshold high, so not used currently. |
440 | 0 | denoiser->qp_threshold_down = 128; |
441 | 0 | // Bitrate thresholds and noise metric (nmse) thresholds for switching to |
442 | 0 | // aggressive mode. |
443 | 0 | // TODO(marpan): Adjust thresholds, including effect on resolution. |
444 | 0 | denoiser->bitrate_threshold = 400000; // (bits/sec). |
445 | 0 | denoiser->threshold_aggressive_mode = 80; |
446 | 0 | if (width * height > 1280 * 720) { |
447 | 0 | denoiser->bitrate_threshold = 3000000; |
448 | 0 | denoiser->threshold_aggressive_mode = 200; |
449 | 0 | } else if (width * height > 960 * 540) { |
450 | 0 | denoiser->bitrate_threshold = 1200000; |
451 | 0 | denoiser->threshold_aggressive_mode = 120; |
452 | 0 | } else if (width * height > 640 * 480) { |
453 | 0 | denoiser->bitrate_threshold = 600000; |
454 | 0 | denoiser->threshold_aggressive_mode = 100; |
455 | 0 | } |
456 | 0 | return 0; |
457 | 0 | } |
458 | | |
459 | 0 | void vp8_denoiser_free(VP8_DENOISER *denoiser) { |
460 | 0 | int i; |
461 | 0 | assert(denoiser); |
462 | 0 |
|
463 | 0 | for (i = 0; i < MAX_REF_FRAMES; ++i) { |
464 | 0 | vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg[i]); |
465 | 0 | } |
466 | 0 | vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg); |
467 | 0 | vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_last_source); |
468 | 0 | vpx_free(denoiser->denoise_state); |
469 | 0 | } |
470 | | |
471 | | void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, MACROBLOCK *x, |
472 | | unsigned int best_sse, unsigned int zero_mv_sse, |
473 | | int recon_yoffset, int recon_uvoffset, |
474 | | loop_filter_info_n *lfi_n, int mb_row, int mb_col, |
475 | | int block_index, int consec_zero_last) |
476 | | |
477 | 0 | { |
478 | 0 | int mv_row; |
479 | 0 | int mv_col; |
480 | 0 | unsigned int motion_threshold; |
481 | 0 | unsigned int motion_magnitude2; |
482 | 0 | unsigned int sse_thresh; |
483 | 0 | int sse_diff_thresh = 0; |
484 | 0 | // Spatial loop filter: only applied selectively based on |
485 | 0 | // temporal filter state of block relative to top/left neighbors. |
486 | 0 | int apply_spatial_loop_filter = 1; |
487 | 0 | MV_REFERENCE_FRAME frame = x->best_reference_frame; |
488 | 0 | MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame; |
489 | 0 |
|
490 | 0 | enum vp8_denoiser_decision decision = FILTER_BLOCK; |
491 | 0 | enum vp8_denoiser_decision decision_u = COPY_BLOCK; |
492 | 0 | enum vp8_denoiser_decision decision_v = COPY_BLOCK; |
493 | 0 |
|
494 | 0 | if (zero_frame) { |
495 | 0 | YV12_BUFFER_CONFIG *src = &denoiser->yv12_running_avg[frame]; |
496 | 0 | YV12_BUFFER_CONFIG *dst = &denoiser->yv12_mc_running_avg; |
497 | 0 | YV12_BUFFER_CONFIG saved_pre, saved_dst; |
498 | 0 | MB_MODE_INFO saved_mbmi; |
499 | 0 | MACROBLOCKD *filter_xd = &x->e_mbd; |
500 | 0 | MB_MODE_INFO *mbmi = &filter_xd->mode_info_context->mbmi; |
501 | 0 | int sse_diff = 0; |
502 | 0 | // Bias on zero motion vector sse. |
503 | 0 | const int zero_bias = denoiser->denoise_pars.denoise_mv_bias; |
504 | 0 | zero_mv_sse = (unsigned int)((int64_t)zero_mv_sse * zero_bias / 100); |
505 | 0 | sse_diff = (int)zero_mv_sse - (int)best_sse; |
506 | 0 |
|
507 | 0 | saved_mbmi = *mbmi; |
508 | 0 |
|
509 | 0 | /* Use the best MV for the compensation. */ |
510 | 0 | mbmi->ref_frame = x->best_reference_frame; |
511 | 0 | mbmi->mode = x->best_sse_inter_mode; |
512 | 0 | mbmi->mv = x->best_sse_mv; |
513 | 0 | mbmi->need_to_clamp_mvs = x->need_to_clamp_best_mvs; |
514 | 0 | mv_col = x->best_sse_mv.as_mv.col; |
515 | 0 | mv_row = x->best_sse_mv.as_mv.row; |
516 | 0 | // Bias to zero_mv if small amount of motion. |
517 | 0 | // Note sse_diff_thresh is intialized to zero, so this ensures |
518 | 0 | // we will always choose zero_mv for denoising if |
519 | 0 | // zero_mv_see <= best_sse (i.e., sse_diff <= 0). |
520 | 0 | if ((unsigned int)(mv_row * mv_row + mv_col * mv_col) <= |
521 | 0 | NOISE_MOTION_THRESHOLD) { |
522 | 0 | sse_diff_thresh = (int)SSE_DIFF_THRESHOLD; |
523 | 0 | } |
524 | 0 |
|
525 | 0 | if (frame == INTRA_FRAME || sse_diff <= sse_diff_thresh) { |
526 | 0 | /* |
527 | 0 | * Handle intra blocks as referring to last frame with zero motion |
528 | 0 | * and let the absolute pixel difference affect the filter factor. |
529 | 0 | * Also consider small amount of motion as being random walk due |
530 | 0 | * to noise, if it doesn't mean that we get a much bigger error. |
531 | 0 | * Note that any changes to the mode info only affects the |
532 | 0 | * denoising. |
533 | 0 | */ |
534 | 0 | x->denoise_zeromv = 1; |
535 | 0 | mbmi->ref_frame = x->best_zeromv_reference_frame; |
536 | 0 |
|
537 | 0 | src = &denoiser->yv12_running_avg[zero_frame]; |
538 | 0 |
|
539 | 0 | mbmi->mode = ZEROMV; |
540 | 0 | mbmi->mv.as_int = 0; |
541 | 0 | x->best_sse_inter_mode = ZEROMV; |
542 | 0 | x->best_sse_mv.as_int = 0; |
543 | 0 | best_sse = zero_mv_sse; |
544 | 0 | } |
545 | 0 |
|
546 | 0 | mv_row = x->best_sse_mv.as_mv.row; |
547 | 0 | mv_col = x->best_sse_mv.as_mv.col; |
548 | 0 | motion_magnitude2 = mv_row * mv_row + mv_col * mv_col; |
549 | 0 | motion_threshold = |
550 | 0 | denoiser->denoise_pars.scale_motion_thresh * NOISE_MOTION_THRESHOLD; |
551 | 0 |
|
552 | 0 | if (motion_magnitude2 < |
553 | 0 | denoiser->denoise_pars.scale_increase_filter * NOISE_MOTION_THRESHOLD) { |
554 | 0 | x->increase_denoising = 1; |
555 | 0 | } |
556 | 0 |
|
557 | 0 | sse_thresh = denoiser->denoise_pars.scale_sse_thresh * SSE_THRESHOLD; |
558 | 0 | if (x->increase_denoising) { |
559 | 0 | sse_thresh = denoiser->denoise_pars.scale_sse_thresh * SSE_THRESHOLD_HIGH; |
560 | 0 | } |
561 | 0 |
|
562 | 0 | if (best_sse > sse_thresh || motion_magnitude2 > motion_threshold) { |
563 | 0 | decision = COPY_BLOCK; |
564 | 0 | } |
565 | 0 |
|
566 | 0 | // If block is considered skin, don't denoise if the block |
567 | 0 | // (1) is selected as non-zero motion for current frame, or |
568 | 0 | // (2) has not been selected as ZERO_LAST mode at least x past frames |
569 | 0 | // in a row. |
570 | 0 | // TODO(marpan): Parameter "x" should be varied with framerate. |
571 | 0 | // In particualar, should be reduced for layers (base layer/LAST). |
572 | 0 | if (x->is_skin && (consec_zero_last < 2 || motion_magnitude2 > 0)) { |
573 | 0 | decision = COPY_BLOCK; |
574 | 0 | } |
575 | 0 |
|
576 | 0 | if (decision == FILTER_BLOCK) { |
577 | 0 | saved_pre = filter_xd->pre; |
578 | 0 | saved_dst = filter_xd->dst; |
579 | 0 |
|
580 | 0 | /* Compensate the running average. */ |
581 | 0 | filter_xd->pre.y_buffer = src->y_buffer + recon_yoffset; |
582 | 0 | filter_xd->pre.u_buffer = src->u_buffer + recon_uvoffset; |
583 | 0 | filter_xd->pre.v_buffer = src->v_buffer + recon_uvoffset; |
584 | 0 | /* Write the compensated running average to the destination buffer. */ |
585 | 0 | filter_xd->dst.y_buffer = dst->y_buffer + recon_yoffset; |
586 | 0 | filter_xd->dst.u_buffer = dst->u_buffer + recon_uvoffset; |
587 | 0 | filter_xd->dst.v_buffer = dst->v_buffer + recon_uvoffset; |
588 | 0 |
|
589 | 0 | if (!x->skip) { |
590 | 0 | vp8_build_inter_predictors_mb(filter_xd); |
591 | 0 | } else { |
592 | 0 | vp8_build_inter16x16_predictors_mb( |
593 | 0 | filter_xd, filter_xd->dst.y_buffer, filter_xd->dst.u_buffer, |
594 | 0 | filter_xd->dst.v_buffer, filter_xd->dst.y_stride, |
595 | 0 | filter_xd->dst.uv_stride); |
596 | 0 | } |
597 | 0 | filter_xd->pre = saved_pre; |
598 | 0 | filter_xd->dst = saved_dst; |
599 | 0 | *mbmi = saved_mbmi; |
600 | 0 | } |
601 | 0 | } else { |
602 | 0 | // zero_frame should always be 1 for real-time mode, as the |
603 | 0 | // ZEROMV mode is always checked, so we should never go into this branch. |
604 | 0 | // If case ZEROMV is not checked, then we will force no denoise (COPY). |
605 | 0 | decision = COPY_BLOCK; |
606 | 0 | } |
607 | 0 |
|
608 | 0 | if (decision == FILTER_BLOCK) { |
609 | 0 | unsigned char *mc_running_avg_y = |
610 | 0 | denoiser->yv12_mc_running_avg.y_buffer + recon_yoffset; |
611 | 0 | int mc_avg_y_stride = denoiser->yv12_mc_running_avg.y_stride; |
612 | 0 | unsigned char *running_avg_y = |
613 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset; |
614 | 0 | int avg_y_stride = denoiser->yv12_running_avg[INTRA_FRAME].y_stride; |
615 | 0 |
|
616 | 0 | /* Filter. */ |
617 | 0 | decision = vp8_denoiser_filter(mc_running_avg_y, mc_avg_y_stride, |
618 | 0 | running_avg_y, avg_y_stride, x->thismb, 16, |
619 | 0 | motion_magnitude2, x->increase_denoising); |
620 | 0 | denoiser->denoise_state[block_index] = |
621 | 0 | motion_magnitude2 > 0 ? kFilterNonZeroMV : kFilterZeroMV; |
622 | 0 | // Only denoise UV for zero motion, and if y channel was denoised. |
623 | 0 | if (denoiser->denoiser_mode != kDenoiserOnYOnly && motion_magnitude2 == 0 && |
624 | 0 | decision == FILTER_BLOCK) { |
625 | 0 | unsigned char *mc_running_avg_u = |
626 | 0 | denoiser->yv12_mc_running_avg.u_buffer + recon_uvoffset; |
627 | 0 | unsigned char *running_avg_u = |
628 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].u_buffer + recon_uvoffset; |
629 | 0 | unsigned char *mc_running_avg_v = |
630 | 0 | denoiser->yv12_mc_running_avg.v_buffer + recon_uvoffset; |
631 | 0 | unsigned char *running_avg_v = |
632 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].v_buffer + recon_uvoffset; |
633 | 0 | int mc_avg_uv_stride = denoiser->yv12_mc_running_avg.uv_stride; |
634 | 0 | int avg_uv_stride = denoiser->yv12_running_avg[INTRA_FRAME].uv_stride; |
635 | 0 | int signal_stride = x->block[16].src_stride; |
636 | 0 | decision_u = vp8_denoiser_filter_uv( |
637 | 0 | mc_running_avg_u, mc_avg_uv_stride, running_avg_u, avg_uv_stride, |
638 | 0 | x->block[16].src + *x->block[16].base_src, signal_stride, |
639 | 0 | motion_magnitude2, 0); |
640 | 0 | decision_v = vp8_denoiser_filter_uv( |
641 | 0 | mc_running_avg_v, mc_avg_uv_stride, running_avg_v, avg_uv_stride, |
642 | 0 | x->block[20].src + *x->block[20].base_src, signal_stride, |
643 | 0 | motion_magnitude2, 0); |
644 | 0 | } |
645 | 0 | } |
646 | 0 | if (decision == COPY_BLOCK) { |
647 | 0 | /* No filtering of this block; it differs too much from the predictor, |
648 | 0 | * or the motion vector magnitude is considered too big. |
649 | 0 | */ |
650 | 0 | x->denoise_zeromv = 0; |
651 | 0 | vp8_copy_mem16x16( |
652 | 0 | x->thismb, 16, |
653 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, |
654 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_stride); |
655 | 0 | denoiser->denoise_state[block_index] = kNoFilter; |
656 | 0 | } |
657 | 0 | if (denoiser->denoiser_mode != kDenoiserOnYOnly) { |
658 | 0 | if (decision_u == COPY_BLOCK) { |
659 | 0 | vp8_copy_mem8x8( |
660 | 0 | x->block[16].src + *x->block[16].base_src, x->block[16].src_stride, |
661 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].u_buffer + recon_uvoffset, |
662 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].uv_stride); |
663 | 0 | } |
664 | 0 | if (decision_v == COPY_BLOCK) { |
665 | 0 | vp8_copy_mem8x8( |
666 | 0 | x->block[20].src + *x->block[20].base_src, x->block[16].src_stride, |
667 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].v_buffer + recon_uvoffset, |
668 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].uv_stride); |
669 | 0 | } |
670 | 0 | } |
671 | 0 | // Option to selectively deblock the denoised signal, for y channel only. |
672 | 0 | if (apply_spatial_loop_filter) { |
673 | 0 | loop_filter_info lfi; |
674 | 0 | int apply_filter_col = 0; |
675 | 0 | int apply_filter_row = 0; |
676 | 0 | int apply_filter = 0; |
677 | 0 | int y_stride = denoiser->yv12_running_avg[INTRA_FRAME].y_stride; |
678 | 0 | int uv_stride = denoiser->yv12_running_avg[INTRA_FRAME].uv_stride; |
679 | 0 |
|
680 | 0 | // Fix filter level to some nominal value for now. |
681 | 0 | int filter_level = 48; |
682 | 0 |
|
683 | 0 | int hev_index = lfi_n->hev_thr_lut[INTER_FRAME][filter_level]; |
684 | 0 | lfi.mblim = lfi_n->mblim[filter_level]; |
685 | 0 | lfi.blim = lfi_n->blim[filter_level]; |
686 | 0 | lfi.lim = lfi_n->lim[filter_level]; |
687 | 0 | lfi.hev_thr = lfi_n->hev_thr[hev_index]; |
688 | 0 |
|
689 | 0 | // Apply filter if there is a difference in the denoiser filter state |
690 | 0 | // between the current and left/top block, or if non-zero motion vector |
691 | 0 | // is used for the motion-compensated filtering. |
692 | 0 | if (mb_col > 0) { |
693 | 0 | apply_filter_col = |
694 | 0 | !((denoiser->denoise_state[block_index] == |
695 | 0 | denoiser->denoise_state[block_index - 1]) && |
696 | 0 | denoiser->denoise_state[block_index] != kFilterNonZeroMV); |
697 | 0 | if (apply_filter_col) { |
698 | 0 | // Filter left vertical edge. |
699 | 0 | apply_filter = 1; |
700 | 0 | vp8_loop_filter_mbv( |
701 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, |
702 | 0 | NULL, NULL, y_stride, uv_stride, &lfi); |
703 | 0 | } |
704 | 0 | } |
705 | 0 | if (mb_row > 0) { |
706 | 0 | apply_filter_row = |
707 | 0 | !((denoiser->denoise_state[block_index] == |
708 | 0 | denoiser->denoise_state[block_index - denoiser->num_mb_cols]) && |
709 | 0 | denoiser->denoise_state[block_index] != kFilterNonZeroMV); |
710 | 0 | if (apply_filter_row) { |
711 | 0 | // Filter top horizontal edge. |
712 | 0 | apply_filter = 1; |
713 | 0 | vp8_loop_filter_mbh( |
714 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, |
715 | 0 | NULL, NULL, y_stride, uv_stride, &lfi); |
716 | 0 | } |
717 | 0 | } |
718 | 0 | if (apply_filter) { |
719 | 0 | // Update the signal block |x|. Pixel changes are only to top and/or |
720 | 0 | // left boundary pixels: can we avoid full block copy here. |
721 | 0 | vp8_copy_mem16x16( |
722 | 0 | denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, |
723 | 0 | y_stride, x->thismb, 16); |
724 | 0 | } |
725 | 0 | } |
726 | 0 | } |