I am receiving the following exception:
Exception thrown:
'System.AccessViolationException' in System.Drawing.dll
When calling the Save function of a Bitmap. The procedure works fine the first time around, but subsequent calls throw this exception.
My application takes a single long image and vertically tiles it out into several separate images. I do this by first breaking out the whole image into bytes, then in a Parallel.For loop I generate the bitmap from a subset byte array.
// Generate Bitmap from width, height and bytes
private Bitmap GenerateBitmap(int width, int height, byte[] bytes)
{
Bitmap bmp = new Bitmap(width, height, Stride(width),
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0));
bmp.SetPalette();
return bmp;
}
That is the bitmap generation routine.
Here is the loop body that calls it.
Parallel.For(0, tileCount, i =>
{
byte[] bytes = new byte[imageWidth * tileHeight];
for (int j = 0; j < bytes.Length; j++)
{
bytes[j] = imageBytes[j + (imageWidth * (tileHeight * i))];
}
arr[i] = GenerateBitmap(imageWidth, tileHeight, bytes);
});
And here is the code elsewhere that the exception is thrown.
foreach(Bitmap tile in pattern.Tiles)
{
Console.WriteLine("Creating Tile " + count);
using (Bitmap bmp = new Bitmap(tile))
{
bmp.Save(Globals.patternOutputPath + "tile_" + count + ".png");
}
count += 1;
}
Where Tiles is a property of the pattern that calls the for loop function (which returns a list of Bitmaps).
I am assuming I'm missing some clean up somewhere in here.
Additional info: all images (input and output) are 256 (index) color format.
Edit: The comments below address the problem at hand, and I THINK I've solved the problem. I changed the GenerateBitmap routine to the following and am no longer getting this exception, but I have some more testing to do.
private Bitmap GenerateBitmap(int width, int height, byte[] bytes)
{
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(bytes, 0, bmpData.Scan0, bytes.Length);
bmp.UnlockBits(bmpData);
return bmp;
/*Bitmap bmp = new Bitmap(width, height, Stride(width),
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0));
bmp.SetPalette();
return bmp;*/
}
Related
I need to save a intraoral x-ray image, I'm try to use the 8bppIndexed pixel format, and using this code to save:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\\Users\\WIM\\Desktop\\teste\\teste\\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
And with this line Marshal.Copy(data, 0, pixelStartAddress, data.Length); I receive the AcessViolationException.
I'm not used to c# so I really don't undertand why I got this error. What I read is that maybe the ptr has been in use, but if I use other pixelFormat, like 16bppRgb565, I can capture the image and transform to grayScale, but the image return deformed. Like this
I'm using an array of short because It's the data I received from the sdk I received from the company we bought the sensor;
In my researches, I found the 16bppGrayScale is a good option, but the GDI+ don't have it encoded yet. But we can make a DICOM for this, but we need to run the application on nootbooks with a hardware not that good, so for that I'm trying to use the 8bppIndexed pixel format.
short[] data the type of data is short, while it should be byte[]
but I think what actually causing the problem is: data.Length should be equal to picData.Stride * pic.Height and in your case, it looks like it is not.
One issue is that you are dealing with unmanaged code. The other is that the exception handling is extremely questionable.
try
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\\Users\\WIM\\Desktop\\teste\\teste\\teste.jpeg");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
There are a number of issues in that code:
Use of Disposeable resoruces without using
The code uses the class Bitmap which needs disposing, but forgoes the using statement. while there is a call to Dispose(), it will not be executed in a exception case. Using is there for that precise
Catching Exception
Catching Exception for anything but logging and letting it go on to kill the process, is a deadly sin of Exception handling. Swallowing anything but a Vexing or Exogenous exception sets you up for more and less undertanable followup exceptions.
There are two articles on proper exception handling I would advise you to read. They explain the things much better then I could:
https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/
https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET
Ignoring any exception can cause more and less understandable follwup errors. Especially if you ignore errors regarding Arrays, you can easily cause a Access Violation Exception.
Note that a Access Violation Exception is good example for a Fatal Exception itself. If you have one, it is safer to let the process die!
using(Bitmap pic = new Bitmap(widht, height, PixelFormat.Format8bppIndexed)){
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic = ConvertToGrayscale(pic);
pic.Save("C:\\Users\\WIM\\Desktop\\teste\\teste\\teste.jpeg");
}
Asuming the Access Violation was not caused by ignoring another Exception:
These 3 lines are the unamanaged code:
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
I try to avoid unmanaged code so I can not truly help you debugging it. But I noticed that you use data twice - but it seems not defined anywhere in this code.
I dicided to answer my own question to make the post more easy to read.
I managed to find a solution through this post: Generate 16-bit grayscale BitmapData and save to file
So please vote on the answer to make more people find the solution more easily.
And the code I'm using now is like this now:
private void SaveImage(short[] data, int widht, int height)
{
try
{
Bitmap pic = new Bitmap(widht, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
//Marshal.Copy(data, 0, pixelStartAddress, data.Length);
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
//SaveBmp(pic, Path.Combine(Directory.GetCurrentDirectory(), "imagem"));
SaveBmp(pic, "C:\\Users\\WIM\\Desktop\\teste\\teste\\teste.bmp");
pic.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);
BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);
bmp.UnlockBits(bitmapData);
FileStream stream = new FileStream(path, FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
stream.Close();
}
private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;
switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;
case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}
return pixelFormats;
}
So now we change the 16bppGrayScale to Gray16.
To use that, you will need to add the reference to the "PresentationCore" on your project.
That you can use then
System.Windows.Media.Format
And anothers objects like BitmapSource.
I am trying to save jpeg image from buffer array of RGBA.
I tried this code
byte[] buffer = new byte[m_FrameProps.ImgSize];
Marshal.Copy(m_BM.BackBuffer, buffer, 0, m_FrameProps.ImgSize); //m_BM is WriteableBitmap
using (MemoryStream imgStream = new MemoryStream(buffer))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(imgStream))
{
image.Save(m_WorkingDir + "1", ImageFormat.Jpeg);
}
}
But I am getting run-time error: "An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll
Additional information: Parameter is not valid."
I tried also to create bitmap and then to use JpegBitmapEncoder
Bitmap bitmap;
using (var ms = new MemoryStream(buffer))
{
bitmap = new Bitmap(ms);
}
But I am getting the same error.
I guess it is because the alpha.
How should I do it? Do I need to loop the values and copy without alpha?
It is not possible to construct an image from an array of pixel data alone. At minimum pixel format information and image dimensions would also be required. This means any attempt to create a Bitmap directly from an ARGB array using streams will fail, the Image.FromStream() and Bitmap() methods both require that the stream contain some kind of header information to construct an image.
That said, given that you appear to know the dimensions and pixel format of the image you wish to save you can use the following method:
public void SaveAsJpeg(int width, int height, byte[] argbData, int sourceStride, string path)
{
using (Bitmap img = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
{
BitmapData data = img.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, img.PixelFormat);
for (int y = 0; y < height; y++)
{
Marshal.Copy(argbData, sourceStride * y, data.Scan0 + data.Stride * y, width * 4);
}
img.UnlockBits(data);
img.Save(path, ImageFormat.Jpeg);
}
}
What is the purpose of ImageLockMode in Bitmap.LockBits?
For ReadOnly the documentation only states that
ReadOnly: Specifies that a portion of the image is locked for reading.
But the following code proves, that this is not true.
I know the question has been asked before, this time I try with some actual code as I couldn't find an answer anywhere else.
If I run the following code, it behaves exactly as explained in the answer.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace LockBits_Trials
{
class Program
{
static readonly Random rnd = new Random(42);
static void Main(string[] args)
{
Bitmap bmp_fromFile = new Bitmap("example.png");
Bitmap bmp_fromCtor = new Bitmap(100, 100, PixelFormat.Format24bppRgb);
marshalCopy(bmp_fromFile, "result_marshalCopy_fromFile.png");
marshalCopy(bmp_fromCtor, "result_marshalCopy_fromCtor.png");
usePointer(bmp_fromFile, "result_usePointer_fromFile.png");
usePointer(bmp_fromCtor, "result_usePointer_fromCtor.png");
}
private static unsafe void usePointer(Bitmap bmp, string filename)
{
ImageLockMode mode = ImageLockMode.ReadOnly;
//code from turgay at http://csharpexamples.com/fast-image-processing-c/
if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
throw new Exception();
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), mode, bmp.PixelFormat);
int bytesPerPixel = 3; int heightInPixels = bitmapData.Height; int widthInBytes = bitmapData.Width * bytesPerPixel;
byte* ptrFirstPixel = (byte*)bitmapData.Scan0;
for (int y = 0; y < heightInPixels; y++) {
byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel) {
currentLine[x] = (byte)rnd.Next(0, 255);
currentLine[x + 1] = (byte)rnd.Next(0, 255);
currentLine[x + 2] = (byte)rnd.Next(0, 255);
}
}
bmp.UnlockBits(bitmapData);
bmp.Save(filename, ImageFormat.Png);
}
private static unsafe void marshalCopy(Bitmap bmp, string filename)
{
ImageLockMode mode = ImageLockMode.ReadOnly;
if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
throw new Exception();
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), mode, bmp.PixelFormat);
IntPtr ptrFirstPixel = bitmapData.Scan0;
int totalBytes = bitmapData.Stride * bitmapData.Height;
byte[] newData = new byte[totalBytes];
for (int i = 0; i < totalBytes; i++)
newData[i] = (byte)rnd.Next(0, 255);
Marshal.Copy(newData, 0, ptrFirstPixel, newData.Length);
bmp.UnlockBits(bitmapData);
bmp.Save(filename, ImageFormat.Png);
}
}
}
The pictures result_marshalCopy_fromFile.png and result_usePointer_fromFile.png both contain the original image, so nothing was overwritten (and no Exception thrown!).
The other two pictures contain the random noise, that was written to them while being locked.
I did not do further test to confirm the behaviour of parallel write access, as I don't do that anyways.
It's not a duplicate, but strongly related:
Does Bitmap.LockBits “pin” a bitmap into memory?
As you've observed, once you have gained access to the raw data pointer, there's nothing to stop you from writing to that memory, regardless of the lock type you requested. The lock type is only really useful in 2 situations:
1) If multiple bits of code request locks simultaneously, only one write lock is allowed to be issued at a time, while read locks can be shared. This, of course, depends on the code acquiring the locks using them appropriately.
2) Not all locks are directly backed by the Bitmap memory. In your example, it is because you created a memory Bitmap and then requested a lock in the same pixel format. However a bitmap may represent other GDI+ objects, such as pixels owned by a device context. Additionally, if you request a pixel format other than that of the source, it may have to be converted. In either of those cases, when a read lock is requested, GDI+ may have to pull a copy of the bitmap from the true source and give it to you in your requested pixel format. If you were to modify that copy, it would not get written back to the source context. If you requested a write lock, GDI+ would know to copy the pixels back to the source once you release the lock.
I've attempted two different methods to achieve this, the first being an Android-style method and the second being an OpenGL style method. From my activity, I create a view which contains the OpenGL (1.1) code.
The first method (android):
Bitmap b = gameView.GetDrawingCache (true); // this is always null
And the second method (opengl):
public Bitmap GrabScreenshot()
{
int size = Width * Height * 4;
byte[] bytes = new byte[size];
GL.ReadPixels<byte>(0, 0, Width, Height, All.Rgba, All.UnsignedByte, bytes);
Bitmap bmp = BitmapFactory.DecodeByteArray (bytes, 0, size);
return bmp;
}
I have not tested this code. I was thinking you might be able to use it as a guide.
How about trying something like this (derived from: OpenTK Forums):
public Bitmap GrabScreenshot()
{
Bitmap bmp = new Bitmap(Width, Height);
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(otkViewport.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.Finish();
GL.ReadPixels(0, 0, this.otkViewport.Width, this.otkViewport.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
I believe a problem might occur due to the formatting of the bytes. In the example, they explicitly state the beginning to the array of data with
data.Scan0
However, you just sends in a byte array.
Here a version that works on Xamarin.Android:
private static Bitmap GraphicsContextToBitmap(int width, int height)
{
GL.Flush();
GL.PixelStore (PixelStoreParameter.PackAlignment, 1);
var bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
var data = bitmap.LockPixels();
GL.ReadPixels(0, 0, width, height, PixelFormat.Rgba, PixelType.UnsignedByte, data);
GL.Finish();
bitmap.UnlockPixels();
return bitmap;
}
I am developing a C# .NET application which will display the image captured from a camera (smart camera). I am using the function below to convert the raw data received from the camera (which is a byte array) to a bitmap image. The problem I face with the below code is that, the image displayed in the C# .NET application's picture box (which should be a live image ideally) looks like a movie reel and it keeps scrolling to the right. Any idea how to solve this issue?
private Image ByteArrayToImage(byte[] myByteArray)
{
if (myByteArray != null)
{
MemoryStream ms = new MemoryStream(myByteArray);
int Height = 504; /*The exact height of image from Smart Camera*/
int Width = 664; /*The exact width of image from Smart Camera*/
BitmapData bmpData =
bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
BitmapData bmpData =
bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite, bmp.PixelFormat);
int offset = 0;
long ptr = bmpData.Scan0.ToInt64();
for (int i = 0; i < Height; i++)
{
Marshal.Copy(myByteArray, offset, new IntPtr(ptr), Width);
offset += (Width);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
return bmp;
}
return null;
}
#Mark, find below the code segment i use for receiving data from camera ...
Logic : I receive the sizeof image first and then open socket connection n loop till i get the sizeof bytes from the camera.
public static byte[] receivedata(Socket clientSock)
{
if (clientSock != null)
{
byte[] size = new byte[10000000];
byte[] buffer = null;
int ch, received, offset;
try
{
clientSock.Receive(size);
//MessageBox.Show("size array is " + size[0]
// + " " + size[1] + " " + size[2] + " " );
ch = BitConverter.ToInt32(size, 0);
//ch = 334656;
//MessageBox.Show("Sizeofimg = " + ch + "");
buffer = new byte[ch];
//MessageBox.Show("Start receiving image from camera");
received = 0;
offset = 0;
do
{
received += clientSock.Receive(buffer,
offset + received,
ch - received,
SocketFlags.None);
} while (received < ch);
//MessageBox.Show("Received " + received + " values");
System.Threading.Thread.Sleep(50);
}
catch (Exception e)
{
MessageBox.Show("Error receiving ...");
MessageBox.Show(e.StackTrace);
}
return buffer;
}
else
{
return null;
}
}
}
Kindly point out the problem ...
Thanks in advance.
"Scrolling to the right" tells me that each image is decoding consistently, but successive images are off in relation to one another. Correct? That means the problem is not in the code you've shown us, but in the code that reads from the camera.
Edit: it seems likely that your width and height are off by a little bit. The only documentation I've found for a 664x504 camera states that the "active area" (the part that actually contains a picture) is really 648x488. If this is the case, your picture would also seem to slant a little as each line would be offset from the next, and would shift up as each frame you read would get part of the next frame's data.