simplify HistogramCombineEntropyBin()

We only perform a single pass, and swap the final histograms
into the beginning of the array as we go. Therefore, they are
already at the correct place at the end of the pass.
-> HistogramCompactBins() is removed, we just truncate the array.

output is bitwise the same.

Change-Id: I9508c96dda0f8903c927a71b06af4e6490c3249c
This commit is contained in:
Pascal Massimino 2016-06-20 17:58:04 +02:00
parent 472a049b4e
commit 8ec7032bc2

View File

@ -470,52 +470,46 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo,
} }
} }
// Compact the histogram set by removing unused entries. // Compact image_histo[] by merging some histograms with same bin_id together if
static void HistogramCompactBins(VP8LHistogramSet* const image_histo) { // it's advantageous.
VP8LHistogram** const histograms = image_histo->histograms;
int i, j;
for (i = 0, j = 0; i < image_histo->size; ++i) {
if (histograms[i] != NULL && histograms[i]->bit_cost_ != 0.) {
if (j < i) {
histograms[j] = histograms[i];
histograms[i] = NULL;
}
++j;
}
}
image_histo->size = j;
}
static VP8LHistogram* HistogramCombineEntropyBin( static VP8LHistogram* HistogramCombineEntropyBin(
VP8LHistogramSet* const image_histo, VP8LHistogramSet* const image_histo,
VP8LHistogram* cur_combo, VP8LHistogram* cur_combo,
uint16_t* const bin_map, int bin_map_size, int num_bins, const uint16_t* const bin_map, int bin_map_size, int num_bins,
double combine_cost_factor, int low_effort) { double combine_cost_factor, int low_effort) {
VP8LHistogram** const histograms = image_histo->histograms; VP8LHistogram** const histograms = image_histo->histograms;
int bin_id; int idx;
// Work in-place: processed histograms are put at the beginning of
// image_histo[]. At the end, we just have to truncate the array.
int size = 0;
struct {
int16_t first; // position of the histogram that accumulates all
// histograms with the same bin_id
uint16_t num_combine_failures; // number of combine failures per bin_id
} bin_info[BIN_SIZE];
for (bin_id = 0; bin_id < num_bins; ++bin_id) { assert(num_bins <= BIN_SIZE);
int num_combine_failures = 0; for (idx = 0; idx < num_bins; ++idx) {
int idx1, idx2; bin_info[idx].first = -1;
for (idx1 = 0; idx1 < bin_map_size; ++idx1) { bin_info[idx].num_combine_failures = 0;
if (bin_map[idx1] == bin_id) break;
} }
if (idx1 == bin_map_size) continue; // no histo with this bin_id
for (idx2 = idx1 + 1; idx2 < bin_map_size; ++idx2) { for (idx = 0; idx < bin_map_size; ++idx) {
if (bin_map[idx2] != bin_id) continue; const int bin_id = bin_map[idx];
// try to merge #idx2 into #idx1 (both share the same bin_id) const int first = bin_info[bin_id].first;
if (low_effort) { assert(size <= idx);
// Merge all histograms with the same bin index, irrespective of cost of if (first == -1) {
// the merged histograms. // just move histogram #idx to its final position
VP8LHistogramAdd(histograms[idx1], histograms[idx2], histograms[idx1]); histograms[size] = histograms[idx];
histograms[idx2]->bit_cost_ = 0.; bin_info[bin_id].first = size++;
} else if (low_effort) {
VP8LHistogramAdd(histograms[idx], histograms[first], histograms[first]);
} else { } else {
const double bit_cost_idx2 = histograms[idx2]->bit_cost_; // try to merge #idx into #first (both share the same bin_id)
if (bit_cost_idx2 > 0.) { const double bit_cost = histograms[idx]->bit_cost_;
const double bit_cost_thresh = -bit_cost_idx2 * combine_cost_factor; const double bit_cost_thresh = -bit_cost * combine_cost_factor;
const double curr_cost_diff = const double curr_cost_diff =
HistogramAddEval(histograms[idx1], histograms[idx2], HistogramAddEval(histograms[first], histograms[idx],
cur_combo, bit_cost_thresh); cur_combo, bit_cost_thresh);
if (curr_cost_diff < bit_cost_thresh) { if (curr_cost_diff < bit_cost_thresh) {
// Try to merge two histograms only if the combo is a trivial one or // Try to merge two histograms only if the combo is a trivial one or
@ -525,25 +519,29 @@ static VP8LHistogram* HistogramCombineEntropyBin(
// histograms as usual to avoid increasing the header size. // histograms as usual to avoid increasing the header size.
const int try_combine = const int try_combine =
(cur_combo->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM) || (cur_combo->trivial_symbol_ != VP8L_NON_TRIVIAL_SYM) ||
((histograms[idx1]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM) && ((histograms[idx]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM) &&
(histograms[idx2]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM)); (histograms[first]->trivial_symbol_ == VP8L_NON_TRIVIAL_SYM));
const int max_combine_failures = 32; const int max_combine_failures = 32;
if (try_combine || (num_combine_failures >= max_combine_failures)) { if (try_combine ||
HistogramSwap(&cur_combo, &histograms[idx1]); bin_info[bin_id].num_combine_failures >= max_combine_failures) {
histograms[idx2]->bit_cost_ = 0.; // move the (better) merged histogram to its final slot
HistogramSwap(&cur_combo, &histograms[first]);
} else { } else {
++num_combine_failures; histograms[size++] = histograms[idx];
} ++bin_info[bin_id].num_combine_failures;
} }
} else {
histograms[size++] = histograms[idx];
} }
} }
} }
image_histo->size = size;
if (low_effort) { if (low_effort) {
// Update the bit_cost for the merged histograms (per bin index). // for low_effort case, update the final cost when everything is merged
UpdateHistogramCost(histograms[idx1]); for (idx = 0; idx < size; ++idx) {
UpdateHistogramCost(histograms[idx]);
} }
} }
HistogramCompactBins(image_histo);
return cur_combo; return cur_combo;
} }
@ -860,14 +858,13 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
const int image_histo_raw_size = histo_xsize * histo_ysize; const int image_histo_raw_size = histo_xsize * histo_ysize;
const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
VP8LHistogramSet* const orig_histo = VP8LHistogramSet* const orig_histo =
VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits); VP8LAllocateHistogramSet(image_histo_raw_size, cache_bits);
VP8LHistogram* cur_combo; VP8LHistogram* cur_combo;
// Don't attempt linear bin-partition heuristic for // Don't attempt linear bin-partition heuristic for
// histograms of small sizes (as bin_map will be very sparse) and // histograms of small sizes (as bin_map will be very sparse) and
// maximum quality q==100 (to preserve the compression gains at that level). // maximum quality q==100 (to preserve the compression gains at that level).
const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
const int entropy_combine = const int entropy_combine =
(orig_histo->size > entropy_combine_num_bins * 2) && (quality < 100); (orig_histo->size > entropy_combine_num_bins * 2) && (quality < 100);
@ -881,7 +878,7 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
cur_combo = tmp_histos->histograms[1]; // pick up working slot cur_combo = tmp_histos->histograms[1]; // pick up working slot
if (entropy_combine) { if (entropy_combine) {
const int bin_map_size = orig_histo->size; const int bin_map_size = orig_histo->size;
// reuse histogram_symbols storage. By definition, it's guaranteed to be ok. // Reuse histogram_symbols storage. By definition, it's guaranteed to be ok.
uint16_t* const bin_map = histogram_symbols; uint16_t* const bin_map = histogram_symbols;
const double combine_cost_factor = const double combine_cost_factor =
GetCombineCostFactor(image_histo_raw_size, quality); GetCombineCostFactor(image_histo_raw_size, quality);