From 31b1e3434227c9614e441adf017baf6002d686ab Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Thu, 20 Oct 2016 15:17:23 +0200 Subject: [PATCH] fix SSIM metric ... by ignoring too-dark area Roughly, if both the source and the reference areas are darker too dark (R/G/B <= ~6), they are ignored. One caveat: SSIM calculation won't work for U/V planes, which are 128-centered and not related to luminance. But WebPPlaneDistortion() enforces the conversion to RGB, if needed. Change-Id: I586c2579c475583b8c90c5baefd766b1d5aea591 --- extras/get_disto.c | 29 +++++++++++++++++------------ src/dsp/enc.c | 28 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/extras/get_disto.c b/extras/get_disto.c index 172bb43f..b364078e 100644 --- a/extras/get_disto.c +++ b/extras/get_disto.c @@ -87,6 +87,7 @@ static int DiffScaleChannel(uint8_t* src1, int stride1, // bigger annoyance of having to open up internal details of libdsp... #define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1 + // struct for accumulating statistical moments typedef struct { uint32_t w; // sum(w_i) : sum of weights @@ -102,20 +103,24 @@ static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) { const uint32_t w2 = N * N; const uint32_t C1 = 20 * w2; const uint32_t C2 = 60 * w2; + const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6 const uint64_t xmxm = (uint64_t)stats->xm * stats->xm; const uint64_t ymym = (uint64_t)stats->ym * stats->ym; - const int64_t xmym = (int64_t)stats->xm * stats->ym; - const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative - const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; - const uint64_t syy = (uint64_t)stats->yym * N - ymym; - // we descale by 8 to prevent overflow during the fnum/fden multiply. - const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; - const uint64_t den_S = (sxx + syy + C2) >> 8; - const uint64_t fnum = (2 * xmym + C1) * num_S; - const uint64_t fden = (xmxm + ymym + C1) * den_S; - const double r = (double)fnum / fden; - assert(r >= 0. && r <= 1.0); - return r; + if (xmxm + ymym >= C3) { + const int64_t xmym = (int64_t)stats->xm * stats->ym; + const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative + const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; + const uint64_t syy = (uint64_t)stats->yym * N - ymym; + // we descale by 8 to prevent overflow during the fnum/fden multiply. + const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; + const uint64_t den_S = (sxx + syy + C2) >> 8; + const uint64_t fnum = (2 * xmym + C1) * num_S; + const uint64_t fden = (xmxm + ymym + C1) * den_S; + const double r = (double)fnum / fden; + assert(r >= 0. && r <= 1.0); + return r; + } + return 1.; // area is too dark to contribute meaningfully } static double SSIMGetClipped(const uint8_t* src1, int stride1, diff --git a/src/dsp/enc.c b/src/dsp/enc.c index 15313b1f..0bdb0b79 100644 --- a/src/dsp/enc.c +++ b/src/dsp/enc.c @@ -704,20 +704,24 @@ static WEBP_INLINE double SSIMCalculation( const uint32_t w2 = N * N; const uint32_t C1 = 20 * w2; const uint32_t C2 = 60 * w2; + const uint32_t C3 = 8 * 8 * w2; // 'dark' limit ~= 6 const uint64_t xmxm = (uint64_t)stats->xm * stats->xm; const uint64_t ymym = (uint64_t)stats->ym * stats->ym; - const int64_t xmym = (int64_t)stats->xm * stats->ym; - const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative - const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; - const uint64_t syy = (uint64_t)stats->yym * N - ymym; - // we descale by 8 to prevent overflow during the fnum/fden multiply. - const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; - const uint64_t den_S = (sxx + syy + C2) >> 8; - const uint64_t fnum = (2 * xmym + C1) * num_S; - const uint64_t fden = (xmxm + ymym + C1) * den_S; - const double r = (double)fnum / fden; - assert(r >= 0. && r <= 1.0); - return r; + if (xmxm + ymym >= C3) { + const int64_t xmym = (int64_t)stats->xm * stats->ym; + const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative + const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; + const uint64_t syy = (uint64_t)stats->yym * N - ymym; + // we descale by 8 to prevent overflow during the fnum/fden multiply. + const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; + const uint64_t den_S = (sxx + syy + C2) >> 8; + const uint64_t fnum = (2 * xmym + C1) * num_S; + const uint64_t fden = (xmxm + ymym + C1) * den_S; + const double r = (double)fnum / fden; + assert(r >= 0. && r <= 1.0); + return r; + } + return 1.; // area is too dark to contribute meaningfully } double VP8SSIMFromStats(const VP8DistoStats* const stats) {