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 18static const unsigned int NOISE_MOTION_THRESHOLD = 25 * 25; 19/* SSE_DIFF_THRESHOLD is selected as ~95% confidence assuming 20 * var(noise) ~= 100. 21 */ 22static const unsigned int SSE_DIFF_THRESHOLD = 16 * 16 * 20; 23static 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 54int 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 138int 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 172void 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 185void 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