Running out of memory when getting a bitmap from a server? - c#

I'm making an application which uses MANY images. The application gets the images from a server, and downloads them one at a time.
After many images the creation of a bitmap returns an exception, but i don't know how to solve this. Here is my function for downloading the images:
public static Bitmap getImageFromWholeURL(String sURL)
{
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(sURL);
myRequest.Method = "GET";
// If it does not exist anything on the url, then return null
try
{
HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(myResponse.GetResponseStream());
myResponse.Close();
return bmp;
}
catch (Exception e)
{
return null;
}
}
Can anyone help me out here?
Thanks in advance!

Stream that response to disk rather than keep it in memory. Then keep around the information about the image you've saved to a temporary place instead of the image itself.

If you're showing these all in a Picturebox (and based on your comments I think you are) then you should Dispose of the old images (this blog entry helps explain it):
if(myPictureBox.Image != null)
{
myPictureBox.Image.Dispose();
}
myPictureBox.Image = getImageFromWholeURL(url);
As a side note on style, method names are supposed to be PascalCase, not camelCase and I'd lose the hungarian notion on the parameter.

"Many images" is of course closely associated with running out of memory. Bitmaps can get large, they'll eat up a lot of unmanaged virtual memory. You'll have to make your program smarter and store less bitmaps in memory. Or save them temporarily to a file. Or re-download them if necessary. And properly clean up their resources with the Dispose() method, especially important for the Bitmap class.

What do you do with the System.Drawing.Bitmap objects? Do you keep all of them in memory? Then it is inevitable that you'll get an out of memory exception at some point.
Based on your needs you should discard the images at some point. If you do need them, store them in a file in flash. Also, try using files of smaller sizes.

Related

.net Compact Framework 2.0 - OutOfMemory Exception When downloading an image

Im creating software for a Symbol MC75A using c# .net CF 2.0. We scan a barcode and it returns stock information but i am trying to add a feature that gets an image from a url. Each scan refreshes the screen with new data from database and also gets the image from the new url. It scans a few barcodes and returns maybe 4/5 images without issue then all of a sudden a OutOfMemoryException occurs. The code im using to Get Image is:
public Bitmap GetImage(string URL)
{
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(URL);
myRequest.Method = "GET";
myRequest.AllowWriteStreamBuffering = false;
HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(myResponse.GetResponseStream());
myResponse.Close();
return bmp;
}
Called by:
pbImage.Image = GetImage(ProductImage);
pbImage.SizeMode = PictureBoxSizeMode.StretchImage;
I have tried to dispose of image before each new GET with pbImage.Image.Dispose() but still getting the same exception. The image sizes are between 100KB and 550KB. Whether it makes a difference the images sizes are always over 1000px each side.
Am i missing the correct way of disposing before re-getting or is it somehow caching all these images which then creates an outofMemory exception?
I have found my solution herer:
OutOfMemoryException loading big image to Bitmap object with the Compact Framework
It seems it is the system.drawing when decompressing the 2000px image it is creating an uncompressed image giving a larger size on the memory hense the exception. The solution shows a way to obtain a thumbnail of the image rather than the whole image.
Thanks for your help again.
Just incase someone has access to the image location. A better work around for me was to creat a webpage named resize.aspx and have the picture box populate from the page with the image filename as a parameter. Now that page (being on the server in the same location as the images) was able to resize the image and return a small 300x300px image. Seemed to be the best solution i could think of. This can also work for images at any url if you pass the full url of the image into another page and that page returns the required thumbnail.

Delete image file right after compressing is complete. How?

