Commit 7ac3e3c1 authored by JackyChen's avatar JackyChen

Multiframe Quality Enhancement(MFQE) in VP9.

It is the first version of MFQE in VP9. There are a few TODOs included
in this version.
Usage: Add flag --enable-vp9-postproc to config the project.
In decoder, use flag --mfqe in the command line to enable
MFQE in postproc.
Note: Need to have key frame with low quality to see the effect of this
new patch. In my experiment, I fixed the qindex to 200 in key frame.

Change-Id: I021f9ce4616ed3574c81e48d968662994b56a396
parent f59cb45e
......@@ -45,6 +45,7 @@ void vp9_free_ref_frame_buffers(VP9_COMMON *cm) {
}
vp9_free_frame_buffer(&cm->post_proc_buffer);
vp9_free_frame_buffer(&cm->post_proc_buffer_int);
}
void vp9_free_context_buffers(VP9_COMMON *cm) {
......
This diff is collapsed.
/*
* Copyright (c) 2014 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.
*/
#ifndef VP9_COMMON_VP9_MFQE_H_
#define VP9_COMMON_VP9_MFQE_H_
#ifdef __cplusplus
extern "C" {
#endif
// Multiframe Quality Enhancement.
// The aim for MFQE is to replace pixel blocks in the current frame with
// the correlated pixel blocks (with higher quality) in the last frame.
// The replacement can only be taken in stationary blocks by checking
// the motion of the blocks and other conditions such as the SAD of
// the current block and correlated block, the variance of the block
// difference, etc.
void vp9_mfqe(struct VP9Common *cm);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // VP9_COMMON_VP9_MFQE_H_
......@@ -113,6 +113,7 @@ typedef struct VP9Common {
int new_fb_idx;
YV12_BUFFER_CONFIG post_proc_buffer;
YV12_BUFFER_CONFIG post_proc_buffer_int;
FRAME_TYPE last_frame_type; /* last frame's frame type for motion search.*/
FRAME_TYPE frame_type;
......
......@@ -79,6 +79,9 @@ const short vp9_rv[] = {
0, 9, 5, 5, 11, 10, 13, 9, 10, 13,
};
static const uint8_t q_diff_thresh = 20;
static const uint8_t last_q_thresh = 170;
void vp9_post_proc_down_and_across_c(const uint8_t *src_ptr,
uint8_t *dst_ptr,
int src_pixels_per_line,
......@@ -616,6 +619,17 @@ void vp9_plane_add_noise_c(uint8_t *start, char *noise,
}
}
static void swap_mi_and_prev_mi(VP9_COMMON *cm) {
// Current mip will be the prev_mip for the next frame.
MODE_INFO *temp = cm->postproc_state.prev_mip;
cm->postproc_state.prev_mip = cm->mip;
cm->mip = temp;
// Update the upper left visible macroblock ptrs.
cm->mi = cm->mip + cm->mi_stride + 1;
cm->postproc_state.prev_mi = cm->postproc_state.prev_mip + cm->mi_stride + 1;
}
int vp9_post_proc_frame(struct VP9Common *cm,
YV12_BUFFER_CONFIG *dest, vp9_ppflags_t *ppflags) {
const int q = MIN(63, cm->lf.filter_level * 10 / 6);
......@@ -633,6 +647,42 @@ int vp9_post_proc_frame(struct VP9Common *cm,
vp9_clear_system_state();
// Alloc memory for prev_mip in the first frame.
if (cm->current_video_frame == 1) {
cm->postproc_state.last_base_qindex = cm->base_qindex;
cm->postproc_state.last_frame_valid = 1;
ppstate->prev_mip = vpx_calloc(cm->mi_alloc_size, sizeof(*cm->mip));
if (!ppstate->prev_mip) {
return 1;
}
ppstate->prev_mi = ppstate->prev_mip + cm->mi_stride + 1;
vpx_memset(ppstate->prev_mip, 0,
cm->mi_stride * (cm->mi_rows + 1) * sizeof(*cm->mip));
}
// Allocate post_proc_buffer_int if needed.
if ((flags & VP9D_MFQE) && !cm->post_proc_buffer_int.buffer_alloc) {
if ((flags & VP9D_DEMACROBLOCK) || (flags & VP9D_DEBLOCK)) {
const int width = ALIGN_POWER_OF_TWO(cm->width, 4);
const int height = ALIGN_POWER_OF_TWO(cm->height, 4);
if (vp9_alloc_frame_buffer(&cm->post_proc_buffer_int, width, height,
cm->subsampling_x, cm->subsampling_y,
#if CONFIG_VP9_HIGHBITDEPTH
cm->use_highbitdepth,
#endif // CONFIG_VP9_HIGHBITDEPTH
VP9_ENC_BORDER_IN_PIXELS) < 0) {
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
"Failed to allocate MFQE framebuffer");
}
// Ensure that postproc is set to all 0s so that post proc
// doesn't pull random data in from edge.
vpx_memset(cm->post_proc_buffer_int.buffer_alloc, 128,
cm->post_proc_buffer.frame_size);
}
}
#if CONFIG_VP9_POSTPROC || CONFIG_INTERNAL_STATS
if (vp9_realloc_frame_buffer(&cm->post_proc_buffer, cm->width, cm->height,
cm->subsampling_x, cm->subsampling_y,
......@@ -644,7 +694,27 @@ int vp9_post_proc_frame(struct VP9Common *cm,
"Failed to allocate post-processing buffer");
#endif
if (flags & VP9D_DEMACROBLOCK) {
if ((flags & VP9D_MFQE) && cm->current_video_frame >= 2 &&
cm->postproc_state.last_frame_valid &&
cm->postproc_state.last_base_qindex <= last_q_thresh &&
cm->base_qindex - cm->postproc_state.last_base_qindex >= q_diff_thresh) {
vp9_mfqe(cm);
// TODO(jackychen): Consider whether enable deblocking by default
// if mfqe is enabled. Need to take both the quality and the speed
// into consideration.
if ((flags & VP9D_DEMACROBLOCK) || (flags & VP9D_DEBLOCK)) {
vp8_yv12_copy_frame(ppbuf, &cm->post_proc_buffer_int);
}
if ((flags & VP9D_DEMACROBLOCK) && cm->post_proc_buffer_int.buffer_alloc) {
deblock_and_de_macro_block(&cm->post_proc_buffer_int, ppbuf,
q + (ppflags->deblocking_level - 5) * 10,
1, 0);
} else if (flags & VP9D_DEBLOCK) {
vp9_deblock(&cm->post_proc_buffer_int, ppbuf, q);
} else {
vp8_yv12_copy_frame(&cm->post_proc_buffer_int, ppbuf);
}
} else if (flags & VP9D_DEMACROBLOCK) {
deblock_and_de_macro_block(cm->frame_to_show, ppbuf,
q + (ppflags->deblocking_level - 5) * 10, 1, 0);
} else if (flags & VP9D_DEBLOCK) {
......@@ -653,6 +723,9 @@ int vp9_post_proc_frame(struct VP9Common *cm,
vp8_yv12_copy_frame(cm->frame_to_show, ppbuf);
}
cm->postproc_state.last_base_qindex = cm->base_qindex;
cm->postproc_state.last_frame_valid = 1;
if (flags & VP9D_ADDNOISE) {
const int noise_level = ppflags->noise_level;
if (ppstate->last_q != q ||
......@@ -673,6 +746,7 @@ int vp9_post_proc_frame(struct VP9Common *cm,
dest->uv_width = dest->y_width >> cm->subsampling_x;
dest->uv_height = dest->y_height >> cm->subsampling_y;
swap_mi_and_prev_mi(cm);
return 0;
}
#endif
......@@ -14,6 +14,8 @@
#include "vpx_ports/mem.h"
#include "vpx_scale/yv12config.h"
#include "vp9/common/vp9_blockd.h"
#include "vp9/common/vp9_mfqe.h"
#include "vp9/common/vp9_ppflags.h"
#ifdef __cplusplus
......@@ -24,6 +26,10 @@ struct postproc_state {
int last_q;
int last_noise;
char noise[3072];
int last_base_qindex;
int last_frame_valid;
MODE_INFO *prev_mip;
MODE_INFO *prev_mi;
DECLARE_ALIGNED(16, char, blackclamp[16]);
DECLARE_ALIGNED(16, char, whiteclamp[16]);
DECLARE_ALIGNED(16, char, bothclamp[16]);
......@@ -31,6 +37,8 @@ struct postproc_state {
struct VP9Common;
#define MFQE_PRECISION 4
int vp9_post_proc_frame(struct VP9Common *cm,
YV12_BUFFER_CONFIG *dest, vp9_ppflags_t *flags);
......
......@@ -26,7 +26,8 @@ enum {
VP9D_DEBUG_TXT_RATE_INFO = 1 << 6,
VP9D_DEBUG_DRAW_MV = 1 << 7,
VP9D_DEBUG_CLR_BLK_MODES = 1 << 8,
VP9D_DEBUG_CLR_FRM_REF_BLKS = 1 << 9
VP9D_DEBUG_CLR_FRM_REF_BLKS = 1 << 9,
VP9D_MFQE = 1 << 10
};
typedef struct {
......
......@@ -72,6 +72,8 @@ VP9_COMMON_SRCS-$(ARCH_X86)$(ARCH_X86_64) += common/x86/vp9_loopfilter_intrin_ss
VP9_COMMON_SRCS-$(HAVE_AVX2) += common/x86/vp9_loopfilter_intrin_avx2.c
VP9_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/vp9_postproc.h
VP9_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/vp9_postproc.c
VP9_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/vp9_mfqe.h
VP9_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/vp9_mfqe.c
VP9_COMMON_SRCS-$(HAVE_MMX) += common/x86/vp9_loopfilter_mmx.asm
VP9_COMMON_SRCS-$(HAVE_SSE2) += common/x86/vp9_subpixel_8t_sse2.asm
VP9_COMMON_SRCS-$(HAVE_SSE2) += common/x86/vp9_subpixel_bilinear_sse2.asm
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment