Marshal.Copy method throws AccessViolationException in C#.NET - c#

I am working on a C# application that would display live images from a camera.
The problem I am facing with the following code snippet is that, I get AccessViolationException in Marshal.Copy when running this function executed continuously in a thread. But, this runs successfully when run once (I get a single static image). I guess it has to do with some memory corruption issue. Any idea/suggestions on how to deal with this problem?
private Image ByteArrayToImage(byte[] myByteArray)
{
if (myByteArray != null)
{
MemoryStream ms = new MemoryStream(myByteArray);
int Height = 504;
int Width = 664;
Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
return null;
}

It looks to me like you are always trying to copy the number of bytes myByteArray.Length to the bitmap buffer.
You are not checking that the bitmap buffer is in fact as big as that - so are probably writing off the end of the bitmap buffer.
Try checking if myByteArray.Length is ever greater than bmpData.Stride x bmp.Height
If this is the case you'll need to relook at the assumptions you've made with your hard coded values for width, height and pixel format.

You shouldn't copy the entire image at once. The memory allocation of the bitmap object might not be what you expect. For example the first scan line may be stored last in memory, which would mean that the data for the second scan line would end up outside the allocated memory area for the bitmap object. Also there may be padding between the scan lines to place them on an even address.
Copy one line at a time, using bmpData.Stride to find the next scan line:
int offset = 0;
long ptr = bmpData.Scan0.ToInt64();
for (int i = 0; i < Height; i++) {
Marshal.Copy(myByteArray, offset, new IntPtr(ptr), Width * 3);
offset += Width * 3;
ptr += bmpData.Stride;
}

I've personally seen some heap corruptions (debugging the crash dumps) because .Length was used.
As in:
IntPtr ptr = bitmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, pixeldata.Length);
The solution to the heap corruption was to do calculate the .Length differently:
IntPtr ptr = bitmapdata.Scan0;
int bytes = Math.Abs(bitmapdata.Stride) * bmp.Height;
Marshal.Copy(pixeldata, 0, ptr, bytes);
bytes and .Length had 1 byte difference, resulting in the heap corruption.
Math.Abs was taken directly from the example of Microsoft.
Because the Stride can be negative for a bottom-up bitmap.
Example microsoft: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#Examples
(+ don't forget the .Unlock and add it in a try-finally statement.)

Answer for me: forgot to ->
// Unlock the bits right after Marshal.Copy
bmp.UnlockBits(bmpData);
Has anyone figured this out? This is about the fourth page without an answer. Using the exact code from msdn: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx which is:
Bitmap bmp = new Bitmap("c:\\picture.jpg");
// 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.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
//This causes read or write protected memory
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
This doesn't work in optimize mode and running as exe not in IDE. Any ideas
I tried to put this is a new project and if I attached to process when i push a button, and press this button multiple times the error occurs, but in my code I'm only calling once, either way not sure why the error.

Maybe
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
has invalid argument for write only - try ReadWrite, or ReadOnly in ImageLockMode?
Maybe that helps.

I was searching a bit and if we skip the possibility that the array is not of the proper size, we end in Remarks for BitmapData.Stride Property:
The stride is the width of a single row of pixels (a scan line),
rounded up to a four-byte boundary. If the stride is positive, the
bitmap is top-down. If the stride is negative, the bitmap is
bottom-up.
So, maybe we should do it this way:
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0 +
( bmpData.Stride >= 0 ? 0 : bmpData.Stride*(bmp.Height-1) ),
myByteArray.Length);
But I wondered: We have created the bitmap: new Bitmap(Width, Height, PixelFormat.Format24bppRgb); ...so, how could it possibly be negative?
Time for ILSpy:
public Bitmap(int width, int height, PixelFormat format)
{
IntPtr zero = IntPtr.Zero;
int num = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, (int)format, NativeMethods.NullHandleRef, out zero);
if (num != 0)
{
throw SafeNativeMethods.Gdip.StatusException(num);
}
base.SetNativeImage(zero);
}
// System.Drawing.SafeNativeMethods.Gdip
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);
And it pointed me here and here:
Bitmap(
[in] INT width,
[in] INT height,
[in] INT stride,
[in] PixelFormat format,
[in] BYTE *scan0
);
stride [in]
Type: INT
Integer that specifies the byte offset between the beginning of one scan line and the next. This is usually (but not necessarily) the
number of bytes in the pixel format (for example, 2 for 16 bits per
pixel) multiplied by the width of the bitmap. The value passed to this
parameter must be a multiple of four.
What does it mean to pass 0? Don't know, couldn't find it. Can anybody? Can the bitmap be created with negative stride? (by .NET new Bitmap(Width, Height, PixelFormat.Format24bppRgb)). In any case, we need to at least check BitmapData.Stride.

