tags:

views:

366

answers:

3

Dear developers,

I have a buffer with many positive 16bit values (which are stored as doubles) that I would like to quantize to 8bit (0-255 values).

According to Wikipedia the process would be:

  • Normalize 16 bit values. I.e. find the largest and divide with this.
  • Use the Q(x) formula with M=8.

So I wonder, if C have a function that can do this quantization, or does anyone know of a C implementation that I could use?

Lots of love, Louise

+1  A: 

Assuming the value d is in the interval [0.0, max]:

unsigned char quantize(double d, double max)
{
    return (unsigned char)((d / max) * 255.0);
}

I'm not sure what you mean by "16-bit values;" double precision values are 64-bit on any system using IEEE-754. However, if you have values of another numeric type, the process is effectively the same.

James McNellis
You need to cast to "unsigned char" if you want to be portable. Or better yet, drop the cast completely.
avakar
Wow. What a great solution! How did you come from the Q(x) formula from Wikipedia to this elegant function?
Louise
+3  A: 

This sounds like waveform audio processing, where your input is 16 PCM data, your output is 8 bit PCM data and you are using doubles as an intermediate value.

However, 8 bit PCM wave data is NOT just quantized, the representation is unsigned values in excess 128 notation. (like the way exponents are stored in floating point numbers)

Finding the largest value first would not only be quantizing, but also scaling. So in pseudo code

double dMax = max_of_all_values(); // 
...
foreach (dValue in array_of_doubles)
{
   signed char bValue = (signed char)((dValue / dMax)*127.0);
}

You could round rather than truncating if you want more accuracy, but in audio processing, it's generally better to randomize the truncation order or even shape it by essentially running a filtering algorithm as part of the truncation from doubles to signed chars.

Note: that signed char is NOT correct if the output is 8 bit PCM data, but since the questions doesn't specifically request that, I left it out.

Edit: if this is to be used as pixel data, then you want unsigned values. I see James already gave the correct answer for unsigned values when the input is unsigned (dB values from normalized data should be all negative, actually)

John Knoeller
Very impressive that you could see what I was working on =) I converted the 16bit input to dB, because I want to plot it as a spectrogram. But sadly GIMP doesn't support 16bit per colour channel yet, so I have to quantize 8bit. I am working in grey scale. GIMP does have some GEGL support, which gives 32bit per colour channel, but the plugin API doesn't support this yet, from what I can tell.
Louise
+1  A: 

It is not clear from your question what the encoding is since "positive 16bit values (which are stored as doubles)" makes no real sense; they are either 16 bit or they are double, they cannot be both.

However assuming that this is 16 bit unsigned data normalised to 1.0 (so the values range from 0.0 <= s <= 1.0), then all you need to do to expand them to 8bit integer values is to multiply each sample by 255.

unsigned char s8 = s * 255 ;

If the range is not 0.0 <= s <= 1.0, but 0.0 <= s <= max then:

unsigned char s8 = s / max * 255 ;

Either way, there is no "quantisation" function other than one you might write yourself; but the necessary transform will no doubt be a simple arithmetic expression (although not so simple if the data is companded perhaps - i.e. μ-lay or A-law encoded for example).

Clifford