Improve compression by better entropy analysis.

Change-Id: I6b56ca8d7d9a046a581baa0e85504136685d1161
This commit is contained in:
Vincent Rabaud 2017-02-27 21:58:22 +01:00
parent 7d985bd1d0
commit 75a9c3c452

View File

@ -188,20 +188,31 @@ static WEBP_INLINE uint32_t HashPix(uint32_t pix) {
static int AnalyzeEntropy(const uint32_t* argb, static int AnalyzeEntropy(const uint32_t* argb,
int width, int height, int argb_stride, int width, int height, int argb_stride,
int use_palette, int use_palette,
int palette_size, int transform_bits,
EntropyIx* const min_entropy_ix, EntropyIx* const min_entropy_ix,
int* const red_and_blue_always_zero) { int* const red_and_blue_always_zero) {
// Allocate histogram set with cache_bits = 0. // Allocate histogram set with cache_bits = 0.
uint32_t* const histo = uint32_t* histo;
(uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256);
if (use_palette && palette_size <= 16) {
// In the case of small palettes, we pack 2, 4 or 8 pixels together. In
// practice, small palette are better than any other transform.
*min_entropy_ix = kPalette;
*red_and_blue_always_zero = 1;
return 1;
}
histo = (uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256);
if (histo != NULL) { if (histo != NULL) {
int i, x, y; int i, x, y;
const uint32_t* prev_row = argb; const uint32_t* prev_row = NULL;
const uint32_t* curr_row = argb + argb_stride; const uint32_t* curr_row = argb;
for (y = 1; y < height; ++y) { for (y = 0; y < height; ++y) {
for (x = 1; x < width; ++x) { for (x = 1; x < width; ++x) {
const uint32_t pix = curr_row[x]; const uint32_t pix = curr_row[x];
const uint32_t pix_diff = VP8LSubPixels(pix, curr_row[x - 1]); const uint32_t pix_diff = VP8LSubPixels(pix, curr_row[x - 1]);
if ((pix_diff == 0) || (pix == prev_row[x])) continue; if ((pix_diff == 0) || (prev_row != NULL && pix == prev_row[x])) {
continue;
}
AddSingle(pix, AddSingle(pix,
&histo[kHistoAlpha * 256], &histo[kHistoAlpha * 256],
&histo[kHistoRed * 256], &histo[kHistoRed * 256],
@ -262,15 +273,41 @@ static int AnalyzeEntropy(const uint32_t* argb,
entropy_comp[kHistoRedPredSubGreen] + entropy_comp[kHistoRedPredSubGreen] +
entropy_comp[kHistoGreenPred] + entropy_comp[kHistoGreenPred] +
entropy_comp[kHistoBluePredSubGreen]; entropy_comp[kHistoBluePredSubGreen];
// Palette mode seems more efficient in a breakeven case. Bias with 1.0. entropy[kPalette] = entropy_comp[kHistoPalette];
entropy[kPalette] = entropy_comp[kHistoPalette] - 1.0;
// When including transforms, there is an overhead in bits from
// storing them. This overhead is small but matters for small images.
// For spatial, there are 14 transformations.
entropy[kSpatial] += VP8LSubSampleSize(width, transform_bits) *
VP8LSubSampleSize(height, transform_bits) *
VP8LFastLog2(14);
// For color transforms: 24 as only 3 channels are considered in a
// ColorTransformElement.
entropy[kSpatialSubGreen] += VP8LSubSampleSize(width, transform_bits) *
VP8LSubSampleSize(height, transform_bits) *
VP8LFastLog2(24);
// For palettes, add the cost of storing the palette.
// We empirically estimate the cost of a compressed entry as 8 bits.
// The palette is differential-coded when compressed hence a much
// lower cost than sizeof(uint32_t)*8.
entropy[kPalette] += palette_size * 8;
if (entropy[kDirect] == 0) {
// If the entropy is null, there should only be one color,
// and that case is handled at the very beginning of that function.
// Unfortunately, we can also have a null entropy because we skip the
// first column (an image with constant values on each line generates
// a null entropy but could have a different color per line).
// TODO(vrabaud) investigate and fix.
*min_entropy_ix = kPalette;
} else {
*min_entropy_ix = kDirect; *min_entropy_ix = kDirect;
for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) { for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
if (entropy[*min_entropy_ix] > entropy[k]) { if (entropy[*min_entropy_ix] > entropy[k]) {
*min_entropy_ix = (EntropyIx)k; *min_entropy_ix = (EntropyIx)k;
} }
} }
}
*red_and_blue_always_zero = 1; *red_and_blue_always_zero = 1;
// Let's check if the histogram of the chosen entropy mode has // Let's check if the histogram of the chosen entropy mode has
// non-zero red and blue values. If all are zero, we can later skip // non-zero red and blue values. If all are zero, we can later skip
@ -358,8 +395,9 @@ static int AnalyzeAndInit(VP8LEncoder* const enc) {
int red_and_blue_always_zero; int red_and_blue_always_zero;
EntropyIx min_entropy_ix; EntropyIx min_entropy_ix;
if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride, if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride,
enc->use_palette_, &min_entropy_ix, enc->use_palette_,
&red_and_blue_always_zero)) { enc->palette_size_, enc->transform_bits_,
&min_entropy_ix, &red_and_blue_always_zero)) {
return 0; return 0;
} }
enc->use_palette_ = (min_entropy_ix == kPalette); enc->use_palette_ = (min_entropy_ix == kPalette);