BitmapSource.CopyPixel: how to copy only area of interest?

713 views Asked by At

I have a System.Windows.Media.Imaging.BitmapSource, and a small Int32Rect.

I want to copy only the bytes of the bitmap that are in the rectangle to a buffer.

Addition: I want to use this buffer to do calculations with the pixel values, in this case: I want to calculate a value that gives an indication of the sharpness of the data in the bitmap: is it in focus? The calculation measures for each pixel the difference with the neighbouring pixels. I don't want to put the data in an other bitmap

For this I think that I should use BitmapSource.CopyPixels:

BitmapSource.CopyPixesl: Copies the bitmap pixel data within the specified rectangle into an array of pixels that has the specified stride starting at the specified offset.

Suppose the bitmap is 100 pixels wide, and each pixel has 32 bits (4 byte, format Bgr32). The indexes of the pixels of the full bitmap are:

Stride 0: [000] [001] [002] [003] ...
Stride 1: [100] [101] [102] [103] ...
Stride 2: [200] [201] [202] [203] ... 
Stride 3: [300] [301] [302] [303] ... 
...

Stride x starts at x * 100.
Pixel y in stride x starts at x * 100 + y

My Area of Interest starts at [2,3]. It is 2 pixels wide, 3 pixels high.
The BitmapSource has 32 bits per pixel. Every pixel is 4 bytes.
So I want 6 copied RGBA values in a byte array at indexes:

[202] RGBA values at 00..03
[203] RGBA values at 04..07
[302] RGBA values at 08..11
[303] RGBA values at 12..15
[402] RGBA values at 16..19
[403] RGBA values at 20..23

I used the following code:

BitmapSource bmp = ...                                    // 100 by 100 pixels
Int32Rect rect = new Int32Rect(0, 0, 2, 3);               // start at [0,0], 2 wide, 3 high

int bytesPerPixel = (bmp.Format.BitsPerPixel + 7) / 8;    // 4 bytes per pixel
int stride = bmp.PixelWidth * bytesPerPixel;              // 400
byte[] largeBuffer = new byte[10000];                     // large buffer; TODO: proper length
bmp.CopyPixels(areaOfInterest, largeBuffer, stride, 0);

The buffer is not filled as expected, it is filled as follows:

bytes 000 .. 007 filled; bytes 008 .. 399 zero
bytes 400 .. 407 filled; bytes 408 .. 799 zero
bytes 800 .. 807 filled; bytes 808 .. end zero

The strange thing is: if I use a different x,y of the rectangle, the result is still at these locations. I thought maybe parameter Stride is the stride in the destination buffer. And indeed this give me a consecutive array, but the values are still not the ones that I want.

So what should I do, to copy only the pixels that I am interested in, to a consecutive buffer?

2

There are 2 answers

0
Clemens On BEST ANSWER

Besides that using a CroppedBitmap might be easier than copying a rectangle and creating a new bitmap from it, the documentation of the CopyPixels method is misleading or even wrong.

The stride parameter is not meant to hold the stride of the source bitmap, but instead that of the cropped area:

var rect = new Int32Rect(0, 0, 2, 3);

var stride = (rect.Width * bmp.Format.BitsPerPixel + 7) / 8; // here

var buffer = new byte[stride * rect.Height];

bmp.CopyPixels(rect, buffer, stride, 0);
3
trix On

Did you try using a CroppedBitmap?

Your code should be something like:

var croppedImage = new CroppedBitmap(image, new Int32Rect(0, 0, 2, 3));