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