Skip to content

Commit eb3ff78

Browse files
committed
Only use valid histograms in VP8LHistogramSet
Empty histograms or one of two merged histograms were set to NULL. That made the code harder to understand. This changes the order of the histograms and therefore the goldens, but at the noise level. Change-Id: I1702637bdcdbaaad1244a1345ca5297459f61132
1 parent 57e324e commit eb3ff78

File tree

1 file changed

+55
-107
lines changed

1 file changed

+55
-107
lines changed

src/enc/histogram_enc.c

Lines changed: 55 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,11 @@ void VP8LHistogramSetClear(VP8LHistogramSet* const set) {
191191
}
192192
}
193193

194-
// Removes the histogram 'i' from 'set' by setting it to NULL.
195-
static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i,
196-
int* const num_used) {
197-
assert(set->histograms[i] != NULL);
198-
set->histograms[i] = NULL;
199-
--*num_used;
200-
// If we remove the last valid one, shrink until the next valid one.
201-
if (i == set->size - 1) {
202-
while (set->size >= 1 && set->histograms[set->size - 1] == NULL) {
203-
--set->size;
204-
}
205-
}
194+
// Removes the histogram 'i' from 'set'.
195+
static void HistogramSetRemoveHistogram(VP8LHistogramSet* const set, int i) {
196+
set->histograms[i] = set->histograms[set->size - 1];
197+
--set->size;
198+
assert(set->size > 0);
206199
}
207200

208201
// -----------------------------------------------------------------------------
@@ -645,8 +638,7 @@ static void HistogramBuild(
645638

646639
// Copies the histograms and computes its bit_cost.
647640
static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo,
648-
VP8LHistogramSet* const image_histo,
649-
int* const num_used) {
641+
VP8LHistogramSet* const image_histo) {
650642
int i;
651643
VP8LHistogram** const orig_histograms = orig_histo->histograms;
652644
VP8LHistogram** const histograms = image_histo->histograms;
@@ -664,7 +656,6 @@ static void HistogramCopyAndAnalyze(VP8LHistogramSet* const orig_histo,
664656
// The first histogram is always used.
665657
assert(i > 0);
666658
orig_histograms[i] = NULL;
667-
--*num_used;
668659
} else {
669660
// Copy histograms from orig_histo[] to image_histo[].
670661
HistogramCopy(histo, histograms[image_histo->size]);
@@ -700,8 +691,7 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo,
700691
// Sets the remaining histograms to NULL.
701692
// 'combine_cost_factor' has to be divided by 100.
702693
static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
703-
int* num_used, VP8LHistogram* cur_combo,
704-
int num_bins,
694+
VP8LHistogram* cur_combo, int num_bins,
705695
int32_t combine_cost_factor,
706696
int low_effort) {
707697
VP8LHistogram** const histograms = image_histo->histograms;
@@ -718,14 +708,15 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
718708
bin_info[idx].num_combine_failures = 0;
719709
}
720710

721-
for (idx = 0; idx < image_histo->size; ++idx) {
711+
for (idx = 0; idx < image_histo->size;) {
722712
const int bin_id = histograms[idx]->bin_id;
723713
const int first = bin_info[bin_id].first;
724714
if (first == -1) {
725715
bin_info[bin_id].first = idx;
716+
++idx;
726717
} else if (low_effort) {
727718
HistogramAdd(histograms[idx], histograms[first], histograms[first]);
728-
HistogramSetRemoveHistogram(image_histo, idx, num_used);
719+
HistogramSetRemoveHistogram(image_histo, idx);
729720
} else {
730721
// try to merge #idx into #first (both share the same bin_id)
731722
const uint64_t bit_cost = histograms[idx]->bit_cost;
@@ -757,17 +748,19 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
757748
bin_info[bin_id].num_combine_failures >= max_combine_failures) {
758749
// move the (better) merged histogram to its final slot
759750
HistogramSwap(&cur_combo, &histograms[first]);
760-
HistogramSetRemoveHistogram(image_histo, idx, num_used);
751+
HistogramSetRemoveHistogram(image_histo, idx);
761752
} else {
762753
++bin_info[bin_id].num_combine_failures;
754+
++idx;
763755
}
756+
} else {
757+
++idx;
764758
}
765759
}
766760
}
767761
if (low_effort) {
768762
// for low_effort case, update the final cost when everything is merged
769763
for (idx = 0; idx < image_histo->size; ++idx) {
770-
if (histograms[idx] == NULL) continue;
771764
ComputeHistogramCost(histograms[idx]);
772765
}
773766
}
@@ -842,6 +835,18 @@ static void HistoQueueUpdateHead(HistoQueue* const histo_queue,
842835
}
843836
}
844837

