开发者

RGB 24 to 16-bit color conversion - Colors are darkening

I noticed that my routine to convert between RGB888 24-bit to 16-bit RGB565 resulted in darkening of the colors progressively each time a conversion took place... The formula uses linear interpolation like so...

typedef struct _RGB24 RGB24;
struct _RGB24 {
   BYTE B;
   BYTE G;
   BYTE R;
};

RGB24 *s; // source
WORD *d; // destination
WORD r;
WORD g;
WORD b;

// Code to convert from 24-bit to 16 bit
r = (WORD)((double)(s[x].r * 31) / 255.0);
g 开发者_如何转开发= (WORD)((double)(s[x].g * 63) / 255.0);
b = (WORD)((double)(s[x].b * 31) / 255.0);
d[x] = (r << REDSHIFT) | (g << GREENSHIFT) | (b << BLUESHIFT);

// Code to convert from 16-bit to 24-bit
s[x].r = (BYTE)((double)(((d[x] & REDMASK) >> REDSHIFT) * 255) / 31.0);
s[x].g = (BYTE)((double)(((d[x] & GREENMASK) >> GREENSHIFT) * 255) / 63.0);
s[x].b = (BYTE)((double)(((d[x] & BLUEMASK) >> BLUESHIFT) * 255) / 31.0);

The conversion from 16-bit to 24-bit is similar but with reverse interpolation... I don't understand how the values keep getting lower and lower each time a color is cycled through the equation if they are opposites... Originally there was no cast to double, but I figured if I made it a floating point divide it would not have the falloff... but it still does...


When you convert your double values to WORD, the values are being truncated. For example, (126 * 31)/ 255 = 15.439, which is truncated to 15. Because the values are truncated, they get progressively lower through each iteration. You need to introduce rounding (by adding 0.5 to the calculated values before converting them to integers)

Continuing the example, you then take 15 and convert back: (15 * 255)/31 = 123.387 which truncates to 123


Don't use floating point for something simple like this. Normal way I've seen is to truncate on the down-conversion but extend on the up-conversion (so 0b11111 goes to 0b11111111).

// Code to convert from 24-bit to 16 bit
r = s[x].r >> (8-REDBITS);
g = s[x].g >> (8-GREENBITS);
b = s[x].b >> (8-BLUEBITS);
d[x] = (r << REDSHIFT) | (g << GREENSHIFT) | (b << BLUESHIFT);

// Code to convert from 16-bit to 24-bit
s[x].r = (d[x] & REDMASK) >> REDSHIFT;                        // 000abcde
s[x].r = s[x].r << (8-REDBITS) | s[x].r >> (2*REDBITS-8);     // abcdeabc
s[x].g = (d[x] & GREENMASK) >> GREENSHIFT;                    // 00abcdef
s[x].g = s[x].g << (8-GREENBITS) | s[x].g >> (2*GREENBITS-8); // abcdefab
s[x].b = (d[x] & BLUEMASK) >> BLUESHIFT;                      // 000abcde
s[x].b = s[x].b << (8-BLUEBITS) | s[x].b >> (2*BLUEBITS-8);   // abcdeabc


Casting double to WORD doesn't round the double value - it truncates the decimal digits. You need to use some kind of rounding routine to get rounding behavior. Typically you want to round half to even. There is a Stack Overflow question on how to round in C++ if you need it.


Also note that the conversion from 24 bit to 16 bits permanently loses information. It's impossible to fit 24 bits of information into 16 bits, of course. You can't get it back by conversion from 16 bits back to 24 bits.


it is because 16 bit works with the values multiplied with 2 for example 2*2*2*2 and it will come out as rrggbb and in same 32 bit case it will multiply the whole bit values with 2.

in short 16 bit 24 bit 32 bit works with multiplication of rgb with 2 and shows you the values in form of color.
for brief u should find the concept of bit color. check it on Wikipedia hope it will help you


Since you're converting to double anyway, at least use it to avoid overflow, i.e. replace

r = (WORD)((double)(s[x].r * 31) / 255.0);

with

r = (WORD)round(s[x].r / 255.0 * 31.0); 

in this way the compiler should also fold 31.0/255.0 in a costant.

Obviously if this has to be repeated for huge quantities of pixels, it would be preferable to create and use a LUT (lookup table) instead.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