| /* | 
 |  *  Copyright (c) 2012 The WebM project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 |  | 
 | /* MFQE: Multiframe Quality Enhancement | 
 |  * In rate limited situations keyframes may cause significant visual artifacts | 
 |  * commonly referred to as "popping." This file implements a postproccesing | 
 |  * algorithm which blends data from the preceeding frame when there is no | 
 |  * motion and the q from the previous frame is lower which indicates that it is | 
 |  * higher quality. | 
 |  */ | 
 |  | 
 | #include "postproc.h" | 
 | #include "variance.h" | 
 | #include "vpx_mem/vpx_mem.h" | 
 | #include "vp8_rtcd.h" | 
 | #include "vpx_scale/yv12config.h" | 
 |  | 
 | #include <limits.h> | 
 | #include <stdlib.h> | 
 |  | 
 | static void filter_by_weight(unsigned char *src, int src_stride, | 
 |                              unsigned char *dst, int dst_stride, | 
 |                              int block_size, int src_weight) | 
 | { | 
 |     int dst_weight = (1 << MFQE_PRECISION) - src_weight; | 
 |     int rounding_bit = 1 << (MFQE_PRECISION - 1); | 
 |     int r, c; | 
 |  | 
 |     for (r = 0; r < block_size; r++) | 
 |     { | 
 |         for (c = 0; c < block_size; c++) | 
 |         { | 
 |             dst[c] = (src[c] * src_weight + | 
 |                       dst[c] * dst_weight + | 
 |                       rounding_bit) >> MFQE_PRECISION; | 
 |         } | 
 |         src += src_stride; | 
 |         dst += dst_stride; | 
 |     } | 
 | } | 
 |  | 
 | void vp8_filter_by_weight16x16_c(unsigned char *src, int src_stride, | 
 |                                  unsigned char *dst, int dst_stride, | 
 |                                  int src_weight) | 
 | { | 
 |     filter_by_weight(src, src_stride, dst, dst_stride, 16, src_weight); | 
 | } | 
 |  | 
 | void vp8_filter_by_weight8x8_c(unsigned char *src, int src_stride, | 
 |                                unsigned char *dst, int dst_stride, | 
 |                                int src_weight) | 
 | { | 
 |     filter_by_weight(src, src_stride, dst, dst_stride, 8, src_weight); | 
 | } | 
 |  | 
 | void vp8_filter_by_weight4x4_c(unsigned char *src, int src_stride, | 
 |                                unsigned char *dst, int dst_stride, | 
 |                                int src_weight) | 
 | { | 
 |     filter_by_weight(src, src_stride, dst, dst_stride, 4, src_weight); | 
 | } | 
 |  | 
 | static void apply_ifactor(unsigned char *y_src, | 
 |                           int y_src_stride, | 
 |                           unsigned char *y_dst, | 
 |                           int y_dst_stride, | 
 |                           unsigned char *u_src, | 
 |                           unsigned char *v_src, | 
 |                           int uv_src_stride, | 
 |                           unsigned char *u_dst, | 
 |                           unsigned char *v_dst, | 
 |                           int uv_dst_stride, | 
 |                           int block_size, | 
 |                           int src_weight) | 
 | { | 
 |     if (block_size == 16) | 
 |     { | 
 |         vp8_filter_by_weight16x16(y_src, y_src_stride, y_dst, y_dst_stride, src_weight); | 
 |         vp8_filter_by_weight8x8(u_src, uv_src_stride, u_dst, uv_dst_stride, src_weight); | 
 |         vp8_filter_by_weight8x8(v_src, uv_src_stride, v_dst, uv_dst_stride, src_weight); | 
 |     } | 
 |     else /* if (block_size == 8) */ | 
 |     { | 
 |         vp8_filter_by_weight8x8(y_src, y_src_stride, y_dst, y_dst_stride, src_weight); | 
 |         vp8_filter_by_weight4x4(u_src, uv_src_stride, u_dst, uv_dst_stride, src_weight); | 
 |         vp8_filter_by_weight4x4(v_src, uv_src_stride, v_dst, uv_dst_stride, src_weight); | 
 |     } | 
 | } | 
 |  | 
 | static unsigned int int_sqrt(unsigned int x) | 
 | { | 
 |     unsigned int y = x; | 
 |     unsigned int guess; | 
 |     int p = 1; | 
 |     while (y>>=1) p++; | 
 |     p>>=1; | 
 |  | 
 |     guess=0; | 
 |     while (p>=0) | 
 |     { | 
 |         guess |= (1<<p); | 
 |         if (x<guess*guess) | 
 |             guess -= (1<<p); | 
 |         p--; | 
 |     } | 
 |     /* choose between guess or guess+1 */ | 
 |     return guess+(guess*guess+guess+1<=x); | 
 | } | 
 |  | 
 | #define USE_SSD | 
 | static void multiframe_quality_enhance_block | 
 | ( | 
 |     int blksize, /* Currently only values supported are 16, 8 */ | 
 |     int qcurr, | 
 |     int qprev, | 
 |     unsigned char *y, | 
 |     unsigned char *u, | 
 |     unsigned char *v, | 
 |     int y_stride, | 
 |     int uv_stride, | 
 |     unsigned char *yd, | 
 |     unsigned char *ud, | 
 |     unsigned char *vd, | 
 |     int yd_stride, | 
 |     int uvd_stride | 
 | ) | 
 | { | 
 |     static const unsigned char VP8_ZEROS[16]= | 
 |     { | 
 |          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 | 
 |     }; | 
 |     int uvblksize = blksize >> 1; | 
 |     int qdiff = qcurr - qprev; | 
 |  | 
 |     int i; | 
 |     unsigned char *up; | 
 |     unsigned char *udp; | 
 |     unsigned char *vp; | 
 |     unsigned char *vdp; | 
 |  | 
 |     unsigned int act, actd, sad, usad, vsad, sse, thr, thrsq, actrisk; | 
 |  | 
 |     if (blksize == 16) | 
 |     { | 
 |         actd = (vp8_variance16x16(yd, yd_stride, VP8_ZEROS, 0, &sse)+128)>>8; | 
 |         act = (vp8_variance16x16(y, y_stride, VP8_ZEROS, 0, &sse)+128)>>8; | 
 | #ifdef USE_SSD | 
 |         sad = (vp8_variance16x16(y, y_stride, yd, yd_stride, &sse)); | 
 |         sad = (sse + 128)>>8; | 
 |         usad = (vp8_variance8x8(u, uv_stride, ud, uvd_stride, &sse)); | 
 |         usad = (sse + 32)>>6; | 
 |         vsad = (vp8_variance8x8(v, uv_stride, vd, uvd_stride, &sse)); | 
 |         vsad = (sse + 32)>>6; | 
 | #else | 
 |         sad = (vp8_sad16x16(y, y_stride, yd, yd_stride, UINT_MAX) + 128) >> 8; | 
 |         usad = (vp8_sad8x8(u, uv_stride, ud, uvd_stride, UINT_MAX) + 32) >> 6; | 
 |         vsad = (vp8_sad8x8(v, uv_stride, vd, uvd_stride, UINT_MAX)+ 32) >> 6; | 
 | #endif | 
 |     } | 
 |     else /* if (blksize == 8) */ | 
 |     { | 
 |         actd = (vp8_variance8x8(yd, yd_stride, VP8_ZEROS, 0, &sse)+32)>>6; | 
 |         act = (vp8_variance8x8(y, y_stride, VP8_ZEROS, 0, &sse)+32)>>6; | 
 | #ifdef USE_SSD | 
 |         sad = (vp8_variance8x8(y, y_stride, yd, yd_stride, &sse)); | 
 |         sad = (sse + 32)>>6; | 
 |         usad = (vp8_variance4x4(u, uv_stride, ud, uvd_stride, &sse)); | 
 |         usad = (sse + 8)>>4; | 
 |         vsad = (vp8_variance4x4(v, uv_stride, vd, uvd_stride, &sse)); | 
 |         vsad = (sse + 8)>>4; | 
 | #else | 
 |         sad = (vp8_sad8x8(y, y_stride, yd, yd_stride, UINT_MAX) + 32) >> 6; | 
 |         usad = (vp8_sad4x4(u, uv_stride, ud, uvd_stride, UINT_MAX) + 8) >> 4; | 
 |         vsad = (vp8_sad4x4(v, uv_stride, vd, uvd_stride, UINT_MAX) + 8) >> 4; | 
 | #endif | 
 |     } | 
 |  | 
 |     actrisk = (actd > act * 5); | 
 |  | 
 |     /* thr = qdiff/16 + log2(act) + log4(qprev) */ | 
 |     thr = (qdiff >> 4); | 
 |     while (actd >>= 1) thr++; | 
 |     while (qprev >>= 2) thr++; | 
 |  | 
 | #ifdef USE_SSD | 
 |     thrsq = thr * thr; | 
 |     if (sad < thrsq && | 
 |         /* additional checks for color mismatch and excessive addition of | 
 |          * high-frequencies */ | 
 |         4 * usad < thrsq && 4 * vsad < thrsq && !actrisk) | 
 | #else | 
 |     if (sad < thr && | 
 |         /* additional checks for color mismatch and excessive addition of | 
 |          * high-frequencies */ | 
 |         2 * usad < thr && 2 * vsad < thr && !actrisk) | 
 | #endif | 
 |     { | 
 |         int ifactor; | 
 | #ifdef USE_SSD | 
 |         /* TODO: optimize this later to not need sqr root */ | 
 |         sad = int_sqrt(sad); | 
 | #endif | 
 |         ifactor = (sad << MFQE_PRECISION) / thr; | 
 |         ifactor >>= (qdiff >> 5); | 
 |  | 
 |         if (ifactor) | 
 |         { | 
 |             apply_ifactor(y, y_stride, yd, yd_stride, | 
 |                           u, v, uv_stride, | 
 |                           ud, vd, uvd_stride, | 
 |                           blksize, ifactor); | 
 |         } | 
 |     } | 
 |     else  /* else implicitly copy from previous frame */ | 
 |     { | 
 |         if (blksize == 16) | 
 |         { | 
 |             vp8_copy_mem16x16(y, y_stride, yd, yd_stride); | 
 |             vp8_copy_mem8x8(u, uv_stride, ud, uvd_stride); | 
 |             vp8_copy_mem8x8(v, uv_stride, vd, uvd_stride); | 
 |         } | 
 |         else  /* if (blksize == 8) */ | 
 |         { | 
 |             vp8_copy_mem8x8(y, y_stride, yd, yd_stride); | 
 |             for (up = u, udp = ud, i = 0; i < uvblksize; ++i, up += uv_stride, udp += uvd_stride) | 
 |                 vpx_memcpy(udp, up, uvblksize); | 
 |             for (vp = v, vdp = vd, i = 0; i < uvblksize; ++i, vp += uv_stride, vdp += uvd_stride) | 
 |                 vpx_memcpy(vdp, vp, uvblksize); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | static int qualify_inter_mb(const MODE_INFO *mode_info_context, int *map) | 
 | { | 
 |     if (mode_info_context->mbmi.mb_skip_coeff) | 
 |         map[0] = map[1] = map[2] = map[3] = 1; | 
 |     else if (mode_info_context->mbmi.mode==SPLITMV) | 
 |     { | 
 |         static int ndx[4][4] = | 
 |         { | 
 |             {0, 1, 4, 5}, | 
 |             {2, 3, 6, 7}, | 
 |             {8, 9, 12, 13}, | 
 |             {10, 11, 14, 15} | 
 |         }; | 
 |         int i, j; | 
 |         for (i=0; i<4; ++i) | 
 |         { | 
 |             map[i] = 1; | 
 |             for (j=0; j<4 && map[j]; ++j) | 
 |                 map[i] &= (mode_info_context->bmi[ndx[i][j]].mv.as_mv.row <= 2 && | 
 |                            mode_info_context->bmi[ndx[i][j]].mv.as_mv.col <= 2); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         map[0] = map[1] = map[2] = map[3] = | 
 |             (mode_info_context->mbmi.mode > B_PRED && | 
 |              abs(mode_info_context->mbmi.mv.as_mv.row) <= 2 && | 
 |              abs(mode_info_context->mbmi.mv.as_mv.col) <= 2); | 
 |     } | 
 |     return (map[0]+map[1]+map[2]+map[3]); | 
 | } | 
 |  | 
 | void vp8_multiframe_quality_enhance | 
 | ( | 
 |     VP8_COMMON *cm | 
 | ) | 
 | { | 
 |     YV12_BUFFER_CONFIG *show = cm->frame_to_show; | 
 |     YV12_BUFFER_CONFIG *dest = &cm->post_proc_buffer; | 
 |  | 
 |     FRAME_TYPE frame_type = cm->frame_type; | 
 |     /* Point at base of Mb MODE_INFO list has motion vectors etc */ | 
 |     const MODE_INFO *mode_info_context = cm->show_frame_mi; | 
 |     int mb_row; | 
 |     int mb_col; | 
 |     int totmap, map[4]; | 
 |     int qcurr = cm->base_qindex; | 
 |     int qprev = cm->postproc_state.last_base_qindex; | 
 |  | 
 |     unsigned char *y_ptr, *u_ptr, *v_ptr; | 
 |     unsigned char *yd_ptr, *ud_ptr, *vd_ptr; | 
 |  | 
 |     /* Set up the buffer pointers */ | 
 |     y_ptr = show->y_buffer; | 
 |     u_ptr = show->u_buffer; | 
 |     v_ptr = show->v_buffer; | 
 |     yd_ptr = dest->y_buffer; | 
 |     ud_ptr = dest->u_buffer; | 
 |     vd_ptr = dest->v_buffer; | 
 |  | 
 |     /* postprocess each macro block */ | 
 |     for (mb_row = 0; mb_row < cm->mb_rows; mb_row++) | 
 |     { | 
 |         for (mb_col = 0; mb_col < cm->mb_cols; mb_col++) | 
 |         { | 
 |             /* if motion is high there will likely be no benefit */ | 
 |             if (frame_type == INTER_FRAME) totmap = qualify_inter_mb(mode_info_context, map); | 
 |             else totmap = (frame_type == KEY_FRAME ? 4 : 0); | 
 |             if (totmap) | 
 |             { | 
 |                 if (totmap < 4) | 
 |                 { | 
 |                     int i, j; | 
 |                     for (i=0; i<2; ++i) | 
 |                         for (j=0; j<2; ++j) | 
 |                         { | 
 |                             if (map[i*2+j]) | 
 |                             { | 
 |                                 multiframe_quality_enhance_block(8, qcurr, qprev, | 
 |                                                                  y_ptr + 8*(i*show->y_stride+j), | 
 |                                                                  u_ptr + 4*(i*show->uv_stride+j), | 
 |                                                                  v_ptr + 4*(i*show->uv_stride+j), | 
 |                                                                  show->y_stride, | 
 |                                                                  show->uv_stride, | 
 |                                                                  yd_ptr + 8*(i*dest->y_stride+j), | 
 |                                                                  ud_ptr + 4*(i*dest->uv_stride+j), | 
 |                                                                  vd_ptr + 4*(i*dest->uv_stride+j), | 
 |                                                                  dest->y_stride, | 
 |                                                                  dest->uv_stride); | 
 |                             } | 
 |                             else | 
 |                             { | 
 |                                 /* copy a 8x8 block */ | 
 |                                 int k; | 
 |                                 unsigned char *up = u_ptr + 4*(i*show->uv_stride+j); | 
 |                                 unsigned char *udp = ud_ptr + 4*(i*dest->uv_stride+j); | 
 |                                 unsigned char *vp = v_ptr + 4*(i*show->uv_stride+j); | 
 |                                 unsigned char *vdp = vd_ptr + 4*(i*dest->uv_stride+j); | 
 |                                 vp8_copy_mem8x8(y_ptr + 8*(i*show->y_stride+j), show->y_stride, | 
 |                                                 yd_ptr + 8*(i*dest->y_stride+j), dest->y_stride); | 
 |                                 for (k = 0; k < 4; ++k, up += show->uv_stride, udp += dest->uv_stride, | 
 |                                                         vp += show->uv_stride, vdp += dest->uv_stride) | 
 |                                 { | 
 |                                     vpx_memcpy(udp, up, 4); | 
 |                                     vpx_memcpy(vdp, vp, 4); | 
 |                                 } | 
 |                             } | 
 |                         } | 
 |                 } | 
 |                 else /* totmap = 4 */ | 
 |                 { | 
 |                     multiframe_quality_enhance_block(16, qcurr, qprev, y_ptr, | 
 |                                                      u_ptr, v_ptr, | 
 |                                                      show->y_stride, | 
 |                                                      show->uv_stride, | 
 |                                                      yd_ptr, ud_ptr, vd_ptr, | 
 |                                                      dest->y_stride, | 
 |                                                      dest->uv_stride); | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 vp8_copy_mem16x16(y_ptr, show->y_stride, yd_ptr, dest->y_stride); | 
 |                 vp8_copy_mem8x8(u_ptr, show->uv_stride, ud_ptr, dest->uv_stride); | 
 |                 vp8_copy_mem8x8(v_ptr, show->uv_stride, vd_ptr, dest->uv_stride); | 
 |             } | 
 |             y_ptr += 16; | 
 |             u_ptr += 8; | 
 |             v_ptr += 8; | 
 |             yd_ptr += 16; | 
 |             ud_ptr += 8; | 
 |             vd_ptr += 8; | 
 |             mode_info_context++;     /* step to next MB */ | 
 |         } | 
 |  | 
 |         y_ptr += show->y_stride  * 16 - 16 * cm->mb_cols; | 
 |         u_ptr += show->uv_stride *  8 - 8 * cm->mb_cols; | 
 |         v_ptr += show->uv_stride *  8 - 8 * cm->mb_cols; | 
 |         yd_ptr += dest->y_stride  * 16 - 16 * cm->mb_cols; | 
 |         ud_ptr += dest->uv_stride *  8 - 8 * cm->mb_cols; | 
 |         vd_ptr += dest->uv_stride *  8 - 8 * cm->mb_cols; | 
 |  | 
 |         mode_info_context++;         /* Skip border mb */ | 
 |     } | 
 | } |