media/libvpx/vp8/encoder/denoising.c

changeset 0
6474c204b198
     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 +}

mercurial