Method that processes pixels in a certain way

1

in a code snippet, I have found the following method that I could not understand:

static void processPixels(int width, int height, int *pixels, int *rgb) {
    int R[256];
    int G[256];
    int B[256];
    for (int i = 0; i < 256; i++) {
        R[i] = (rgb[i] << 16) & 0x00FF0000;
        G[i] = (rgb[i] << 8) & 0x0000FF00;
        B[i] = rgb[i] & 0x000000FF;
    }

    for (int i = 0; i < width * height; i++) {
        pixels[i] =
                (0xFF000000 & pixels[i]) | (R[(pixels[i] >> 16) & 0xFF]) | (G[(pixels[i] >> 8) & 0xFF]) | (B[pixels[i] & 0xFF]);
    }

}

Can somebody explain what the purpose of those shiftings are ? Are they extracting sth. ? And what is happening in the last line ?

After a little search, I found out that each pixel consists of three channels: Red, Green and Blue. And because they use a width and height variable, I assume that the *pixels is a pointer to an image. So, the for-loop walks through the pixels of an image pointed to by *pixels and modifies them each. But again, I could not understand what the last line does.

So, every help would be nice.

c
image-processing
pixel
asked on Stack Overflow Aug 7, 2019 by abdullah celik • edited Aug 7, 2019 by Cris Luengo

3 Answers

1

This function separately transforms each color in an image using an intensity map specified by rgb. By supplying an appropriate map, this can be used to implement any transformation in which the colors and pixels are independent, such as making a negative image, brightening, darkening, posterization, and more.

The function processPixels accepts an image and a transformation map. The image is described by the parameters width, height, and pixels:

  • The image is width pixels wide and height pixels high.
  • pixels points to an array of 32-bit int.
  • Each int contains data about one pixel, containing an alpha value in the high eight bits, and intensities for red, green, and blue in the following three sets of eight bits. (An alpha value is a measure of opaqueness, from 0 being fully transparent to 255 being fully opaque.)

The map is described by the parameter rgb:

  • rgb points to an array of 256 int, each of which contains, in its low eight bits, an intensity value.

The function is intended to replace the intensity of each color in each pixel by an intensity specified by the map. Specifically, if any color has intensity x, it is replaced by rgb[x] (considering only the low eight bits).

There is a shortcoming in the function: It uses int for the pixel data and shifts it right, but the values will be negative with any alpha value of 128 or greater, and the results of right shifting negative data are implementation-defined. It would have been better to use unsigned int.

In the first loop, the function prepares lookup tables for each of the red, green, and blue positions:

for (int i = 0; i < 256; i++) {
    R[i] = (rgb[i] << 16) & 0x00FF0000;
    G[i] = (rgb[i] << 8) & 0x0000FF00;
    B[i] = rgb[i] & 0x000000FF;
}

In each case, the value in rgb[i] is shifted to the position corresponding to the color, and then a mask is applied to extract eight bits. In each case, the same eight bits are extracted, differing only in the locations to which they have been shifted. This code could have been written more clearly as:

for (int i = 0; i < 256; ++i)
{
    unsigned x = rgb[i] & 0xff;
    R[i] = x << 2*8;
    G[i] = x << 1*8;
    B[i] = x << 0*8;
}

This makes it visually apparent that the code is using the same data for each array, and they differ solely in position. In other words, all this code is doing is preparing shifted copies of rgb[i].

In the second loop, the function substitutes the pixel intensities with those given by the map. i loops through each pixel in the image. Then, in:

    pixels[i] =
            (0xFF000000 & pixels[i]) | (R[(pixels[i] >> 16) & 0xFF]) | (G[(pixels[i] >> 8) & 0xFF]) | (B[pixels[i] & 0xFF]);

we have these parts:

  • 0xFF000000 & pixels[i] separates the alpha component.
  • R[(pixels[i] >> 16) & 0xFF] takes the data for one pixel, pixels[i], and shifts it right 16 bits, which moves the red component to the low eight bits. Then it masks the shifted value to extract just the low eight bits. This produces the intensity for the red component. Then R[…] looks up this intensity in the R array, which produces the pre-shifted value prepared in the first loop—it contains the intensity from rgb already shifted to the red position.
  • G[(pixels[i] >> 8) & 0xFF] and B[pixels[i] & 0xFF] do the same for the green and blue components.
  • Finally, the values for the alpha, red, blue, and green components are merged with OR operators (|) and stored back in the same pixel, pixel[i].

It should be noted that the code is defective in that it fails to document its purpose, requirements, and methods.

answered on Stack Overflow Aug 7, 2019 by Eric Postpischil • edited Aug 7, 2019 by Eric Postpischil
0

There are different color packing schemes and sampling patterns out there. Some have industry standards and some have not. Your code snippet looks like a non-standardized color packing, tailored for a custom application.

Now let's dissect your code!

R, G, B definitely stands for Red, Green, and Blue. Each of them is given 256 entries which led me to believe we are dealing with 8-bit color graphics. 0x00FF0000 is hex value for bright red. Consecutively, 0x0000FF00 is for green and 0x000000FF is for blue.

The purpose of the bit shifting is to create space for each palette so that each Red, Blue and Green can be stored inside a 24-bit integer. Each color can be chosen from a palette of 16,777,216 colors (24 bits: 8 red, 8 green, 8 blue).

Blue is the first 8 bits (b[0]-B[7]). Green is then bit shifted to left by 8 bits, So b[8]-b[15] represents green. And finally, Red is shifted to left by 16 bits. b[16]-b[23] represents red.

The hight and width represents the dimension of the image you are trying to parse. However, this line below doesn't make much sense, because you are trying to combine them into an integer as you bit shifting right. I would verify this line:

pixels[i] = (0xFF000000 & pixels[i]) | (R[(pixels[i] >> 16) & 0xFF]) | (G[(pixels[i] >> 8) & 0xFF]) | (B[pixels[i] & 0xFF]);

answered on Stack Overflow Aug 7, 2019 by blackbeard
0

regarding:

        pixels[i] =
            (0xFF000000 & pixels[i]) | (R[(pixels[i] >> 16) & 0xFF]) | (G[(pixels[i] >> 8) & 0xFF]) | (B[pixels[i] & 0xFF]);

and your question:

I could not understand what the last line does.

The last line keeps the 'alpha' of each pixel, However, it 'OR's all the color (Reg, blue, green) information into the 'blue' part of the pixel.

I can think of no reason to do that.

answered on Stack Overflow Aug 8, 2019 by user3629249

User contributions licensed under CC BY-SA 3.0