I found the rawStride formula in the code example for the BitmapSource Class. It seems worth trying creating an array using the code below and attempting to execute your copy method multiple times without it bombing. If you can there is a fair chance that this is an array sizing problem. If the data from your camera doesn't match the size of the bitmap in memory, you'll probably have to copy the data line by line.
private byte[] CreateImageByteArray(int width, int height, PixelFormat pixelFormat)
{
int rawStride = (width * pixelFormat.BitsPerPixel + 7) / 8;
byte[] rawImage = new byte[rawStride * height];
return rawImage;
}
The other thing you should do is make sure that Bitmap object is being Disposed properly when you finish with it. I have occasionally seen weird results with objects that have been operated on with unmanaged code and not cleaned up afterwards.
Also, transiting objects across threads can be tricky sometimes. See How to: Make Thread-Safe Calls to Windows Forms Controls and Thread-Safe Calls Using Windows Form Controls in C# article.

Lets try to change ThreadApartmentState to Single Threaded.
Also, check for cross-thread operations causing this errors.

It happens to me...
bmpData.Scan0 point to the first line of the bitmap and
bmpData.Stride is the number of bytes of a line. It is most of the time a negative number.
it means that the second line of the bitmap is at the address
bmpData.Scan0 - MAth.abs(bmpData.Stride)
the n line of the bitmap is at the address
bmpData.Scan0 - n * Math.Abs(bmpData.Stride)
so if you copy from bmpData.Scan0 more than one line , you have an exception

Related

What does LockBits/UnlockBits do in c#?

I have a method in c# that the only thing it does its LockBits, and then UnlockBits, and the images(input/output, transformed to byte arrays) are different. The one from output has less 100 and something bytes than the one from the input. This happens only with .jpg files. And checking the files in HxD I came to the understanding that it´s removing a part of the header, the exif signature to be exact. But I don't know how and why.
Does someone know what this is doing?
Here's the code:
public Image Validate (image){
BitmapData original = null;
Bitmap originalBMP = null;
try{
originalBMP = image as Bitmap;
original = originalBMP.LockBits(new Rectangle(0, 0,
originalBMP.Width, originalBMP.Height),
ImageLockMode.ReadWrite,
originalBMP.PixelFormat);
originalBMP.UnlockBits(original);
}catch{}
return image;
}
Calling Bitmap.LockBits() followed by Bitmap.UnlockBits() does nothing.
The behavior you observe is because of loading a JPEG image, and then saving it again. JPEG uses a lossy algorithm. So what happens:
You load the JPEG from disk
The JPEG data gets decoded into individual pixels with color information, i.e. a bitmap
You save the bitmap again in the JPEG format, resulting in a different file than #1
You also potentially lose metadata that was present in the JPEG file in doing so. So yes, the file is different and probably smaller, because every time you do this, you lose some pixel data or metadata.
Lockbits/Unlockbits are used to allow the program to manipulate the image data in memory. Nothing more, nothing less. See also the documentation for those methods.
Use the LockBits method to lock an existing bitmap in system memory so that it can be changed programmatically. You can change the color of an image with the SetPixel method, although the LockBits method offers better performance for large-scale changes.
A Rectangle structure that specifies the portion of the Bitmap to lock.
Example:
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// 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.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}

Copying bytes from Bitmap to byte array and back with Marshall.Copy doesn't work right

I want to copy bytes with Marshall.Copy. My code work, but bytes is strange for me. I think I got indexes, not real bytes. If this compute and saves back, I got different colors in the image with a lot bigger byte size (image size is the same).
Bitmap bmp = new Bitmap(imagepath);
Width = bmp.Width;
Height = bmp.Height;
byte[] data;
BitmapData bdata;
switch (bmp.PixelFormat)
{
case PixelFormat.Format8bppIndexed:
{
data = new byte[Width * Height];
bdata = bmp.LockBits(new Rectangle(0, 0, Width, Height),ImageLockMode.ReadOnly, bmp.PixelFormat);
Marshal.Copy(bdata.Scan0, data, 0, data.Length);
bmp.UnlockBits(bdata);
break;
}
}
Save image from bytes:
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(data, 0, pNative, Width * Height);
bmp.UnlockBits(bmData);
bmp.Save("output.gif",ImageFormat.Gif); //without format, have same problem
If I read color from first pixel, I got: Color [A=0, R=0, G=0, B=2]. Is this really color in the input image?
I don't know, why the output is soo different from the input. Where is the problem?
Example from input and output (sorry for small images):
You did not show how you created the second bmp for reloading the bytes. But the PixelFormat is 8bbpIndexed, which means that your data array will contain palette indices instead of direct color information. When you create your second bmp with 8 bit pixel format it will use a default palette, which may be different from the original one.
So you must save the bmp.Palette of the first image, then use it to restore the actual colors of your second bmp instance.
Update: Though you can set the palette entries one by one, it has no effect. Because bmp.Palette is a property. You must set the whole palette instead. Additionally, here is a post with indexed bitmap manipulation (see the ConvertPixelFormat) method.

