| /* |
| * 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 "./vp8_rtcd.h" |
| #include "./vpx_dsp_rtcd.h" |
| #include "vp8/common/postproc.h" |
| #include "vpx_dsp/variance.h" |
| #include "vpx_mem/vpx_mem.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 = (vpx_variance16x16(yd, yd_stride, VP8_ZEROS, 0, &sse)+128)>>8; |
| act = (vpx_variance16x16(y, y_stride, VP8_ZEROS, 0, &sse)+128)>>8; |
| #ifdef USE_SSD |
| vpx_variance16x16(y, y_stride, yd, yd_stride, &sse); |
| sad = (sse + 128)>>8; |
| vpx_variance8x8(u, uv_stride, ud, uvd_stride, &sse); |
| usad = (sse + 32)>>6; |
| vpx_variance8x8(v, uv_stride, vd, uvd_stride, &sse); |
| vsad = (sse + 32)>>6; |
| #else |
| sad = (vpx_sad16x16(y, y_stride, yd, yd_stride) + 128) >> 8; |
| usad = (vpx_sad8x8(u, uv_stride, ud, uvd_stride) + 32) >> 6; |
| vsad = (vpx_sad8x8(v, uv_stride, vd, uvd_stride)+ 32) >> 6; |
| #endif |
| } |
| else /* if (blksize == 8) */ |
| { |
| actd = (vpx_variance8x8(yd, yd_stride, VP8_ZEROS, 0, &sse)+32)>>6; |
| act = (vpx_variance8x8(y, y_stride, VP8_ZEROS, 0, &sse)+32)>>6; |
| #ifdef USE_SSD |
| vpx_variance8x8(y, y_stride, yd, yd_stride, &sse); |
| sad = (sse + 32)>>6; |
| vpx_variance4x4(u, uv_stride, ud, uvd_stride, &sse); |
| usad = (sse + 8)>>4; |
| vpx_variance4x4(v, uv_stride, vd, uvd_stride, &sse); |
| vsad = (sse + 8)>>4; |
| #else |
| sad = (vpx_sad8x8(y, y_stride, yd, yd_stride) + 32) >> 6; |
| usad = (vpx_sad4x4(u, uv_stride, ud, uvd_stride) + 8) >> 4; |
| vsad = (vpx_sad4x4(v, uv_stride, vd, uvd_stride) + 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) |
| memcpy(udp, up, uvblksize); |
| for (vp = v, vdp = vd, i = 0; i < uvblksize; ++i, vp += uv_stride, vdp += uvd_stride) |
| 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) |
| { |
| memcpy(udp, up, 4); |
| 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 */ |
| } |
| } |