'RGB565 to Grayscale
I am trying to convert an RGB565 to the Grayscale image.
I used the below code to do the conversion. I have a rgb565 pixel. I am getting R,G and B values and doing the conversion operation. Once the grayscale is obtained, I am reconstructing a 16 bit pixel values wherein R, G and B will have the same grayscale value.
int16_t pixel = *buffer;
int16_t red = ((pixel & 0xF800)>>11);
int16_t green = ((pixel & 0x07E0)>>5);
int16_t blue = (pixel & 0x001F);
int16_t grayscale = (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
*buffer=(grayscale<<11)+(grayscale<<5)+grayscale;
Here, I expected a grayscale image. However, what is happening is ->
Green color has 6 bits of space (has more magnitude). The resulting grayscale value should be 5bits so that, i will fit for R G and B. However, the calculation done provides a 6 bit grayscale because of which I am not getting a proper grayscale image.
Could anyone help me to convert a rgb565 to grayscale and store it as 16 bits.
Solution 1:[1]
I think Pibben's answer still has some shift/mask bugs in it, and it uses floating point which, depending on your environment, could range from abysmally slow to not even possible. I had done this myself some time ago, so here is what I wrote up:
The first thing I wanted is a monochrome viewfinder mode... I want to work in 16-bit integer space, for speed, so I need to use standard fixed-point scaled math. Starting with https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems, which uses coefficients that work with gamma-corrected values:
Y = 0.2126×R + 0.7152×G + 0.0722×B
Given RMAX = GMAX = BMAX = 63, a scale factor of 1000 will avoid overflowing a U16. So, we have to lose the LSD of the given four-digit coefficients. To avoid division by 1000 after the summation we can scale all factors up by 1024/1000, then our division becomes a right-shift by 10 bits. So for our coefficients: (2126×1024)÷10000 = 217.7, rounded to 218. Etc. Given these calculated coefficients and RGB565 source format, YMAX = 64,220, which does not overflow a U16; check. Divided by 1024 the maximum result is 62.7, which can be rounded to 63; Perfect. (Rounding is simply adding back the most-significant bit lost in the shift, something that is usually nearly trivial in assembly language [add 0 with carry] but somewhat messier in C.) That gives us a 0..63 6-bit integer luminance value that is easy to pack back into RGB565. The final de-colorization code becomes:
void
decolorize(U16 *usp, int count)
{
U16 us, rc, gc, bc, luma;
while (--count) {
us = *usp;
rc = (us >> 10) & 0x3E; /* 6-bit Red Component. */
gc = (us >> 5) & 0x3F; /* 6-bit Green Component. */
bc = (us << 1) & 0x3E; /* 6-bit Blue Component. */
luma = (rc * 218) + (gc * 732) + (bc * 74); /* Wx*1024/10000. */
luma = (luma >> 10) + ((luma >> 9) & 1); /* 6-bit Luminance value. */
*usp++ = ((luma & 0x3E) << 10) | (luma << 5) | (luma >> 1); /* RGB565 */
}
}
Moreover, if you can afford a 128kB lookup table, initialized by code similar to above, the RGB565 pixels can be translated directly by array lookup, which is pretty fast on most processors. I don't think you can beat this for speed unless you are using a GPU, or a vector processor. The LUT is how I ended up actually using this code.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
