Background:
I'm building an application which will open a potentially large number of photos, generate a thumbnail to present to the user, then allow things like exif data viewing/clearing and minor post processing.
I want to allow the user to scroll through the images without pausing to load each one as it becomes visible, but I also don't want to keep dozens or hundreds of full size bitmap images in memory.
I had built a prototype of this task using System.Drawing using Image objects and their GenerateThumbnailImage method, but decided to move to WPF and use System.Windows.Media.ImageSource derived objects and the TransformedBitmap with a ScaledTransform to generate the thumbnail.
What I found, though, is that when I create a TransformedBitmap, it has a reference back to the source image, which is available and still present in memory. How do I release this source object?
Some relevant C# code:
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using System;
...
private void LoadImage(){
//Called by my class internally to handle generating the thumbnail
//Intent is to keep only metadata and a thumbnail bitmap in memory
Stream handle = File.OpenRead(FileName);
BitmapDecoder source = BitmapDecoder.Create(handle,BitmapCreateOptions.None,BitmapCacheOption.OnLoad);
handle.Dispose();
//Determine a scaling ratio to force the larger of height or width to fit inside my desired thumbnail size (int)MaxDim.
ScaleRatio = Math.Min(MaxDim/Math.Max(source.Frames[0].PixelHeight,source.Frames[0].PixelWidth),1); //a public member of the class, Double
_ImageSource = new TransformedBitmap(source.Frames[0],new ScaleTransform(ScaleRatio,ScaleRatio)); //private member of the class, ImageSource
_ImageSource.Freeze();
_Exif = source.Frames[0].Metadata; //private member of the class, ImageMetadata
}
The problem here is that while I hoped that the (BitmapDecoder)source would be released, I can still access the object via _ImageSource.Source.
I have considered using CopyPixels or encoding the TransformedBitmap back into a byte[] stream to create a new, hopefully unattached bitmap, but both of those methods seem like unnecessary reprocessing if I can just abandon or dispose of the source or if there is some simple and fast way to create a shallow clone that I haven't discovered. My attempt at a shallow clone using BitmapFrame.Create(TransformedBitmap) doesn't free the memory either, but also doesn't leave me with an obvious reference.
Some testing watching memory consumption shows each image loaded costs about 30MB. An approximately 200x200#32bpp image should be about 160kB, not counting overhead.
The question again as a TL;DR: how do I release the reference to the source bitmap after a TransformedBitmap uses it?
Related
Why is Mat not enough in EmguCV?
Why can't Matrix<> load an image from a file itself?
For instance,
Mat img = new Mat(path);
is a valid operation. But,
Matrix<byte> img = new Matrix<byte>(path);
or,
Matrix<byte> img = Matrix<byte>.FromFile(path);
aren't valid operations.
Based on the information from the Emgu Wiki the fundamental difference between the two types is whether the underlying data array is managed or not.
Mat is a wrapper around the C++ cv::Mat class. Generally this class acts as a smart pointer which manages the memory allocated for the data array it owns (although it's able to just observe as well -- a good example of this capability is the ability to return a Mat header for a Matrix instance in C#). This means that OpenCV is able to (re)allocate the memory as necesssary. The trade-off is that in such cases it's more difficult to access the underlying data effectively in C#.
The Matrix class uses a managed array to hold the data. That means you can easily access the underlying data array in C#.
Honestly, the best person to tell you why it's not possible to load Matrix from an image file would be the author. My guess would be that it's intended to represent other things than images. Technically this could be added in the same way as the ability to load an image file was given to the Mat wrapper (the C++ equivalent has no such feature).
This question is specific to System.Drawing.Bitmap.
Consider this situation: I create a bitmap like so
public Bitmap GetImage(string sourceImage)
{
//sourceImage contains something like "C:\\Users\\my.name\\path\\to\\image.jpg"
//error/valid filename checking omitted in this example
Bitmap img = new Bitmap(sourceImage);
return img;
}
Now my code goes and does things with that Bitmap. Later, an unrelated section of the code decides that it is done with that image and it doesn't need to retain the Bitmap object anymore. However, there is a chance that it will be needed again, so it wants to remember what sourceImage was so that if it does need it again, it knows how to recreate it. This is mostly due to memory; storing a pathway string takes less space than storing a (potentially) large image.
Does a Bitmap remember where it came from, or must it carry around that information with it?
I considered just assigning sourceImage to the Tag property, however I'm using Tag for other things and I'd rather not overcomplicate it. I'm hoping the source information is still accessible to save me the trouble.
I would recommend creating a container class to provide the extra functionality
class BitmapContainer : IDisposable
{
Bitmap Value {get; private set;}
string OriginalLocation {get; private set;}
public BitmapContainer(string sourceImage)
{
Value = new Bitmap(sourceImage);
OriginalLocation = sourceImage;
//you get the picture
}
//Don't forget to implement a dispose pattern because Bitmap uses native resources
}
You could even make the bitmap getter intelligent and load the Bitmap if not currently loaded, and have another method for unloading (Dispose and set to null) until needed again
Is there a way to access the underlying memory of a BitmapImage object with c# pointers?
I know that there's a CopyPixels method but it makes a copy of the pixels array into a new array (duplicating memory requirements). If you open a large image (2gb) it allocates a lot of un-useful stuff. And if you want to operate some sort of elaboration, like CCLA, it takes a huge amount of memory.
I need only to read the pixel array.
Is it possible to address pixels directly like you can do in System.Drawing.Bitmap?
I wrote a fast bitmap access for System.Drawing.Bitmap, but as I'm using WPF, I need the same functionality for BitmapSource. Otherwise I have to duplicate the image loading (Bitmap for my old method and BitmapSource to show the image in WPF) taking a lot of memory and time.
Thank you
Lorenzo
A bitmap source does not necessarily have backing memory for the entire image. An example of when it does not would be an image file on disk which is lazy loaded.
The only access you have with WIC, and therefore WPF, is the CopyPixels method. Certain subclasses of BitmapSource will allow access to a buffer, but they are internally just allocating memory and calling CopyPixels themselves.
I would assume that whatever operation does not require access to the entire image at a time. If so, you can call CopyPixels to a smaller buffer, and window your access to the image. Most decoders, when a single pixel is requested, will buffer the entire stride, or in the case of JPEG, then entire block.
I am not sure what CCLA is, and cannot find a definition that seems to fit, but if it is some sort of transform on the source image, you can implement it as a BitmapSource. That way, you can compose a full chain which will
read the image from disk (TiffBitmapEncoder et al.)
scale or translate them (TransformedBitmap)
then window it to only the portion you need (CroppedBitmap)
use a format conversion (FormatConvertedBitmap)
pass it to your algorithm (CclaTransformBitmap)
and finally render it to a WritableBitmap, which gives you access to the buffer
With careful attention to the CacheOption used on the source image, as well as the order of transforms, you should be able to access an arbitrarily large image without significant memory impact.
If you already have a performant algorithm for GDI (System.Drawing), there is no sense re-implementing it. You can still display your final bitmap using Imaging.CreateBitmapSourceFromHBitmap or by using a WindowsFormsHost to host the control you previously built.
I have a collections of objects, each object contains a BitmapImage. I have this collection bound to a FlipView. When user flips a page in he FlipView, the BitmapInmage of the selected object gets loaded from ApplicationData and I set the BitmapImage of the previous object to null to relieve the memory. The problem is, that the memory never gets relieved and the app crashes after some time of flipping. When I look at the collection, only the actual item has its BitmapImage set, all the others have it as null. So how do I relieve the memory?
The way I load the images:
StorageFile s = await ApplicationData.Current.LocalFolder.GetFileAsync(localFilename);
BitmapImage bitmapImage = new BitmapImage();
using (var stream = await s.OpenAsync(FileAccessMode.Read))
{
bitmapImage.SetSource(stream);
}
return bitmapImage;
Edit: I think the problem may be the way I load the Image, I guess the file stream does not get freed
I ran into a similar issue with Windows Phone 7 applications.
One trick that worked for me was removing the image from the parent element.
grid1.Children.Remove(image1);
image1 = null;
I'm not sure if this will help in your situation though.
More details about this issue from my blog post.
What worked for me was setting the UriSource property to null before setting the object itself to null.
If these images are using large amounts of memory then it might make sense to force Garbage Collection to free the memory. This will only work if you've removed all strong references to the memory. You can run into performance problems if you're too aggressive in forcing garbage collection, so you'll want to experiment with this. For example, you might want to force collection only after "nulling out" a few images.
You can force garbage collection using the GC.Collect method.
http://msdn.microsoft.com/en-us/library/bb384155.aspx
If you are using x:Bindings in UWP, you may find a similar issue, however, if you run Bindings.Update() after clearing out your classes (Say your image is in a class, held in an observable collection), first run .Clear() on that ObservableCollection, then Bindings.Update(), then you will find that your will regain free space.
In .net setting something to null never does anything - GC is unrelated and happens when it chooses. Instead, check whether the image implements IDisposable: if it does, you should be calling Dispose() when you have finished with it. Moat commonly, you would do this automatically via a "using" statement:
using(var img = GetImage()) {
// todo: some stuff involving img
}
I have some code that does
MemoryStream ms = new MemoryStream();
...
return Image.FromStream(ms);
It fails in very eclectic ways since the Image object does not hold a ref to the stream, so it can get disposed if the GC kicks in which results in GDI+ errors.
How do I work around this (without saving the stream to disk, or altering my method sigs) ?
This seems highly unlikely to me - it would cause a problem for almost any use of Image.FromStream.
It seems more likely to me that something's disposing of your MemoryStream, which it shouldn't.
Could you provide a short but complete program which demonstrates the problem? Forcing garbage collection should make it relatively easy to reproduce - you could even create your own class deriving from MemoryStream with a finalizer to show whether or not it really is being collected (well, finalized at least).
There isn't a way to do it without changing your code somewhat. The Remarks section for the documentation for the static FromStream method on the Image class states:
You must keep the stream open for the
lifetime of the Image.
That being said, you have to make sure that while the Image is accessing the Stream, the stream is open. It would also appear (looking through Reflector) that the FromImage method doesn't actually cause the Image instance to hold onto a reference to the Stream the image was loaded from.
That being said, you to somehow link the image and the MemoryStream (or Stream) together so that it doesn't get GCed. If don't really retain "ownership" of the image (it is passed around), then I recommend that you create a data structure which will hold the reference to the Image and to the Stream and pass the two around in tandem.