838+
// Replaces the bad_id with good_id in the pair.
839+
static void HistoQueueFixPair(int bad_id, int good_id,
840+
HistogramPair* const pair) {
841+
if (pair->idx1 == bad_id) pair->idx1 = good_id;
842+
if (pair->idx2 == bad_id) pair->idx2 = good_id;
843+
if (pair->idx1 > pair->idx2) {
844+
const int tmp = pair->idx1;
845+
pair->idx1 = pair->idx2;
846+
pair->idx2 = tmp;
847+
}
848+
}
849+
845850
// Update the cost diff and combo of a pair of histograms. This needs to be
846851
// called when the histograms have been merged with a third one.
847852
// Returns 1 if the cost diff is less than the threshold.
@@ -896,8 +901,7 @@ static int64_t HistoQueuePush(HistoQueue* const histo_queue,
896901

897902
// Combines histograms by continuously choosing the one with the highest cost
898903
// reduction.
899-
static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
900-
int* const num_used) {
904+
static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) {
901905
int ok = 0;
902906
const int image_histo_size = image_histo->size;
903907
int i, j;
@@ -916,11 +920,9 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
916920
goto End;
917921
}
918922

923+
// Initialize the queue.
919924
for (i = 0; i < image_histo_size; ++i) {
920-
if (image_histo->histograms[i] == NULL) continue;
921925
for (j = i + 1; j < image_histo_size; ++j) {
922-
// Initialize queue.
923-
if (image_histo->histograms[j] == NULL) continue;
924926
HistoQueuePush(&histo_queue, histograms, i, j, 0);
925927
}
926928
}
@@ -933,7 +935,7 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
933935
histo_queue.queue[0].costs, histograms[idx1]);
934936

935937
// Remove merged histogram.
936-
HistogramSetRemoveHistogram(image_histo, idx2, num_used);
938+
HistogramSetRemoveHistogram(image_histo, idx2);
937939

938940
// Remove pairs intersecting the just combined best pair.
939941
for (i = 0; i < histo_queue.size;) {
@@ -942,14 +944,15 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
942944
p->idx1 == idx2 || p->idx2 == idx2) {
943945
HistoQueuePopPair(&histo_queue, p);
944946
} else {
947+
HistoQueueFixPair(image_histo->size, idx2, p);
945948
HistoQueueUpdateHead(&histo_queue, p);
946949
++i;
947950
}
948951
}
949952

950953
// Push new pairs formed with combined histogram to the queue.
951954
for (i = 0; i < image_histo->size; ++i) {
952-
if (i == idx1 || image_histo->histograms[i] == NULL) continue;
955+
if (i == idx1) continue;
953956
HistoQueuePush(&histo_queue, image_histo->histograms, idx1, i, 0);
954957
}
955958
}
@@ -964,17 +967,13 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo,
964967
// Perform histogram aggregation using a stochastic approach.
965968
// 'do_greedy' is set to 1 if a greedy approach needs to be performed
966969
// afterwards, 0 otherwise.
967-
static int PairComparison(const void* idx1, const void* idx2) {
968-
// To be used with bsearch: <0 when *idx1<*idx2, >0 if >, 0 when ==.
969-
return (*(int*) idx1 - *(int*) idx2);
970-
}
971970
static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
972-
int* const num_used, int min_cluster_size,
971+
int min_cluster_size,
973972
int* const do_greedy) {
974973
int j, iter;
975974
uint32_t seed = 1;
976975
int tries_with_no_success = 0;
977-
const int outer_iters = *num_used;
976+
const int outer_iters = image_histo->size;
978977
const int num_tries_no_success = outer_iters / 2;
979978
VP8LHistogram** const histograms = image_histo->histograms;
980979
// Priority queue of histogram pairs. Its size of 'kHistoQueueSize'
@@ -983,49 +982,34 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
983982
HistoQueue histo_queue;
984983
const int kHistoQueueSize = 9;
985984
int ok = 0;
986-
// mapping from an index in image_histo with no NULL histogram to the full
987-
// blown image_histo.
988-
int* mappings;
989985

990-
if (*num_used < min_cluster_size) {
986+
if (image_histo->size < min_cluster_size) {
991987
*do_greedy = 1;
992988
return 1;
993989
}
994990

995-
mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings));
996-
if (mappings == NULL) return 0;
997991
if (!HistoQueueInit(&histo_queue, kHistoQueueSize)) goto End;
998-
// Fill the initial mapping.
999-
for (j = 0, iter = 0; iter < image_histo->size; ++iter) {
1000-
if (histograms[iter] == NULL) continue;
1001-
mappings[j++] = iter;
1002-
}
1003-
assert(j == *num_used);
1004992

