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,
int width, int height, int argb_stride,
int use_palette,
int palette_size, int transform_bits,
EntropyIx* const min_entropy_ix,
int* const red_and_blue_always_zero) {
// Allocate histogram set with cache_bits = 0.
uint32_t* const histo =
(uint32_t*)WebPSafeCalloc(kHistoTotal, sizeof(*histo) * 256);
uint32_t* histo;
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) {
int i, x, y;
const uint32_t* prev_row = argb;
const uint32_t* curr_row = argb + argb_stride;
for (y = 1; y < height; ++y) {
const uint32_t* prev_row = NULL;
const uint32_t* curr_row = argb;
for (y = 0; y < height; ++y) {
for (x = 1; x < width; ++x) {
const uint32_t pix = curr_row[x];
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,
&histo[kHistoAlpha * 256],
&histo[kHistoRed * 256],
@ -262,13 +273,39 @@ static int AnalyzeEntropy(const uint32_t* argb,
entropy_comp[kHistoRedPredSubGreen] +
entropy_comp[kHistoGreenPred] +
entropy_comp[kHistoBluePredSubGreen];
// Palette mode seems more efficient in a breakeven case. Bias with 1.0.
entropy[kPalette] = entropy_comp[kHistoPalette] - 1.0;
entropy[kPalette] = entropy_comp[kHistoPalette];
*min_entropy_ix = kDirect;
for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
if (entropy[*min_entropy_ix] > entropy[k]) {
*min_entropy_ix = (EntropyIx)k;
// 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;
for (k = kDirect + 1; k <= last_mode_to_analyze; ++k) {
if (entropy[*min_entropy_ix] > entropy[k]) {
*min_entropy_ix = (EntropyIx)k;
}
}
}
*red_and_blue_always_zero = 1;
@ -358,8 +395,9 @@ static int AnalyzeAndInit(VP8LEncoder* const enc) {
int red_and_blue_always_zero;
EntropyIx min_entropy_ix;
if (!AnalyzeEntropy(pic->argb, width, height, pic->argb_stride,
enc->use_palette_, &min_entropy_ix,
&red_and_blue_always_zero)) {
enc->use_palette_,
enc->palette_size_, enc->transform_bits_,
&min_entropy_ix, &red_and_blue_always_zero)) {
return 0;
}
enc->use_palette_ = (min_entropy_ix == kPalette);