mirror of
https://github.com/webmproject/libwebp.git
synced 2025-09-24 03:32:05 +02:00
use DC error diffusion for U/V at low-quality
This fixes some color smearing due to heavy quantization. This is only enabled for q <= 30 (cf ERROR_DIFFUSION_QUALITY) Change-Id: I07e83a4d38461357a32c9e214f7eadc6db73baa9
This commit is contained in:
@@ -826,6 +826,80 @@ static int ReconstructIntra4(VP8EncIterator* const it,
|
||||
return nz;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DC-error diffusion
|
||||
|
||||
// Diffusion weights. We under-correct a bit (3/4th of the error is actually
|
||||
// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0.
|
||||
#define C1 2 // fraction of error sent to the 4x4 block below
|
||||
#define C2 1 // fraction of error sent to the 4x4 block on the right
|
||||
#define DSHIFT 2
|
||||
|
||||
// Quantize as usual, but also compute and return the quantization error.
|
||||
// Error is already divided by DSHIFT.
|
||||
static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
|
||||
int V = *v;
|
||||
const int sign = (V < 0);
|
||||
if (sign) V = -V;
|
||||
if (V > (int)mtx->zthresh_[0]) {
|
||||
const int qV = QUANTDIV(V, mtx->iq_[0], mtx->bias_[0]) * mtx->q_[0];
|
||||
const int err = (V - qV);
|
||||
*v = sign ? -qV : qV;
|
||||
return (sign ? -err : err) >> DSHIFT;
|
||||
}
|
||||
*v = 0;
|
||||
return (sign ? -V : V) >> DSHIFT;
|
||||
}
|
||||
|
||||
static void CorrectDCValues(const VP8EncIterator* const it,
|
||||
const VP8Matrix* const mtx,
|
||||
int16_t tmp[][16], VP8ModeScore* const rd) {
|
||||
// | top[0] | top[1]
|
||||
// --------+--------+---------
|
||||
// left[0] | tmp[0] tmp[1] <-> err0 err1
|
||||
// left[1] | tmp[2] tmp[3] err2 err3
|
||||
//
|
||||
// Final errors {err1,err2,err3} are preserved and later restored
|
||||
// as top[]/left[] on the next block.
|
||||
int ch;
|
||||
for (ch = 0; ch <= 1; ++ch) {
|
||||
const int16_t* const top = it->top_derr_[it->x_][ch];
|
||||
const int16_t* const left = it->left_derr_[ch];
|
||||
int16_t (* const c)[16] = &tmp[ch * 4];
|
||||
int err0, err1, err2, err3;
|
||||
c[0][0] += C1 * top[0] + C2 * left[0];
|
||||
err0 = QuantizeSingle(&c[0][0], mtx);
|
||||
c[1][0] += C1 * top[1] + C2 * err0;
|
||||
err1 = QuantizeSingle(&c[1][0], mtx);
|
||||
c[2][0] += C1 * err0 + C2 * left[1];
|
||||
err2 = QuantizeSingle(&c[2][0], mtx);
|
||||
c[3][0] += C1 * err1 + C2 * err2;
|
||||
err3 = QuantizeSingle(&c[3][0], mtx);
|
||||
rd->derr[ch][0] = err1;
|
||||
rd->derr[ch][1] = err2;
|
||||
rd->derr[ch][2] = err3;
|
||||
}
|
||||
}
|
||||
|
||||
static void StoreDiffusionErrors(VP8EncIterator* const it,
|
||||
const VP8ModeScore* const rd) {
|
||||
int ch;
|
||||
for (ch = 0; ch <= 1; ++ch) {
|
||||
int16_t* const top = it->top_derr_[it->x_][ch];
|
||||
int16_t* const left = it->left_derr_[ch];
|
||||
left[0] = rd->derr[ch][0]; // restore err1
|
||||
left[1] = rd->derr[ch][2]; // ... err3
|
||||
top[0] = rd->derr[ch][1]; // ... err2
|
||||
top[1] = rd->derr[ch][2]; // ... err3.
|
||||
}
|
||||
}
|
||||
|
||||
#undef C1
|
||||
#undef C2
|
||||
#undef DSHIFT
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
|
||||
uint8_t* const yuv_out, int mode) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
@@ -839,6 +913,8 @@ static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
|
||||
for (n = 0; n < 8; n += 2) {
|
||||
VP8FTransform2(src + VP8ScanUV[n], ref + VP8ScanUV[n], tmp[n]);
|
||||
}
|
||||
if (it->top_derr_ != NULL) CorrectDCValues(it, &dqm->uv_, tmp, rd);
|
||||
|
||||
if (DO_TRELLIS_UV && it->do_trellis_) {
|
||||
int ch, x, y;
|
||||
for (ch = 0, n = 0; ch <= 2; ch += 2) {
|
||||
@@ -1101,6 +1177,9 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
|
||||
CopyScore(&rd_best, &rd_uv);
|
||||
rd->mode_uv = mode;
|
||||
memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels));
|
||||
if (it->top_derr_ != NULL) {
|
||||
memcpy(rd->derr, rd_uv.derr, sizeof(rd_uv.derr));
|
||||
}
|
||||
SwapPtr(&dst, &tmp_dst);
|
||||
}
|
||||
}
|
||||
@@ -1109,6 +1188,9 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
|
||||
if (dst != dst0) { // copy 16x8 block if needed
|
||||
VP8Copy16x8(dst, dst0);
|
||||
}
|
||||
if (it->top_derr_ != NULL) { // store diffusion errors for next block
|
||||
StoreDiffusionErrors(it, rd);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user