1005993
// Collapse similar histograms in 'image_histo'.
1006-
for (iter = 0;
1007-
iter < outer_iters && *num_used >= min_cluster_size &&
1008-
++tries_with_no_success < num_tries_no_success;
1009-
++iter) {
1010-
int* mapping_index;
994+
for (iter = 0; iter < outer_iters && image_histo->size >= min_cluster_size &&
995+
++tries_with_no_success < num_tries_no_success;
996+
++iter) {
1011997
int64_t best_cost =
1012998
(histo_queue.size == 0) ? 0 : histo_queue.queue[0].cost_diff;
1013999
int best_idx1 = -1, best_idx2 = 1;
1014-
const uint32_t rand_range = (*num_used - 1) * (*num_used);
1015-
// (*num_used) / 2 was chosen empirically. Less means faster but worse
1016-
// compression.
1017-
const int num_tries = (*num_used) / 2;
1000+
const uint32_t rand_range = (image_histo->size - 1) * (image_histo->size);
1001+
// (image_histo->size) / 2 was chosen empirically. Less means faster but
1002+
// worse compression.
1003+
const int num_tries = (image_histo->size) / 2;
10181004

10191005
// Pick random samples.
1020-
for (j = 0; *num_used >= 2 && j < num_tries; ++j) {
1006+
for (j = 0; image_histo->size >= 2 && j < num_tries; ++j) {
10211007
int64_t curr_cost;
10221008
// Choose two different histograms at random and try to combine them.
10231009
const uint32_t tmp = MyRand(&seed) % rand_range;
1024-
uint32_t idx1 = tmp / (*num_used - 1);
1025-
uint32_t idx2 = tmp % (*num_used - 1);
1010+
uint32_t idx1 = tmp / (image_histo->size - 1);
1011+
uint32_t idx2 = tmp % (image_histo->size - 1);
10261012
if (idx2 >= idx1) ++idx2;
1027-
idx1 = mappings[idx1];
1028-
idx2 = mappings[idx2];
10291013

10301014
// Calculate cost reduction on combination.
10311015
curr_cost =
@@ -1042,25 +1026,18 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
10421026
best_idx1 = histo_queue.queue[0].idx1;
10431027
best_idx2 = histo_queue.queue[0].idx2;
10441028
assert(best_idx1 < best_idx2);
1045-
// Pop best_idx2 from mappings.
1046-
mapping_index = (int*) bsearch(&best_idx2, mappings, *num_used,
1047-
sizeof(best_idx2), &PairComparison);
1048-
assert(mapping_index != NULL);
1049-
memmove(mapping_index, mapping_index + 1, sizeof(*mapping_index) *
1050-
((*num_used) - (mapping_index - mappings) - 1));
10511029
// Merge the histograms and remove best_idx2 from the queue.
10521030
HistogramAdd(histograms[best_idx2], histograms[best_idx1],
10531031
histograms[best_idx1]);
10541032
UpdateHistogramCost(histo_queue.queue[0].cost_combo,
10551033
histo_queue.queue[0].costs, histograms[best_idx1]);
1056-
HistogramSetRemoveHistogram(image_histo, best_idx2, num_used);
1034+
HistogramSetRemoveHistogram(image_histo, best_idx2);
10571035
// Parse the queue and update each pair that deals with best_idx1,
10581036
// best_idx2 or image_histo_size.
10591037
for (j = 0; j < histo_queue.size;) {
10601038
HistogramPair* const p = histo_queue.queue + j;
10611039
const int is_idx1_best = p->idx1 == best_idx1 || p->idx1 == best_idx2;
10621040
const int is_idx2_best = p->idx2 == best_idx1 || p->idx2 == best_idx2;
1063-
int do_eval = 0;
10641041
// The front pair could have been duplicated by a random pick so
10651042
// check for it all the time nevertheless.
10661043
if (is_idx1_best && is_idx2_best) {
@@ -1069,38 +1046,26 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
10691046
}
10701047
// Any pair containing one of the two best indices should only refer to
10711048
// best_idx1. Its cost should also be updated.
1072-
if (is_idx1_best) {
1073-
p->idx1 = best_idx1;
1074-
do_eval = 1;
1075-
} else if (is_idx2_best) {
1076-
p->idx2 = best_idx1;
1077-
do_eval = 1;
1078-
}
1079-
// Make sure the index order is respected.
1080-
if (p->idx1 > p->idx2) {
1081-
const int tmp = p->idx2;
1082-
p->idx2 = p->idx1;
1083-
p->idx1 = tmp;
1084-
}
1085-
if (do_eval) {
1049+
if (is_idx1_best || is_idx2_best) {
1050+
HistoQueueFixPair(best_idx2, best_idx1, p);
10861051
// Re-evaluate the cost of an updated pair.
10871052
if (!HistoQueueUpdatePair(histograms[p->idx1], histograms[p->idx2], 0,
10881053
p)) {
10891054
HistoQueuePopPair(&histo_queue, p);
10901055
continue;
10911056
}
10921057
}
1058+
HistoQueueFixPair(image_histo->size, best_idx2, p);
10931059
HistoQueueUpdateHead(&histo_queue, p);
10941060
++j;
10951061
}
10961062
tries_with_no_success = 0;
10971063
}
1098-
*do_greedy = (*num_used <= min_cluster_size);
1064+
*do_greedy = (image_histo->size <= min_cluster_size);
10991065
ok = 1;
11001066

11011067
End:
11021068
HistoQueueClear(&histo_queue);
1103-
WebPSafeFree(mappings);
11041069
return ok;
11051070
}
11061071

@@ -1168,16 +1133,6 @@ static int32_t GetCombineCostFactor(int histo_size, int quality) {
11681133
return combine_cost_factor;
11691134
}
11701135

1171-
static void RemoveEmptyHistograms(VP8LHistogramSet* const image_histo) {
1172-
uint32_t size;
1173-
int i;
1174-
for (i = 0, size = 0; i < image_histo->size; ++i) {
1175-
if (image_histo->histograms[i] == NULL) continue;
1176-
image_histo->histograms[size++] = image_histo->histograms[i];
1177-
}
1178-
image_histo->size = size;
1179-
}
1180-
11811136
int VP8LGetHistoImageSymbols(int xsize, int ysize,
11821137
const VP8LBackwardRefs* const refs, int quality,
11831138
int low_effort, int histogram_bits, int cache_bits,
@@ -1198,29 +1153,25 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
11981153
// maximum quality q==100 (to preserve the compression gains at that level).
11991154
const int entropy_combine_num_bins = low_effort ? NUM_PARTITIONS : BIN_SIZE;
12001155
int entropy_combine;
1201-
int num_used = image_histo_raw_size;
12021156
if (orig_histo == NULL) {
12031157
WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
12041158
goto Error;
12051159
}
12061160

12071161
// Construct the histograms from backward references.
12081162
HistogramBuild(xsize, histogram_bits, refs, orig_histo);
1209-
// Copies the histograms and computes its bit_cost.
1210-
// histogram_symbols is optimized
1211-
HistogramCopyAndAnalyze(orig_histo, image_histo, &num_used);
1163+
HistogramCopyAndAnalyze(orig_histo, image_histo);
12121164
entropy_combine =
1213-
(num_used > entropy_combine_num_bins * 2) && (quality < 100);
1165+
(image_histo->size > entropy_combine_num_bins * 2) && (quality < 100);
12141166

12151167
if (entropy_combine) {
12161168
const int32_t combine_cost_factor =
12171169
GetCombineCostFactor(image_histo_raw_size, quality);
12181170

12191171
HistogramAnalyzeEntropyBin(image_histo, low_effort);
12201172
// Collapse histograms with similar entropy.
1221-
HistogramCombineEntropyBin(image_histo, &num_used, tmp_histo,
1222-
entropy_combine_num_bins, combine_cost_factor,
1223-
low_effort);
1173+
HistogramCombineEntropyBin(image_histo, tmp_histo, entropy_combine_num_bins,
1174+
combine_cost_factor, low_effort);
12241175
}
12251176

12261177
// Don't combine the histograms using stochastic and greedy heuristics for
@@ -1231,22 +1182,19 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize,
12311182
(int)(1 + DivRound(quality * quality * quality * (MAX_HISTO_GREEDY - 1),
12321183
100 * 100 * 100));
12331184
int do_greedy;
1234-
if (!HistogramCombineStochastic(image_histo, &num_used, threshold_size,
1235-
&do_greedy)) {
1185+
if (!HistogramCombineStochastic(image_histo, threshold_size, &do_greedy)) {
12361186
WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
12371187
goto Error;
12381188
}
12391189
if (do_greedy) {
1240-
RemoveEmptyHistograms(image_histo);
1241-
if (!HistogramCombineGreedy(image_histo, &num_used)) {
1190+
if (!HistogramCombineGreedy(image_histo)) {
12421191
WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
12431192
goto Error;
12441193
}
12451194
}
12461195
}
12471196

12481197
// Find the optimal map from original histograms to the final ones.
1249-
RemoveEmptyHistograms(image_histo);
12501198
HistogramRemap(orig_histo, image_histo, histogram_symbols);
12511199

12521200
if (!WebPReportProgress(pic, *percent + percent_range, percent)) {

0 commit comments

Comments
 (0)