Move noise level estimate outside denoiser.
Source noise level estimate is also useful for setting variance encoder parameters (variance thresholds, qp-delta, mode selection, etc), so allow it to be used also if denoising is not on. Change-Id: I4fe23d47607b4e17a35287057f489c29114beed1
This commit is contained in:
Родитель
c2f6a7df8d
Коммит
c7da053d4b
|
@ -490,12 +490,9 @@ void vp9_cyclic_refresh_update_parameters(VP9_COMP *const cpi) {
|
|||
cr->rate_ratio_qdelta = 3.0;
|
||||
} else {
|
||||
cr->rate_ratio_qdelta = 2.0;
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0 &&
|
||||
cpi->denoiser.denoising_level >= kMedium)
|
||||
if (cpi->noise_estimate.enabled && cpi->noise_estimate.level >= kMedium)
|
||||
// Reduce the delta-qp if the estimated source noise is above threshold.
|
||||
cr->rate_ratio_qdelta = 1.5;
|
||||
#endif
|
||||
}
|
||||
// Adjust some parameters for low resolutions at low bitrates.
|
||||
if (cm->width <= 352 &&
|
||||
|
|
|
@ -323,7 +323,7 @@ void vp9_denoiser_denoise(VP9_DENOISER *denoiser, MACROBLOCK *mb,
|
|||
struct buf_2d src = mb->plane[0].src;
|
||||
int is_skin = 0;
|
||||
|
||||
if (bs <= BLOCK_16X16 && denoiser->denoising_level >= kMedium) {
|
||||
if (bs <= BLOCK_16X16 && denoiser->denoising_level >= kDenMedium) {
|
||||
// Take center pixel in block to determine is_skin.
|
||||
const int y_width_shift = (4 << b_width_log2_lookup[bs]) >> 1;
|
||||
const int y_height_shift = (4 << b_height_log2_lookup[bs]) >> 1;
|
||||
|
@ -343,13 +343,13 @@ void vp9_denoiser_denoise(VP9_DENOISER *denoiser, MACROBLOCK *mb,
|
|||
mv_col = ctx->best_sse_mv.as_mv.col;
|
||||
mv_row = ctx->best_sse_mv.as_mv.row;
|
||||
motion_magnitude = mv_row * mv_row + mv_col * mv_col;
|
||||
if (denoiser->denoising_level == kHigh && motion_magnitude < 16) {
|
||||
if (denoiser->denoising_level == kDenHigh && motion_magnitude < 16) {
|
||||
denoiser->increase_denoising = 1;
|
||||
} else {
|
||||
denoiser->increase_denoising = 0;
|
||||
}
|
||||
|
||||
if (denoiser->denoising_level >= kMedium)
|
||||
if (denoiser->denoising_level >= kDenMedium)
|
||||
decision = perform_motion_compensation(denoiser, mb, bs,
|
||||
denoiser->increase_denoising,
|
||||
mi_row, mi_col, ctx,
|
||||
|
@ -524,27 +524,9 @@ int vp9_denoiser_alloc(VP9_DENOISER *denoiser, int width, int height,
|
|||
#endif
|
||||
denoiser->increase_denoising = 0;
|
||||
denoiser->frame_buffer_initialized = 1;
|
||||
vp9_denoiser_init_noise_estimate(denoiser, width, height);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vp9_denoiser_init_noise_estimate(VP9_DENOISER *denoiser,
|
||||
int width,
|
||||
int height) {
|
||||
// Denoiser is off by default, i.e., no denoising is performed.
|
||||
// Noise level is measured periodically, and if observed to be above
|
||||
// thresh_noise_estimate, then denoising is performed.
|
||||
denoiser->denoising_level = kLow;
|
||||
denoiser->noise_estimate = 0;
|
||||
denoiser->noise_estimate_count = 0;
|
||||
denoiser->thresh_noise_estimate = 20;
|
||||
if (width * height >= 1920 * 1080) {
|
||||
denoiser->thresh_noise_estimate = 70;
|
||||
} else if (width * height >= 1280 * 720) {
|
||||
denoiser->thresh_noise_estimate = 40;
|
||||
}
|
||||
}
|
||||
|
||||
void vp9_denoiser_free(VP9_DENOISER *denoiser) {
|
||||
int i;
|
||||
denoiser->frame_buffer_initialized = 0;
|
||||
|
@ -558,123 +540,9 @@ void vp9_denoiser_free(VP9_DENOISER *denoiser) {
|
|||
vpx_free_frame_buffer(&denoiser->last_source);
|
||||
}
|
||||
|
||||
void vp9_denoiser_update_noise_estimate(VP9_COMP *const cpi) {
|
||||
const VP9_COMMON *const cm = &cpi->common;
|
||||
CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
|
||||
int frame_period = 10;
|
||||
int thresh_consec_zeromv = 8;
|
||||
unsigned int thresh_sum_diff = 128;
|
||||
int num_frames_estimate = 20;
|
||||
int min_blocks_estimate = cm->mi_rows * cm->mi_cols >> 7;
|
||||
// Estimate of noise level every frame_period frames.
|
||||
// Estimate is between current source and last source.
|
||||
if (cm->current_video_frame % frame_period != 0 ||
|
||||
cpi->denoiser.last_source.y_buffer == NULL ||
|
||||
cpi->resize_pending != 0) {
|
||||
copy_frame(&cpi->denoiser.last_source, cpi->Source);
|
||||
return;
|
||||
} else {
|
||||
int num_samples = 0;
|
||||
uint64_t avg_est = 0;
|
||||
int bsize = BLOCK_16X16;
|
||||
static const unsigned char const_source[16] = {
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128};
|
||||
// Loop over sub-sample of 16x16 blocks of frame, and for blocks that have
|
||||
// been encoded as zero/small mv at least x consecutive frames, compute
|
||||
// the variance to update estimate of noise in the source.
|
||||
const uint8_t *src_y = cpi->Source->y_buffer;
|
||||
const int src_ystride = cpi->Source->y_stride;
|
||||
const uint8_t *last_src_y = cpi->denoiser.last_source.y_buffer;
|
||||
const int last_src_ystride = cpi->denoiser.last_source.y_stride;
|
||||
const uint8_t *src_u = cpi->Source->u_buffer;
|
||||
const uint8_t *src_v = cpi->Source->v_buffer;
|
||||
const int src_uvstride = cpi->Source->uv_stride;
|
||||
const int y_width_shift = (4 << b_width_log2_lookup[bsize]) >> 1;
|
||||
const int y_height_shift = (4 << b_height_log2_lookup[bsize]) >> 1;
|
||||
const int uv_width_shift = y_width_shift >> 1;
|
||||
const int uv_height_shift = y_height_shift >> 1;
|
||||
int mi_row, mi_col;
|
||||
for (mi_row = 0; mi_row < cm->mi_rows; mi_row ++) {
|
||||
for (mi_col = 0; mi_col < cm->mi_cols; mi_col ++) {
|
||||
// 16x16 blocks, 1/4 sample of frame.
|
||||
if (mi_row % 4 == 0 && mi_col % 4 == 0) {
|
||||
int bl_index = mi_row * cm->mi_cols + mi_col;
|
||||
int bl_index1 = bl_index + 1;
|
||||
int bl_index2 = bl_index + cm->mi_cols;
|
||||
int bl_index3 = bl_index2 + 1;
|
||||
// Only consider blocks that are likely steady background. i.e, have
|
||||
// been encoded as zero/low motion x (= thresh_consec_zeromv) frames
|
||||
// in a row. consec_zero_mv[] defined for 8x8 blocks, so consider all
|
||||
// 4 sub-blocks for 16x16 block. Also, avoid skin blocks.
|
||||
const uint8_t ysource =
|
||||
src_y[y_height_shift * src_ystride + y_width_shift];
|
||||
const uint8_t usource =
|
||||
src_u[uv_height_shift * src_uvstride + uv_width_shift];
|
||||
const uint8_t vsource =
|
||||
src_v[uv_height_shift * src_uvstride + uv_width_shift];
|
||||
int is_skin = vp9_skin_pixel(ysource, usource, vsource);
|
||||
if (cr->consec_zero_mv[bl_index] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index1] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index2] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index3] > thresh_consec_zeromv &&
|
||||
!is_skin) {
|
||||
// Compute variance.
|
||||
unsigned int sse;
|
||||
unsigned int variance = cpi->fn_ptr[bsize].vf(src_y,
|
||||
src_ystride,
|
||||
last_src_y,
|
||||
last_src_ystride,
|
||||
&sse);
|
||||
// Only consider this block as valid for noise measurement if the
|
||||
// average term (sse - variance = N * avg^{2}, N = 16X16) of the
|
||||
// temporal residual is small (avoid effects from lighting change).
|
||||
if ((sse - variance) < thresh_sum_diff) {
|
||||
unsigned int sse2;
|
||||
const unsigned int spatial_variance =
|
||||
cpi->fn_ptr[bsize].vf(src_y, src_ystride, const_source,
|
||||
0, &sse2);
|
||||
avg_est += variance / (10 + spatial_variance);
|
||||
num_samples++;
|
||||
}
|
||||
}
|
||||
}
|
||||
src_y += 8;
|
||||
last_src_y += 8;
|
||||
src_u += 4;
|
||||
src_v += 4;
|
||||
}
|
||||
src_y += (src_ystride << 3) - (cm->mi_cols << 3);
|
||||
last_src_y += (last_src_ystride << 3) - (cm->mi_cols << 3);
|
||||
src_u += (src_uvstride << 2) - (cm->mi_cols << 2);
|
||||
src_v += (src_uvstride << 2) - (cm->mi_cols << 2);
|
||||
}
|
||||
// Update noise estimate if we have at a minimum number of block samples,
|
||||
// and avg_est > 0 (avg_est == 0 can happen if the application inputs
|
||||
// duplicate frames).
|
||||
if (num_samples > min_blocks_estimate && avg_est > 0) {
|
||||
// Normalize.
|
||||
avg_est = (avg_est << 8) / num_samples;
|
||||
// Update noise estimate.
|
||||
cpi->denoiser.noise_estimate = (3 * cpi->denoiser.noise_estimate +
|
||||
avg_est) >> 2;
|
||||
cpi->denoiser.noise_estimate_count++;
|
||||
if (cpi->denoiser.noise_estimate_count == num_frames_estimate) {
|
||||
// Reset counter and check noise level condition.
|
||||
cpi->denoiser.noise_estimate_count = 0;
|
||||
if (cpi->denoiser.noise_estimate >
|
||||
(cpi->denoiser.thresh_noise_estimate << 1))
|
||||
cpi->denoiser.denoising_level = kHigh;
|
||||
else
|
||||
if (cpi->denoiser.noise_estimate >
|
||||
cpi->denoiser.thresh_noise_estimate)
|
||||
cpi->denoiser.denoising_level = kMedium;
|
||||
else
|
||||
cpi->denoiser.denoising_level = kLow;
|
||||
}
|
||||
}
|
||||
}
|
||||
copy_frame(&cpi->denoiser.last_source, cpi->Source);
|
||||
void vp9_denoiser_set_noise_level(VP9_DENOISER *denoiser,
|
||||
int noise_level) {
|
||||
denoiser->denoising_level = noise_level;
|
||||
}
|
||||
|
||||
#ifdef OUTPUT_YUV_DENOISED
|
||||
|
|
|
@ -27,9 +27,9 @@ typedef enum vp9_denoiser_decision {
|
|||
} VP9_DENOISER_DECISION;
|
||||
|
||||
typedef enum vp9_denoiser_level {
|
||||
kLow,
|
||||
kMedium,
|
||||
kHigh
|
||||
kDenLow,
|
||||
kDenMedium,
|
||||
kDenHigh
|
||||
} VP9_DENOISER_LEVEL;
|
||||
|
||||
typedef struct vp9_denoiser {
|
||||
|
@ -39,9 +39,6 @@ typedef struct vp9_denoiser {
|
|||
int increase_denoising;
|
||||
int frame_buffer_initialized;
|
||||
VP9_DENOISER_LEVEL denoising_level;
|
||||
int noise_estimate;
|
||||
int thresh_noise_estimate;
|
||||
int noise_estimate_count;
|
||||
} VP9_DENOISER;
|
||||
|
||||
struct VP9_COMP;
|
||||
|
@ -82,11 +79,8 @@ static int total_adj_strong_thresh(BLOCK_SIZE bs, int increase_denoising) {
|
|||
|
||||
void vp9_denoiser_free(VP9_DENOISER *denoiser);
|
||||
|
||||
void vp9_denoiser_init_noise_estimate(VP9_DENOISER *denoiser,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
void vp9_denoiser_update_noise_estimate(struct VP9_COMP *const cpi);
|
||||
void vp9_denoiser_set_noise_level(VP9_DENOISER *denoiser,
|
||||
int noise_level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
@ -489,16 +489,14 @@ static void set_vbp_thresholds(VP9_COMP *cpi, int64_t thresholds[], int q) {
|
|||
thresholds[2] = threshold_base >> 2;
|
||||
thresholds[3] = threshold_base << 2;
|
||||
} else {
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0) {
|
||||
// Increase base variance threshold is estimated noise level is high.
|
||||
if (cpi->denoiser.denoising_level == kHigh)
|
||||
threshold_base = threshold_base << 2;
|
||||
else
|
||||
if (cpi->denoiser.denoising_level == kMedium)
|
||||
threshold_base = threshold_base << 1;
|
||||
}
|
||||
#endif
|
||||
// Increase base variance threshold if estimated noise level is high.
|
||||
if (cpi->noise_estimate.enabled) {
|
||||
if (cpi->noise_estimate.level == kHigh)
|
||||
threshold_base = threshold_base << 2;
|
||||
else
|
||||
if (cpi->noise_estimate.level == kMedium)
|
||||
threshold_base = threshold_base << 1;
|
||||
}
|
||||
thresholds[1] = threshold_base;
|
||||
if (cm->width <= 352 && cm->height <= 288) {
|
||||
thresholds[0] = threshold_base >> 2;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "vp9/encoder/vp9_ethread.h"
|
||||
#include "vp9/encoder/vp9_firstpass.h"
|
||||
#include "vp9/encoder/vp9_mbgraph.h"
|
||||
#include "vp9/encoder/vp9_noise_estimate.h"
|
||||
#include "vp9/encoder/vp9_picklpf.h"
|
||||
#include "vp9/encoder/vp9_ratectrl.h"
|
||||
#include "vp9/encoder/vp9_rd.h"
|
||||
|
@ -805,6 +806,8 @@ static void init_config(struct VP9_COMP *cpi, VP9EncoderConfig *oxcf) {
|
|||
cpi->ref_frame_flags = 0;
|
||||
|
||||
init_buffer_indices(cpi);
|
||||
|
||||
vp9_noise_estimate_init(&cpi->noise_estimate, cm->width, cm->height);
|
||||
}
|
||||
|
||||
static void set_rc_buffer_sizes(RATE_CONTROL *rc,
|
||||
|
@ -3160,6 +3163,7 @@ static void set_frame_size(VP9_COMP *cpi) {
|
|||
// TODO(agrange) Scale cpi->max_mv_magnitude if frame-size has changed.
|
||||
set_mv_search_params(cpi);
|
||||
|
||||
vp9_noise_estimate_init(&cpi->noise_estimate, cm->width, cm->height);
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
// Reset the denoiser on the resized frame.
|
||||
if (cpi->oxcf.noise_sensitivity > 0) {
|
||||
|
@ -3241,21 +3245,18 @@ static void encode_without_recode_loop(VP9_COMP *cpi,
|
|||
|
||||
// Avoid scaling last_source unless its needed.
|
||||
// Last source is currently only used for screen-content mode,
|
||||
// or if partition_search_type == SOURCE_VAR_BASED_PARTITION.
|
||||
// if partition_search_type == SOURCE_VAR_BASED_PARTITION, or if noise
|
||||
// estimation is enabled.
|
||||
if (cpi->unscaled_last_source != NULL &&
|
||||
(cpi->oxcf.content == VP9E_CONTENT_SCREEN ||
|
||||
cpi->sf.partition_search_type == SOURCE_VAR_BASED_PARTITION))
|
||||
cpi->sf.partition_search_type == SOURCE_VAR_BASED_PARTITION ||
|
||||
cpi->noise_estimate.enabled))
|
||||
cpi->Last_Source = vp9_scale_if_required(cm,
|
||||
cpi->unscaled_last_source,
|
||||
&cpi->scaled_last_source,
|
||||
(cpi->oxcf.pass == 0));
|
||||
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0 &&
|
||||
cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) {
|
||||
vp9_denoiser_update_noise_estimate(cpi);
|
||||
}
|
||||
#endif
|
||||
vp9_update_noise_estimate(cpi);
|
||||
|
||||
if (cpi->oxcf.pass == 0 &&
|
||||
cpi->oxcf.rc_mode == VPX_CBR &&
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "vp9/encoder/vp9_lookahead.h"
|
||||
#include "vp9/encoder/vp9_mbgraph.h"
|
||||
#include "vp9/encoder/vp9_mcomp.h"
|
||||
#include "vp9/encoder/vp9_noise_estimate.h"
|
||||
#include "vp9/encoder/vp9_quantize.h"
|
||||
#include "vp9/encoder/vp9_ratectrl.h"
|
||||
#include "vp9/encoder/vp9_rd.h"
|
||||
|
@ -490,6 +491,8 @@ typedef struct VP9_COMP {
|
|||
int resize_buffer_underflow;
|
||||
int resize_count;
|
||||
|
||||
NOISE_ESTIMATE noise_estimate;
|
||||
|
||||
// VAR_BASED_PARTITION thresholds
|
||||
// 0 - threshold_64x64; 1 - threshold_32x32;
|
||||
// 2 - threshold_16x16; 3 - vbp_threshold_8x8;
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "./vpx_dsp_rtcd.h"
|
||||
#include "vpx_dsp/vpx_dsp_common.h"
|
||||
#include "vpx_scale/yv12config.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vp9/common/vp9_reconinter.h"
|
||||
#include "vp9/encoder/vp9_context_tree.h"
|
||||
#include "vp9/encoder/vp9_noise_estimate.h"
|
||||
#include "vp9/encoder/vp9_encoder.h"
|
||||
|
||||
void vp9_noise_estimate_init(NOISE_ESTIMATE *const ne,
|
||||
int width,
|
||||
int height) {
|
||||
ne->enabled = 0;
|
||||
ne->level = kLow;
|
||||
ne->value = 0;
|
||||
ne->count = 0;
|
||||
ne->thresh = 20;
|
||||
if (width * height >= 1920 * 1080) {
|
||||
ne->thresh = 70;
|
||||
} else if (width * height >= 1280 * 720) {
|
||||
ne->thresh = 40;
|
||||
}
|
||||
}
|
||||
|
||||
int enable_noise_estimation(VP9_COMP *const cpi) {
|
||||
// Enable noise estimation if denoising is on (and cyclic refresh, since
|
||||
// noise estimate is currently using a struct defined in cyclic refresh).
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0 &&
|
||||
cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
|
||||
return 1;
|
||||
#endif
|
||||
// Only allow noise estimate under certain encoding mode.
|
||||
// Enabled for 1 pass CBR, speed >=5, and if resolution is same as original.
|
||||
// Not enabled for SVC mode and screen_content_mode.
|
||||
// Not enabled for low resolutions.
|
||||
if (cpi->oxcf.pass == 0 &&
|
||||
cpi->oxcf.rc_mode == VPX_CBR &&
|
||||
cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ &&
|
||||
cpi->oxcf.speed >= 5 &&
|
||||
cpi->resize_state == ORIG &&
|
||||
!cpi->use_svc &&
|
||||
cpi->oxcf.content != VP9E_CONTENT_SCREEN &&
|
||||
cpi->common.width > 352 &&
|
||||
cpi->common.height > 288)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_frame(YV12_BUFFER_CONFIG * const dest,
|
||||
const YV12_BUFFER_CONFIG * const src) {
|
||||
int r;
|
||||
const uint8_t *srcbuf = src->y_buffer;
|
||||
uint8_t *destbuf = dest->y_buffer;
|
||||
|
||||
assert(dest->y_width == src->y_width);
|
||||
assert(dest->y_height == src->y_height);
|
||||
|
||||
for (r = 0; r < dest->y_height; ++r) {
|
||||
memcpy(destbuf, srcbuf, dest->y_width);
|
||||
destbuf += dest->y_stride;
|
||||
srcbuf += src->y_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void vp9_update_noise_estimate(VP9_COMP *const cpi) {
|
||||
const VP9_COMMON *const cm = &cpi->common;
|
||||
CYCLIC_REFRESH *const cr = cpi->cyclic_refresh;
|
||||
NOISE_ESTIMATE *const ne = &cpi->noise_estimate;
|
||||
// Estimate of noise level every frame_period frames.
|
||||
int frame_period = 10;
|
||||
int thresh_consec_zeromv = 8;
|
||||
unsigned int thresh_sum_diff = 128;
|
||||
int num_frames_estimate = 20;
|
||||
int min_blocks_estimate = cm->mi_rows * cm->mi_cols >> 7;
|
||||
// Estimate is between current source and last source.
|
||||
YV12_BUFFER_CONFIG *last_source = cpi->Last_Source;
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0)
|
||||
last_source = &cpi->denoiser.last_source;
|
||||
#endif
|
||||
ne->enabled = enable_noise_estimation(cpi);
|
||||
if (!ne->enabled ||
|
||||
cm->current_video_frame % frame_period != 0 ||
|
||||
last_source == NULL) {
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0)
|
||||
copy_frame(&cpi->denoiser.last_source, cpi->Source);
|
||||
#endif
|
||||
return;
|
||||
} else {
|
||||
int num_samples = 0;
|
||||
uint64_t avg_est = 0;
|
||||
int bsize = BLOCK_16X16;
|
||||
static const unsigned char const_source[16] = {
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128};
|
||||
// Loop over sub-sample of 16x16 blocks of frame, and for blocks that have
|
||||
// been encoded as zero/small mv at least x consecutive frames, compute
|
||||
// the variance to update estimate of noise in the source.
|
||||
const uint8_t *src_y = cpi->Source->y_buffer;
|
||||
const int src_ystride = cpi->Source->y_stride;
|
||||
const uint8_t *last_src_y = last_source->y_buffer;
|
||||
const int last_src_ystride = last_source->y_stride;
|
||||
const uint8_t *src_u = cpi->Source->u_buffer;
|
||||
const uint8_t *src_v = cpi->Source->v_buffer;
|
||||
const int src_uvstride = cpi->Source->uv_stride;
|
||||
const int y_width_shift = (4 << b_width_log2_lookup[bsize]) >> 1;
|
||||
const int y_height_shift = (4 << b_height_log2_lookup[bsize]) >> 1;
|
||||
const int uv_width_shift = y_width_shift >> 1;
|
||||
const int uv_height_shift = y_height_shift >> 1;
|
||||
int mi_row, mi_col;
|
||||
for (mi_row = 0; mi_row < cm->mi_rows; mi_row++) {
|
||||
for (mi_col = 0; mi_col < cm->mi_cols; mi_col++) {
|
||||
// 16x16 blocks, 1/4 sample of frame.
|
||||
if (mi_row % 4 == 0 && mi_col % 4 == 0) {
|
||||
int bl_index = mi_row * cm->mi_cols + mi_col;
|
||||
int bl_index1 = bl_index + 1;
|
||||
int bl_index2 = bl_index + cm->mi_cols;
|
||||
int bl_index3 = bl_index2 + 1;
|
||||
// Only consider blocks that are likely steady background. i.e, have
|
||||
// been encoded as zero/low motion x (= thresh_consec_zeromv) frames
|
||||
// in a row. consec_zero_mv[] defined for 8x8 blocks, so consider all
|
||||
// 4 sub-blocks for 16x16 block. Also, avoid skin blocks.
|
||||
const uint8_t ysource =
|
||||
src_y[y_height_shift * src_ystride + y_width_shift];
|
||||
const uint8_t usource =
|
||||
src_u[uv_height_shift * src_uvstride + uv_width_shift];
|
||||
const uint8_t vsource =
|
||||
src_v[uv_height_shift * src_uvstride + uv_width_shift];
|
||||
int is_skin = vp9_skin_pixel(ysource, usource, vsource);
|
||||
if (cr->consec_zero_mv[bl_index] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index1] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index2] > thresh_consec_zeromv &&
|
||||
cr->consec_zero_mv[bl_index3] > thresh_consec_zeromv &&
|
||||
!is_skin) {
|
||||
// Compute variance.
|
||||
unsigned int sse;
|
||||
unsigned int variance = cpi->fn_ptr[bsize].vf(src_y,
|
||||
src_ystride,
|
||||
last_src_y,
|
||||
last_src_ystride,
|
||||
&sse);
|
||||
// Only consider this block as valid for noise measurement if the
|
||||
// average term (sse - variance = N * avg^{2}, N = 16X16) of the
|
||||
// temporal residual is small (avoid effects from lighting change).
|
||||
if ((sse - variance) < thresh_sum_diff) {
|
||||
unsigned int sse2;
|
||||
const unsigned int spatial_variance =
|
||||
cpi->fn_ptr[bsize].vf(src_y, src_ystride, const_source,
|
||||
0, &sse2);
|
||||
avg_est += variance / (10 + spatial_variance);
|
||||
num_samples++;
|
||||
}
|
||||
}
|
||||
}
|
||||
src_y += 8;
|
||||
last_src_y += 8;
|
||||
src_u += 4;
|
||||
src_v += 4;
|
||||
}
|
||||
src_y += (src_ystride << 3) - (cm->mi_cols << 3);
|
||||
last_src_y += (last_src_ystride << 3) - (cm->mi_cols << 3);
|
||||
src_u += (src_uvstride << 2) - (cm->mi_cols << 2);
|
||||
src_v += (src_uvstride << 2) - (cm->mi_cols << 2);
|
||||
}
|
||||
// Update noise estimate if we have at a minimum number of block samples,
|
||||
// and avg_est > 0 (avg_est == 0 can happen if the application inputs
|
||||
// duplicate frames).
|
||||
if (num_samples > min_blocks_estimate && avg_est > 0) {
|
||||
// Normalize.
|
||||
avg_est = (avg_est << 8) / num_samples;
|
||||
// Update noise estimate.
|
||||
ne->value = (3 * ne->value + avg_est) >> 2;
|
||||
ne->count++;
|
||||
if (ne->count == num_frames_estimate) {
|
||||
// Reset counter and check noise level condition.
|
||||
ne->count = 0;
|
||||
if (ne->value > (ne->thresh << 1))
|
||||
ne->level = kHigh;
|
||||
else
|
||||
if (ne->value > ne->thresh)
|
||||
ne->level = kMedium;
|
||||
else
|
||||
ne->level = kLow;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0) {
|
||||
copy_frame(&cpi->denoiser.last_source, cpi->Source);
|
||||
vp9_denoiser_set_noise_level(&cpi->denoiser, ne->level);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef VP9_ENCODER_NOISE_ESTIMATE_H_
|
||||
#define VP9_ENCODER_NOISE_ESTIMATE_H_
|
||||
|
||||
#include "vp9/encoder/vp9_block.h"
|
||||
#include "vp9/encoder/vp9_skin_detection.h"
|
||||
#include "vpx_scale/yv12config.h"
|
||||
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
#include "vp9/encoder/vp9_denoiser.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum noise_level {
|
||||
kLow,
|
||||
kMedium,
|
||||
kHigh
|
||||
} NOISE_LEVEL;
|
||||
|
||||
typedef struct noise_estimate {
|
||||
int enabled;
|
||||
NOISE_LEVEL level;
|
||||
int value;
|
||||
int thresh;
|
||||
int count;
|
||||
} NOISE_ESTIMATE;
|
||||
|
||||
struct VP9_COMP;
|
||||
|
||||
void vp9_noise_estimate_init(NOISE_ESTIMATE *const ne,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
void vp9_update_noise_estimate(struct VP9_COMP *const cpi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // VP9_ENCODER_NOISE_ESTIMATE_H_
|
|
@ -1073,12 +1073,9 @@ int set_intra_cost_penalty(const VP9_COMP *const cpi, BLOCK_SIZE bsize) {
|
|||
// Reduce the intra cost penalty for small blocks (<=16x16).
|
||||
int reduction_fac =
|
||||
(bsize <= BLOCK_16X16) ? ((bsize <= BLOCK_8X8) ? 4 : 2) : 0;
|
||||
#if CONFIG_VP9_TEMPORAL_DENOISING
|
||||
if (cpi->oxcf.noise_sensitivity > 0 &&
|
||||
cpi->denoiser.denoising_level == kHigh)
|
||||
if (cpi->noise_estimate.enabled && cpi->noise_estimate.level == kHigh)
|
||||
// Don't reduce intra cost penalty if estimated noise level is high.
|
||||
reduction_fac = 0;
|
||||
#endif
|
||||
return vp9_get_intra_cost_penalty(
|
||||
cm->base_qindex, cm->y_dc_delta_q, cm->bit_depth) >> reduction_fac;
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ VP9_CX_SRCS-yes += encoder/vp9_aq_complexity.c
|
|||
VP9_CX_SRCS-yes += encoder/vp9_aq_complexity.h
|
||||
VP9_CX_SRCS-yes += encoder/vp9_skin_detection.c
|
||||
VP9_CX_SRCS-yes += encoder/vp9_skin_detection.h
|
||||
VP9_CX_SRCS-yes += encoder/vp9_noise_estimate.c
|
||||
VP9_CX_SRCS-yes += encoder/vp9_noise_estimate.h
|
||||
ifeq ($(CONFIG_VP9_POSTPROC),yes)
|
||||
VP9_CX_SRCS-$(CONFIG_INTERNAL_STATS) += common/vp9_postproc.h
|
||||
VP9_CX_SRCS-$(CONFIG_INTERNAL_STATS) += common/vp9_postproc.c
|
||||
|
|
Загрузка…
Ссылка в новой задаче