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 "vpx_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 /* don't need one for intra start at 1 */ 144 for (i = 1; 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 173 void vp8_denoiser_free(VP8_DENOISER *denoiser) 174 { 175 int i; 176 assert(denoiser); 177 178 /* we don't have one for intra ref frame */ 179 for (i = 1; i < MAX_REF_FRAMES ; i++) 180 { 181 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg[i]); 182 } 183 vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg); 184 } 185 186 187 void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, 188 MACROBLOCK *x, 189 unsigned int best_sse, 190 unsigned int zero_mv_sse, 191 int recon_yoffset, 192 int recon_uvoffset) 193 { 194 int mv_row; 195 int mv_col; 196 unsigned int motion_magnitude2; 197 198 MV_REFERENCE_FRAME frame = x->best_reference_frame; 199 MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame; 200 201 enum vp8_denoiser_decision decision = FILTER_BLOCK; 202 203 if (zero_frame) 204 { 205 YV12_BUFFER_CONFIG *src = &denoiser->yv12_running_avg[frame]; 206 YV12_BUFFER_CONFIG *dst = &denoiser->yv12_mc_running_avg; 207 YV12_BUFFER_CONFIG saved_pre,saved_dst; 208 MB_MODE_INFO saved_mbmi; 209 MACROBLOCKD *filter_xd = &x->e_mbd; 210 MB_MODE_INFO *mbmi = &filter_xd->mode_info_context->mbmi; 211 int mv_col; 212 int mv_row; 213 int sse_diff = zero_mv_sse - best_sse; 214 215 saved_mbmi = *mbmi; 216 217 /* Use the best MV for the compensation. */ 218 mbmi->ref_frame = x->best_reference_frame; 219 mbmi->mode = x->best_sse_inter_mode; 220 mbmi->mv = x->best_sse_mv; 221 mbmi->need_to_clamp_mvs = x->need_to_clamp_best_mvs; 222 mv_col = x->best_sse_mv.as_mv.col; 223 mv_row = x->best_sse_mv.as_mv.row; 224 225 if (frame == INTRA_FRAME || 226 ((unsigned int)(mv_row *mv_row + mv_col *mv_col) 227 <= NOISE_MOTION_THRESHOLD && 228 sse_diff < (int)SSE_DIFF_THRESHOLD)) 229 { 230 /* 231 * Handle intra blocks as referring to last frame with zero motion 232 * and let the absolute pixel difference affect the filter factor. 233 * Also consider small amount of motion as being random walk due 234 * to noise, if it doesn't mean that we get a much bigger error. 235 * Note that any changes to the mode info only affects the 236 * denoising. 237 */ 238 mbmi->ref_frame = 239 x->best_zeromv_reference_frame; 240 241 src = &denoiser->yv12_running_avg[zero_frame]; 242 243 mbmi->mode = ZEROMV; 244 mbmi->mv.as_int = 0; 245 x->best_sse_inter_mode = ZEROMV; 246 x->best_sse_mv.as_int = 0; 247 best_sse = zero_mv_sse; 248 } 249 250 saved_pre = filter_xd->pre; 251 saved_dst = filter_xd->dst; 252 253 /* Compensate the running average. */ 254 filter_xd->pre.y_buffer = src->y_buffer + recon_yoffset; 255 filter_xd->pre.u_buffer = src->u_buffer + recon_uvoffset; 256 filter_xd->pre.v_buffer = src->v_buffer + recon_uvoffset; 257 /* Write the compensated running average to the destination buffer. */ 258 filter_xd->dst.y_buffer = dst->y_buffer + recon_yoffset; 259 filter_xd->dst.u_buffer = dst->u_buffer + recon_uvoffset; 260 filter_xd->dst.v_buffer = dst->v_buffer + recon_uvoffset; 261 262 if (!x->skip) 263 { 264 vp8_build_inter_predictors_mb(filter_xd); 265 } 266 else 267 { 268 vp8_build_inter16x16_predictors_mb(filter_xd, 269 filter_xd->dst.y_buffer, 270 filter_xd->dst.u_buffer, 271 filter_xd->dst.v_buffer, 272 filter_xd->dst.y_stride, 273 filter_xd->dst.uv_stride); 274 } 275 filter_xd->pre = saved_pre; 276 filter_xd->dst = saved_dst; 277 *mbmi = saved_mbmi; 278 279 } 280 281 mv_row = x->best_sse_mv.as_mv.row; 282 mv_col = x->best_sse_mv.as_mv.col; 283 motion_magnitude2 = mv_row * mv_row + mv_col * mv_col; 284 if (best_sse > SSE_THRESHOLD || motion_magnitude2 285 > 8 * NOISE_MOTION_THRESHOLD) 286 { 287 decision = COPY_BLOCK; 288 } 289 290 if (decision == FILTER_BLOCK) 291 { 292 /* Filter. */ 293 decision = vp8_denoiser_filter(&denoiser->yv12_mc_running_avg, 294 &denoiser->yv12_running_avg[LAST_FRAME], 295 x, 296 motion_magnitude2, 297 recon_yoffset, recon_uvoffset); 298 } 299 if (decision == COPY_BLOCK) 300 { 301 /* No filtering of this block; it differs too much from the predictor, 302 * or the motion vector magnitude is considered too big. 303 */ 304 vp8_copy_mem16x16( 305 x->thismb, 16, 306 denoiser->yv12_running_avg[LAST_FRAME].y_buffer + recon_yoffset, 307 denoiser->yv12_running_avg[LAST_FRAME].y_stride); 308 } 309 } 310