1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libvpx/vp8/encoder/denoising.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,305 @@ 1.4 +/* 1.5 + * Copyright (c) 2012 The WebM project authors. All Rights Reserved. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license 1.8 + * that can be found in the LICENSE file in the root of the source 1.9 + * tree. An additional intellectual property rights grant can be found 1.10 + * in the file PATENTS. All contributing project authors may 1.11 + * be found in the AUTHORS file in the root of the source tree. 1.12 + */ 1.13 + 1.14 +#include "denoising.h" 1.15 + 1.16 +#include "vp8/common/reconinter.h" 1.17 +#include "vpx/vpx_integer.h" 1.18 +#include "vpx_mem/vpx_mem.h" 1.19 +#include "vp8_rtcd.h" 1.20 + 1.21 +static const unsigned int NOISE_MOTION_THRESHOLD = 25 * 25; 1.22 +/* SSE_DIFF_THRESHOLD is selected as ~95% confidence assuming 1.23 + * var(noise) ~= 100. 1.24 + */ 1.25 +static const unsigned int SSE_DIFF_THRESHOLD = 16 * 16 * 20; 1.26 +static const unsigned int SSE_THRESHOLD = 16 * 16 * 40; 1.27 + 1.28 +/* 1.29 + * The filter function was modified to reduce the computational complexity. 1.30 + * Step 1: 1.31 + * Instead of applying tap coefficients for each pixel, we calculated the 1.32 + * pixel adjustments vs. pixel diff value ahead of time. 1.33 + * adjustment = filtered_value - current_raw 1.34 + * = (filter_coefficient * diff + 128) >> 8 1.35 + * where 1.36 + * filter_coefficient = (255 << 8) / (256 + ((absdiff * 330) >> 3)); 1.37 + * filter_coefficient += filter_coefficient / 1.38 + * (3 + motion_magnitude_adjustment); 1.39 + * filter_coefficient is clamped to 0 ~ 255. 1.40 + * 1.41 + * Step 2: 1.42 + * The adjustment vs. diff curve becomes flat very quick when diff increases. 1.43 + * This allowed us to use only several levels to approximate the curve without 1.44 + * changing the filtering algorithm too much. 1.45 + * The adjustments were further corrected by checking the motion magnitude. 1.46 + * The levels used are: 1.47 + * diff adjustment w/o motion correction adjustment w/ motion correction 1.48 + * [-255, -16] -6 -7 1.49 + * [-15, -8] -4 -5 1.50 + * [-7, -4] -3 -4 1.51 + * [-3, 3] diff diff 1.52 + * [4, 7] 3 4 1.53 + * [8, 15] 4 5 1.54 + * [16, 255] 6 7 1.55 + */ 1.56 + 1.57 +int vp8_denoiser_filter_c(YV12_BUFFER_CONFIG *mc_running_avg, 1.58 + YV12_BUFFER_CONFIG *running_avg, MACROBLOCK *signal, 1.59 + unsigned int motion_magnitude, int y_offset, 1.60 + int uv_offset) 1.61 +{ 1.62 + unsigned char *sig = signal->thismb; 1.63 + int sig_stride = 16; 1.64 + unsigned char *mc_running_avg_y = mc_running_avg->y_buffer + y_offset; 1.65 + int mc_avg_y_stride = mc_running_avg->y_stride; 1.66 + unsigned char *running_avg_y = running_avg->y_buffer + y_offset; 1.67 + int avg_y_stride = running_avg->y_stride; 1.68 + int r, c, i; 1.69 + int sum_diff = 0; 1.70 + int adj_val[3] = {3, 4, 6}; 1.71 + 1.72 + /* If motion_magnitude is small, making the denoiser more aggressive by 1.73 + * increasing the adjustment for each level. */ 1.74 + if (motion_magnitude <= MOTION_MAGNITUDE_THRESHOLD) 1.75 + { 1.76 + for (i = 0; i < 3; i++) 1.77 + adj_val[i] += 1; 1.78 + } 1.79 + 1.80 + for (r = 0; r < 16; ++r) 1.81 + { 1.82 + for (c = 0; c < 16; ++c) 1.83 + { 1.84 + int diff = 0; 1.85 + int adjustment = 0; 1.86 + int absdiff = 0; 1.87 + 1.88 + diff = mc_running_avg_y[c] - sig[c]; 1.89 + absdiff = abs(diff); 1.90 + 1.91 + /* When |diff| < 4, use pixel value from last denoised raw. */ 1.92 + if (absdiff <= 3) 1.93 + { 1.94 + running_avg_y[c] = mc_running_avg_y[c]; 1.95 + sum_diff += diff; 1.96 + } 1.97 + else 1.98 + { 1.99 + if (absdiff >= 4 && absdiff <= 7) 1.100 + adjustment = adj_val[0]; 1.101 + else if (absdiff >= 8 && absdiff <= 15) 1.102 + adjustment = adj_val[1]; 1.103 + else 1.104 + adjustment = adj_val[2]; 1.105 + 1.106 + if (diff > 0) 1.107 + { 1.108 + if ((sig[c] + adjustment) > 255) 1.109 + running_avg_y[c] = 255; 1.110 + else 1.111 + running_avg_y[c] = sig[c] + adjustment; 1.112 + 1.113 + sum_diff += adjustment; 1.114 + } 1.115 + else 1.116 + { 1.117 + if ((sig[c] - adjustment) < 0) 1.118 + running_avg_y[c] = 0; 1.119 + else 1.120 + running_avg_y[c] = sig[c] - adjustment; 1.121 + 1.122 + sum_diff -= adjustment; 1.123 + } 1.124 + } 1.125 + } 1.126 + 1.127 + /* Update pointers for next iteration. */ 1.128 + sig += sig_stride; 1.129 + mc_running_avg_y += mc_avg_y_stride; 1.130 + running_avg_y += avg_y_stride; 1.131 + } 1.132 + 1.133 + if (abs(sum_diff) > SUM_DIFF_THRESHOLD) 1.134 + return COPY_BLOCK; 1.135 + 1.136 + vp8_copy_mem16x16(running_avg->y_buffer + y_offset, avg_y_stride, 1.137 + signal->thismb, sig_stride); 1.138 + return FILTER_BLOCK; 1.139 +} 1.140 + 1.141 +int vp8_denoiser_allocate(VP8_DENOISER *denoiser, int width, int height) 1.142 +{ 1.143 + int i; 1.144 + assert(denoiser); 1.145 + 1.146 + for (i = 0; i < MAX_REF_FRAMES; i++) 1.147 + { 1.148 + denoiser->yv12_running_avg[i].flags = 0; 1.149 + 1.150 + if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_running_avg[i]), width, 1.151 + height, VP8BORDERINPIXELS) 1.152 + < 0) 1.153 + { 1.154 + vp8_denoiser_free(denoiser); 1.155 + return 1; 1.156 + } 1.157 + vpx_memset(denoiser->yv12_running_avg[i].buffer_alloc, 0, 1.158 + denoiser->yv12_running_avg[i].frame_size); 1.159 + 1.160 + } 1.161 + denoiser->yv12_mc_running_avg.flags = 0; 1.162 + 1.163 + if (vp8_yv12_alloc_frame_buffer(&(denoiser->yv12_mc_running_avg), width, 1.164 + height, VP8BORDERINPIXELS) < 0) 1.165 + { 1.166 + vp8_denoiser_free(denoiser); 1.167 + return 1; 1.168 + } 1.169 + 1.170 + vpx_memset(denoiser->yv12_mc_running_avg.buffer_alloc, 0, 1.171 + denoiser->yv12_mc_running_avg.frame_size); 1.172 + return 0; 1.173 +} 1.174 + 1.175 +void vp8_denoiser_free(VP8_DENOISER *denoiser) 1.176 +{ 1.177 + int i; 1.178 + assert(denoiser); 1.179 + 1.180 + for (i = 0; i < MAX_REF_FRAMES ; i++) 1.181 + { 1.182 + vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_running_avg[i]); 1.183 + } 1.184 + vp8_yv12_de_alloc_frame_buffer(&denoiser->yv12_mc_running_avg); 1.185 +} 1.186 + 1.187 + 1.188 +void vp8_denoiser_denoise_mb(VP8_DENOISER *denoiser, 1.189 + MACROBLOCK *x, 1.190 + unsigned int best_sse, 1.191 + unsigned int zero_mv_sse, 1.192 + int recon_yoffset, 1.193 + int recon_uvoffset) 1.194 +{ 1.195 + int mv_row; 1.196 + int mv_col; 1.197 + unsigned int motion_magnitude2; 1.198 + 1.199 + MV_REFERENCE_FRAME frame = x->best_reference_frame; 1.200 + MV_REFERENCE_FRAME zero_frame = x->best_zeromv_reference_frame; 1.201 + 1.202 + enum vp8_denoiser_decision decision = FILTER_BLOCK; 1.203 + 1.204 + if (zero_frame) 1.205 + { 1.206 + YV12_BUFFER_CONFIG *src = &denoiser->yv12_running_avg[frame]; 1.207 + YV12_BUFFER_CONFIG *dst = &denoiser->yv12_mc_running_avg; 1.208 + YV12_BUFFER_CONFIG saved_pre,saved_dst; 1.209 + MB_MODE_INFO saved_mbmi; 1.210 + MACROBLOCKD *filter_xd = &x->e_mbd; 1.211 + MB_MODE_INFO *mbmi = &filter_xd->mode_info_context->mbmi; 1.212 + int sse_diff = zero_mv_sse - best_sse; 1.213 + 1.214 + saved_mbmi = *mbmi; 1.215 + 1.216 + /* Use the best MV for the compensation. */ 1.217 + mbmi->ref_frame = x->best_reference_frame; 1.218 + mbmi->mode = x->best_sse_inter_mode; 1.219 + mbmi->mv = x->best_sse_mv; 1.220 + mbmi->need_to_clamp_mvs = x->need_to_clamp_best_mvs; 1.221 + mv_col = x->best_sse_mv.as_mv.col; 1.222 + mv_row = x->best_sse_mv.as_mv.row; 1.223 + 1.224 + if (frame == INTRA_FRAME || 1.225 + ((unsigned int)(mv_row *mv_row + mv_col *mv_col) 1.226 + <= NOISE_MOTION_THRESHOLD && 1.227 + sse_diff < (int)SSE_DIFF_THRESHOLD)) 1.228 + { 1.229 + /* 1.230 + * Handle intra blocks as referring to last frame with zero motion 1.231 + * and let the absolute pixel difference affect the filter factor. 1.232 + * Also consider small amount of motion as being random walk due 1.233 + * to noise, if it doesn't mean that we get a much bigger error. 1.234 + * Note that any changes to the mode info only affects the 1.235 + * denoising. 1.236 + */ 1.237 + mbmi->ref_frame = 1.238 + x->best_zeromv_reference_frame; 1.239 + 1.240 + src = &denoiser->yv12_running_avg[zero_frame]; 1.241 + 1.242 + mbmi->mode = ZEROMV; 1.243 + mbmi->mv.as_int = 0; 1.244 + x->best_sse_inter_mode = ZEROMV; 1.245 + x->best_sse_mv.as_int = 0; 1.246 + best_sse = zero_mv_sse; 1.247 + } 1.248 + 1.249 + saved_pre = filter_xd->pre; 1.250 + saved_dst = filter_xd->dst; 1.251 + 1.252 + /* Compensate the running average. */ 1.253 + filter_xd->pre.y_buffer = src->y_buffer + recon_yoffset; 1.254 + filter_xd->pre.u_buffer = src->u_buffer + recon_uvoffset; 1.255 + filter_xd->pre.v_buffer = src->v_buffer + recon_uvoffset; 1.256 + /* Write the compensated running average to the destination buffer. */ 1.257 + filter_xd->dst.y_buffer = dst->y_buffer + recon_yoffset; 1.258 + filter_xd->dst.u_buffer = dst->u_buffer + recon_uvoffset; 1.259 + filter_xd->dst.v_buffer = dst->v_buffer + recon_uvoffset; 1.260 + 1.261 + if (!x->skip) 1.262 + { 1.263 + vp8_build_inter_predictors_mb(filter_xd); 1.264 + } 1.265 + else 1.266 + { 1.267 + vp8_build_inter16x16_predictors_mb(filter_xd, 1.268 + filter_xd->dst.y_buffer, 1.269 + filter_xd->dst.u_buffer, 1.270 + filter_xd->dst.v_buffer, 1.271 + filter_xd->dst.y_stride, 1.272 + filter_xd->dst.uv_stride); 1.273 + } 1.274 + filter_xd->pre = saved_pre; 1.275 + filter_xd->dst = saved_dst; 1.276 + *mbmi = saved_mbmi; 1.277 + 1.278 + } 1.279 + 1.280 + mv_row = x->best_sse_mv.as_mv.row; 1.281 + mv_col = x->best_sse_mv.as_mv.col; 1.282 + motion_magnitude2 = mv_row * mv_row + mv_col * mv_col; 1.283 + if (best_sse > SSE_THRESHOLD || motion_magnitude2 1.284 + > 8 * NOISE_MOTION_THRESHOLD) 1.285 + { 1.286 + decision = COPY_BLOCK; 1.287 + } 1.288 + 1.289 + if (decision == FILTER_BLOCK) 1.290 + { 1.291 + /* Filter. */ 1.292 + decision = vp8_denoiser_filter(&denoiser->yv12_mc_running_avg, 1.293 + &denoiser->yv12_running_avg[INTRA_FRAME], 1.294 + x, 1.295 + motion_magnitude2, 1.296 + recon_yoffset, recon_uvoffset); 1.297 + } 1.298 + if (decision == COPY_BLOCK) 1.299 + { 1.300 + /* No filtering of this block; it differs too much from the predictor, 1.301 + * or the motion vector magnitude is considered too big. 1.302 + */ 1.303 + vp8_copy_mem16x16( 1.304 + x->thismb, 16, 1.305 + denoiser->yv12_running_avg[INTRA_FRAME].y_buffer + recon_yoffset, 1.306 + denoiser->yv12_running_avg[INTRA_FRAME].y_stride); 1.307 + } 1.308 +}