Reputation: 4895
Hey getting this error:
An exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll but was not handled in user code
Additional information: Out of memory.
It occurs in the below method on the DrawImage
call
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <returns>The resized image.</returns>
public Bitmap ResizeImage(Image image, System.Drawing.Size newSize)
{
var destRect = new Rectangle(0, 0, newSize.Width, newSize.Height);
var destImage = new Bitmap(newSize.Width, newSize.Height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
I'm not sure why it occurs, I call this method multiple times, the result is converted into a Base64 String and stored within an ObservableCollection.
/// <summary>
/// Convert Image and Resize
/// </summary>
/// <param name="loc"></param>
/// <returns></returns>
public async Task<string> GenerateThumbnailBinary(string loc)
{
return await Task<string>.Factory.StartNew(() =>
{
Image image = Image.FromFile(loc, true);
// Figure out the ratio
double ratioX = (double)Properties.Settings.Default.ThumbnailWidth.Width / (double)image.Width;
double ratioY = (double)Properties.Settings.Default.ThumbnailWidth.Height / (double)image.Height;
// use whichever multiplier is smaller
double ratio = ratioX < ratioY ? ratioX : ratioY;
System.Drawing.Size newSize =
new System.Drawing.Size(
(int)(image.Width * ratio),
(int)(image.Height * ratio));
Image resized = ResizeImage(image, newSize);
return ImageToBase64(resized, ImageFormat.Jpeg);
});
}
I also display each of the strings back as an image by binding to the Collection and using a converter to convert the Base64 string back into a Bitmap, this is just for the UI to display what has been converted.
Where would my issue be starting? Could I be attempting to store too many images in memory when I display them on the UI and use the converter to convert the string to the image?
The high points in the image below obviously when it's running the method loop, but it still seems to stay higher than before the method is run at the end, do this help?
Edit: This is the loop which starts the Tasks and runs the method.
// Generate List of images to upload
var files = Directory.EnumerateFiles(sel.Name, "*.*", SearchOption.AllDirectories)
.Where(s => s.EndsWith(".jpeg") ||
s.EndsWith(".jpg") ||
s.EndsWith(".png") ||
s.EndsWith(".JPG"));
int b = 1;
if (files.Count() > 0)
{
/// <summary>
/// Resize Images
/// </summary>
/// Set current Task first
UploadTask = Steps[0].Message;
try
{
foreach (string item in files)
{
// Generate new name
string oldname = Path.GetFileNameWithoutExtension(item);
string newName = Common.Security.KeyGenerator.GetUniqueKey(32);
string t = await GenerateThumbnailBinary(item);
ImageUploadObjects.Add(
new ImageUploadObject { OldName = oldname,
NewName = newName,
ByteImage = t });
UploadProgress = (int)Math.Round((double)(100 * b / files.Count()));
b++;
}
// Complete
Steps[0].Complete = true;
}
catch(Exception e)
{
Steps[0].Error = e.InnerException.ToString();
}
/// <summary>
/// Move full resoluation images
/// </summary>
/// Set current Task first
UploadTask = Steps[1].Message;
try
{
foreach (string item in files)
{
}
// Complete
Steps[1].Complete = true;
}
catch (Exception e)
{
Steps[1].Error = e.InnerException.ToString();
}
}
}
Edit:
How can I tell if the memory is still being used? I have added another image below, the first snapshot is before I execute the method, and the last one is when it finishes, 2 - 4 are whilst it's running
Upvotes: 0
Views: 225
Reputation: 6222
The Image resized = ResizeImage(image, newSize)
is not being disposed of. Because the memory allocated will not be released until the finalizer thread is run, you could be leaking memory all over the place.
Image image = Image.FromFile(loc, true);
...
Image resized = ResizeImage(image, newSize);
image.Dispose();
string base64Image = ImageToBase64(resized, ImageFormat.Jpeg);
resized.Dispose();
return base64Image;
Upvotes: 0
Reputation: 27039
I think the easiest way to solve your issue is to do it in chunks/batches. For example, if you have 100 files, you are creating 100 tasks, which load the file content into images into memory. Perhaps do 10 (or some other number) and once that has been completely done, do the next 10 (or some other number). I am sure this will fix your issue.
Also make sure to call Dispose on any class which implements Disposable, i.e., Image and Bitmap etc.
In addition to the above, here is summarily what you are trying to do: 1. Read a directory and take all the files. 2. Create thumbnail images for each file. 3. Add thumbnail to a collection in memory. 4. Transfer the images to another location.
For item 2 above, I would not keep all the thumbnails in memory. Even if I need to show this in UI, I will incorporate paging and pull them as needed.
Upvotes: 1