2012-01-27 19:23:52 +01:00
|
|
|
/*
|
|
|
|
* 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 "vpx_rtcd.h"
|
|
|
|
#include "vpx_scale/yv12config.h"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2012-03-21 18:20:36 +01:00
|
|
|
static void filter_by_weight(unsigned char *src, int src_stride,
|
|
|
|
unsigned char *dst, int dst_stride,
|
|
|
|
int block_size, int src_weight)
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-03-21 18:20:36 +01:00
|
|
|
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)
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
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
|
2012-01-27 19:23:52 +01:00
|
|
|
static void multiframe_quality_enhance_block
|
|
|
|
(
|
2012-03-30 20:34:36 +02:00
|
|
|
int blksize, /* Currently only values supported are 16, 8, 4 */
|
2012-01-27 19:23:52 +01:00
|
|
|
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]=
|
|
|
|
{
|
2012-03-30 20:34:36 +02:00
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
2012-01-27 19:23:52 +01:00
|
|
|
};
|
2012-04-03 23:02:58 +02:00
|
|
|
int uvblksize = blksize >> 1;
|
2012-01-27 19:23:52 +01:00
|
|
|
int qdiff = qcurr - qprev;
|
|
|
|
|
2012-03-30 20:34:36 +02:00
|
|
|
int i, j;
|
|
|
|
unsigned char *yp;
|
|
|
|
unsigned char *ydp;
|
2012-01-27 19:23:52 +01:00
|
|
|
unsigned char *up;
|
|
|
|
unsigned char *udp;
|
|
|
|
unsigned char *vp;
|
|
|
|
unsigned char *vdp;
|
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
unsigned int act, actd, sad, usad, vsad, sse, thr, thrsq, actrisk;
|
2012-01-27 19:23:52 +01:00
|
|
|
|
|
|
|
if (blksize == 16)
|
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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, INT_MAX)+128)>>8;
|
|
|
|
usad = (vp8_sad8x8(u, uv_stride, ud, uvd_stride, INT_MAX)+32)>>6;
|
|
|
|
vsad = (vp8_sad8x8(v, uv_stride, vd, uvd_stride, INT_MAX)+32)>>6;
|
|
|
|
#endif
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
2012-03-30 20:34:36 +02:00
|
|
|
else if (blksize == 8)
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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, INT_MAX)+32)>>6;
|
|
|
|
usad = (vp8_sad4x4(u, uv_stride, ud, uvd_stride, INT_MAX)+8)>>4;
|
|
|
|
vsad = (vp8_sad4x4(v, uv_stride, vd, uvd_stride, INT_MAX)+8)>>4;
|
|
|
|
#endif
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
actrisk = (actd > act * 5);
|
|
|
|
|
2012-01-27 19:23:52 +01:00
|
|
|
/* thr = qdiff/8 + log2(act) + log4(qprev) */
|
2012-03-30 20:34:36 +02:00
|
|
|
thr = (qdiff >> 3);
|
2012-04-03 23:02:58 +02:00
|
|
|
while (actd >>= 1) thr++;
|
2012-03-30 20:34:36 +02:00
|
|
|
while (qprev >>= 2) thr++;
|
2012-01-27 19:23:52 +01:00
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
#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
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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);
|
2012-03-30 20:34:36 +02:00
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
if (ifactor)
|
2012-03-30 20:34:36 +02:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
apply_ifactor(y, y_stride, yd, yd_stride,
|
|
|
|
u, v, uv_stride,
|
|
|
|
ud, vd, uvd_stride,
|
|
|
|
blksize, ifactor);
|
2012-03-30 20:34:36 +02:00
|
|
|
}
|
|
|
|
}
|
2012-04-03 23:02:58 +02:00
|
|
|
else /* else implicitly copy from previous frame */
|
2012-03-30 20:34:36 +02:00
|
|
|
{
|
|
|
|
if (blksize == 16)
|
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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);
|
2012-03-30 20:34:36 +02:00
|
|
|
}
|
2012-04-03 23:02:58 +02:00
|
|
|
else /* if (blksize == 8) */
|
2012-03-30 20:34:36 +02:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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);
|
2012-03-30 20:34:36 +02:00
|
|
|
}
|
|
|
|
}
|
2012-04-03 23:02:58 +02:00
|
|
|
}
|
2012-03-30 20:34:36 +02:00
|
|
|
|
2012-04-03 23:02:58 +02:00
|
|
|
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)
|
2012-03-30 20:34:36 +02:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
static int ndx[4][4] =
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
{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);
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
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);
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
2012-04-03 23:02:58 +02:00
|
|
|
return (map[0]+map[1]+map[2]+map[3]);
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2012-04-03 23:02:58 +02:00
|
|
|
int totmap, map[4];
|
2012-01-27 19:23:52 +01:00
|
|
|
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 */
|
2012-04-03 23:02:58 +02:00
|
|
|
if (frame_type == INTER_FRAME) totmap = qualify_inter_mb(mode_info_context, map);
|
|
|
|
else totmap = (frame_type == KEY_FRAME ? 4 : 0);
|
|
|
|
if (totmap)
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
2012-04-03 23:02:58 +02:00
|
|
|
if (totmap < 4)
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
for (i=0; i<2; ++i)
|
|
|
|
for (j=0; j<2; ++j)
|
2012-04-03 23:02:58 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-27 19:23:52 +01:00
|
|
|
}
|
2012-04-03 23:02:58 +02:00
|
|
|
else /* totmap = 4 */
|
2012-01-27 19:23:52 +01:00
|
|
|
{
|
|
|
|
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 */
|
|
|
|
}
|
|
|
|
}
|