histogram.c revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// This code is licensed under the same terms as WebM:
4//  Software License Agreement:  http://www.webmproject.org/license/software/
5//  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Author: Jyrki Alakuijala (jyrki@google.com)
9//
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
14#include <math.h>
15#include <stdio.h>
16
17#include "./backward_references.h"
18#include "./histogram.h"
19#include "../dsp/lossless.h"
20#include "../utils/utils.h"
21
22static void HistogramClear(VP8LHistogram* const p) {
23  memset(p->literal_, 0, sizeof(p->literal_));
24  memset(p->red_, 0, sizeof(p->red_));
25  memset(p->blue_, 0, sizeof(p->blue_));
26  memset(p->alpha_, 0, sizeof(p->alpha_));
27  memset(p->distance_, 0, sizeof(p->distance_));
28  p->bit_cost_ = 0;
29}
30
31void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
32                            VP8LHistogram* const histo) {
33  int i;
34  for (i = 0; i < refs->size; ++i) {
35    VP8LHistogramAddSinglePixOrCopy(histo, &refs->refs[i]);
36  }
37}
38
39void VP8LHistogramCreate(VP8LHistogram* const p,
40                         const VP8LBackwardRefs* const refs,
41                         int palette_code_bits) {
42  if (palette_code_bits >= 0) {
43    p->palette_code_bits_ = palette_code_bits;
44  }
45  HistogramClear(p);
46  VP8LHistogramStoreRefs(refs, p);
47}
48
49void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) {
50  p->palette_code_bits_ = palette_code_bits;
51  HistogramClear(p);
52}
53
54VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) {
55  int i;
56  VP8LHistogramSet* set;
57  VP8LHistogram* bulk;
58  const uint64_t total_size = (uint64_t)sizeof(*set)
59                            + size * sizeof(*set->histograms)
60                            + size * sizeof(**set->histograms);
61  uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory));
62  if (memory == NULL) return NULL;
63
64  set = (VP8LHistogramSet*)memory;
65  memory += sizeof(*set);
66  set->histograms = (VP8LHistogram**)memory;
67  memory += size * sizeof(*set->histograms);
68  bulk = (VP8LHistogram*)memory;
69  set->max_size = size;
70  set->size = size;
71  for (i = 0; i < size; ++i) {
72    set->histograms[i] = bulk + i;
73    VP8LHistogramInit(set->histograms[i], cache_bits);
74  }
75  return set;
76}
77
78// -----------------------------------------------------------------------------
79
80void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
81                                     const PixOrCopy* const v) {
82  if (PixOrCopyIsLiteral(v)) {
83    ++histo->alpha_[PixOrCopyLiteral(v, 3)];
84    ++histo->red_[PixOrCopyLiteral(v, 2)];
85    ++histo->literal_[PixOrCopyLiteral(v, 1)];
86    ++histo->blue_[PixOrCopyLiteral(v, 0)];
87  } else if (PixOrCopyIsCacheIdx(v)) {
88    int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v);
89    ++histo->literal_[literal_ix];
90  } else {
91    int code, extra_bits_count, extra_bits_value;
92    PrefixEncode(PixOrCopyLength(v),
93                 &code, &extra_bits_count, &extra_bits_value);
94    ++histo->literal_[256 + code];
95    PrefixEncode(PixOrCopyDistance(v),
96                 &code, &extra_bits_count, &extra_bits_value);
97    ++histo->distance_[code];
98  }
99}
100
101
102
103static double BitsEntropy(const int* const array, int n) {
104  double retval = 0.;
105  int sum = 0;
106  int nonzeros = 0;
107  int max_val = 0;
108  int i;
109  double mix;
110  for (i = 0; i < n; ++i) {
111    if (array[i] != 0) {
112      sum += array[i];
113      ++nonzeros;
114      retval -= VP8LFastSLog2(array[i]);
115      if (max_val < array[i]) {
116        max_val = array[i];
117      }
118    }
119  }
120  retval += VP8LFastSLog2(sum);
121
122  if (nonzeros < 5) {
123    if (nonzeros <= 1) {
124      return 0;
125    }
126    // Two symbols, they will be 0 and 1 in a Huffman code.
127    // Let's mix in a bit of entropy to favor good clustering when
128    // distributions of these are combined.
129    if (nonzeros == 2) {
130      return 0.99 * sum + 0.01 * retval;
131    }
132    // No matter what the entropy says, we cannot be better than min_limit
133    // with Huffman coding. I am mixing a bit of entropy into the
134    // min_limit since it produces much better (~0.5 %) compression results
135    // perhaps because of better entropy clustering.
136    if (nonzeros == 3) {
137      mix = 0.95;
138    } else {
139      mix = 0.7;  // nonzeros == 4.
140    }
141  } else {
142    mix = 0.627;
143  }
144
145  {
146    double min_limit = 2 * sum - max_val;
147    min_limit = mix * min_limit + (1.0 - mix) * retval;
148    return (retval < min_limit) ? min_limit : retval;
149  }
150}
151
152double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) {
153  double retval = BitsEntropy(&p->literal_[0], VP8LHistogramNumCodes(p))
154                + BitsEntropy(&p->red_[0], 256)
155                + BitsEntropy(&p->blue_[0], 256)
156                + BitsEntropy(&p->alpha_[0], 256)
157                + BitsEntropy(&p->distance_[0], NUM_DISTANCE_CODES);
158  // Compute the extra bits cost.
159  int i;
160  for (i = 2; i < NUM_LENGTH_CODES - 2; ++i) {
161    retval +=
162        (i >> 1) * p->literal_[256 + i + 2];
163  }
164  for (i = 2; i < NUM_DISTANCE_CODES - 2; ++i) {
165    retval += (i >> 1) * p->distance_[i + 2];
166  }
167  return retval;
168}
169
170
171// Returns the cost encode the rle-encoded entropy code.
172// The constants in this function are experimental.
173static double HuffmanCost(const int* const population, int length) {
174  // Small bias because Huffman code length is typically not stored in
175  // full length.
176  static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
177  static const double kSmallBias = 9.1;
178  double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
179  int streak = 0;
180  int i = 0;
181  for (; i < length - 1; ++i) {
182    ++streak;
183    if (population[i] == population[i + 1]) {
184      continue;
185    }
186 last_streak_hack:
187    // population[i] points now to the symbol in the streak of same values.
188    if (streak > 3) {
189      if (population[i] == 0) {
190        retval += 1.5625 + 0.234375 * streak;
191      } else {
192        retval += 2.578125 + 0.703125 * streak;
193      }
194    } else {
195      if (population[i] == 0) {
196        retval += 1.796875 * streak;
197      } else {
198        retval += 3.28125 * streak;
199      }
200    }
201    streak = 0;
202  }
203  if (i == length - 1) {
204    ++streak;
205    goto last_streak_hack;
206  }
207  return retval;
208}
209
210// Estimates the Huffman dictionary + other block overhead size.
211static double HistogramEstimateBitsHeader(const VP8LHistogram* const p) {
212  return HuffmanCost(&p->alpha_[0], 256) +
213         HuffmanCost(&p->red_[0], 256) +
214         HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) +
215         HuffmanCost(&p->blue_[0], 256) +
216         HuffmanCost(&p->distance_[0], NUM_DISTANCE_CODES);
217}
218
219double VP8LHistogramEstimateBits(const VP8LHistogram* const p) {
220  return HistogramEstimateBitsHeader(p) + VP8LHistogramEstimateBitsBulk(p);
221}
222
223static void HistogramBuildImage(int xsize, int histo_bits,
224                                const VP8LBackwardRefs* const backward_refs,
225                                VP8LHistogramSet* const image) {
226  int i;
227  int x = 0, y = 0;
228  const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits);
229  VP8LHistogram** const histograms = image->histograms;
230  assert(histo_bits > 0);
231  for (i = 0; i < backward_refs->size; ++i) {
232    const PixOrCopy* const v = &backward_refs->refs[i];
233    const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits);
234    VP8LHistogramAddSinglePixOrCopy(histograms[ix], v);
235    x += PixOrCopyLength(v);
236    while (x >= xsize) {
237      x -= xsize;
238      ++y;
239    }
240  }
241}
242
243static uint32_t MyRand(uint32_t *seed) {
244  *seed *= 16807U;
245  if (*seed == 0) {
246    *seed = 1;
247  }
248  return *seed;
249}
250
251static int HistogramCombine(const VP8LHistogramSet* const in,
252                            VP8LHistogramSet* const out, int num_pairs) {
253  int ok = 0;
254  int i, iter;
255  uint32_t seed = 0;
256  int tries_with_no_success = 0;
257  const int min_cluster_size = 2;
258  int out_size = in->size;
259  const int outer_iters = in->size * 3;
260  VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos));
261  VP8LHistogram* cur_combo = histos + 0;    // trial merged histogram
262  VP8LHistogram* best_combo = histos + 1;   // best merged histogram so far
263  if (histos == NULL) goto End;
264
265  // Copy histograms from in[] to out[].
266  assert(in->size <= out->size);
267  for (i = 0; i < in->size; ++i) {
268    in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]);
269    *out->histograms[i] = *in->histograms[i];
270  }
271
272  // Collapse similar histograms in 'out'.
273  for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) {
274    // We pick the best pair to be combined out of 'inner_iters' pairs.
275    double best_cost_diff = 0.;
276    int best_idx1 = 0, best_idx2 = 1;
277    int j;
278    seed += iter;
279    for (j = 0; j < num_pairs; ++j) {
280      double curr_cost_diff;
281      // Choose two histograms at random and try to combine them.
282      const uint32_t idx1 = MyRand(&seed) % out_size;
283      const uint32_t tmp = ((j & 7) + 1) % (out_size - 1);
284      const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1);
285      const uint32_t idx2 = (idx1 + diff + 1) % out_size;
286      if (idx1 == idx2) {
287        continue;
288      }
289      *cur_combo = *out->histograms[idx1];
290      VP8LHistogramAdd(cur_combo, out->histograms[idx2]);
291      cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo);
292      // Calculate cost reduction on combining.
293      curr_cost_diff = cur_combo->bit_cost_
294                     - out->histograms[idx1]->bit_cost_
295                     - out->histograms[idx2]->bit_cost_;
296      if (best_cost_diff > curr_cost_diff) {    // found a better pair?
297        {     // swap cur/best combo histograms
298          VP8LHistogram* const tmp_histo = cur_combo;
299          cur_combo = best_combo;
300          best_combo = tmp_histo;
301        }
302        best_cost_diff = curr_cost_diff;
303        best_idx1 = idx1;
304        best_idx2 = idx2;
305      }
306    }
307
308    if (best_cost_diff < 0.0) {
309      *out->histograms[best_idx1] = *best_combo;
310      // swap best_idx2 slot with last one (which is now unused)
311      --out_size;
312      if (best_idx2 != out_size) {
313        out->histograms[best_idx2] = out->histograms[out_size];
314        out->histograms[out_size] = NULL;   // just for sanity check.
315      }
316      tries_with_no_success = 0;
317    }
318    if (++tries_with_no_success >= 50) {
319      break;
320    }
321  }
322  out->size = out_size;
323  ok = 1;
324
325 End:
326  free(histos);
327  return ok;
328}
329
330// -----------------------------------------------------------------------------
331// Histogram refinement
332
333// What is the bit cost of moving square_histogram from
334// cur_symbol to candidate_symbol.
335// TODO(skal): we don't really need to copy the histogram and Add(). Instead
336// we just need VP8LDualHistogramEstimateBits(A, B) estimation function.
337static double HistogramDistance(const VP8LHistogram* const square_histogram,
338                                const VP8LHistogram* const candidate) {
339  const double previous_bit_cost = candidate->bit_cost_;
340  double new_bit_cost;
341  VP8LHistogram modified_histo;
342  modified_histo = *candidate;
343  VP8LHistogramAdd(&modified_histo, square_histogram);
344  new_bit_cost = VP8LHistogramEstimateBits(&modified_histo);
345
346  return new_bit_cost - previous_bit_cost;
347}
348
349// Find the best 'out' histogram for each of the 'in' histograms.
350// Note: we assume that out[]->bit_cost_ is already up-to-date.
351static void HistogramRemap(const VP8LHistogramSet* const in,
352                           const VP8LHistogramSet* const out,
353                           uint16_t* const symbols) {
354  int i;
355  for (i = 0; i < in->size; ++i) {
356    int best_out = 0;
357    double best_bits = HistogramDistance(in->histograms[i], out->histograms[0]);
358    int k;
359    for (k = 1; k < out->size; ++k) {
360      const double cur_bits =
361          HistogramDistance(in->histograms[i], out->histograms[k]);
362      if (cur_bits < best_bits) {
363        best_bits = cur_bits;
364        best_out = k;
365      }
366    }
367    symbols[i] = best_out;
368  }
369
370  // Recompute each out based on raw and symbols.
371  for (i = 0; i < out->size; ++i) {
372    HistogramClear(out->histograms[i]);
373  }
374  for (i = 0; i < in->size; ++i) {
375    VP8LHistogramAdd(out->histograms[symbols[i]], in->histograms[i]);
376  }
377}
378
379int VP8LGetHistoImageSymbols(int xsize, int ysize,
380                             const VP8LBackwardRefs* const refs,
381                             int quality, int histo_bits, int cache_bits,
382                             VP8LHistogramSet* const image_in,
383                             uint16_t* const histogram_symbols) {
384  int ok = 0;
385  const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1;
386  const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1;
387  const int num_histo_pairs = 10 + quality / 2;  // For HistogramCombine().
388  const int histo_image_raw_size = histo_xsize * histo_ysize;
389  VP8LHistogramSet* const image_out =
390      VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits);
391  if (image_out == NULL) return 0;
392
393  // Build histogram image.
394  HistogramBuildImage(xsize, histo_bits, refs, image_out);
395  // Collapse similar histograms.
396  if (!HistogramCombine(image_out, image_in, num_histo_pairs)) {
397    goto Error;
398  }
399  // Find the optimal map from original histograms to the final ones.
400  HistogramRemap(image_out, image_in, histogram_symbols);
401  ok = 1;
402
403Error:
404  free(image_out);
405  return ok;
406}
407