how to pass bitmap to c++ dll?

The program has no tip errors ,But there is no any picture display in c++ form,Tested the simple data types, such as int, can be normal delivery,please give me a simple example ,tell me what to do
C# Code
[DllImport("dllTestForm.dll", EntryPoint = "showFormC")]
static extern void testShowFormC(byte[] photo,int len);
private void button6_Click(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)Image.FromFile(#"d:\1\1.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
testShowFormC(rgbValues, bytes);
}
C++ Code
void __stdcall showFormC(byte *photo,int len)
{
ThelloWorld *a=new ThelloWorld(Application); //winForm,display photo
a->ImageEnView2->IO->LoadFromBuffer(photo,0,len);
a->Show();
}
Buffer got from C# code is not completed.
You only get bitmap pixel data but not header. Header contains info to describe pixel data (8 bit, 16 bit, 24 bit, color palette and so on).
For a->ImageEnView2->IO->LoadFromBuffer(photo,0,len);, I guess you are using TImageEnIO component. From help http://www.imageen.com/help/ImageEn/TImageEnIO.LoadFromBuffer.html, why not pass whole jpeg file data to LoadFromBuffer ?
You can use Bitmap.Save() method to convert it to a memory stream and then send it to C++ dll.
image.Save(imageMemoryStream, System.Drawing.Imaging.ImageFormat.Png);
Then, in C++ dll use decoding such as cv::imdecode() method to get the image.
Details are here.

Crop image using memcpy fails on 24bpp image

I’m trying to crop a 24bpp image using memcpy like I read here: cropping an area from BitmapData with C#. The problem I’m having is that it only works when my sourceImage is 32bpp. It gives me a corrupt image when my sourceImage is 24bpp.
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static unsafe extern int memcpy(byte* dest, byte* src, long count);
static void Main(string[] args)
{
var image = new Bitmap(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\adsadas.png");
//Creates a 32bpp image - Will work eventhough I treat it as a 24bpp image in the CropBitmap method...
//Bitmap newBitmap = new Bitmap(image);
//Creates a 24bpp image - Will produce a corrupt cropped bitmap
Bitmap newBitmap = (Bitmap)image.Clone();
var croppedBitmap = CropBitmap(newBitmap, new Rectangle(0, 0, 150, 150));
croppedBitmap.Save(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\PieceOfShit.png", ImageFormat.Png);
Console.ReadLine();
}
static public Bitmap CropBitmap(Bitmap sourceImage, Rectangle rectangle)
{
Console.WriteLine("Bits per pixel of sourceImage: {0}", Image.GetPixelFormatSize(sourceImage.PixelFormat));
var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format24bppRgb);
var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
memcpy(croppedImagePointer, sourceImagePointer, croppedBitmapData.Stride * rectangle.Height);
}
sourceImage.UnlockBits(sourceBitmapdata);
croppedImage.UnlockBits(croppedBitmapData);
return croppedImage;
}
}
I’m very confused, because the only thing I’m changing is the sourceImage PixelFormat, not any of the code in the CropBitmap method. So I always call LockBits using 24bpp Pixelformat, even if the sourceImage is 32bpp.
I’ve tried different methods of calculating the number of bytes I’m copying but everything resulted in more or less the same corrupted image.
Any help is appreciated!
You are trying to copy the data as if it was one continuous block, but it isn't.
The image data is arranged in scan lines, but as you are selecting a part of the image, you don't want all the data from each scan line, you only want the data that represents the pixels that you have selected. A scan line contains the data for the pixels that you specified when you called LockBits, but also data for the pixels outside that area.
The Stride value is the difference in memory address from one scan line to the next. The Stride value may also include padding between the scan lines. Note also that the Stride value can be negative, which happens when the image data is stored upside down in memory.
You want to copy the relevant data from one line of the source image to the line in the destination image. As there can be gaps both in the source data and destination data, you can't copy the data as a single chunk of data.
You would need to loop through the lines and copy each line separately, I haven't tested this code, but something like this:
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
int width = rectange.Width * 3; // for 24 bpp pixel data
for (int y = 0; y < rectangle.Height; y++) {
memcpy(croppedImagePointer, sourceImagePointer, width);
sourceImagePointer += sourceBitmapdata.Stride;
croppedImagePointer += croppedBitmapData.Stride;
}