I have a website which allow a user to upload some image on the server via upload form. When this image is uploaded, asp.net service should compress that particular image. Compressing an image already works great but I would like to delete an original image from server's disk when compressing is complete.
Please take a moment and look at my code below:
if (fileUl.PostedFile.ContentType == "image/jpeg" || fileUl.PostedFile.ContentType == "image/png")
{
fileUl.SaveAs(fullPath);
System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath);
compressImage(destinationPath, image, 40);
System.IO.File.Delete(fullPath);
} // nested if
If I try to run code above I'm getting
System.IO.IOException: The process cannot access the file [filepath] because it is being used by another process.
I actually expect that because I think it's because, server is still compressing an image when next line of code wants to delete that image (I think this is it). So my question is:
How to wait for compressing to be complete and then run "delete" code?
using (System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath))
{
//DO compression;
}
System.IO.File.Delete(fullPath);
Better do it all in compress function:
public void DoCompression(string destination, string fullPath, int ratio)
{
using (System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath))
{
//DO compression by defined compression ratio.
}
}
The caller function may look like:
DoCompression(destinationPath, fullPath, 40);
DoCompression(destinationPath, fullPath, ??);
System.IO.File.Delete(fullPath);
From Image.FromFile:
The file remains locked until the Image is disposed.
Try:
System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath);
compressImage(destinationPath, image, 40);
image.Dispose(); //Release file lock
System.IO.File.Delete(fullPath);
or (slightly cleaner if an exception is thrown):
using(System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath))
{
compressImage(destinationPath, image, 40);
}
System.IO.File.Delete(fullPath);
It depends on what you are doing in compressImage. if you are using threads to compress the image then you are right. but I think different. You have to Dispose the image object.
Several approaches
Use sequential execution
Blocking compress image
Blocking delete image
As others suggested make sure you free the resources correctly after each stage
Use wait handles, such as ManualResetEvent
Implement a queueing producer-consumer, where producer enqueues an image to be processed, and consumer dequeues an item, compresses it and deletes original image. Producer and consumer are likely to be different processes.
I would probably go for the first approach as it is simple. Second approach is easy to implement, but it holds threads and degrades performance of web application. The third approach is useful if your web site is under heavy load.
Wrap the image in a Using statmement to ensure proper disposal of the image object.
if (fileUl.PostedFile.ContentType == "image/jpeg" || fileUl.PostedFile.ContentType == "image/png")
{
fileUl.SaveAs(fullPath);
using(System.Drawing.Image image = System.Drawing.Image.FromFile(fullPath))
{
compressImage(destinationPath, image, 40);
}
System.IO.File.Delete(fullPath);
} // nested if

OutOfMemory Exception when loading lots of Images from Isolated storage

