mirror of
https://github.com/webmproject/libwebp.git
synced 2025-04-23 17:26:45 +02:00
Merge "Generalize trivial histograms" into main
This commit is contained in:
commit
7191a602b0
@ -41,7 +41,7 @@ typedef enum {
|
|||||||
RED,
|
RED,
|
||||||
BLUE,
|
BLUE,
|
||||||
ALPHA,
|
ALPHA,
|
||||||
DISTANCE,
|
DISTANCE
|
||||||
} HistogramIndex;
|
} HistogramIndex;
|
||||||
|
|
||||||
// Return the size of the histogram for a given cache_bits.
|
// Return the size of the histogram for a given cache_bits.
|
||||||
@ -105,7 +105,8 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits,
|
|||||||
if (init_arrays) {
|
if (init_arrays) {
|
||||||
HistogramClear(p);
|
HistogramClear(p);
|
||||||
} else {
|
} else {
|
||||||
p->trivial_symbol = 0;
|
int i;
|
||||||
|
for (i = 0; i < 5; ++i) p->trivial_symbol[i] = VP8L_NON_TRIVIAL_SYM;
|
||||||
p->bit_cost = 0;
|
p->bit_cost = 0;
|
||||||
memset(p->costs, 0, sizeof(p->costs));
|
memset(p->costs, 0, sizeof(p->costs));
|
||||||
memset(p->is_used, 0, sizeof(p->is_used));
|
memset(p->is_used, 0, sizeof(p->is_used));
|
||||||
@ -317,7 +318,7 @@ static uint64_t FinalHuffmanCost(const VP8LStreaks* const stats) {
|
|||||||
// Get the symbol entropy for the distribution 'population'.
|
// Get the symbol entropy for the distribution 'population'.
|
||||||
// Set 'trivial_sym', if there's only one symbol present in the distribution.
|
// Set 'trivial_sym', if there's only one symbol present in the distribution.
|
||||||
static uint64_t PopulationCost(const uint32_t* const population, int length,
|
static uint64_t PopulationCost(const uint32_t* const population, int length,
|
||||||
uint32_t* const trivial_sym,
|
uint16_t* const trivial_sym,
|
||||||
uint8_t* const is_used) {
|
uint8_t* const is_used) {
|
||||||
VP8LBitEntropy bit_entropy;
|
VP8LBitEntropy bit_entropy;
|
||||||
VP8LStreaks stats;
|
VP8LStreaks stats;
|
||||||
@ -332,13 +333,41 @@ static uint64_t PopulationCost(const uint32_t* const population, int length,
|
|||||||
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE void GetPopulationInfo(const VP8LHistogram* const histo,
|
||||||
|
HistogramIndex index,
|
||||||
|
const uint32_t** population,
|
||||||
|
int* length) {
|
||||||
|
switch (index) {
|
||||||
|
case LITERAL:
|
||||||
|
*population = histo->literal;
|
||||||
|
*length = VP8LHistogramNumCodes(histo->palette_code_bits);
|
||||||
|
break;
|
||||||
|
case RED:
|
||||||
|
*population = histo->red;
|
||||||
|
*length = NUM_LITERAL_CODES;
|
||||||
|
break;
|
||||||
|
case BLUE:
|
||||||
|
*population = histo->blue;
|
||||||
|
*length = NUM_LITERAL_CODES;
|
||||||
|
break;
|
||||||
|
case ALPHA:
|
||||||
|
*population = histo->alpha;
|
||||||
|
*length = NUM_LITERAL_CODES;
|
||||||
|
break;
|
||||||
|
case DISTANCE:
|
||||||
|
*population = histo->distance;
|
||||||
|
*length = NUM_DISTANCE_CODES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// trivial_at_end is 1 if the two histograms only have one element that is
|
// trivial_at_end is 1 if the two histograms only have one element that is
|
||||||
// non-zero: both the zero-th one, or both the last one.
|
// non-zero: both the zero-th one, or both the last one.
|
||||||
// 'index' is the index of the symbol in the histogram (literal, red, blue,
|
// 'index' is the index of the symbol in the histogram (literal, red, blue,
|
||||||
// alpha, distance).
|
// alpha, distance).
|
||||||
static WEBP_INLINE uint64_t GetCombinedEntropy(
|
static WEBP_INLINE uint64_t
|
||||||
const VP8LHistogram* const histo_X, const VP8LHistogram* const histo_Y,
|
GetCombinedEntropy(const VP8LHistogram* const histo_X,
|
||||||
HistogramIndex index, int trivial_at_end) {
|
const VP8LHistogram* const histo_Y, HistogramIndex index) {
|
||||||
const uint32_t* X;
|
const uint32_t* X;
|
||||||
const uint32_t* Y;
|
const uint32_t* Y;
|
||||||
int length;
|
int length;
|
||||||
@ -346,35 +375,18 @@ static WEBP_INLINE uint64_t GetCombinedEntropy(
|
|||||||
VP8LBitEntropy bit_entropy;
|
VP8LBitEntropy bit_entropy;
|
||||||
const int is_X_used = histo_X->is_used[index];
|
const int is_X_used = histo_X->is_used[index];
|
||||||
const int is_Y_used = histo_Y->is_used[index];
|
const int is_Y_used = histo_Y->is_used[index];
|
||||||
|
const int is_trivial =
|
||||||
|
histo_X->trivial_symbol[index] != VP8L_NON_TRIVIAL_SYM &&
|
||||||
|
histo_X->trivial_symbol[index] == histo_Y->trivial_symbol[index];
|
||||||
|
|
||||||
if (trivial_at_end || !is_X_used || !is_Y_used) {
|
if (is_trivial || !is_X_used || !is_Y_used) {
|
||||||
if (is_X_used) return histo_X->costs[index];
|
if (is_X_used) return histo_X->costs[index];
|
||||||
return histo_Y->costs[index];
|
return histo_Y->costs[index];
|
||||||
}
|
}
|
||||||
assert(is_X_used && is_Y_used);
|
assert(is_X_used && is_Y_used);
|
||||||
|
|
||||||
if (index == LITERAL) {
|
GetPopulationInfo(histo_X, index, &X, &length);
|
||||||
X = histo_X->literal;
|
GetPopulationInfo(histo_Y, index, &Y, &length);
|
||||||
Y = histo_Y->literal;
|
|
||||||
length = VP8LHistogramNumCodes(histo_X->palette_code_bits);
|
|
||||||
} else if (index == RED) {
|
|
||||||
X = histo_X->red;
|
|
||||||
Y = histo_Y->red;
|
|
||||||
length = NUM_LITERAL_CODES;
|
|
||||||
} else if (index == BLUE) {
|
|
||||||
X = histo_X->blue;
|
|
||||||
Y = histo_Y->blue;
|
|
||||||
length = NUM_LITERAL_CODES;
|
|
||||||
} else if (index == ALPHA) {
|
|
||||||
X = histo_X->alpha;
|
|
||||||
Y = histo_Y->alpha;
|
|
||||||
length = NUM_LITERAL_CODES;
|
|
||||||
} else {
|
|
||||||
assert(index == DISTANCE);
|
|
||||||
X = histo_X->distance;
|
|
||||||
Y = histo_Y->distance;
|
|
||||||
length = NUM_DISTANCE_CODES;
|
|
||||||
}
|
|
||||||
VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats);
|
VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats);
|
||||||
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats);
|
||||||
}
|
}
|
||||||
@ -411,38 +423,19 @@ static WEBP_INLINE void SaturateAdd(uint64_t a, int64_t* b) {
|
|||||||
WEBP_NODISCARD static int GetCombinedHistogramEntropy(
|
WEBP_NODISCARD static int GetCombinedHistogramEntropy(
|
||||||
const VP8LHistogram* const a, const VP8LHistogram* const b,
|
const VP8LHistogram* const a, const VP8LHistogram* const b,
|
||||||
int64_t cost_threshold_in, uint64_t* cost, uint64_t costs[5]) {
|
int64_t cost_threshold_in, uint64_t* cost, uint64_t costs[5]) {
|
||||||
int trivial_at_end = 0, i;
|
int i;
|
||||||
const uint64_t cost_threshold = (uint64_t)cost_threshold_in;
|
const uint64_t cost_threshold = (uint64_t)cost_threshold_in;
|
||||||
assert(a->palette_code_bits == b->palette_code_bits);
|
assert(a->palette_code_bits == b->palette_code_bits);
|
||||||
if (cost_threshold_in <= 0) return 0;
|
if (cost_threshold_in <= 0) return 0;
|
||||||
*cost = costs[LITERAL] =
|
*cost = 0;
|
||||||
GetCombinedEntropy(a, b, LITERAL, /*trivial_at_end=*/0);
|
|
||||||
// No need to add the extra cost for lengths as it is a constant that does not
|
|
||||||
// influence the histograms.
|
|
||||||
if (*cost >= cost_threshold) return 0;
|
|
||||||
|
|
||||||
if (a->trivial_symbol != VP8L_NON_TRIVIAL_SYM &&
|
// No need to add the extra cost for length and distance as it is a constant
|
||||||
a->trivial_symbol == b->trivial_symbol) {
|
// that does not influence the histograms.
|
||||||
// A, R and B are all 0 or 0xff.
|
for (i = 0; i < 5; ++i) {
|
||||||
const uint32_t color_a = (a->trivial_symbol >> 24) & 0xff;
|
costs[i] = GetCombinedEntropy(a, b, (HistogramIndex)i);
|
||||||
const uint32_t color_r = (a->trivial_symbol >> 16) & 0xff;
|
|
||||||
const uint32_t color_b = (a->trivial_symbol >> 0) & 0xff;
|
|
||||||
if ((color_a == 0 || color_a == 0xff) &&
|
|
||||||
(color_r == 0 || color_r == 0xff) &&
|
|
||||||
(color_b == 0 || color_b == 0xff)) {
|
|
||||||
trivial_at_end = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i <= 4; ++i) {
|
|
||||||
costs[i] =
|
|
||||||
GetCombinedEntropy(a, b, (HistogramIndex)i,
|
|
||||||
/*trivial_at_end=*/i <= 3 ? trivial_at_end : 0);
|
|
||||||
*cost += costs[i];
|
*cost += costs[i];
|
||||||
if (*cost >= cost_threshold) return 0;
|
if (*cost >= cost_threshold) return 0;
|
||||||
}
|
}
|
||||||
// No need to add the extra cost for distances as it is a constant that does
|
|
||||||
// not influence the histograms.
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -450,10 +443,13 @@ WEBP_NODISCARD static int GetCombinedHistogramEntropy(
|
|||||||
static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a,
|
static WEBP_INLINE void HistogramAdd(const VP8LHistogram* const a,
|
||||||
const VP8LHistogram* const b,
|
const VP8LHistogram* const b,
|
||||||
VP8LHistogram* const out) {
|
VP8LHistogram* const out) {
|
||||||
|
int i;
|
||||||
VP8LHistogramAdd(a, b, out);
|
VP8LHistogramAdd(a, b, out);
|
||||||
out->trivial_symbol = (a->trivial_symbol == b->trivial_symbol)
|
for (i = 0; i < 5; ++i) {
|
||||||
? a->trivial_symbol
|
out->trivial_symbol[i] = a->trivial_symbol[i] == b->trivial_symbol[i]
|
||||||
: VP8L_NON_TRIVIAL_SYM;
|
? a->trivial_symbol[i]
|
||||||
|
: VP8L_NON_TRIVIAL_SYM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
|
// Performs out = a + b, computing the cost C(a+b) - C(a) - C(b) while comparing
|
||||||
@ -533,28 +529,18 @@ static void UpdateDominantCostRange(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateHistogramCost(VP8LHistogram* const h) {
|
static void UpdateHistogramCost(VP8LHistogram* const h) {
|
||||||
uint32_t alpha_sym, red_sym, blue_sym;
|
int i;
|
||||||
const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits);
|
// No need to add the extra cost for length and distance as it is a constant
|
||||||
h->costs[ALPHA] = PopulationCost(h->alpha, NUM_LITERAL_CODES, &alpha_sym,
|
// that does not influence the histograms.
|
||||||
&h->is_used[ALPHA]);
|
for (i = 0; i < 5; ++i) {
|
||||||
// No need to add the extra cost as it is a constant that does not influence
|
const uint32_t* population;
|
||||||
// the histograms.
|
int length;
|
||||||
h->costs[DISTANCE] = PopulationCost(h->distance, NUM_DISTANCE_CODES, NULL,
|
GetPopulationInfo(h, i, &population, &length);
|
||||||
&h->is_used[DISTANCE]);
|
h->costs[i] = PopulationCost(population, length, &h->trivial_symbol[i],
|
||||||
h->costs[LITERAL] =
|
&h->is_used[i]);
|
||||||
PopulationCost(h->literal, num_codes, NULL, &h->is_used[LITERAL]);
|
}
|
||||||
h->costs[RED] =
|
|
||||||
PopulationCost(h->red, NUM_LITERAL_CODES, &red_sym, &h->is_used[RED]);
|
|
||||||
h->costs[BLUE] =
|
|
||||||
PopulationCost(h->blue, NUM_LITERAL_CODES, &blue_sym, &h->is_used[BLUE]);
|
|
||||||
h->bit_cost = h->costs[LITERAL] + h->costs[RED] + h->costs[BLUE] +
|
h->bit_cost = h->costs[LITERAL] + h->costs[RED] + h->costs[BLUE] +
|
||||||
h->costs[ALPHA] + h->costs[DISTANCE];
|
h->costs[ALPHA] + h->costs[DISTANCE];
|
||||||
if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) {
|
|
||||||
h->trivial_symbol = VP8L_NON_TRIVIAL_SYM;
|
|
||||||
} else {
|
|
||||||
h->trivial_symbol =
|
|
||||||
((uint32_t)alpha_sym << 24) | (red_sym << 16) | (blue_sym << 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetBinIdForEntropy(uint64_t min, uint64_t max, uint64_t val) {
|
static int GetBinIdForEntropy(uint64_t min, uint64_t max, uint64_t val) {
|
||||||
@ -695,16 +681,26 @@ static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
|
|||||||
-DivRound((int64_t)bit_cost * combine_cost_factor, 100);
|
-DivRound((int64_t)bit_cost * combine_cost_factor, 100);
|
||||||
if (HistogramAddEval(histograms[first], histograms[idx], cur_combo,
|
if (HistogramAddEval(histograms[first], histograms[idx], cur_combo,
|
||||||
bit_cost_thresh)) {
|
bit_cost_thresh)) {
|
||||||
|
const int max_combine_failures = 32;
|
||||||
// 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
|
||||||
// the two candidate histograms are already non-trivial.
|
// the two candidate histograms are already non-trivial.
|
||||||
// For some images, 'try_combine' turns out to be false for a lot of
|
// For some images, 'try_combine' turns out to be false for a lot of
|
||||||
// histogram pairs. In that case, we fallback to combining
|
// histogram pairs. In that case, we fallback to combining
|
||||||
// histograms as usual to avoid increasing the header size.
|
// histograms as usual to avoid increasing the header size.
|
||||||
const int try_combine =
|
int try_combine =
|
||||||
(cur_combo->trivial_symbol != VP8L_NON_TRIVIAL_SYM) ||
|
cur_combo->trivial_symbol[RED] != VP8L_NON_TRIVIAL_SYM &&
|
||||||
((histograms[idx]->trivial_symbol == VP8L_NON_TRIVIAL_SYM) &&
|
cur_combo->trivial_symbol[BLUE] != VP8L_NON_TRIVIAL_SYM &&
|
||||||
(histograms[first]->trivial_symbol == VP8L_NON_TRIVIAL_SYM));
|
cur_combo->trivial_symbol[ALPHA] != VP8L_NON_TRIVIAL_SYM;
|
||||||
const int max_combine_failures = 32;
|
if (!try_combine) {
|
||||||
|
try_combine =
|
||||||
|
histograms[idx]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM ||
|
||||||
|
histograms[idx]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM ||
|
||||||
|
histograms[idx]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM;
|
||||||
|
try_combine &=
|
||||||
|
histograms[first]->trivial_symbol[RED] == VP8L_NON_TRIVIAL_SYM ||
|
||||||
|
histograms[first]->trivial_symbol[BLUE] == VP8L_NON_TRIVIAL_SYM ||
|
||||||
|
histograms[first]->trivial_symbol[ALPHA] == VP8L_NON_TRIVIAL_SYM;
|
||||||
|
}
|
||||||
if (try_combine ||
|
if (try_combine ||
|
||||||
bin_info[bin_id].num_combine_failures >= max_combine_failures) {
|
bin_info[bin_id].num_combine_failures >= max_combine_failures) {
|
||||||
// move the (better) merged histogram to its final slot
|
// move the (better) merged histogram to its final slot
|
||||||
|
@ -24,7 +24,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Not a trivial literal symbol.
|
// Not a trivial literal symbol.
|
||||||
#define VP8L_NON_TRIVIAL_SYM (0xffffffff)
|
#define VP8L_NON_TRIVIAL_SYM ((uint16_t)(0xffff))
|
||||||
|
|
||||||
// A simple container for histograms of data.
|
// A simple container for histograms of data.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -37,8 +37,9 @@ typedef struct {
|
|||||||
// Backward reference prefix-code histogram.
|
// Backward reference prefix-code histogram.
|
||||||
uint32_t distance[NUM_DISTANCE_CODES];
|
uint32_t distance[NUM_DISTANCE_CODES];
|
||||||
int palette_code_bits;
|
int palette_code_bits;
|
||||||
uint32_t trivial_symbol; // True, if histograms for Red, Blue & Alpha
|
// Index of the unique value of a histogram if any, VP8L_NON_TRIVIAL_SYM
|
||||||
// literal symbols are single valued.
|
// otherwise.
|
||||||
|
uint16_t trivial_symbol[5];
|
||||||
uint64_t bit_cost; // Cached value of total bit cost.
|
uint64_t bit_cost; // Cached value of total bit cost.
|
||||||
// Cached values of entropy costs: literal, red, blue, alpha, distance
|
// Cached values of entropy costs: literal, red, blue, alpha, distance
|
||||||
uint64_t costs[5];
|
uint64_t costs[5];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user