Can I copy this array any faster?

Is this the absolute fastest I could possibly copy a Bitmap to a Byte[] in C#?
If there is a speedier way I am dying to know!
const int WIDTH = /* width */;
const int HEIGHT = /* height */;
Bitmap bitmap = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format32bppRgb);
Byte[] bytes = new byte[WIDTH * HEIGHT * 4];
BitmapToByteArray(bitmap, bytes);
private unsafe void BitmapToByteArray(Bitmap bitmap, Byte[] bytes)
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, WIDTH, HEIGHT), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
fixed(byte* pBytes = &bytes[0])
{
MoveMemory(pBytes, bitmapData.Scan0.ToPointer(), WIDTH * HEIGHT * 4);
}
bitmap.UnlockBits(bitmapData);
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
private static unsafe extern void MoveMemory(void* dest, void* src, int size);
Well, using Marshal.Copy() would be wiser here, that at least cuts out on the (one time) cost of looking up the DLL export. But that's it, they both use the C runtime memcpy() function. Speed is entirely throttled by the RAM bus bandwidth, only buying a more expensive machine can speed it up.
Beware that profiling is tricky, accessing the bitmap data the first time causes page faults to get the pixel data into memory. How long that takes is critically dependent on what your hard disk is doing and the state of the file system cache.
I'm reading into this that you're looking to do pixel manipulation of a bitmap.
So logically, you're wanting to access the pixels as an array in order to do this.
The problem your facing is fundamentally flawed, and the other guys didn't pick up on this - and maybe this is something they can take away too?
Basically, you've gotten so far to be messing with pointers from bitmaps, I'm going to give you one of my hard earned "secrets".
YOU DON'T NEED TO COPY THE BITMAP INTO AN ARRAY. Just use it as it is.
Unsafe pointers are your friend in this case. When you hit "bitmapData.Scan0.ToPointer()" you missed the trick.
So long as you have a pointer to the first pixel, and the Stride, and you're aware of the number of bytes per pixel, then you're on to a winner.
I define a bitmap specifically with 32 bits per pixel (memory efficient for UInt32 access) and I get a pointer to first pixel. I then treat the pointer as an array, and can both read and write pixel data as UInt32 values.
Code speaks louder than words. have a look.
I salute you for making it this far!
This is untested code, but much is copied from my source.
public delegate void BitmapWork(UInt32* ptr, int stride);
/// <summary>
/// you don't want to forget to unlock a bitmap do you? I've made that mistake too many times...
/// </summary>
unsafe private void UnlockBitmapAndDoWork(Bitmap bmp, BitmapWork work)
{
var s = new Rectangle (0, 0, bmp.Width, bmp.Height);
var locker = bmp.LockBits(new Rectangle(0, 0, 320, 200), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
var ptr = (UInt32*)locker.Scan0.ToPointer();
// so many times I've forgotten the stride is expressed in bytes, but I'm accessing with UInt32's. So, divide by 4.
var stride = locker.Stride / 4;
work(ptr, stride);
bmp.UnlockBits(locker);
}
//using System.Drawing.Imaging;
unsafe private void randomPixels()
{
Random r = new Random(DateTime.Now.Millisecond);
// 32 bits per pixel. You might need to concern youself with the Alpha part depending on your use of the bitmap
Bitmap bmp = new Bitmap(300, 200, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
UnlockBitmapAndDoWork(bmp, (ptr, stride) =>
{
var calcLength = 300 * 200; // so we only have one loop, not two. It's quicker!
for (int i = 0; i < calcLength; i++)
{
// You can use the pointer like an array. But there's no bounds checking.
ptr[i] = (UInt32)r.Next();
}
});
}
I would also look at System.Buffer.BlockCopy. This function is also very fast and it might be competetive in this setup as you are copying from managed to managed in your case. Unfortunately I can not provide some performance tests.
I think this is faster (I actually used it):
// Bitmap bytes have to be created via a direct memory copy of the bitmap
private byte[] BmpToBytes_MemStream (Bitmap bmp)
{
MemoryStream ms = new MemoryStream();
// Save to memory using the Jpeg format
bmp.Save(ms, ImageFormat.Jpeg);
// read to end
byte[] bmpBytes = ms.GetBuffer();
bmp.Dispose();
ms.Close();
return bmpBytes;
}
Original Source

Categories