Image.GetThumbnailImage throws OutOfMemoryException - c#

The following C# 3.5 code throws an exception in GetThumbnailImage:
Image img = null;
Image scaledImg = null;
byte[] imageData = File.ReadAllBytes("11001.jpg");
MemoryStream stream = new MemoryStream(imageData);
img = Image.FromStream(stream);
stream.Close();
stream.Dispose();
scaledImg = img.GetThumbnailImage(64, 64, null, IntPtr.Zero);
The problem is the disposing of the stream. If I remove the Close() and Dispose() statement, everything works fine. Does anyone know why this exception is thrown? Using a callback instead of null as parameter does not change the behavior.
I don't need a solution, I can use new Bitmap(img, new Size(width, height) for scaling. This is probably better and shoud have been used from the beginning.
Edit: Sorry, I forgot to mention that the exception only occurs in WindowsXP. Win7 and Win8 seem to handle the above code just fine.

GetThumbnailImage is a native method, using the same stream you passed while creating the Image. Only the .NET methods are using the actual data from the stream (it's loaded as the last thing while creating the image from a stream).
It's a pretty typical leaky abstraction. You thought you have an Image object that already has its data loaded, but the data is only loaded in the .NET part. Any method that works directly with the GDI+ image handle will still need the stream to be live.
Another example of the same problem is while trying to save the image in a different format. When you try to save with the same format, .NET just saves the byte data it has in memory, simple. If it's not the same, it will use the GDI+ method SaveImageToFile, which again requires the original stream to be preserved.
If you don't mind having the stream alive, you can just move the Dispose after everything you do with the images produced from that stream:
using (var stream = new MemoryStream(imageData))
{
var img = Image.FromStream(stream);
var scaledImg = img.GetThumbnailImage(64, 64, null, IntPtr.Zero);
scaledImg.Save(...);
}
If you do mind, the easiest thing you can do is this:
var bmp = new Bitmap(Image.FromStream(stream));
The Bitmap constructor will copy the image data, and you no longer need to keep the stream.

I think that GetThumbnailImage uses the stream to do its job so it needs it to be open.You can do it like this :
Image img = null;
Image scaledImg = null;
byte[] imageData = File.ReadAllBytes("11001.jpg");
using(MemoryStream stream = new MemoryStream(imageData))
{
img = Image.FromStream(stream);
scaledImg = img.GetThumbnailImage(64, 64, null, IntPtr.Zero);
}
(using closes and disposes the MemoryStream but it also handles exceptional situations to avoid memory leaks)

The Image object uses lazy evaluation. As you have already closed the stream, when it actually tries to read the image in order to get the thumbnail, it's not there anymore. Hence the error.

Related

C# How to dispose of OpenFileDialog file

I use OpenFileDialog to get the path of an image and then set it to my image source property imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute)); in a WPF application.
The thing is, I need to open that file again later but I can't open it since the file is being used by another process ( System.IO.IOException -> The process cannot access the file pathToFile because it is being used by another process.).
The code that needs to access it later it as follow :
bitmapSource = JpegBitmapDecoder.Create(File.Open(filenameSteg, FileMode.Open),
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
This bitmapSource is used to give that image to a WriteableBitmap and from there I go through the pixels.
Is there any way to dispose of a File opened with an OpenFileDialog ?
I tried to cast to to IDisposable, to use a using block and so on but this thing is persistent.
EDIT :
1 - I tried this (#ctacke answer) :
using (var stream = File.Open(filenameSteg, FileMode.Open)){
bitmapSource = JpegBitmapDecoder.Create(stream, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad).Frames[0];}
But it still gives me the error about the process being already in used by another process because even though it will be disposed after, I still am trying to open the same file (filenameSteg) than I opened in the OpenFileDialog. (Or at least, that's how I see it.)
2 - I then tried this (based on #ctacke recommended link:
using (FileStream fileStream = new FileStream(filenameSteg+1, FileMode.Create)){
MemoryStream memoryStream = new MemoryStream();
BitmapImage bi = new BitmapImage();
byte[] fileBytes = File.ReadAllBytes(filenameSteg);
memoryStream.Write(fileBytes, 0, fileBytes.Length);
memoryStream.Position = 0;
bi.BeginInit();
bi.StreamSource = memoryStream;
bi.EndInit();
bitmapSource = bi;}
Note : Notice that I here ask for filenameSteg +1. That is because I wanted to test the rest of my method so I create a copy the file and simply added a 1 to its name. That being said, when using filenameSteg for real, it gave me the same error about being already in used which I again suspect is that I still am asking to open the same image that was previously opened in the OpenFileDialog.
3 - I thought of another approach which does not require me to dispose the opened image :
When I open the image for the first time in the OpenFileDialog, I store the byte array of the image in a variable so I could create the WriteableBitmap using the BitmapFactory and the byte array.
// This is in the OpenFileDialog. It is where I stock the image "pixels" in a byte array.
bytesArrayImage = File.ReadAllBytes(filenameSteg);
//And then later when I needed the WriteableBitmap, I used the byte array and the BitmapFactory
//imgSelected being the Image containing the image I opened in the OpenFileDialog, I used it's
//width and height
wb = BitmapFactory.New(Convert.ToInt32(imgSelected.Width),
Convert.ToInt32(imgSelected.Height)).FromByteArray(bytesArrayImage);
The problem with this approach is that, some pictures works fine and I can use the byte array to create the WriteableBitmap and go through it's pixels but in other cases it gives me an AccessViolationException stating : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.. In other words, trying to bypass the dispose problem got me into another problem.
You should release the original image, something like this:
if(imgSelected.Source != null)
{
imgSelected.Source.Dispose;
}
imgSelected.Source = new BitmapImage(new Uri(filenameSteg, UriKind.Absolute));
Next, File.Open returns a stream, which you need to explicitly release.
using(var stream = File.Open(filenameSteg, FileMode.Open))
{
var bitmapSource = JpegBitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
}
See also: Load a BitmapSource and save using the same name in WPF -> IOException

Loading saved byte array to memory stream causes out of memory exception

At some point in my program the user selects a bitmap to use as the background image of a Panel object. When the user does this, the program immediately draws the panel with the background image and everything works fine. When the user clicks "Save", the following code saves the bitmap to a DataTable object.
MyDataSet.MyDataTableRow myDataRow = MyDataSet.MyDataTableRow.NewMyDataTableRow(); //has a byte[] column named BackgroundImageByteArray
using (MemoryStream stream = new MemoryStream())
{
this.Panel.BackgroundImage.Save(stream, ImageFormat.Bmp);
myDataRow.BackgroundImageByteArray = stream.ToArray();
}
Everything works fine, there is no out of memory exception with this stream, even though it contains all the image bytes. However, when the application launches and loads saved data, the following code throws an Out of Memory Exception:
using (MemoryStream stream = new MemoryStream(myDataRow.BackGroundImageByteArray))
{
this.Panel.BackgroundImage = Image.FromStream(stream);
}
The streams are the same length. I don't understand how one throws an out of memory exception and the other doesn't. How can I load this bitmap?
P.S. I've also tried
using (MemoryStream stream = new MemoryStream(myDataRow.BackgroundImageByteArray.Length))
{
stream.Write(myDataRow.BackgroundImageByteArray, 0, myDataRow.BackgroundImageByteArray.Length); //throw OoM exception here.
}
The issue I think is here:
myDataRow.BackgroundImageByteArray = stream.ToArray();
Stream.ToArray() . Be advised, this will convert the stream to an array of bytes with length = stream.Length. Stream.Legnth is size of the buffer of the stream, which is going to be larger than the actual data that is loaded into it. You can solve this by using Stream.ReadByte() in a while loop until it returns a -1, indicating the end of the data within the stream.
You might give this library a look.
http://arraysegments.codeplex.com/
Project Description
Lightweight extension methods for ArraySegment, particularly useful for byte arrays.
Supports .NET 4.0 (client and full), .NET 4.5, Metro/WinRT, Silverlight 4 and 5, Windows Phone 7 and 7.5, all portable library profiles, and XBox.

Generic error occurred in GDI+

I am getting the error "Generic error occurred in GDI+" in my sample code below. What I do is that I make a request to get response for many jpeg files available at live site.
When I get response, I save the file to my application's local folder
and converting these images to binary (bytes of array) so that I can save it into database.
private byte[] GetBinaryImageData(string imgURL)
{
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(imgURL);
WebResponse response = Request.GetResponse();
Stream str = response.GetResponseStream();
System.Drawing.Image objTempImg = System.Drawing.Image.FromStream(str);
objTempImg.Save(FileName, ImageFormat.Jpeg);
FileStream fileStream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, (int)fileStream.Length);
fileStream.Close();
return buffer;
}
i don't get this error for all images, but it occurs for some of images. Anybody know the solution? I have already spent 2 days to oversome this
If I had to guess; it is having problems with handles occasionally, for the reason that you aren't disposing things correctly. This is especially important for things like GDI+. Introduce a few using statements to your code, since pretty much all of those objects are IDisposable:
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(imgURL);
using(WebResponse response = Request.GetResponse())
using(Stream str = response.GetResponseStream())
using(System.Drawing.Image objTempImg = System.Drawing.Image.FromStream(str))
{
objTempImg.Save(FileName, ImageFormat.Jpeg);
}
return File.ReadAllBytes(FileName);
(note I changed your file-reading code too, since it was hugely unreliable; it is incorrect to assume that Stream.Read actually reads all the data; you are supposed to check the return value and loop; but since you want it all, File.ReadAllBytes is easier).
Judging by this article it appears the stream the image is loaded from must exist for the lifetime of the image or bad things can happen.
I've run into this from time to time and found it easiest to copy the image data to a memory stream or a file my process controls and construct the image from that.
From the linked article:
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.

.NET BitmapSource is locking file

I am using this C# code to access an image file in order to read metadata from it.
BitmapSource img = BitmapFrame.Create(uri);
Unfortunately the image file specified by uri becomes locked until the program ends. How do I prevent the image from being locked?
maybe this could help ?
edit
BitmapSource img = BitmapFrame.Create(uri,BitmapCreateOptions.None,BitmapCacheOption.OnLoad);
BitmapCreateOptions.None = default option
BitmapCacheOption.OnLoad = Caches the entire image into memory at load time. All requests for image data are filled from the memory store.
from here
If you want to be able to delete/change the file immediately afterwards, read the whole file into memory, and then give it the MemoryStream instead. For example:
MemoryStream data = new MemoryStream(File.ReadAllBytes(file));
BitmapSource bitmap = BitmapFrame.Create(data);
You can also use generic stream:
Stream stream = File.OpenRead(filename);
Bitmap template = new Bitmap(stream); // or (Bitmap) Bitmap.FromStream(stream)
stream.Close();

WPF BitmapSource ImageSource

I am binding an Image.Source property to the result of the property shown below.
public BitmapSource MyImageSource
{
get
{
BitmapSource source = null;
PngBitmapDecoder decoder;
using (var stream = new FileStream(#"C:\Temp\logo.png", FileMode.Open, FileAccess.Read, FileShare.Read))
{
decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
if (decoder.Frames != null && decoder.Frames.Count > 0)
source = decoder.Frames[0];
}
return source;
}
}
For some reason this is failing during the rendering of the image (Deep in the PresentationCore assembly). I am certain the image is not corrupt as I can successfully show the same image w/o the binding
<Image Name="FooImage" Source="/logo.png" />
I have to bind the image source in code because I will eventually be creating the image stream from a base64 string.
Anyone know if this is a bug w/ WPF? or am I doing something incorrectly?
The problem was the BitmapCacheOption option, changing to BitmapCacheOption.OnLoad works.
With BitmapCacheOption.None the BitmapSource isn’t decoded until the image is rendered, but the stream with the png in it is already disposed at that point. If you cache OnLoad, it’ll decode right away and cache the results, rather than trying to decode later when the stream no longer exists.
Also, have you tried just using a BitmapImage to load the image? It works fine with PNG, BMP, and JPEG.
It's also a specialized type of BitmapSource, so you could just replace your code in your property with this:
BitmapImage img = new BitmapImage(new Uri(#"C:\Temp\logo.png"));
return img;
Are you positive it's a PNG and not just a renamed Bitmap or Jpeg? If you create a new Bitmap image and then just rename it and change the file extension, this error is reproducible.
If I use a known PNG with your code, I don't get your issue, but a COM exception is thrown:
The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))
Can you try it out with a random PNG off the web and see if you get the same result?

Categories

Resources