Payam
Payam

Reputation: 751

Extracting RGB from a pixel without color object in C#

I am trying to extract R G B values from a pixel in the following code:

for ( int i=0; i < pixeldata.length; i++)
    {
        IntPtr ptr = bmd.Scan0+i;
        byte* pixel = (byte*)ptr;

        //here is the problem :O

        float r = pixel[1];
        float g = pixel[2];
        float b = pixel[3];
     }
....

where bmd is an array of pixels data:

BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, source.PixelFormat);

and source is the Bitmap of my input, which is an image. I am trying to avoid the use of Color object. I have already done that and it works, I want to use this other way, but the issue is that ptr is a number and I have to extract the R G B from it.

Upvotes: 3

Views: 2416

Answers (3)

Payam
Payam

Reputation: 751

This is the solution that gives you the right answer.

Bitmap source = new Bitmap(image);

            Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);

            BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

            int totalPixels = rect.Height * rect.Width;
            int[] pixelData = new int[totalPixels];
            for (int i = 0; i < totalPixels; i++)
            {
                byte* pixel = (byte*)bmd.Scan0;
                pixel = pixel + (i * 4);

                byte b = pixel[0];
                byte g = pixel[1];
                byte r = pixel[2];

                int luma = (int)(r * 0.3 + g * 0.59 + b * 0.11);
                pixelData[i] = luma;
            }

Upvotes: 3

SergeyS
SergeyS

Reputation: 3553

Ok, this was interesting, and I have written some code to play with. Assuming that your image has pixels in format Format24bppRgb (more info about formats here: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx). This format store B, G, R values in 24 bits one after another.

Below code which will parse some d:\\24bits.bmp image from your hard drive and creates new identical one "d:\\24bits_1.bmp" using information B, G, R information from bytes array of first image data.

unsafe private static void TestBMP()
{
    Bitmap bmp = new Bitmap("d:\\24bits.bmp");

    // Ensure that format is Format24bppRgb.
    Console.WriteLine(bmp.PixelFormat);

    Bitmap copyBmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

    // Copy all pixels of initial image for verification.
    int pixels = bmp.Height * bmp.Width;
    Color[,] allPixels = new Color[bmp.Height, bmp.Width];
    for (int i = 0; i < bmp.Height; i++)
        for (int j = 0; j < bmp.Width; j++)
            allPixels[i, j] = bmp.GetPixel(j, i);

    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    System.Drawing.Imaging.BitmapData bmpData =
        bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
        bmp.PixelFormat);

    IntPtr ptr = bmpData.Scan0;

    byte* stream = (byte*)ptr;

    for (int y = 0; y < bmp.Height; y++)
        for (int x = 0; x < bmp.Width; x++)
        {
            int byteIndex = y * bmpData.Stride + x * 3;

            byte r = stream[byteIndex + 2];
            byte g = stream[byteIndex + 1];
            byte b = stream[byteIndex];

            Color c = allPixels[y, x];
            if (r != c.R || g != c.G || b != c.B)
            {
                Console.WriteLine("This should never appear");
            }
            copyBmp.SetPixel(x, y, Color.FromArgb(255, r, g, b));
        }

    // Save new image. It should be the same as initial one.
    copyBmp.Save("d:\\24bits_1.bmp");
}

Upvotes: 0

Eric J.
Eric J.

Reputation: 150108

If you have a format that stores R, G, and B as one byte each linearly in memory in that order, the code to extract the RGB values should look like

byte r = pixel[0];
byte g = pixel[1];
byte b = pixel[2];

Note that the index offset begins at 0, and that the values returned are byte not float (though you can certainly cast if you wish).

Additionally you would have to increment i by 3 rather than 1 because 3 adjacent bytes represent a single pixel.

You would be wise to test that source.PixelFormat indeed uses the format you are assuming.

You also have to compile with the /unsafe switch in order to use pointers in C#.

UPDATE

Per @Don's comment as well as your own, the order in linear memory would be ABGR. That means the code would be:

for ( int i=0; i < pixeldata.length; i+=4)
{
    IntPtr ptr = bmd.Scan0+i;
    byte* pixel = (byte*)ptr;

    byte a = pixel[0]; // You can ignore if you do not need alpha.
    byte b = pixel[1];
    byte g = pixel[2];
    byte r = pixel[3];
}

Upvotes: 0

Related Questions