Bitmap.Lockbytes AccessViolation
-
Hi I'm trying to push a double[,] array of pixel values from a greyscale buffer into a bitmap using lockbit using:
Bitmap bmp;
byte pixVal;
unsafe{bmp = new Bitmap(Width,Height);
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, Width, Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
byte* row;
for (int j = 0; j < Height; j++){
row = (byte*)bmpData.Scan0 + (j * bmpData.Stride);
for (int i = 0; i < Width; i++){
pixVal = (byte)(PixelScaleFactor *
_internalPixelArray[i, j] - PixelOffset));row\[i \* PixelDepth\] = pixVal; row\[i \* PixelDepth + 1\] = pixVal; row\[i \* PixelDepth + 2\] = pixVal; }
}
bmp.UnlockBits(bmpData);}
in which _internalPixelArray is a 1000x1000 array of doubles. The scale factor and pixelOffset have been calculated correctly. When run I get an AccessViolationException when j = 991 and i = 918. Could there be a reason for this? Cheers,
-
Hi I'm trying to push a double[,] array of pixel values from a greyscale buffer into a bitmap using lockbit using:
Bitmap bmp;
byte pixVal;
unsafe{bmp = new Bitmap(Width,Height);
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, Width, Height),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
byte* row;
for (int j = 0; j < Height; j++){
row = (byte*)bmpData.Scan0 + (j * bmpData.Stride);
for (int i = 0; i < Width; i++){
pixVal = (byte)(PixelScaleFactor *
_internalPixelArray[i, j] - PixelOffset));row\[i \* PixelDepth\] = pixVal; row\[i \* PixelDepth + 1\] = pixVal; row\[i \* PixelDepth + 2\] = pixVal; }
}
bmp.UnlockBits(bmpData);}
in which _internalPixelArray is a 1000x1000 array of doubles. The scale factor and pixelOffset have been calculated correctly. When run I get an AccessViolationException when j = 991 and i = 918. Could there be a reason for this? Cheers,
Hi, 1. what are the actual dimensions of the bitmap? 2.
Ylno wrote:
PixelFormat.Format24bppRgb
It would be cleaner to use bmp.PixelFormat 3. which is the exact line that throws the exception? 4. you should make local copies of Width and Height. As it is now, and assuming the code resides inside a Form class, resizing the Form while that code executes could result in havoc. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
-
Hi, 1. what are the actual dimensions of the bitmap? 2.
Ylno wrote:
PixelFormat.Format24bppRgb
It would be cleaner to use bmp.PixelFormat 3. which is the exact line that throws the exception? 4. you should make local copies of Width and Height. As it is now, and assuming the code resides inside a Form class, resizing the Form while that code executes could result in havoc. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
Hi Luc, 1) in this test 1000x1000 but in practice 1024x1024 or 2048x2048 2) using bmp.PixelFormat (see below) 3) the line with
row[i * PixelDepth] = pixVal;
within the inner loop throws. background: I'm parsing grayscale pixel data from an obscure image format used in electron-microscopy. Along with the pixel values I get the Width, Height and PixelDepth. The actual image has been acquired on expensive ccd equipment therefore pixels can be quite deep. I want to be able to display an array of greyscale pixel values (double[,] pixelValues) within a picture box. For this I see no other way than creating a Bitmap/Image in memory and displaying this. It appears .NET does not support deep greyscale therefore I thought for the purposes of display only I could rescale the pixelArray from double[,] to byte[,] and assign each of the RGB channels for a given pixel to the byte value from the temporary byte[,] array. Towards this end my code creates a byte-buffer vector in which I encode a Bitmap. First I write the header in which I define the pixelDepth to be 32. Previously I thought I could use ARGB 32bbp format and push the byte pixelValue in R, G and B a constant in the A channel. This worked:
....... code to write the bitmap header here ........ for (int j = Height - 1; j >= 0; j--) { for (int i = 0; i < Width; i++) { pixVal = (byte)(PixelScaleFactor * (_internalPixelArray[i, j] - PixelOffset)); buffer.writeByte(pixVal); buffer.writeByte(pixVal); buffer.writeByte(pixVal); buffer.writeByte(0); // the alpha channel. } }
I then use the bitmap encoded byte[] buffer to create a memory stream from which I create an image by:Image.FromStream(new System.IO.MemoryStream(imageData))
This works but takes 200ms to encode and display an image. To make things quicker I thought I might be able to by encoding a buffer with a bitmap header as above and not assign the pixel values immediately but instead create a Bitmap using the memory stream method which results in an images with all pixels as zero. Then once I have the Bitmap object used Lockbits to set the pixels. I got to point and got an AccessViolation. I thought my buffer might not be long enough so I backed up a bit and started testing with the code I posted. i.e. create a simple bitmap using Bitmap(width, height) constructor and specify the pixelDepth with the Lockbits method. This way I thought I would be allocat
-
Hi Luc, 1) in this test 1000x1000 but in practice 1024x1024 or 2048x2048 2) using bmp.PixelFormat (see below) 3) the line with
row[i * PixelDepth] = pixVal;
within the inner loop throws. background: I'm parsing grayscale pixel data from an obscure image format used in electron-microscopy. Along with the pixel values I get the Width, Height and PixelDepth. The actual image has been acquired on expensive ccd equipment therefore pixels can be quite deep. I want to be able to display an array of greyscale pixel values (double[,] pixelValues) within a picture box. For this I see no other way than creating a Bitmap/Image in memory and displaying this. It appears .NET does not support deep greyscale therefore I thought for the purposes of display only I could rescale the pixelArray from double[,] to byte[,] and assign each of the RGB channels for a given pixel to the byte value from the temporary byte[,] array. Towards this end my code creates a byte-buffer vector in which I encode a Bitmap. First I write the header in which I define the pixelDepth to be 32. Previously I thought I could use ARGB 32bbp format and push the byte pixelValue in R, G and B a constant in the A channel. This worked:
....... code to write the bitmap header here ........ for (int j = Height - 1; j >= 0; j--) { for (int i = 0; i < Width; i++) { pixVal = (byte)(PixelScaleFactor * (_internalPixelArray[i, j] - PixelOffset)); buffer.writeByte(pixVal); buffer.writeByte(pixVal); buffer.writeByte(pixVal); buffer.writeByte(0); // the alpha channel. } }
I then use the bitmap encoded byte[] buffer to create a memory stream from which I create an image by:Image.FromStream(new System.IO.MemoryStream(imageData))
This works but takes 200ms to encode and display an image. To make things quicker I thought I might be able to by encoding a buffer with a bitmap header as above and not assign the pixel values immediately but instead create a Bitmap using the memory stream method which results in an images with all pixels as zero. Then once I have the Bitmap object used Lockbits to set the pixels. I got to point and got an AccessViolation. I thought my buffer might not be long enough so I backed up a bit and started testing with the code I posted. i.e. create a simple bitmap using Bitmap(width, height) constructor and specify the pixelDepth with the Lockbits method. This way I thought I would be allocat
Ylno wrote:
- using bmp.PixelFormat (see below)
I don't see it! 5. We haven't discussed PixelDepth yet. Is it 3? or 24? or ...? 6. I have no idea why it would work well for most of the image, and then throw for i=918; I see no reasonable combination of parameter values that would work well till row 918 and then fail. 7. you don't need to construct a bitmap header, you can let .NET do that for you as Bitmap has a constructor that takes a pixel array pointer (raw pixels, no metadata). Check MSDN, and make sure you read the remarks. 8. FWIW: I would never store image data in a 2D array, a linear array is what I prefer. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
-
Ylno wrote:
- using bmp.PixelFormat (see below)
I don't see it! 5. We haven't discussed PixelDepth yet. Is it 3? or 24? or ...? 6. I have no idea why it would work well for most of the image, and then throw for i=918; I see no reasonable combination of parameter values that would work well till row 918 and then fail. 7. you don't need to construct a bitmap header, you can let .NET do that for you as Bitmap has a constructor that takes a pixel array pointer (raw pixels, no metadata). Check MSDN, and make sure you read the remarks. 8. FWIW: I would never store image data in a 2D array, a linear array is what I prefer. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
Thanks again for getting back to me on this subject.
Luc Pattyn wrote:
I don't see it!
sorry, I simply meant my discussion about having pixels stored in a double[,] array but not really caring with what pixel-depth the bitmap is since is it is only for display purposes not calculation. 5. I guess "see above" 6. Same here. Other than GDI+ not fully supporting 16bpp. There is very little on the net regarding using 16bpp greyscale Bitmaps and what there is is old and contradictory. 7. I went with your suggestion here however I couldn't get things to work using PixelFormat.Format16bppGrayScale. Instead I went with 24bppRGB instead. For the benefit of others see code below. 8. yes, I was aware of this (once) but could you remind me of the benefits? My reasons for using 2D are: 8.1 - the dimensions are carried with the data in array.GetLength(int dimension). The vector approach requires lacks this. 8.2 - the array is analogous to the raster image and therefore conceptually simpler. 8.3 - this is my first 'real' stab at writing an heavy image processing application. Aren't the 2D and 1D arrays arranged the same in memory? Thanks again for your help. Pushed me towards a solution. :) and finally here's what I came up with:
// DEFINE AN IMAGE ALONG WITH ITS DIMENSIONS // 24bpp -> three channels each of one byte depth int width = 1000; int height = 1000; byte\[\] pixelVector = new byte\[width \* height \* 3\]; // FILL THE PIXELS WITH SOMEDATA AVOIDING OVERFLOW for (int loop = 0; loop < (width \* height ); loop+=3) { pixelVector\[loop\] = (byte)((loop \* 256) / (width \* height )); pixelVector\[loop + 1\] = (byte)((loop \* 256) / (width \* height )); pixelVector\[loop + 2\] = (byte)((loop \* 256) / (width \* height )); } // CREATE THE DESTINATION BITMAP WITH THE REQUIRED PIXEL-DEPTH Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb); // LOCK IT'S DATA FOR EXCLUSIVE ACCESS BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat); unsafe { // GET THE ADDRESS OF THE FIRST PIXEL IN MEMORY byte\* row = (byte\*)bmpData.Scan0; // LOOP THROUGH THE PIX
-
Thanks again for getting back to me on this subject.
Luc Pattyn wrote:
I don't see it!
sorry, I simply meant my discussion about having pixels stored in a double[,] array but not really caring with what pixel-depth the bitmap is since is it is only for display purposes not calculation. 5. I guess "see above" 6. Same here. Other than GDI+ not fully supporting 16bpp. There is very little on the net regarding using 16bpp greyscale Bitmaps and what there is is old and contradictory. 7. I went with your suggestion here however I couldn't get things to work using PixelFormat.Format16bppGrayScale. Instead I went with 24bppRGB instead. For the benefit of others see code below. 8. yes, I was aware of this (once) but could you remind me of the benefits? My reasons for using 2D are: 8.1 - the dimensions are carried with the data in array.GetLength(int dimension). The vector approach requires lacks this. 8.2 - the array is analogous to the raster image and therefore conceptually simpler. 8.3 - this is my first 'real' stab at writing an heavy image processing application. Aren't the 2D and 1D arrays arranged the same in memory? Thanks again for your help. Pushed me towards a solution. :) and finally here's what I came up with:
// DEFINE AN IMAGE ALONG WITH ITS DIMENSIONS // 24bpp -> three channels each of one byte depth int width = 1000; int height = 1000; byte\[\] pixelVector = new byte\[width \* height \* 3\]; // FILL THE PIXELS WITH SOMEDATA AVOIDING OVERFLOW for (int loop = 0; loop < (width \* height ); loop+=3) { pixelVector\[loop\] = (byte)((loop \* 256) / (width \* height )); pixelVector\[loop + 1\] = (byte)((loop \* 256) / (width \* height )); pixelVector\[loop + 2\] = (byte)((loop \* 256) / (width \* height )); } // CREATE THE DESTINATION BITMAP WITH THE REQUIRED PIXEL-DEPTH Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb); // LOCK IT'S DATA FOR EXCLUSIVE ACCESS BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bmp.PixelFormat); unsafe { // GET THE ADDRESS OF THE FIRST PIXEL IN MEMORY byte\* row = (byte\*)bmpData.Scan0; // LOOP THROUGH THE PIX
Hi again, 5. Now I see it. 6. Yes, sthe formats aren't documented well, and the RGB approach is justified. 7. The suggestion actually was and is to fill an array with RGB bytes, then use the constructor rather than your loops, should go faster. Drawback is you have to keep the array alive, or make another copy of the bitmap. 8. Having only one pointer or one index ("loop") typically is simpler and faster than having two indexes. 9. your code as shown wouldn't work, why are you storing bytes thrice, the last two bytes will throw an IndexOutOfBoundsException; storing everything thrice isn't useful if you have an explicit copy loop anyway, it only makes sense when using the IntPtr-based constructor. 10. I'd like to know what performance you are getting when it works, I recall you used to have 200msec (for 1000*1000?). :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
-
Hi again, 5. Now I see it. 6. Yes, sthe formats aren't documented well, and the RGB approach is justified. 7. The suggestion actually was and is to fill an array with RGB bytes, then use the constructor rather than your loops, should go faster. Drawback is you have to keep the array alive, or make another copy of the bitmap. 8. Having only one pointer or one index ("loop") typically is simpler and faster than having two indexes. 9. your code as shown wouldn't work, why are you storing bytes thrice, the last two bytes will throw an IndexOutOfBoundsException; storing everything thrice isn't useful if you have an explicit copy loop anyway, it only makes sense when using the IntPtr-based constructor. 10. I'd like to know what performance you are getting when it works, I recall you used to have 200msec (for 1000*1000?). :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
Fed up by FireFox memory leaks I switched to Opera and now CP doesn't perform its paste magic, so links will not be offered. Sorry.
Hi Luc, You spotted the mistake! Was just making sure you were paying attention. There was a factor of 3 missing from my display-array [width*height*3] and the increment on the loop is 3 not 1 (loop+=3 not loop++). I'm filling the pixels thrice to have RGB values each the same. This gives me a greyscale and also some flexibility in that I can drop values from one of the channels and achieve colour a colour overlay effect with the same image. With the code above using a 1000x1000 array input array ( i.e. a (1000x1000)x3 display array) I get 19ms total runtime for creating the display array, making the pixel assignments and displaying in a picture box! Nice. Cheers for your help.