Finding specific pixel colors of a BitmapImage Finding specific pixel colors of a BitmapImage wpf wpf

Finding specific pixel colors of a BitmapImage


Here is how I would manipulate pixels in C# using multidimensional arrays:

[StructLayout(LayoutKind.Sequential)]public struct PixelColor{  public byte Blue;  public byte Green;  public byte Red;  public byte Alpha;}public PixelColor[,] GetPixels(BitmapSource source){  if(source.Format!=PixelFormats.Bgra32)    source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);  int width = source.PixelWidth;  int height = source.PixelHeight;  PixelColor[,] result = new PixelColor[width, height];  source.CopyPixels(result, width * 4, 0);  return result;}

usage:

var pixels = GetPixels(image);if(pixels[7, 3].Red > 4){  ...}

If you want to update pixels, very similar code works except you will create a WriteableBitmap, and use this:

public void PutPixels(WriteableBitmap bitmap, PixelColor[,] pixels, int x, int y){  int width = pixels.GetLength(0);  int height = pixels.GetLength(1);  bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width*4, x, y);}

thusly:

var pixels = new PixelColor[4, 3];pixels[2,2] = new PixelColor { Red=128, Blue=0, Green=255, Alpha=255 };PutPixels(bitmap, pixels, 7, 7);

Note that this code converts bitmaps to Bgra32 if they arrive in a different format. This is generally fast, but in some cases may be a performance bottleneck, in which case this technique would be modified to match the underlying input format more closely.

Update

Since BitmapSource.CopyPixels doesn't accept a two-dimensional array it is necessary to convert the array between one-dimensional and two-dimensional. The following extension method should do the trick:

public static class BitmapSourceHelper{#if UNSAFE  public unsafe static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)  {    fixed(PixelColor* buffer = &pixels[0, 0])      source.CopyPixels(        new Int32Rect(0, 0, source.PixelWidth, source.PixelHeight),        (IntPtr)(buffer + offset),        pixels.GetLength(0) * pixels.GetLength(1) * sizeof(PixelColor),        stride);  }#else  public static void CopyPixels(this BitmapSource source, PixelColor[,] pixels, int stride, int offset)  {    var height = source.PixelHeight;    var width = source.PixelWidth;    var pixelBytes = new byte[height * width * 4];    source.CopyPixels(pixelBytes, stride, 0);    int y0 = offset / width;    int x0 = offset - width * y0;    for(int y=0; y<height; y++)      for(int x=0; x<width; x++)        pixels[x+x0, y+y0] = new PixelColor        {          Blue  = pixelBytes[(y*width + x) * 4 + 0],          Green = pixelBytes[(y*width + x) * 4 + 1],          Red   = pixelBytes[(y*width + x) * 4 + 2],          Alpha = pixelBytes[(y*width + x) * 4 + 3],        };  }#endif}

There are two implementations here: The first one is fast but uses unsafe code to get an IntPtr to an array (must compile with /unsafe option). The second one is slower but does not require unsafe code. I use the unsafe version in my code.

WritePixels accepts two-dimensional arrays, so no extension method is required.

Edit: As Jerry pointed out in the comments, because of the memory layout, the two-dimensional array has the vertical coordinate first, in other words it must be dimensioned as Pixels[Height,Width] not Pixels[Width,Height] and addressed as Pixels[y,x].


I'd like to add to Ray´s answer that you can also declare PixelColor struct as a union:

[StructLayout(LayoutKind.Explicit)]public struct PixelColor{    // 32 bit BGRA     [FieldOffset(0)] public UInt32 ColorBGRA;    // 8 bit components    [FieldOffset(0)] public byte Blue;    [FieldOffset(1)] public byte Green;    [FieldOffset(2)] public byte Red;    [FieldOffset(3)] public byte Alpha;}

And that way you'll also have access to the UInit32 BGRA (for fast pixel access or copy), besides the individual byte components.


The interpretation of the resulting byte array is dependent upon the pixel format of the source bitmap, but in the simplest case of a 32 bit, ARGB image, each pixel will be composed of four bytes in the byte array. The first pixel would be interpreted thusly:

alpha = pixelByteArray[0];red   = pixelByteArray[1];green = pixelByteArray[2];blue  = pixelByteArray[3];

To process each pixel in the image, you would probably want to create nested loops to walk the rows and the columns, incrementing an index variable by the number of bytes in each pixel.

Some bitmap types combine multiple pixels into a single byte. For instance, a monochrome image packs eight pixels into each byte. If you need to deal with images other than 24/32 bit per pixels (the simple ones), then I would suggest finding a good book that covers the underlying binary structure of bitmaps.