I have an image that was originally a PNG that I have converted to a byte[] and saved in a database. Originally, I simply read the PNG into a memory stream and converted the stream into a byte[]. Now I want to read the byte[] back and convert it to a BitmapImage, so that I can bind a WPF Image control to it.
I am seeing a lot of contradictory and confusing code online to accomplish the task of converting a byte[] to a BitmapImage. I am not sure whether I need to add any code due to the fact that the image was originally a PNG.
How does one convert a stream to a BitmapImage?
This should do it:
using (var stream = new MemoryStream(data))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
}
The BitmapCacheOption.OnLoad is important in this case because otherwise the BitmapImage might try to access the stream when loading on demand and the stream might already be closed.
Freezing the bitmap is optional but if you do freeze it you can share the bitmap across threads which is otherwise impossible.
You don't have to do anything special regarding the image format - the BitmapImage will deal with it.
using (var stream = new MemoryStream(data))
{
var bi = BitmapFrame.Create(stream , BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
}
Related
May be this sound stupid but, which one is the most efficient way to load image?
A
BitmapImage bmp = new BitmapImage();
using(FileStream fileStream = new FileStream(source_path, FileMode.Open))
{
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.StreamSource = fileStream;
bmp.EndInit();
if (bmp.CanFreeze)
bmp.Freeze();
images.source = bmp;
}
B
BitmapImage bmp = new BitmapImage();
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmp.UriSource = new Uri(source_path);
bmp.EndInit();
if (bmp.CanFreeze)
bmp.Freeze();
images.Source = bmp;
I remember I read somewhere that loading from a stream completely disables the cache. If thats true, does it means loading from a stream is better in term on memory management?
As far as i have understood, when you load a BitmapImage by setting its UriSource property, the image is always cached. I don't know of any way to avoid this. At least setting BitmapCreateOptions.IgnoreImageCache only ensures that an image is not retrieved from the cache, but it does not prevent that the image is stored in the cache.
The "Remarks" in BitmapCreateOptions says that
When IgnoreImageCache is selected, any existing entries in the image
cache are replaced even if they share the same Uri
My conclusion from this is that caching is only performed when an image is loaded by an Uri. In other words, if you really need to inhibit image caching, you will have to load an image by its StreamSource property.
However, if this is really "better in terms of memory management" is perhaps worth an experiment. You could try out both alternatives and see if you observe any significant difference in memory consumption.
I'm receiving byte[] image-data (from a webcam source, transmitted over network) and want to display it in my WPF Image control.
If I set the resolution to 160x120, I see a nice tiny image being displayed.
For any other resolution though I see nada, nothing, zilch, squat.
If I write the raw bytes to disk I can see that the data is indeed a valid jpg-image.
Just to make sure it's not jpeg-incompatibility I've tested it with png- and bmp-encoding/decoding as well with the same results; still no image.
Anyone got a bright idea ?
private bool OnImage(byte[] inJpg)
{
this.Dispatch(() =>
{
//File.WriteAllBytes("h:\\tmp\\test" + sImageNum++ + ".jpg", inJpg);
using (MemoryStream ms = new MemoryStream(inJpg))
{
BitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.None, BitmapCacheOption.Default);
ImageRemoteVideo.Source = decoder.Frames[0];
}
}
);
}
Assuming ImageRemoteVideo is an instance of a WPF Image control, you may be getting bit by some lazy resolve issues. The Image control has "BeginInit" and "EndInit" methods that you should probably be using. Put the BeginInit method before you set the stream, the other after you set it. Also, if I remember right, the Image class supports a StreamSource. You could try setting your MemoryStream (without disposing of it and without using the JpegBitmapDecoder) to that Property. If that works, then see if you can dispose it after the EndInit call.
I have just found a way to make it work, but I'd love it if someone can explain WHY it works :)
First off, I'm using the raw bytestream to create a BitmapImage.
BUT - if I use a "using" clause around the MemoryStream it doesn't work, if I don't it works.
So this doesn't work :
using (MemoryStream byteStream = new MemoryStream(inJpg))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.EndInit();
ImageRemoteVideo.Source = image;
}
But this does :
MemoryStream byteStream = new MemoryStream(inJpg);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.EndInit();
ImageRemoteVideo.Source = image;
Found it. It's like Brannon said a case of delayed loading.
Specifying "CacheOption = BitmapCacheOption.OnLoad;" fixes it !
Cheers !
Working version :
MemoryStream byteStream = new MemoryStream(inJpg);
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageRemoteVideo.BeginInit();
ImageRemoteVideo.Source = image;
ImageRemoteVideo.EndInit();
}
First, I load a BitmapImage into the Image control, whice is located on the Window.
Then I work with the Image control and then close the Window.
I do it 2-3 times in a minute and my memory fills up very quickly because the images do not unload from the memory for some reason when the window is closed.
So how do I unload BitmapImage from Image.Source control manually to free the RAM?
I believe the solution you are looking for is at http://www.ridgesolutions.ie/index.php/2012/02/03/net-wpf-bitmapimage-file-locking/. In my case, I was trying to find a way to delete the file after it was created, but it appears to be a solution to both issues.
Doesn't free up memory:
var bitmap = new BitmapImage(new Uri(imageFilePath));
Frees up memory, and allows file to be deleted:
var bitmap = new BitmapImage();
var stream = File.OpenRead(imageFilePath);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
stream.Close();
stream.Dispose();
Optionally, also freeze the BitmapImage:
bitmap.Freeze();
In my situation it seems that the bitmap caching was the issue. I was previously loading bitmaps like this:
Bitmap bitmap = new Bitmap();
using(var stream = new FileStream(...))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
image.Source = bitmap;
Continuously replacing image.Source the same way just built up memory, manually forcing garbage collection wasn't really helping.
Instead, disabling the caching and having it use the stream (requires leaving the stream open until the image is displayed) paired with manual garbage collection eliminated memory build up for me.
Stream mediaStream;
void DisposeMediaStream()
{
if (mediaStream != null)
{
mediaStream.Close();
mediaStream.Dispose();
mediaStream = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
}
}
void Update()
{
DisposeMediaStream();
var bitmap = new BitmapImage();
mediaStream = new FileStream(...);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.None;
bitmap.StreamSource = mediaStream;
bitmap.EndInit();
bitmap.Freeze();
ControlImage.Source = bitmap;
}
This way I can cycle through tons of images (like Windows Photo Viewer) and memory stays low. Note that the stream does not have to stay open once the image has actually rendered.
You can set the object to null, so that the BitmapImage object is no longer referenced.
In this situation, the GC should take care of freeing up resources.
You can call GC.Collect but it can affect performance if used too often.
You can call Dispose() on the images in the window's Closed event. I think it may also be possible to optimise your memory footprint using different caching options.
Edit:
You can't call Dispose(), instead, you might consider BitmapCacheOption.None. The image will be read directly from disk and not cached in memory.
I have a very large image (600mb) 30000x30000 and want to load it into a wpf image control.
I can watch this image with the Windows Photo Viewer!
I set my testapp to 64bit and used the following code.
var image = new BitmapImage();
image.BeginInit();
// load into memory and unlock file
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = uri;
image.EndInit();
imagecontrol.source = image;
The test app just shows a white screen with this large image.
Smaller ones like 100mb and 7000x7000 are working.
What am I doing wrong? Sry for my bad english and thanks in advance.
64-Bit Applications.
As with 32-bit Windows operating systems, there is a 2GB limit on the size of an object you can create while running a 64-bit managed application on a 64-bit Windows operating system.
Picture can be fetched directly from the hard drive to reduce a memory usage.
You can use BitmapCacheOption
Code bellow will read the image directly from the HDD and small thumbnail will be generated without the original image cache:
public BitmapImage memoryOptimizedBitmapRead(string url,FrameworkElement imageContainer)
{
if (string.IsNullOrEmpty(url) || imageContainer.ActualHeight<= 0)
{
return null;
}
var bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.None;
bi.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bi.DecodePixelHeight = (int)imageContainer.ActualHeight;
bi.UriSource = new Uri(url, UriKind.Absolute);
// End initialization.
bi.EndInit();
bi.Freeze();
return bi;
}
Max thumbnail size is determined by the image container size.
Method usage example:
var image= new Image();
image.Source = memoryOptimizedBitmapRead(...);
I'd divide it into 10 (3000x3000) segments and put them into 10 files.
Also check what format you're using it. It may be filling up the threshold for file size or for that particular format. Try TIF format, then try JPG, then try BMP, etc.. Also see if you can compress it with the JPG format to 40-50% and see if that changes anything.
Let me know what you find out.
I need to open all frames from Tiff image in WPF into memory and then delete the source. And after that I eventually need to render that image (resized according to window size). My solution is quite slow and I cannot delete file source before the first require. Any best practices?
Use CacheOption = BitmapCacheOption.OnLoad
This option can be used with the BitmapImage.CacheOption property or as an argument to BitmapDecoder.Create() If you want to access multiple frames once the images is loaded you'll have to use BitmapDecoder.Create. In either case the file will be loaded fully and closed.
See also my answer to this question
Update
The following code works perfectly for loading in all the frames of an image and deleting the file:
var decoder = BitmapDecoder.Create(new Uri(imageFileName), BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
List<BitmapFrame> images = decoder.Frames.ToList();
File.Delete(imageFileName);
You can also access decoder.Frames after the file is deleted, of course.
This variant also works if you prefer to open the stream yourself:
List<BitmapFrame> images;
using(var stream = File.OpenRead(imageFileName))
{
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
images = decoder.Frames.ToList();
}
File.Delete(imageFileName);
In either case it is more efficient than creating a MemoryStream because a MemoryStream keeps two copies of the data in memory at once: The decoded copy and the undecoded copy.
I figured it out. I have to use MemoryStream:
MemoryStream ms = new MemoryStream(File.ReadAllBytes(image));
TiffBitmapDecoder decoder = new TiffBitmapDecoder(ms, BitmapCreateOptions.None, BitmapCacheOption.None);
List<BitmapFrame> images = new List<BitmapFrame>();
foreach (BitmapFrame frame in decoder.Frames) images.Add(frame);