The best practice to avoid out of memory exception - c#

I need to convert tif to webp and I am using wep wrapper class from this project - https://github.com/JosePineiro/WebP-wrapper/blob/master/WebPTest/WebPWrapper.cs
If I use regular for loop everything seems to be working correctly but I need to sped up the process and that is why I am using parallel for each to execute this method. I am getting out of memory exception in some cases. How can I avoid that? I was reading that I can use long memorySize = currentProcess.PrivateMemorySize64
Is this the best practice? I have never used this approach.
And I am looping it like:
Parallel.ForEach(MyList, (row) =>
{
byte[] rawWebP;
Image image = Image.FromFile(row.linkToImage);
Bitmap bmp = (Bitmap)image;
using (WebP webp = new WebP())
rawWebP = webp.EncodeLossless(bmp, row.linkToImage);
});

Related

BitMiracle Tiff.ClientOpen() Fails

I am trying to open an image byte array with the Tiff.ClientOpen method as follows:
using (MemoryStream ms = new MemoryStream(img))
{
using (Tiff input = Tiff.ClientOpen("InMemory", "r", ms, new TiffStream()))
{
}
}
Where img = byte[].
But inside my second 'using' input = null. I am 100% sure img has data, and stepping through the debug process it even worked a few times.
Has anyone experienced this?
Seems like the issue is with the format of the tiff I am reading into the memory stream. By using the library to create a tiff as shown in the example here:
https://bitmiracle.github.io/libtiff.net/?topic=html/e4f25423-eede-4ef6-a920-9cb539d056c6.htm
then passing the result of that to the memory stream, after that then the ClientOpen() works. Not sure why. This is when you wish BitMiracle provided support ;).
Replace InMemory with in-memory, and make sure you selected a valid image.
using (Tiff image = Tiff.ClientOpen("in-memory", "r", ms, new TiffStream()))

A Generic error occured in GDI+ in Image.Save() method

