summaryrefslogtreecommitdiffstats
path: root/media/libvpx/libvpx/vp8/common/mfqe.c
diff options
context:
space:
mode:
Diffstat (limited to 'media/libvpx/libvpx/vp8/common/mfqe.c')
-rw-r--r--media/libvpx/libvpx/vp8/common/mfqe.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/media/libvpx/libvpx/vp8/common/mfqe.c b/media/libvpx/libvpx/vp8/common/mfqe.c
new file mode 100644
index 0000000000..1fe7363f17
--- /dev/null
+++ b/media/libvpx/libvpx/vp8/common/mfqe.c
@@ -0,0 +1,327 @@
+/*
+ * 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/common.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 {
+ 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 {
+ 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 {
+ 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;
+ vp8_zero(*map);
+ 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->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 */
+ }
+}