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
This commit is contained in:
Pascal Massimino 2016-10-20 15:17:23 +02:00
parent 2f51b614b0
commit 31b1e34342
2 changed files with 33 additions and 24 deletions

View File

@ -87,6 +87,7 @@ static int DiffScaleChannel(uint8_t* src1, int stride1,
// bigger annoyance of having to open up internal details of libdsp... // bigger annoyance of having to open up internal details of libdsp...
#define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1 #define SSIM_KERNEL 3 // total size of the kernel: 2 * SSIM_KERNEL + 1
// struct for accumulating statistical moments // struct for accumulating statistical moments
typedef struct { typedef struct {
uint32_t w; // sum(w_i) : sum of weights 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 w2 = N * N;
const uint32_t C1 = 20 * w2; const uint32_t C1 = 20 * w2;
const uint32_t C2 = 60 * 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 xmxm = (uint64_t)stats->xm * stats->xm;
const uint64_t ymym = (uint64_t)stats->ym * stats->ym; const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
const int64_t xmym = (int64_t)stats->xm * stats->ym; if (xmxm + ymym >= C3) {
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative const int64_t xmym = (int64_t)stats->xm * stats->ym;
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
const uint64_t syy = (uint64_t)stats->yym * N - ymym; const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
// we descale by 8 to prevent overflow during the fnum/fden multiply. const uint64_t syy = (uint64_t)stats->yym * N - ymym;
const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; // we descale by 8 to prevent overflow during the fnum/fden multiply.
const uint64_t den_S = (sxx + syy + C2) >> 8; const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
const uint64_t fnum = (2 * xmym + C1) * num_S; const uint64_t den_S = (sxx + syy + C2) >> 8;
const uint64_t fden = (xmxm + ymym + C1) * den_S; const uint64_t fnum = (2 * xmym + C1) * num_S;
const double r = (double)fnum / fden; const uint64_t fden = (xmxm + ymym + C1) * den_S;
assert(r >= 0. && r <= 1.0); const double r = (double)fnum / fden;
return r; 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, static double SSIMGetClipped(const uint8_t* src1, int stride1,

View File

@ -704,20 +704,24 @@ static WEBP_INLINE double SSIMCalculation(
const uint32_t w2 = N * N; const uint32_t w2 = N * N;
const uint32_t C1 = 20 * w2; const uint32_t C1 = 20 * w2;
const uint32_t C2 = 60 * 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 xmxm = (uint64_t)stats->xm * stats->xm;
const uint64_t ymym = (uint64_t)stats->ym * stats->ym; const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
const int64_t xmym = (int64_t)stats->xm * stats->ym; if (xmxm + ymym >= C3) {
const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative const int64_t xmym = (int64_t)stats->xm * stats->ym;
const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm; const int64_t sxy = (int64_t)stats->xym * N - xmym; // can be negative
const uint64_t syy = (uint64_t)stats->yym * N - ymym; const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
// we descale by 8 to prevent overflow during the fnum/fden multiply. const uint64_t syy = (uint64_t)stats->yym * N - ymym;
const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8; // we descale by 8 to prevent overflow during the fnum/fden multiply.
const uint64_t den_S = (sxx + syy + C2) >> 8; const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
const uint64_t fnum = (2 * xmym + C1) * num_S; const uint64_t den_S = (sxx + syy + C2) >> 8;
const uint64_t fden = (xmxm + ymym + C1) * den_S; const uint64_t fnum = (2 * xmym + C1) * num_S;
const double r = (double)fnum / fden; const uint64_t fden = (xmxm + ymym + C1) * den_S;
assert(r >= 0. && r <= 1.0); const double r = (double)fnum / fden;
return r; assert(r >= 0. && r <= 1.0);
return r;
}
return 1.; // area is too dark to contribute meaningfully
} }
double VP8SSIMFromStats(const VP8DistoStats* const stats) { double VP8SSIMFromStats(const VP8DistoStats* const stats) {