C# Image.Clone Out of Memory Exception

C# System.Drawing.Bitmap throwing Out of Memory Exception when cloning

Finally I have done below (thanks to Alex K. for the suggestion):

RectangleF rectangleF = new System.Drawing.Rectangle(left, top, right - left + 1, bottom - top);
GraphicsUnit units = GraphicsUnit.Pixel;
RectangleF bmpRectangleF = bmp.GetBounds(ref units);
if (bmpRectangleF.Contains(rectangleF))
{
Bitmap bmpCrop = bmp.Clone(rectangleF, bmp.PixelFormat);
return (Bitmap)(bmpCrop);
}

How to fix Clone.Bitmap system out of memory in C#

The Bitmap class hold unmanaged resources with the operating system that need to be released. You need to dispose all your bitmaps after you create them.

Any bitmap you create, or clone need to be disposed (after you finish with them). I suggest looking at the using statement

Provides a convenient syntax that ensures the correct use of
IDisposable objects.

Out Of Memory Exception - Bitmap.Clone

Bitmap is a wrapper around the Win32 GDI methods. The error codes from those methods are sometimes translated somewhat confusingly into .NET exceptions. Thus, you probably don't have OOM, but rather some other GDI error.

GDI has a number of rules, which are not the case with WPF, and which could cause such an error. For example, the pixel dimensions of an element have to be at least 1x1. Also, any coordinates and sizes have to be valid. Probably your error has to do with an error there.

C# WinForms Out of Memory Exception on Bitmap Clone

Leon, I've changed your code you've uploaded to not lock any resources.
I've notices that if I keep your application open, I cannot delete the output folders because some files are in use. This usually means that you DID NOT release all file handles, basically it is always the last file.

I couldn't reproduce the out of memory issue on my computer before and after my changes, seems to be an issue with very big files maybe?

Ok anyways, I found that you use ImageBox to load the white and black resource and to load the image from disc. This is not needed at all, intead use the resources directly

Bitmap white = OomTestApp.Properties.Resources.white;
Bitmap black = OomTestApp.Properties.Resources.black;

Then to load an image from disc, simply use Bitmap.FromFile

I have added some lines to release your resources properly .Dispose where no using blocks are used.

And I also removed the Clone() call because it is absolutely not needed I think, because you are just computing the pixels of the original image and you do not draw something into that image at that point. So what was the need to have the image cloned?

Here is the full code (starting after you created the folders)

if (errors == 0)
{
this.Height = 323;
goButton.Enabled = false;
stopButton.Enabled = true;

Bitmap white = OomTestApp.Properties.Resources.white;
Bitmap black = OomTestApp.Properties.Resources.black;
Bitmap bmWatermark = black;
Bitmap processImage = null;

progressBar1.Maximum = filesToProcess.Count;

foreach (string handleFile in filesToProcess)
{
string fileName = System.IO.Path.GetFileName(handleFile);
fileNameLabel.Text = "File: " + System.IO.Path.GetFileName(handleFile);
try
{
// create backup if checked
if (diOriginal != null)
{
System.IO.File.Move(handleFile, System.IO.Path.Combine(diOriginal.FullName, fileName));
processImage = (Bitmap)Bitmap.FromFile(System.IO.Path.Combine(diOriginal.FullName, fileName));
}
else
{
processImage = (Bitmap)Bitmap.FromFile(handleFile);
}

double aspectRatio = (double)processImage.Width / (double)processImage.Height;

using (Graphics gWatermark = Graphics.FromImage(processImage))
{
gWatermark.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
System.Drawing.SolidBrush drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);

// position watermark - watermark should be 10% of the image height
int watermarkHeight = (int)(processImage.Height * 0.1);
int watermarkPadding = (int)(watermarkHeight * 0.1); // not completely true, but asume watermark is square
// calculate rectangle. if there is extra text, add extra padding below
Rectangle watermarkArea = new Rectangle(watermarkPadding, processImage.Height - (watermarkPadding + (watermarkText.Text.Length == 0 ? 0 : watermarkPadding) + watermarkHeight), watermarkHeight, watermarkHeight);

// determine color watermark
bmWatermark = black;
if (watermarkCombo.SelectedIndex == 0)
{
/*using (Bitmap watermarkClone = processImage.Clone(watermarkArea, processImage.PixelFormat))
{*/
var pixels = Pixels(processImage);
if (pixels.Average((Func<Color, decimal>)Intensity) < 110) // human eye adoption; normal threshold should be 128
{
bmWatermark = white;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}
//}
}
else if (watermarkCombo.SelectedIndex == 1)
{
bmWatermark = white;
drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White);
}

// draw the watermark
gWatermark.DrawImage(bmWatermark, watermarkArea.X, watermarkArea.Y, watermarkArea.Width, watermarkArea.Height);

// draw the text (if needed)
if (watermarkText.Text.Length > 0)
{
System.Drawing.Font drawFont = new System.Drawing.Font("Tahoma", (float)watermarkPadding);
gWatermark.DrawString(watermarkText.Text, drawFont, drawBrush, watermarkPadding, processImage.Height - (watermarkPadding * 2));
drawFont.Dispose();
}
// disposing resources
drawBrush.Dispose();

}
// save the watermarked file
processImage.Save(System.IO.Path.Combine(diWatermarked.FullName, fileName), System.Drawing.Imaging.ImageFormat.Jpeg);

// stop button pressed?
Application.DoEvents();
if (stopProcess) break;

// update exection progress
progressBar1.Value++;
percentLabel.Text = ((int)((progressBar1.Value * 100) / filesToProcess.Count)).ToString() + "%";
fileCountLabel.Text = "File " + progressBar1.Value.ToString() + "/" + filesToProcess.Count.ToString();
}
catch (Exception ex)
{
try
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(System.IO.Path.Combine(folderText.Text, "errorlog.txt"), true))
{
sw.WriteLine("File: " + fileName);
while (ex != null)
{
sw.WriteLine("Message: " + ex.Message);
sw.WriteLine(ex.StackTrace);
sw.WriteLine(ex.Source);
ex = ex.InnerException;
}
sw.WriteLine();
}
}
catch
{
// nothing to do - it already failed
}
errors++;
}
finally
{
if (processImage != null) processImage.Dispose();
}
}

