Reputation: 751
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
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
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
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