mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-26 13:48:21 +01:00
Merge "fast auto-determined filtering strength"
This commit is contained in:
commit
d3408720d8
@ -33,7 +33,7 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
config->target_PSNR = 0.;
|
||||
config->method = 4;
|
||||
config->sns_strength = 50;
|
||||
config->filter_strength = 60; // rather high filtering, helps w/ gradients.
|
||||
config->filter_strength = 60; // mid-filtering
|
||||
config->filter_sharpness = 0;
|
||||
config->filter_type = 1; // default: strong (so U/V is filtered too)
|
||||
config->partitions = 0;
|
||||
|
@ -11,12 +11,58 @@
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
|
||||
#include <assert.h>
|
||||
#include "./vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// this table gives, for a given sharpness, the filtering strength to be
|
||||
// used (at least) in order to filter a given edge step delta.
|
||||
#define MAX_DELTA_SIZE 64
|
||||
static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18,
|
||||
20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42,
|
||||
44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19,
|
||||
20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43,
|
||||
44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19,
|
||||
21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43,
|
||||
45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20,
|
||||
21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44,
|
||||
45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20,
|
||||
22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44,
|
||||
46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21,
|
||||
22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45,
|
||||
46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21,
|
||||
23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45,
|
||||
47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }
|
||||
};
|
||||
|
||||
int VP8FilterStrengthFromDelta(int sharpness, int delta) {
|
||||
const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1;
|
||||
assert(sharpness >= 0 && sharpness <= 7);
|
||||
return kLevelsFromDelta[sharpness][pos];
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// NOTE: clip1, tables and InitTables are repeated entries of dsp.c
|
||||
static uint8_t abs0[255 + 255 + 1]; // abs(i)
|
||||
static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
|
||||
@ -353,12 +399,13 @@ void VP8InitFilter(VP8EncIterator* const it) {
|
||||
|
||||
void VP8StoreFilterStats(VP8EncIterator* const it) {
|
||||
int d;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
const int s = it->mb_->segment_;
|
||||
const int level0 = it->enc_->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
|
||||
const int level0 = enc->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
|
||||
|
||||
// explore +/-quant range of values around level0
|
||||
const int delta_min = -it->enc_->dqm_[s].quant_;
|
||||
const int delta_max = it->enc_->dqm_[s].quant_;
|
||||
const int delta_min = -enc->dqm_[s].quant_;
|
||||
const int delta_max = enc->dqm_[s].quant_;
|
||||
const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
|
||||
|
||||
if (it->lf_stats_ == NULL) return;
|
||||
@ -385,9 +432,9 @@ void VP8StoreFilterStats(VP8EncIterator* const it) {
|
||||
}
|
||||
|
||||
void VP8AdjustFilterStrength(VP8EncIterator* const it) {
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
if (it->lf_stats_ != NULL) {
|
||||
int s;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
int i, best_level = 0;
|
||||
// Improvement over filter level 0 should be at least 1e-5 (relatively)
|
||||
@ -401,8 +448,27 @@ void VP8AdjustFilterStrength(VP8EncIterator* const it) {
|
||||
}
|
||||
enc->dqm_[s].fstrength_ = best_level;
|
||||
}
|
||||
} else if (enc->config_->filter_strength > 0) {
|
||||
int max_level = 0;
|
||||
int s;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
VP8SegmentInfo* const dqm = &enc->dqm_[s];
|
||||
// this '>> 3' accounts for some inverse WHT scaling
|
||||
const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3;
|
||||
const int level =
|
||||
VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta);
|
||||
if (level > dqm->fstrength_) {
|
||||
dqm->fstrength_ = level;
|
||||
}
|
||||
if (max_level < dqm->fstrength_) {
|
||||
max_level = dqm->fstrength_;
|
||||
}
|
||||
}
|
||||
enc->filter_hdr_.level_ = max_level;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h> // for abs()
|
||||
|
||||
#include "./vp8enci.h"
|
||||
#include "./cost.h"
|
||||
@ -181,8 +182,6 @@ static void SetupMatrices(VP8Encoder* enc) {
|
||||
q16 = ExpandMatrix(&m->y2_, 1);
|
||||
quv = ExpandMatrix(&m->uv_, 2);
|
||||
|
||||
// TODO: Switch to kLambda*[] tables?
|
||||
{
|
||||
m->lambda_i4_ = (3 * q4 * q4) >> 7;
|
||||
m->lambda_i16_ = (3 * q16 * q16);
|
||||
m->lambda_uv_ = (3 * quv * quv) >> 6;
|
||||
@ -191,7 +190,9 @@ static void SetupMatrices(VP8Encoder* enc) {
|
||||
m->lambda_trellis_i16_ = (q16 * q16) >> 2;
|
||||
m->lambda_trellis_uv_ = (quv *quv) << 1;
|
||||
m->tlambda_ = (tlambda_scale * q4) >> 5;
|
||||
}
|
||||
|
||||
m->min_disto_ = 10 * m->y1_.q_[0]; // quantization-aware min disto
|
||||
m->max_edge_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,16 +201,21 @@ static void SetupMatrices(VP8Encoder* enc) {
|
||||
|
||||
// Very small filter-strength values have close to no visual effect. So we can
|
||||
// save a little decoding-CPU by turning filtering off for these.
|
||||
#define FSTRENGTH_CUTOFF 3
|
||||
#define FSTRENGTH_CUTOFF 2
|
||||
|
||||
static void SetupFilterStrength(VP8Encoder* const enc) {
|
||||
int i;
|
||||
const int level0 = enc->config_->filter_strength;
|
||||
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
|
||||
const int level0 = 5 * enc->config_->filter_strength;
|
||||
for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
|
||||
// Segments with lower quantizer will be less filtered. TODO: tune (wrt SNS)
|
||||
const int level = level0 * 256 * enc->dqm_[i].quant_ / 128;
|
||||
const int f = level / (256 + enc->dqm_[i].beta_);
|
||||
enc->dqm_[i].fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f;
|
||||
VP8SegmentInfo* const m = &enc->dqm_[i];
|
||||
// We focus on the quantization of AC coeffs.
|
||||
const int qstep = kAcTable[clip(m->quant_, 0, 127)] >> 2;
|
||||
const int base_strength =
|
||||
VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, qstep);
|
||||
// Segments with lower complexity ('beta') will be less filtered.
|
||||
const int f = base_strength * level0 / (256 + m->beta_);
|
||||
m->fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f;
|
||||
}
|
||||
// We record the initial strength (mainly for the case of 1-segment only).
|
||||
enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_;
|
||||
@ -648,10 +654,10 @@ static int ReconstructIntra16(VP8EncIterator* const it,
|
||||
VP8ModeScore* const rd,
|
||||
uint8_t* const yuv_out,
|
||||
int mode) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
|
||||
const uint8_t* const src = it->yuv_in_ + Y_OFF;
|
||||
const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
|
||||
VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
|
||||
int nz = 0;
|
||||
int n;
|
||||
int16_t tmp[16][16], dc_tmp[16];
|
||||
@ -757,6 +763,17 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
|
||||
// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost.
|
||||
// Pick the mode is lower RD-cost = Rate + lamba * Distortion.
|
||||
|
||||
static void StoreMaxDelta(VP8SegmentInfo* const dqm, const int16_t DCs[16]) {
|
||||
// We look at the first three AC coefficients to determine what is the average
|
||||
// delta between each sub-4x4 block.
|
||||
const int v0 = abs(DCs[1]);
|
||||
const int v1 = abs(DCs[4]);
|
||||
const int v2 = abs(DCs[5]);
|
||||
int max_v = (v0 > v1) ? v1 : v0;
|
||||
max_v = (v2 > max_v) ? v2 : max_v;
|
||||
if (max_v > dqm->max_edge_) dqm->max_edge_ = max_v;
|
||||
}
|
||||
|
||||
static void SwapPtr(uint8_t** a, uint8_t** b) {
|
||||
uint8_t* const tmp = *a;
|
||||
*a = *b;
|
||||
@ -768,8 +785,8 @@ static void SwapOut(VP8EncIterator* const it) {
|
||||
}
|
||||
|
||||
static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
|
||||
const int lambda = dqm->lambda_i16_;
|
||||
const int tlambda = dqm->tlambda_;
|
||||
const uint8_t* const src = it->yuv_in_ + Y_OFF;
|
||||
@ -804,6 +821,13 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) {
|
||||
}
|
||||
SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision.
|
||||
VP8SetIntra16Mode(it, rd->mode_i16);
|
||||
|
||||
// we have a blocky macroblock (only DCs are non-zero) with fairly high
|
||||
// distortion, record max delta so we can later adjust the minimal filtering
|
||||
// strength needed to smooth these blocks out.
|
||||
if ((rd->nz & 0xffff) == 0 && rd->D > dqm->min_disto_) {
|
||||
StoreMaxDelta(dqm, rd->y_dc_levels);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -248,6 +248,8 @@ typedef struct {
|
||||
int beta_; // filter-susceptibility, range [0,255].
|
||||
int quant_; // final segment quantizer.
|
||||
int fstrength_; // final in-loop filtering strength
|
||||
int max_edge_; // max edge delta (for filtering strength)
|
||||
int min_disto_; // minimum distortion required to trigger filtering record
|
||||
// reactivities
|
||||
int lambda_i16_, lambda_i4_, lambda_uv_;
|
||||
int lambda_mode_, lambda_trellis_, tlambda_;
|
||||
@ -555,6 +557,10 @@ void VP8InitFilter(VP8EncIterator* const it);
|
||||
void VP8StoreFilterStats(VP8EncIterator* const it);
|
||||
void VP8AdjustFilterStrength(VP8EncIterator* const it);
|
||||
|
||||
// returns the approximate filtering strength needed to smooth a edge
|
||||
// step of 'delta', given a sharpness parameter 'sharpness'.
|
||||
int VP8FilterStrengthFromDelta(int sharpness, int delta);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
Loading…
Reference in New Issue
Block a user