// dispose resources
white.Dispose();
black.Dispose();
bmWatermark.Dispose();

if (!stopProcess)
{
// set status to complete
fileCountLabel.Text = "File " + filesToProcess.Count.ToString() + "/" + filesToProcess.Count.ToString();
percentLabel.Text = "100%";
fileNameLabel.Text = "Completed...";
}
else
{
fileNameLabel.Text = "Aborted...";
}

fileNameLabel.Text += errors.ToString() + " error(s) encountered";

// defaults to screen
progressBar1.Value = progressBar1.Maximum;
stopProcess = false;
goButton.Enabled = true;
stopButton.Enabled = false;

c# Out of Memory Exception with System.Drawing.Image

Issue

You are building an object

  using (System.Drawing.Image imgOriginal = System.Drawing.Image.FromFile(sOriginalPath, true))

Then you are returning it...but it is already disposed of...you need to not dispose of the object by unwrapping it with a using...whatever consumes this will need to dispose of the object.

Other Issue

bitmap is also a memory leak and needs to be wrapped with a using or dispose called implicitly.


Final Function Example

public System.Drawing.Image GetImage(string sOriginalPath, string sLogoPath)
{
System.Drawing.Image imgOriginal = System.Drawing.Image.FromFile(sOriginalPath, true);
using (System.Drawing.Image imgLogo = System.Drawing.Image.FromFile(sLogoPath, true)) //This is where it throws the exception
{
using (Graphics gra = Graphics.FromImage(imgOriginal))
{
using(Bitmap bmLogo = new Bitmap(imgLogo))
{
int nWidth = bmLogo.Size.Width;
int nHeight = bmLogo.Size.Height;
int nLeft = (imgOriginal.Width / 2) - (nWidth / 2);
int nTop = (imgOriginal.Height / 2) - (nHeight / 2);
gra.DrawImage(bmLogo, nLeft, nTop, nWidth, nHeight);
}
}
}
return imgOriginal;
}

Example Console App Demo

I've tested the below and it worked as expected.

using System.Drawing;

namespace SO_Test
{
class Program
{
static void Main(string[] args)
{
using(Image newImage = GetImage("C:\\Users\\username\\Pictures\\image.png", "C:\\Users\\username\\Pictures\\watermark.jpg"))
{
newImage.Save("C:\\Users\\username\\Pictures\\newImage.png");
}
}

static Image GetImage(string sOriginalPath, string sLogoPath)
{
Image imgOriginal = Image.FromFile(sOriginalPath, true);
using (Image imgLogo = Image.FromFile(sLogoPath, true)) //This is where it throws the exception
{
using (Graphics gra = Graphics.FromImage(imgOriginal))
{
using (Bitmap bmLogo = new Bitmap(imgLogo))
{
int nWidth = bmLogo.Size.Width;
int nHeight = bmLogo.Size.Height;
int nLeft = (imgOriginal.Width/2) - (nWidth/2);
int nTop = (imgOriginal.Height/2) - (nHeight/2);
gra.DrawImage(bmLogo, nLeft, nTop, nWidth, nHeight);
}
}
}
return imgOriginal;
}
}
}


Related Topics



Leave a reply



Submit