Fix FindClosestDiscretized in near lossless:

- The result is now indeed closest among possible results for all inputs, which
  was not the case for bits>4, where the mapping was not even monotonic because
  GetValAndDistance was correct only if the significant part of initial fit in
  a byte at most twice.

- The set of results for a larger number of bits dropped is a subset of values
  for a smaller number of bits dropped. This implies that subsequent
  discretizations for a smaller number of bits dropped do not change already
  discretized pixels, which improves the quality (changes do not accumulate)
  and compression density (values tend to repeat more often).

- Errors are more fairly distributed between upwards and downwards thanks to
  bankers’ rounding, which avoids images getting darker or lighter in overall.

- Deltas between discretized values are more repetitive. This improves
  compression density if delta encoding is used.

Also, the implementation is much shorter now.

Change-Id: I0a98e7d5255e91a7b9c193a156cf5405d9701f16
This commit is contained in:
Marcin Kowalczyk 2016-03-02 12:45:54 +01:00 committed by Pascal Massimino
parent 8200643017
commit e8feb20e39

View File

@ -14,6 +14,7 @@
// Author: Jyrki Alakuijala (jyrki@google.com)
// Converted to C by Aleksander Kramarz (akramarz@google.com)
#include <assert.h>
#include <stdlib.h>
#include "../dsp/lossless.h"
@ -23,42 +24,14 @@
#define MIN_DIM_FOR_NEAR_LOSSLESS 64
#define MAX_LIMIT_BITS 5
// Computes quantized pixel value and distance from original value.
static void GetValAndDistance(int a, int initial, int bits,
int* const val, int* const distance) {
const int mask = ~((1 << bits) - 1);
*val = (initial & mask) | (initial >> (8 - bits));
*distance = 2 * abs(a - *val);
}
// Clamps the value to range [0, 255].
static int Clamp8b(int val) {
const int min_val = 0;
const int max_val = 0xff;
return (val < min_val) ? min_val : (val > max_val) ? max_val : val;
}
// Quantizes values {a, a+(1<<bits), a-(1<<bits)} and returns the nearest one.
// Quantizes the value up or down to a multiple of 1<<bits (or to 255),
// choosing the closer one, resolving ties using bankers' rounding.
static int FindClosestDiscretized(int a, int bits) {
int best_val = a, i;
int min_distance = 256;
for (i = -1; i <= 1; ++i) {
int candidate, distance;
const int val = Clamp8b(a + i * (1 << bits));
GetValAndDistance(a, val, bits, &candidate, &distance);
if (i != 0) {
++distance;
}
// Smallest distance but favor i == 0 over i == -1 and i == 1
// since that keeps the overall intensity more constant in the
// images.
if (distance < min_distance) {
min_distance = distance;
best_val = candidate;
}
}
return best_val;
const int mask = (1 << bits) - 1;
const int biased = a + (mask >> 1) + ((a >> bits) & 1);
assert(bits > 0);
if (biased > 0xff) return 0xff;
return biased & ~mask;
}
// Applies FindClosestDiscretized to all channels of pixel.