I'm trying to use this class but I'm getting a Generic error occured in GDI+ in Image.Save() method. From what I read it's some stream I need to close but I don't know which one.
I'm calling using this:
Image image = Image.FromFile(#"C:\a.jpg");
using (var resized = ImageUtilities.ResizeImage(image, 50, 100))
{
//save the resized image as a jpeg with a quality of 90
ImageUtilities.SaveJpeg(#"C:\myimage.jpeg", resized, 90);
}
Why is that error and how do I solve this?
Unless your program is running as administrator you can not save directly to the root of C: make a folder and save it inside there instead.
Have you tested saving the images in different locations?
If it is still failing then without knowing exactly what is going on in your code I would hazard a guess to say that the original image is getting disposed somewhere before it should be. That's usually the most common cause of the error.
I've written a library that handles many different imaging operations whilst ensuring that memory is correctly handled. It's well tested and very simple to use.
You can get it here. http://imageprocessor.org/
Example code using the library:
using (ImageFactory imageFactory = new ImageFactory())
{
// Load, resize, set the quality and save an image.
imageFactory.Load(#"C:\a.jpg")
.Resize(new Size(50, 100))
.Quality(90)
.Save(#"C:\myimage.jpeg);
}

High performing Bitmap Drawing solution in c#

I have a use case where I need to render a collage of bitmaps as a preview. The application is implemented as a MVC based REST service, and I have a fairly vanilla implementation:
using (var bitmap = new Bitmap((int)maxWidth, (int)maxHeight))
{
using (var graphic = Graphics.FromImage(bitmap))
{
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
// now again for each mod
foreach (var mod in mods)
{
var frame = frames.First(f => f.Index == mod.FrameIndex);
var fileInfo = GetFileInfo(mod);
using (var modImage = Image.FromFile(fileInfo.FullName))
{
graphic.DrawImage(modImage, (int)frame.Left, (int)frame.Top);
}
}
bitmap.Save(previewFileName);
}
}
While this code works fine, it performs very poorly (especially with larger images). I am open to using third party libraries as well, I just need a faster performing solution.
Any help would be mostly appreciated.
Update
To clarify the use case, caching doesn't help. These images are uploaded by the customer, then they request a preview of the selected collage. It's the writing of the images to the collage that is slow.
I just now realized that you're drawing the modImages in their original size (because you're using the override that only takes a left and top coordinate). If that's what you want, you could as well just use the DrawImageUnscaled()-Method of the graphics class, that should be much faster:
var frame = frames.First(f => f.Index == mod.FrameIndex);
var fileInfo = GetFileInfo(mod);
using (var modImage = Image.FromFile(fileInfo.FullName))
{
graphic.DrawImageUnscaled(modImage, (int)frame.Left, (int)frame.Top);
}
How important is the image quality? If you need faster results, you should try InterpolationMode.NearestNeighbour, this should be much faster, but results will be rather low-quality.
HeighQualityBicubic (what you're currently using) produces best results, but at lowest performance.
If you have a static number of Thumbnails, similar to Netflix, I would create a Cache in memory that keeps the thumbnail bitmaps. This could even be a static object. Once you have generated the thumbnail, you never need to generate it again. It would be a cleaner approach than trying to find a faster hammer to hit it with every time.
Example :
if(MyThumbnailImageDictionary["whateverimageId"] != null)
return (Bitmap)MyThumbnailImageDictionary["whateverimageId"];
If the thumbnails are dynamic such as from a user uploading random pictures, then this method will not help as the size of the image cache will grow tremendous.
You can use the following code to generate a Thubnail, but I am not sure how much faster it would be :
Image image = Image.FromFile(fileName);
Image thumb = image.GetThumbnailImage(120, 120, ()=>false, IntPtr.Zero);

Resize image on the fly in .net and c#

I'm looking for a way to resize images without saving them on the server. The ways that i have found includes a controller file and such.
Is there a way to get the image from the stream, resize it and add it to the response?
Check out ImageResizer - it's a suite of NuGet packages designed for this exact purpose.
It runs eBay in Denmark, MSN Olympics, and a few other big sites.
Dynamic image processing can be done safely and efficiently, but not in a sane amount of code. It's trickier than it appears.
I wouldn't recommend this but you can do next thing:
using (Image img = Image.FromStream(originalImage))
{
using (Bitmap bitmap = new Bitmap(img, width, height))
{
bitmap.Save(outputStream, ImageFormat.Jpeg);
}
}
Be aware that this could cause OutOfMemoryException.

WinRT image handling

A friend and I spent the better part of last night nearly tearing our hair out trying to work with some images in a metro app. We got images into the app with the share charm, and then I wanted to do some other work with them, cropping the images and saving them back into the appdata folder. This proved extremely frustrating.
My question, at the end of all this, is going to be "What's the proper way of doing this, without feeling like I'm hammering together a bunch of mismatched jigsaw puzzle pieces?"
When sharing multiple images with the app, they come in as a list of Windows.Storage.StorageFiles. Here's some code used to handle that.
var storageItems = await _shareOperation.Data.GetStorageItemsAsync();
foreach (StorageFile item in storageItems)
{
var stream = await item.OpenReadAsync();
var properties = await item.Properties.GetImagePropertiesAsync();
var image = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
image.SetSource(stream);
images.Add(image);
}
Some searching online has indicated that currently, a Windows.UI.Xaml.Media.Imaging.WriteableBitmap is the only thing capable of letting you access the pixel data in the image. This question includes a helpful answer full of extension methods for saving images to a file, so we used those.
Our problems were the worst when I tried opening the files again later. I did something similar to before:
var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();
foreach (var file in files)
{
var fileStream = await file.OpenReadAsync();
var properties = await file.Properties.GetImagePropertiesAsync();
var bitmap = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
bitmap.SetSource(fileStream);
System.IO.Stream stream = bitmap.PixelBuffer.AsStream();
Here comes a problem. How long is this stream, if I want the bytes out of it?
// CRASH! Length isn't supported on an IRandomAccessStream.
var pixels = new byte[fileStream.Length];
Ok try again.
var pixels = new byte[stream.Length];
This works, except... if the image is compressed, the stream is shorter than you would expect, so you will eventually get an out of bounds exception. For now pretend it's an uncompressed bitmap.
await _stream.ReadAsync(pixels, 0, pixels.Length);
Well guess what. Even though I said bitmap.SetSource(fileStream); in order to read in the data, my byte array is still full of zeroes. I have no idea why. If I pass this same bitmap into a my UI through the sample data group, the image shows up just fine. So it has clearly got the pixel data in that bitmap somewhere, but I can't read it out of bitmap.PixelBuffer? Why not?
Finally, here's what ended up actually working.
var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, fileStream);
var data = await decoder.GetPixelDataAsync();
var bytes = data.DetachPixelData();
/* process my data, finally */
} // end of that foreach I started a while ago
So now I have by image data, but I still have a big problem. In order to do anything with it, I have to make assumptions about its format. I have no idea whether it's rgba, rgb, abgr, bgra, whatever they can be. If I guess wrong my processing just fails. I've had dozens of test runs spit out zero byte and corrupted images, upside down images (???), wrong colors, etc. I would have expected to find some of this info in the properties that I got from calling await file.Properties.GetImagePropertiesAsync();, but no luck. That only contains the image width and height, plus some other useless things. Minimal documentation here.
So, why is this process so painful? Is this just reflecting the immaturity of the libraries right now, and can I expect it to get better? Or is there already some standard way of doing this? I wish it were as easy as in System.Drawing. That gave you all the data you ever needed, and happily loaded any image type correctly, without making you deal with streams yourself.
From what I have seen - when you are planning on loading the WriteableBitmap with a stream - you don't need to check the image dimensions - just do new WriteableBitmap(1,1), then call SetSource().
Not sure why you were thinking var pixels = new byte[fileStream.Length]; would work, since the fileStream has the compressed image bytes and not a pixel array.
You might need to seek to the beginning of the stream to get the pixels array:
var pixelStream = pixelBuffer.AsStream();
var bytes = new byte[this.pixelStream.Length];
this.pixelStream.Seek(0, SeekOrigin.Begin);
this.pixelStream.Read(bytes, 0, Bytes.Length);
I had started working on a WinRT port of WriteableBitmapEx - maybe it could help you: http://bit.ly/WriteableBitmapExWinRT. I have not tested it well and it is based on an older version of WBX, but it is fairly complete in terms of feature support. Might be a tad slower than it is possible too.

Categories