From b33e8a05ee949320f4498a2de9af59eccf527d41 Mon Sep 17 00:00:00 2001 From: Vikas Arora Date: Mon, 3 Mar 2014 13:49:54 -0800 Subject: [PATCH] Refactor code for HistogramCombine. Refactor code for HistogramCombine and optimize the code by calculating the combined entropy and avoid un-necessary Histogram merges. This speeds up lossless encoding by 1-2% and almost no impact on compression density. Change-Id: Iedfcf4c1f3e88077bc77fc7b8c780c4cd5d6362b --- src/enc/backward_references.c | 3 +- src/enc/histogram.c | 246 +++++++++++++++++++++------------- src/enc/histogram.h | 4 +- src/enc/vp8l.c | 6 +- 4 files changed, 157 insertions(+), 102 deletions(-) diff --git a/src/enc/backward_references.c b/src/enc/backward_references.c index 04dbe55c..24304f60 100644 --- a/src/enc/backward_references.c +++ b/src/enc/backward_references.c @@ -436,7 +436,8 @@ static int CostModelBuild(CostModel* const m, int xsize, int ysize, } VP8LHistogramCreate(&histo, &refs, cache_bits); ConvertPopulationCountTableToBitEstimates( - VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_); + VP8LHistogramNumCodes(histo.palette_code_bits_), + histo.literal_, m->literal_); ConvertPopulationCountTableToBitEstimates( VALUES_IN_BYTE, histo.red_, m->red_); ConvertPopulationCountTableToBitEstimates( diff --git a/src/enc/histogram.c b/src/enc/histogram.c index abd253bd..7d1056b6 100644 --- a/src/enc/histogram.c +++ b/src/enc/histogram.c @@ -98,25 +98,9 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, } } -static double BitsEntropy(const int* const array, int n) { - double retval = 0.; - int sum = 0; - int nonzeros = 0; - int max_val = 0; - int i; +static WEBP_INLINE double BitsEntropyRefine(int nonzeros, int sum, int max_val, + double retval) { double mix; - for (i = 0; i < n; ++i) { - if (array[i] != 0) { - sum += array[i]; - ++nonzeros; - retval -= VP8LFastSLog2(array[i]); - if (max_val < array[i]) { - max_val = array[i]; - } - } - } - retval += VP8LFastSLog2(sum); - if (nonzeros < 5) { if (nonzeros <= 1) { return 0; @@ -147,42 +131,108 @@ static double BitsEntropy(const int* const array, int n) { } } -// Returns the cost encode the rle-encoded entropy code. -// The constants in this function are experimental. -static double HuffmanCost(const int* const population, int length) { +static double BitsEntropy(const int* const array, int n) { + double retval = 0.; + int sum = 0; + int nonzeros = 0; + int max_val = 0; + int i; + for (i = 0; i < n; ++i) { + if (array[i] != 0) { + sum += array[i]; + ++nonzeros; + retval -= VP8LFastSLog2(array[i]); + if (max_val < array[i]) { + max_val = array[i]; + } + } + } + retval += VP8LFastSLog2(sum); + return BitsEntropyRefine(nonzeros, sum, max_val, retval); +} + +static double BitsEntropyCombined(const int* const X, const int* const Y, + int n) { + double retval = 0.; + int sum = 0; + int nonzeros = 0; + int max_val = 0; + int i; + for (i = 0; i < n; ++i) { + const int xy = X[i] + Y[i]; + if (xy != 0) { + sum += xy; + ++nonzeros; + retval -= VP8LFastSLog2(xy); + if (max_val < xy) { + max_val = xy; + } + } + } + retval += VP8LFastSLog2(sum); + return BitsEntropyRefine(nonzeros, sum, max_val, retval); +} + +static WEBP_INLINE double InitialHuffmanCost(void) { // Small bias because Huffman code length is typically not stored in // full length. static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; static const double kSmallBias = 9.1; - double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias; + return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; + } + +static WEBP_INLINE double HuffmanCostRefine(int streak, int val) { + double retval; + if (streak > 3) { + if (val == 0) { + retval = 1.5625 + 0.234375 * streak; + } else { + retval = 2.578125 + 0.703125 * streak; + } + } else { + if (val == 0) { + retval = 1.796875 * streak; + } else { + retval = 3.28125 * streak; + } + } + return retval; + } + +// Returns the cost encode the rle-encoded entropy code. +// The constants in this function are experimental. +static double HuffmanCost(const int* const population, int length) { int streak = 0; int i = 0; + double retval = InitialHuffmanCost(); for (; i < length - 1; ++i) { ++streak; if (population[i] == population[i + 1]) { continue; } - last_streak_hack: - // population[i] points now to the symbol in the streak of same values. - if (streak > 3) { - if (population[i] == 0) { - retval += 1.5625 + 0.234375 * streak; - } else { - retval += 2.578125 + 0.703125 * streak; - } - } else { - if (population[i] == 0) { - retval += 1.796875 * streak; - } else { - retval += 3.28125 * streak; - } - } + retval += HuffmanCostRefine(streak, population[i]); streak = 0; } - if (i == length - 1) { + retval += HuffmanCostRefine(++streak, population[i]); + return retval; +} + +static double HuffmanCostCombined(const int* const X, const int* const Y, + int length) { + int streak = 0; + int i = 0; + double retval = InitialHuffmanCost(); + for (; i < length - 1; ++i) { + const int xy = X[i] + Y[i]; + const int xy_next = X[i + 1] + Y[i + 1]; ++streak; - goto last_streak_hack; + if (xy == xy_next) { + continue; + } + retval += HuffmanCostRefine(streak, xy); + streak = 0; } + retval += HuffmanCostRefine(++streak, X[i] + Y[i]); return retval; } @@ -190,6 +240,12 @@ static double PopulationCost(const int* const population, int length) { return BitsEntropy(population, length) + HuffmanCost(population, length); } +static double GetCombinedEntropy(const int* const X, const int* const Y, + int length) { + return BitsEntropyCombined(X, Y, length) + HuffmanCostCombined(X, Y, length); + +} + static double ExtraCost(const int* const population, int length) { int i; double cost = 0.; @@ -197,9 +253,21 @@ static double ExtraCost(const int* const population, int length) { return cost; } +static double ExtraCostCombined(const int* const X, const int* const Y, + int length) { + int i; + double cost = 0.; + for (i = 2; i < length - 2; ++i) { + const int xy = X[i + 2] + Y[i + 2]; + cost += (i >> 1) * xy; + } + return cost; +} + // Estimates the Entropy + Huffman + other block overhead size cost. double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { - return PopulationCost(p->literal_, VP8LHistogramNumCodes(p)) + return + PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_)) + PopulationCost(p->red_, 256) + PopulationCost(p->blue_, 256) + PopulationCost(p->alpha_, 256) @@ -209,7 +277,8 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { } double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { - return BitsEntropy(p->literal_, VP8LHistogramNumCodes(p)) + return + BitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_)) + BitsEntropy(p->red_, 256) + BitsEntropy(p->blue_, 256) + BitsEntropy(p->alpha_, 256) @@ -238,6 +307,35 @@ static void HistogramAdd(const VP8LHistogram* const in, } } +static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, + const VP8LHistogram* const b, + double cost_threshold, + double* cost) { + const int palette_code_bits = + (a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ : + b->palette_code_bits_; + *cost += GetCombinedEntropy(a->literal_, b->literal_, + VP8LHistogramNumCodes(palette_code_bits)); + *cost += ExtraCostCombined(a->literal_ + 256, b->literal_ + 256, + NUM_LENGTH_CODES); + if (*cost > cost_threshold) return 0; + + *cost += GetCombinedEntropy(a->red_, b->red_, 256); + if (*cost > cost_threshold) return 0; + + *cost += GetCombinedEntropy(a->blue_, b->blue_, 256); + if (*cost > cost_threshold) return 0; + + *cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES); + *cost += ExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES); + if (*cost > cost_threshold) return 0; + + *cost += GetCombinedEntropy(a->alpha_, b->alpha_, 256); + if (*cost > cost_threshold) return 0; + + return 1; +} + // Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing // to the threshold value 'cost_threshold'. The score returned is // Score = C(a+b) - C(a) - C(b), where C(a) + C(b) is known and fixed. @@ -251,40 +349,25 @@ static double HistogramAddEval(const VP8LHistogram* const a, double cost = 0; const double sum_cost = a->bit_cost_ + b->bit_cost_; int i; - cost_threshold += sum_cost; - // palette_code_bits_ is part of the cost evaluation for literal_. - // TODO(skal): remove/simplify this palette_code_bits_? - out->palette_code_bits_ = - (a->palette_code_bits_ > b->palette_code_bits_) ? a->palette_code_bits_ : - b->palette_code_bits_; + if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) { for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { out->literal_[i] = a->literal_[i] + b->literal_[i]; } - cost += PopulationCost(out->literal_, VP8LHistogramNumCodes(out)); - cost += ExtraCost(out->literal_ + 256, NUM_LENGTH_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->red_[i] = a->red_[i] + b->red_[i]; - cost += PopulationCost(out->red_, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->blue_[i] = a->blue_[i] + b->blue_[i]; - cost += PopulationCost(out->blue_, 256); - if (cost > cost_threshold) return cost; - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { out->distance_[i] = a->distance_[i] + b->distance_[i]; } - cost += PopulationCost(out->distance_, NUM_DISTANCE_CODES); - cost += ExtraCost(out->distance_, NUM_DISTANCE_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) out->alpha_[i] = a->alpha_[i] + b->alpha_[i]; - cost += PopulationCost(out->alpha_, 256); - + for (i = 0; i < 256; ++i) { + out->red_[i] = a->red_[i] + b->red_[i]; + out->blue_[i] = a->blue_[i] + b->blue_[i]; + out->alpha_[i] = a->alpha_[i] + b->alpha_[i]; + } + out->palette_code_bits_ = (a->palette_code_bits_ > b->palette_code_bits_) ? + a->palette_code_bits_ : b->palette_code_bits_; out->bit_cost_ = cost; + } + return cost - sum_cost; } @@ -294,37 +377,8 @@ static double HistogramAddEval(const VP8LHistogram* const a, static double HistogramAddThresh(const VP8LHistogram* const a, const VP8LHistogram* const b, double cost_threshold) { - int tmp[PIX_OR_COPY_CODES_MAX]; // <= max storage we'll need - int i; double cost = -a->bit_cost_; - - for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) { - tmp[i] = a->literal_[i] + b->literal_[i]; - } - // note that the tests are ordered so that the usually largest - // cost shares come first. - cost += PopulationCost(tmp, VP8LHistogramNumCodes(a)); - cost += ExtraCost(tmp + 256, NUM_LENGTH_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->red_[i] + b->red_[i]; - cost += PopulationCost(tmp, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->blue_[i] + b->blue_[i]; - cost += PopulationCost(tmp, 256); - if (cost > cost_threshold) return cost; - - for (i = 0; i < NUM_DISTANCE_CODES; ++i) { - tmp[i] = a->distance_[i] + b->distance_[i]; - } - cost += PopulationCost(tmp, NUM_DISTANCE_CODES); - cost += ExtraCost(tmp, NUM_DISTANCE_CODES); - if (cost > cost_threshold) return cost; - - for (i = 0; i < 256; ++i) tmp[i] = a->alpha_[i] + b->alpha_[i]; - cost += PopulationCost(tmp, 256); - + GetCombinedHistogramEntropy(a, b, cost_threshold, &cost); return cost; } diff --git a/src/enc/histogram.h b/src/enc/histogram.h index 4d346a85..c3643425 100644 --- a/src/enc/histogram.h +++ b/src/enc/histogram.h @@ -82,9 +82,9 @@ double VP8LHistogramEstimateBits(const VP8LHistogram* const p); // represent the entropy code itself. double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p); -static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) { +static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) { return 256 + NUM_LENGTH_CODES + - ((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0); + ((palette_code_bits > 0) ? (1 << palette_code_bits) : 0); } // Builds the histogram image. diff --git a/src/enc/vp8l.c b/src/enc/vp8l.c index 81bcd300..0aae9f1e 100644 --- a/src/enc/vp8l.c +++ b/src/enc/vp8l.c @@ -186,9 +186,9 @@ static int GetHuffBitLengthsAndCodes( const VP8LHistogram* const histo = histogram_image->histograms[i]; HuffmanTreeCode* const codes = &huffman_codes[5 * i]; for (k = 0; k < 5; ++k) { - const int num_symbols = (k == 0) ? VP8LHistogramNumCodes(histo) - : (k == 4) ? NUM_DISTANCE_CODES - : 256; + const int num_symbols = + (k == 0) ? VP8LHistogramNumCodes(histo->palette_code_bits_) : + (k == 4) ? NUM_DISTANCE_CODES : 256; codes[k].num_symbols = num_symbols; total_length_size += num_symbols; }