EDIT: I keep getting OutOfMemoryException was unhandled,
I think it's how I am saving the image to isolated storage ,I think this is where I can solve my problem how do I reduce the size of the image before I save it? (added code where I save Image)
I am opening images from Isolated storage sometimes over 100 images and I want to loop over them images but I get a OutOfMemory Exception when there is around 100 to 150 images loaded in to a storyboard. How can I handle this exception, I have already brought down the resolution of the images. How can I handle this exception and stop my app from crashing?
I get the exception at this line here
image.SetSource(isStoreTwo.OpenFile(projectFolder + "\\MyImage" + i + ".jpg", FileMode.Open, FileAccess.Read));//images from isolated storage
here's my code
private void OnLoaded(object sender, RoutedEventArgs e)
{
IsolatedStorageFile isStoreTwo = IsolatedStorageFile.GetUserStoreForApplication();
try
{
storyboard = new Storyboard
{
//RepeatBehavior = RepeatBehavior.Forever
};
var animation = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTarget(animation, projectImage);
Storyboard.SetTargetProperty(animation, new PropertyPath("Source"));
storyboard.Children.Add(animation);
for (int i = 1; i <= savedCounter; i++)
{
BitmapImage image = new BitmapImage();
image.SetSource(isStoreTwo.OpenFile(projectFolder + "\\MyImage" + i + ".jpg", FileMode.Open, FileAccess.Read));//images from isolated storage
var keyframe = new DiscreteObjectKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(100 * i)),
Value = image
};
animation.KeyFrames.Add(keyframe);
}
}
catch (OutOfMemoryException exc)
{
//throw;
}
Resources.Add("ProjectStoryBoard", storyboard);
storyboard.Begin();
}
EDIT This is how I am saving the image to Isolated storage, I think this is where I can solve my problem, How do I reduce the size of the image when saving it to isolated storage?
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
string fileName = folderName+"\\MyImage" + savedCounter + ".jpg";
try
{
// Save picture to the library camera roll.
//library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
// Save picture as JPEG to isolated storage.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the image to isolated storage.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}
I would appreciate if you could help me thanks.
It doesn't matter how large your images are on disk because when you load them into memory they're going to be uncompressed. The memory required for the image will be approximately (stride * height). stride is width * bitsPerPixel)/8, and then rounded up to the next multiple of 4 bytes. So an image that's 1024x768 and 24 bits per pixel will take up about 2.25 MB.
You should figure out how large your images are, uncompressed, and use that number to determine the memory requirements.
You are getting the OutOfMemory Exception because you are storing all the images in memory at the same time in order to create your StoryBoard. I don't think you will be able to overcome the uncompressed bitmap size that the images require to be displayed on screen.
So to get past this we must think about your goal rather than trying to fix the error. If your goal is to show a new image in sequence every X ms then you have a few options.
Keep using StoryBoards but chain them using the OnCompleted event. This way you don't have to create them all at once but can just generate the next few. It might not be fast enough though if you're changing images every 100ms.
Use CompositionTarget.Rendering as mentioned in my answer here. This would probably take the least amount of memory if you just preload the next one (as opposed to having them all preloaded as your current solution does). You'd need to manually check the elapsed time though.
Rethink what you're doing. If you state what you are going after people might have more alternatives.
To answer the edit at the top of your post, try ImageResizer. There's a NuGet package, and a HanselBlog episode on it. Obviously , this is Asp.Net based, but I'm sure you could butcher it to work in your scenario.
Tackling these kind of problems at design layer usually works better.
Making application smart about the running environment via some configurations makes your application more robust. For example you can define some variables like image size, image count, image quality... based on available memory and set these variables at run-time in your App. So your application always works; fast on high memory machines and slow on low memory ones; but never crash. (Don't believe working in managed environment means no worry about the environment... Design always matters)
Also there are some known design patterns like Lazy Loading you can benefit from.
I don't know about windows phone in particular, but in .net winforms, you need to use a separate thread when doing a long-running task. Are you using a BackgroundWorker or equivalent? The finalizer thread can become blocked, which will prevent the resources for the images from being disposed. Using a separate thread from the UI thread will allow will allow the Dispose method to be run automatically.
Ok, an image (1024x768) has at least a memsize of 3 mb (argb)
Don't know how ObjectAnimationUsingKeyFrames works internal. Maybe you can force the gc by destroying the instances of BitmapImage (and KeyFrames) without loss of its data in the animation.
(not possible, see comments!)
Based on one of your comments, you are building a Time Lapse app. Commercial time-lapse apps for WP7 compress the images to video, not stills. e.g. Time Lapse Pro
The whole point of video playback is to reduce similar, or time-related, images to highly compressed stream that do not require massive amounts of memory to play back.
If you can add the ability to encode to video, in your app, you will avoid the problem of trying to emulate a video player (using 100s of single full-resolution frames as a flick-book).
Processing the images into video server-side may be another option (but not as friendly as in-camera).

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.

Loading an image from a stream without keeping the stream open

Is it possible to use the FromStream method of System.Drawing.Image without having to keep the stream open for the lifetime of the image?
I have an application which loads a bunch of toolbar graphics from resource files, using a combination of Image.FromStream and Assembly.GetManifestResourceStream.
The problem I'm having is while this works fine on Windows 7, on Windows XP the application crashes if a user interface element linked to one of these images is disabled. On Windows 7, the image is rendered in grayscale. On XP, it crashes with an out of memory exception.
After a load of hairpulling I have finally traced it to the initial loading of the image. As a matter of course, if I create any object implementing IDisposable that is also destroyed in the same method, I wrap it in a using statement, for example
using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
{
image = Image.FromStream(resourceStream);
}
If I remove the using statement so that the stream isn't disposed, then the application no longer crashes on XP. But I now have a bunch of "orphan" streams hanging about - the images are stored in command classes and these correctly dispose of the images when they themselves are disposed, but the original stream isn't.
I checked the documentation for FromStream and it confirms the stream needs to remain open. Why this hasn't crashed and burned on the Windows 7 development system is a mystery however!
I really don't want this stream hanging around, and I certainly don't want to have to store a reference to this stream as well as the image so I can dispose of it later. I only have need of that stream once so I want to get rid of it :)
Is it possible to create the image and then kill of the stream there and then?
The reason the stream needs to be open is the following:
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.
The documented workaround is to create either a non-indexed image using Graphics.DrawImage or to create an indexed Bitmap from the original image as described here:
Bitmap and Image constructor dependencies
According to the documentation of Image.FromStream, the stream must be kept open while the image is in use. Therefore, even if closing worked (and there's nothing to say you can't close a stream before it's disposed, as far as the stream object itself goes) it may not be a very reliable approach.
You could copy the image to another image object, and use that. However, this is likely to be more memory intensive than just keeping the stream open.
You could save the stream to a temporary file and use the Image.FromFile method. Or simply don't embed the image, keep it as a file and load it from this file at runtime.
I'am sure this will help someone :)
I used it for my dataGridView_SelectionChanged:
private void dataGridViewAnzeige_SelectionChanged(object sender, EventArgs e)
{
var imageAsByteArray = File.ReadAllBytes(path);
pictureBox1.Image = byteArrayToImage(imageAsByteArray);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}

Categories