.NET Bitmap.Clone() throws OutOfMemoryException - c#

Why does the following simple piece of code (bitmap.Clone) throw an OutOfMemoryException?
List<Bitmap> bitmapList = new List<Bitmap>();
try
{
for (int i = 0; i < 1000; ++i)
{
using (Bitmap bitmap = new Bitmap(#"C:\temp\test.gif"))
//using (Bitmap bitmap = new Bitmap(bitmap2))
{
Bitmap clonedBitmap = bitmap.Clone(new Rectangle(0, 0, bitmap.Width, bitmap.Height), PixelFormat.Format8bppIndexed);
bitmapList.Add(clonedBitmap);
Debug.WriteLine("round " + i);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
foreach (Bitmap bitmap in bitmapList)
{
bitmap.Dispose();
}
}
The exception is first thrown around round 95 (sometimes round 94 or 96), so the code works quite a few times before failing. The original GIF image is not very big so I cannot believe it is a real out of memory condition. I know that GDI+ maps many error conditions to OutOfMemoryException but what is the real underlying cause?
If I uncomment the second using line and change the file bitmap name to bitmap2, i.e. I create a new temporary bitmap which I then clone then the code works ok. Why?
using (Bitmap bitmap2 = new Bitmap(#"C:\temp\test.gif"))
using (Bitmap bitmap = new Bitmap(bitmap2))
{
The HResult in the exception is 0x8007000e which according to Microsoft documentation can mean out of memory condition among many other things. Maybe some native resource problem but why should it manifest like this and be corrected by making an extra bitmap?
The cause is not the GIF image format either, I have tried with JPG and PNG images and the same problem appears.
My real purpose is to convert 24-bit RGB bitmaps to indexed 8-bit bitmaps. The resulting image from Clone is not very high quality though. Anybody know some better quality way to convert to 8-bit indexed bitmap or do I have to do it all myself from scratch including palette quantization?

Related

Reading a ROS CompressedImage data byte array in C#

I would like to access the value of each individual pixel value of a 16UC1-formatted png image, which I receive as a byte[].
I am realtively new to image processing in C# and I got stuck at this problem for days now.
I can work with a "typical" bgr8-formatted jpg/png byte array simply by:
private static Bitmap getBitmap(byte[] array)
{
return new Bitmap(new MemoryStream(array));
}
I tried many things for the 16UC1-format. The furthest i got is:
private Bitmap getBitmap(byte[] array)
{
var bitmap = new Bitmap(640,480,PixelFormat.Format16bppRgb555);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, 640, 480), ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb555);
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, array, 0, array.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
this at least returns a bitmap, though it is completely black.
Trying PixelFormat.Format16bppGrayScale instead of PixelFormat.Format16bppRgb555 gives me a "General error in GDI+".
When writing the byte array to a file by e.g. by
File.WriteAllBytes(filename, array);
I can see the image with image viewers like IrfanView, though Windows photo viewer fails.
Reading the file as a Bitmap is not required. I want to avoid file operations for performance reasons. I simply want to access each individual xy-pixel of that image.
Update:
I started using Emgu.CV and applying imdecode as Dan suggested below.
private Bitmap getCompressedDepthBitmap(byte[] data)
{
Mat result = new Mat(new Size(640, 480), DepthType.Cv16U, 1);
CvInvoke.Imdecode(data,LoadImageType.AnyDepth, result);
return result.Bitmap;
}
This again gives me a black image. (By saving the byte array via WriteAllBytes I see useful contents.) I also tried
Image<Gray, float> image = result.ToImage<Gray, float>();
image.Save(Path.Combine(localPath, "image.png"));
which as well gave me a black image.
I am planning to normalize the Mat now somehow, maybe this helps...
Thank you for your interest and your support!
After hours and hours of wasted working time and despair I finally found the solution...
One important thing I was missing in my description above is that the image data byte[] is coming from of a ROS sensor_msgs/CompressedImage.msg.
The data byte array, which is supposed to contain the png data, sometimes starts with a 12byte header; seemingly only if the data is (1 channel) compressedDepth image. I acciendently found this info here.
Removing these awesomely obnoxous 12 bytes and continung as usual does the job:
var bitmap = new Bitmap(new MemoryStream(dataSkip(12).ToArray()));

Unusual Physical memory usage when huge number of bitmap images are created and saved as one bit per pixel image

I am trying to create large number of 1 bit per pixel bmp image from base 64 string and saving. As per the requirement a very huge number of images being created in a short period of time( an average of 50,000 to 1,00,000 in a short duration). I am using the below code.
public void CreateoneBppImageAndSave(String base64ImageString,String ImagePathToSave)
{
byte[] byteArray = Convert.FromBase64String(base64ImageString);
System.Drawing.Image img = byteArrayToImage(byteArray);
Bitmap objBitmap = new Bitmap(img);
BitmapData bmpData = objBitmap.LockBits(new Rectangle(0, 0, objBitmap.Width, objBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
Bitmap oneBppBitmap = new Bitmap(objBitmap.Width, objBitmap.Height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0);
oneBppBitmap.Save(ImagePathToSave, ImageFormat.Bmp);
img.Dispose();
objBitmap.Dispose();
objBitmap.UnlockBits(bmpData);
oneBppBitmap.Dispose();
}
private Image byteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
return Image.FromStream(ms);
}
}
Here the physical memory usage going very high. Usually the images are generated with size of 200x200 to 754x1024 . After certain duration physical memory usage reaching to the extreme and out of memory exception is being thrown.The physical memory is getting increased by 0.01 GB by every 5-10 seconds. Please help me to optimize the code in terms of memory usage.
You call LockBits on objBitmap however you call UnlockBits on oneBppBitmap. You should be calling unlock on the same object you called lock on.
As for using statements like I mentioned in the comments, a using statement turns this
using(SomeType obj = new SomeType())
{
// Some code
}
in to the equivalent of this
SomeType obj = new SomeType())
try
{
// Some code
}
finally
{
obj.Dispose();
}
That guarantees that even if a exception is thrown in // Some Code the dispose action will still happen. Your code, as it is right now, will not dispose any of its objects if any of your functions between the creation and dispose throws an exception.
Here is a re-written version with all the corrections I mentioned plus a few others..
public void CreateoneBppImageAndSave(String base64ImageString,String ImagePathToSave)
{
byte[] byteArray = Convert.FromBase64String(base64ImageString);
using(Image img = byteArrayToImage(byteArray))
using(Bitmap objBitmap = new Bitmap(img))
{
BitmapData bmpData = objBitmap.LockBits(new Rectangle(0, 0, objBitmap.Width, objBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
try
{
using(Bitmap oneBppBitmap = new Bitmap(objBitmap.Width, objBitmap.Height, bmpData.Stride, System.Drawing.Imaging.PixelFormat.Format1bppIndexed, bmpData.Scan0))
{
oneBppBitmap.Save(ImagePathToSave, ImageFormat.Bmp);
}
}
finally
{
//put the unlock in a finally to make sure it happens.
objBitmap.UnlockBits(bmpData);
}
}
}
EDIT: If this really is in your code
objBitmap.Dispose();
objBitmap.UnlockBits(bmpData);
That is the source of your problem, you should not call any methods on a class after you dispose. That is another benefit of using, you can't call methods late because the variable goes out of scope when you leave the using block.

C# Image: To preserve image's checksum

I have the following codes to convert an image(bitmap) to byte array:
public byte[] ConvertImageToByteArray(Image imageToConvert, ImageFormat formatOfImage)
{
byte[] Ret;
try
{
using (MemoryStream ms = new MemoryStream())
{
imageToConvert.Save(ms, formatOfImage);
Ret = ms.ToArray();
}
}
catch (Exception)
{
throw;
}
return Ret;
}
and Convert byte array back to image(bitmap):
public Bitmap ConvertByteArrayToImage(byte[] myByteArray)
{
Image newImage;
using (MemoryStream ms = new MemoryStream(myByteArray, 0, myByteArray.Length))
{
ms.Write(myByteArray, 0, myByteArray.Length);
newImage = Image.FromStream(ms, true);
}
return newImage;
}
Here's my Main Program:
byte[] test = ConvertImageToByteArray(Image.FromFile("oldImage.bmp"), ImageFormat.Bmp);
Bitmap bmp = ConvertByteArrayToImage(test);
bmp.Save("newImage.bmp");
But when I compare both of the image files(old & new bitmap images), their checksum appeared to be different. Any reason for that happening? How to fix it to maintain its integrity?
Basically, there are many ways an identical image can be encoded in a BMP file. If I try your example on a random image I found, I see the .NET Bitmap class saves the file without filling the biSizeImage field in the BITMAPINFOHEADER structure in the BMP header (but the original image produced by IrfanView has it filled), which is a completely correct and documented possibility. (“This may be set to zero for BI_RGB bitmaps.”)
And this is definitely not the only variable thing in the BMP format. For instance, there are multiple possible orderings of pixel data in the image (top-to-bottom, bottom-to-top), specified in the header. (“If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.”)
So, if you receive any BMP file from a source not under your control and really need to produce an image using exactly the same BMP variant, you have a lot work to do, and I don’t think you could use the standard .NET helper classes for that.
See also this question: Save bitmap to file has zero in image size field
After chatting a bit, you solution comes down to reading and writing bytes, take the image object out the equation and just deal with the raw bytes.
To read the file:
MemoryStream ms = new MemoryStream(File.ReadAllBytes("filename"));
To write the file:
File.WriteAllBytes("outputfile", ms.ToArray());

Free file locked by new Bitmap(filePath)

I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:
"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file "A" because it is being used by another process."
I'm setting the Image as follows:
pbAvatar.Image = new Bitmap(filePath);
How can I unlock the first file?
Here is my approach to opening an image without locking the file...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
UPDATE: I did some perf tests to see which method was the fastest. I compared it to #net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
The results seem to make sense since you have to create the image twice using the copy from bitmap method.
UPDATE:
if you need a BitMap you can do:
return (Bitmap)Image.FromStream(ms);
This is a common locking question widely discussed over the web.
The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.
Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.
What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
This is how the image should be loaded:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.
Using a filestream will unlock the file once it has been read from and disposed:
using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.
THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.
You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)
Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.
Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
And then you can invoke it like such:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Here's the technique I'm currently using, and seems to work best. It has the advantage of producing a Bitmap object with the same pixel format (24-bit or 32-bit) and resolution (72 dpi, 96 dpi, whatever) as the source file.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
This can be used as often as needed, as follows:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Edit:
Here's an update of the above technique: https://stackoverflow.com/a/16576471/253938
(The accepted answer is wrong. When you try to LockBits(...) on the cloned bitmap eventually you will encounter GDI+ errors.)
I see only 3 ways to get out of this:
copy your file to a temporary file and open that the easy way new Bitmap(temp_filename)
open your file, read image, create a pixel-size-pixelformat copy (don't Clone()) and dispose the first bitmap
(accept the locked-file-feature)
I suggest to use PixelMap which is available on NuGet
Very easy to use and much faster than standard Bitmap from .NET
PixelMap pixelMap = new PixelMap(path);
pictureBox1.Image = pixelMap.GetBitmap();
Read it into the stream, create bitmap, close the stream.

C# image manipulation using a console application throwing 'Out of Memory' Exception

I have a console application that successfully re-sizes an image while maintaining aspect ratio.
I now need to crop the image the code I am using is below:
using (var thumbnail = CropPicture(image, rectangle)) {
EncoderParameters encParams = new EncoderParameters(1);
encParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)90);
thumbnail.Save(destination, GetImageCodecInfo(image.RawFormat), encParams);
}
public static Image CropPicture(Image source, Rectangle cropArea) {
using (var bitmap = new Bitmap(source)) {
return (Image)(bitmap.Clone(cropArea, source.PixelFormat));
}
}
It seems to be throwing an Out of memory exception on the line
return (Image)(bitmap.Clone(cropArea, source.PixelFormat));
Any ideas what's going on? I think it's an open file can't be 100% sure.
instead of returning the Image inside the using why not create a reference before returning it inside the using statement.
public static Image CropPicture(Image source, Rectangle cropArea) {
Bitmap retImg;
using (var bitmap = new Bitmap(source)) {
retImg = bitmap.Clone(cropArea, source.PixelFormat);
}
return (Image)regImg;
}
I'm not sure but it looks like the Bitmap is not disposed before you return the image.
According to the MSDN documentation, Bitmap.Clone(Rectangle, PixelFormat) can throw an OutOfMemoryException if the first parameter is "outside of the source bitmap bounds".
Verify the first parameter to Bitmap.Clone; make sure that the rect is entirely within the bounds of the image.

Categories

Resources