Optimize cache estimate logic.

Optimize 'VP8LCalculateEstimateForCacheSize' for lower quality ranges (Q < 50).
The entropy is generally lower for higher cache_bits, so start searching from
higher cache_bits and settle for a local minima, instead of evaluating all
values.

This speeds up the lossless encoding at lower qualities by 10-15%.

Change-Id: I33c1e958515a2549f2e6f64b1aab3f128660dcec
This commit is contained in:
Vikas Arora 2014-02-11 10:38:47 -08:00
parent 7fb6095b03
commit a7d2ee39be
3 changed files with 31 additions and 23 deletions

View File

@ -815,24 +815,27 @@ int VP8LGetBackwardReferences(int width, int height,
return ok; return ok;
} }
// Returns 1 on success. // Returns entropy for the given cache bits.
static int ComputeCacheHistogram(const uint32_t* const argb, static double ComputeCacheEntropy(const uint32_t* const argb,
int xsize, int ysize, int xsize, int ysize,
const VP8LBackwardRefs* const refs, const VP8LBackwardRefs* const refs,
int cache_bits, int cache_bits) {
VP8LHistogram* const histo) {
int pixel_index = 0; int pixel_index = 0;
int i; int i;
uint32_t k; uint32_t k;
VP8LColorCache hashers;
const int use_color_cache = (cache_bits > 0); const int use_color_cache = (cache_bits > 0);
int cc_init = 0; int cc_init = 0;
double entropy;
const double kSmallPenaltyForLargeCache = 4.0;
VP8LColorCache hashers;
VP8LHistogram histo;
if (use_color_cache) { if (use_color_cache) {
cc_init = VP8LColorCacheInit(&hashers, cache_bits); cc_init = VP8LColorCacheInit(&hashers, cache_bits);
if (!cc_init) return 0; if (!cc_init) return 1e99;
} }
VP8LHistogramInit(&histo, cache_bits);
for (i = 0; i < refs->size; ++i) { for (i = 0; i < refs->size; ++i) {
const PixOrCopy* const v = &refs->refs[i]; const PixOrCopy* const v = &refs->refs[i];
if (PixOrCopyIsLiteral(v)) { if (PixOrCopyIsLiteral(v)) {
@ -841,12 +844,12 @@ static int ComputeCacheHistogram(const uint32_t* const argb,
// push pixel as a cache index // push pixel as a cache index
const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]);
const PixOrCopy token = PixOrCopyCreateCacheIdx(ix); const PixOrCopy token = PixOrCopyCreateCacheIdx(ix);
VP8LHistogramAddSinglePixOrCopy(histo, &token); VP8LHistogramAddSinglePixOrCopy(&histo, &token);
} else { } else {
VP8LHistogramAddSinglePixOrCopy(histo, v); VP8LHistogramAddSinglePixOrCopy(&histo, v);
} }
} else { } else {
VP8LHistogramAddSinglePixOrCopy(histo, v); VP8LHistogramAddSinglePixOrCopy(&histo, v);
} }
if (use_color_cache) { if (use_color_cache) {
for (k = 0; k < PixOrCopyLength(v); ++k) { for (k = 0; k < PixOrCopyLength(v); ++k) {
@ -858,34 +861,38 @@ static int ComputeCacheHistogram(const uint32_t* const argb,
assert(pixel_index == xsize * ysize); assert(pixel_index == xsize * ysize);
(void)xsize; // xsize is not used in non-debug compilations otherwise. (void)xsize; // xsize is not used in non-debug compilations otherwise.
(void)ysize; // ysize is not used in non-debug compilations otherwise. (void)ysize; // ysize is not used in non-debug compilations otherwise.
entropy = VP8LHistogramEstimateBits(&histo) +
kSmallPenaltyForLargeCache * cache_bits;
if (cc_init) VP8LColorCacheClear(&hashers); if (cc_init) VP8LColorCacheClear(&hashers);
return 1; return entropy;
} }
// Returns how many bits are to be used for a color cache. // Returns how many bits are to be used for a color cache.
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
int xsize, int ysize, int xsize, int ysize, int quality,
int* const best_cache_bits) { int* const best_cache_bits) {
int ok = 0; int ok = 0;
int cache_bits; int cache_bits;
// Number of tries to get optimal cache-bits after finding a local minima.
const int max_tries_after_min = 3 + (quality >> 4);
int num_tries_after_min = 0;
double lowest_entropy = 1e99; double lowest_entropy = 1e99;
VP8LBackwardRefs refs; VP8LBackwardRefs refs;
static const double kSmallPenaltyForLargeCache = 4.0;
static const int quality = 30;
if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) || if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) ||
!BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) { !BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) {
goto Error; goto Error;
} }
for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) { // The entropy is generally lower for higher cache_bits. Start searching from
double cur_entropy; // the highest value and settle for a local minima.
VP8LHistogram histo; for (cache_bits = MAX_COLOR_CACHE_BITS;
VP8LHistogramInit(&histo, cache_bits); cache_bits >= 0 && num_tries_after_min < max_tries_after_min;
ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo); --cache_bits, ++num_tries_after_min) {
cur_entropy = VP8LHistogramEstimateBits(&histo) + const double cur_entropy = ComputeCacheEntropy(argb, xsize, ysize,
kSmallPenaltyForLargeCache * cache_bits; &refs, cache_bits);
if (cache_bits == 0 || cur_entropy < lowest_entropy) { if (cur_entropy < lowest_entropy) {
*best_cache_bits = cache_bits; *best_cache_bits = cache_bits;
lowest_entropy = cur_entropy; lowest_entropy = cur_entropy;
num_tries_after_min = 0;
} }
} }
ok = 1; ok = 1;

View File

@ -142,7 +142,7 @@ int VP8LGetBackwardReferences(int width, int height,
// Produce an estimate for a good color cache size for the image. // Produce an estimate for a good color cache size for the image.
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
int xsize, int ysize, int xsize, int ysize, int quality,
int* const best_cache_bits); int* const best_cache_bits);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -1042,7 +1042,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
if (enc->cache_bits_ > 0) { if (enc->cache_bits_ > 0) {
if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_,
height, &enc->cache_bits_)) { height, quality,
&enc->cache_bits_)) {
err = VP8_ENC_ERROR_INVALID_CONFIGURATION; err = VP8_ENC_ERROR_INVALID_CONFIGURATION;
goto Error; goto Error;
} }