I am new to sdl. I have basically have the following codes which can load the images. I know how to access the pixel as below. What I need it to convert the image to 8 bit grey level and 1 bit monochrome separately. I know it got to do with pixel manipulation. Any hint or idea how to to this? I am not too sure what to set for SDL_CreateRGBSurface and also once I get the pixel what manipulation to be done?
1 Answer
Essentially you're just looking for a conversion function, i.e. breaking down a three component color into a one component color. This can be done different ways, probably easiest one being a simple average function:
gray(r, g, b) = (r + g + b) / 3
In C/C++ this could be done the following way (assuming you've got 24/32 bit colors with alpha being unused/occupying the most significant byte; one byte per color; i.e. RGB888 or RGBA8888):
int source_color = SDL_GetPixel(...); // some color
const unsigned int component1 = source_color & 0xff; // red or blue
const unsigned int component2 = (source_color >> 8) & 0xff; // green
const unsigned int component3 = (source_color >> 16) & 0xff; // blue or red
// calculating the average component/color (i.e. grayscale)
const unsigned int gray = (component1 + component2 + component3) / 3;
// back to SDL's RGB color (optional)
const int gray_rgb = 0xff000000 | (gray << 16) | (gray << 8) | gray;
// There are multiple ways to get the monochrom image, based on the format you'd like (e.g. just a boolean value or just a RGB value).
// First version (actually not recommended, but might save you code e.g. having one fixed conversion function):
// calculating the monochromatic color with a given threshold (127)
const unsigned int mono = gray > 127 ? 255 : 0;
// once again back to SDL's RGB model (optional)
const int mono_rgb = 0xff000000 | (mono << 16) | (mono << 8) | mono;
// Second version:
// just return the final color in one step
// this returns pure white or pure black
const int mono_rgb = gray > 127 ? 0xffffffff : 0xff000000;
Note that there are similar approaches using different weightings applied to the color components. Also you can try different thresholds for the monochrome conversion to lighten/darken the image.
As for the actual SDL Surface, that's up to you. I'd just stick with SDL_PIXELFORMAT_RGB888 or SDL_PIXELFORMAT_RGBA8888 to avoid further conversions, unless it understands grayscale images now (which I don't think?).
Further explanation on the conversions:
If you're storing a color in an integer value, you'll usually use one byte per color. In hex notation this will mean that you've got two digits per color, essentially translating to something like AABBGGRR, where AA are the byte for alpha, BB the byte for blue, etc. If you're familiar with the hexadecimal notation of colors in HTML/CSS (like #ff0000 for red), then this should sound familiar to you.
If you'd like to store a grayscale value x in (A)RGB notation, you'd end up setting all three color components (red, green, and blue) to the same value (of x). This happens in this line:
const int gray_rgb = 0xff000000 | (gray << 16) | (gray << 8) | gray;
- The logical or (
|) essentially adds the various components. 0xff000000represents full opacity (alpha = 255).gray << 16moves the value ofgrayto the correct position for red or blue (depends on pixel format). This is known as a left shift, e.g.0x00000012 << 8will result in0x00001200(values moved to the left by 8 bits).gray << 8moves the value ofgrayto the correct position for green.grayfinally stays as is, which will be the value for blue or red (again depending on the pixel format).
Finally, const unsigned int mono = gray > 127 ? 255 : 0; will set the monochrome color (which is actually grayscale again, 256 different values!) based on the grayscale value. 255 represents white, 0 represents black.
-
\$\begingroup\$ yes I saw some articles give different weights for RGB but some are doing averaging like yours. I am not so here what are you doing here const int gray_rgb = 0xff000000 | (gray << 16) | (gray << 8) | gray; What is is this if statement for gray > 127 ? 255 : 0; I guess for single bit image right? \$\endgroup\$biz14– biz142013-09-08 14:24:00 +00:00Commented Sep 8, 2013 at 14:24
-
\$\begingroup\$ Exactly, instead of using
gray > 127 ? 255 : 0you could as well just usegray > 127to get a true boolean value (i.e. 1 bit), but you can't use that to get an RGB color directly. \$\endgroup\$Mario– Mario2013-09-08 14:55:16 +00:00Commented Sep 8, 2013 at 14:55 -
\$\begingroup\$ Further updated the answer, the monochrome color generation has been a bit too "complicated" based on what you're trying to achieve. Also, just to note: SDL is a library to display images, write games, etc. If you'd like to do image manipulation and computer vision stuff, I'd suggest you have a look at OpenCV, which offers far better support for different pixel formats, conversions, etc. (lots of stuff built in as well). \$\endgroup\$Mario– Mario2013-09-08 14:58:08 +00:00Commented Sep 8, 2013 at 14:58
-
\$\begingroup\$ ok I am very clear now but before that we got to confirm whether is RGBA or ARGB right how normally your confirm on that else the whole of this will be wrong const int gray_rgb = 0xff000000 | (gray << 16) | (gray << 8) | gray; right? One more things once all 3 have some color value how will they be gray scale when they still have color values in it? \$\endgroup\$biz14– biz142013-09-08 15:27:11 +00:00Commented Sep 8, 2013 at 15:27
-
\$\begingroup\$ You'll have to pick the proper pixel format to ensure you're setting the correct values, which will influence the order in which you shift values. The generated values are indeed grayscale due to the way the RGB color system works. In short: All components are identical = gray(scale). Have a look at Wikipedia and other sites. Of course, there are more efficient formats for grayscale images, like 1 byte per pixel, but SDL isn't really made for these. \$\endgroup\$Mario– Mario2013-09-08 15:31:40 +00:00Commented Sep 8, 2013 at 15:31