WPF Images not rendering from in-memory sources - c#

I have a data class in my application which maintains a collection of byte arrays representing JPEG images. It's defined as:
private ArrayList FrameList = new ArrayList();
I'm having some troubles with my Image object rendering a blank page (and taking its sweet time to do it as well). When I insert a blank image with a 2K in-memory byte array (byte x[] = { lots of hex values };):
FrameList.Insert(currFrame, x);
and then import a JPEG file over it later on with:
byte[] bytes = File.ReadAllBytes(fspec);
FrameList[currFrame] = bytes;
the array is read correctly into memory and stored in the ArrayList (confirmed with the debugger).
However,I then have a function to get the image:
public BitmapImage getCurrPicture()
{
MemoryStream strm;
BitmapImage bmp = new BitmapImage();
strm = new MemoryStream((byte[])FrameList[currFrame-1]);
bmp.CacheOption = BitmapCacheOption.None;
bmp.BeginInit();
bmp.StreamSource = strm;
bmp.EndInit();
strm.Close();
return bmp;
}
which is called:
imgPicB.Source = data.getCurrPicture();
and it doesn't always render.
imgPicB is defined in my XAML as:
<Image x:Name="imgPicB"
Width="400"
Height="300"
Stretch="Fill"
VerticalAlignment="Top" />
Funny thing is, if I use the exact same JPEG setting the source with setting the source to the file URI directly, it renders fine.
Is there some problem with using in-memory JPEG images in WPF? Is there some extra smarts performed when loading from a file (say auto-detection of the image type)?

Try this:
public BitmapSource GetCurrPicture()
{
var bitmapImage = new BitmapImage();
using (Stream stream = new MemoryStream((byte[])FrameList[currFrame-1]))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
This works by having WPF decode the image immediately with OnLoad, and then release the reference to the stream when it is done. This means the stream can be garbage collected when it leaves the function. Otherwise, BitmapImage could hold onto the stream until it is rendered.
In the code posted in the question, the render would fail because:
the decode is delayed; and
the stream has been closed at the point where the decode tries to happen.

Link
DwayneNeed 20 Jun 2008 5:11 PM Comments
Caching BitmapImage will store the decoded bitmap in system memory. You can control when this happens by setting the CacheOption property. BitmapImage also maintains a cache of previous BitmapImage instances (via weak references) so that loading the same Uri multiple times will share the same instance. To avoid this cache, you can include the BitmapCreateOptions.IgnoreImageCache flag in the CreateOptions property.

Related

Is it possible to persist the original data used to build a BitmapImage without reencoding?

Elaborating on the title a little bit more, we have some code that loads an image file from disk, then converts it into a BitmapImage for display on screen. We then need to persist that data to a different storage mechanism as a byte array so it can be reloaded as-is later.
The issue is from what I understand, to convert a BitmapImage back into a byte array, you have to use an encoder, but if you use an encoder, you're re-encoding the image on every save so unless you use a lossless format, you could be downgrading the quality of your image, but to not use a lossless format means you're potentially doubling storage space over something like jpeg data.
My thought is to create a 'holder' type that holds the original, raw data (again, say from a jpg file) and use it to create a BitmapImage on-demand, caching it for future retrieval.
Here's what I have so far for the 'holder' type. (Note the implicit converter so I can use it wherever a BitmapImage is accepted):
#nullable enable
class PersistableBitmapImage{
public PersistableBitmapImage(byte[] bytes)
=> _bytes = bytes;
private byte[] _bytes;
public byte[] Bytes {
get => _bytes;
set{
_bytes = value;
_bitmapImage = null;
}
}
private BitmapImage? _bitmapImage;
public BitmapImage BitmapImage
=> _bitmapImage ?? (_bitmapImage = Bytes.MakeBitmapImage());
#region Implicit Operators
public static implicit operator BitmapImage(PersistableBitmapImage persistableBitmapImage)
=> persistableBitmapImage.BitmapImage;
#endregion Implicit Operators
}
Here's the helper extension to create the BitmapImage:
public static class ByteArrayExtensions {
public static BitmapImage MakeBitmapImage(this byte[] bytes){
using var ms = new MemoryStream(bytes);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
return bitmapImage;
}
}
The issue I have with this design is in cases where that BitmapImage is accessed, I'm now holding the image data twice, once in compressed format (the Data property) and another in the realized/expanded BitmapImage property. The only up-side is the latter is only 'realized' if accessed and the compressed former is what's persisted.
Still feels a little 'icky' to me. Is there a way to do this without holding the image data twice?
You can avoid disposing MemoryStream here (disposing of MemoryStream while good practice doesn't do anything interesting anyway, there are no resources to dispose):
using var ms = new MemoryStream(bytes);
And then be able to access that same memory stream later by accessing bitmapImage.StreamSource. Note that returned stream Position might be not 0, because BitmapImage might have read it already, so don't forget to reset position to 0 if necessary. Usually it's bad idea to call MemoryStream.GetBuffer() (ToArray() is preferred), but in this particular case it might be appropriate to avoid unnecessary copy.

Displaying live images

I have a function that returns an array of bytes containing the data of a bmp img live from a camera (including header).
I write that array in to a MemoryStream object.
That object, I pass to a Image object constructor, which will be passed to a PictureBox.
tl;dr:
byte[] AoB = GetImage();
MemoryStream ms = new MemoryStream();
ms.Write(AoB, 0, AoB.Length);
pictureBoxImage.Image = Image.FromStream(ms);
ms.Dispose();
All of this is done in a timer with a delay of 200 ms (5fps).
It works fine for about a minute or 2 until OutOfMemory exception.
For some reason, even though I dispose of the memory used, it keeps generating new one.
I've also tried to declare ms as global and flush it every time, still no go.
How can I stream the images while using the same memory space?
Try disposing the Image objects when you're done with them as well:
byte[] AoB = GetImage();
using (MemoryStream ms = new MemoryStream()) {
ms.Write(AoB, 0, AoB.Length);
Image old = pictureBoxImage.Image;
pictureBoxImage.Image = Image.FromStream(ms);
if (old != null) {
old.Dispose();
}
}
You definitely should dispose the old images when you are done with them (as adv12 mentioned), however you are also creating two byte[]s in memory. The first one is the one you get from GetImage() the second one is the one stored inside the MemoryStream and that one could be larger than your source array due to its growing algorithms.
Instead use this overload of the MemoryStream constructor to allow you to pass the byte[] directly in and the MemoryStream will reuse that single array for its internal store, reducing the memory requirement.
byte[] AoB = GetImage();
using (MemoryStream ms = new MemoryStream(AoB)) {
Image old = pictureBoxImage.Image;
pictureBoxImage.Image = Image.FromStream(ms);
old.Dispose();
}
Try setting your timer's AutoReset=false and manually starting it over at the end of the last call.
myTimer.AutoReset = false;
Start after image assignment.
byte[] AoB = GetImage();
MemoryStream ms = new MemoryStream();
ms.Write(AoB, 0, AoB.Length);
pictureBoxImage.Image = Image.FromStream(ms);
ms.Dispose();
myTimer.Start().
The timer has the potential to get ahead of the retrieval of the images.

convert array of bytes to bitmapimage

I'm going to convert array of bytes to System.Windows.Media.Imaging.BitmapImage and show the BitmapImage in an image control.
When I'm using the first code, noting happens! no error and no image is displayed. But when I'm using the second one it works fine! can anyone say what is going on?
first code is here:
public BitmapImage ToImage(byte[] array)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(array))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = ms;
image.EndInit();
return image;
}
}
second code is here:
public BitmapImage ToImage(byte[] array)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = new System.IO.MemoryStream(array);
image.EndInit();
return image;
}
In the first code example the stream is closed (by leaving the using block) before the image is actually loaded. You must also set BitmapCacheOptions.OnLoad to achieve that the image is loaded immediately, otherwise the stream needs to be kept open, as in your second example.
public BitmapImage ToImage(byte[] array)
{
using (var ms = new System.IO.MemoryStream(array))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
return image;
}
}
From the Remarks section in BitmapImage.StreamSource:
Set the CacheOption property to BitmapCacheOption.OnLoad if you wish
to close the stream after the BitmapImage is created.
Besides that, you can also use built-in type conversion to convert from type byte[] to type ImageSource (or the derived BitmapSource):
var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(array);
ImageSourceConverter is called implicitly when you bind a property of type ImageSource (e.g. the Image control's Source property) to a source property of type string, Uri or byte[].
In the first case, you defined your MemoryStream in a using block, which causes the object to be disposed when you go out of the block. So you return a BitmapImage with a disposes (and non-existing) stream.
MemoryStreams keep no unmanaged resources, so you can leave the memory and let the GC handle the freeing process (but that's not a good practice).

Create a BitmapImage from ushort[]

Is it possible to create a BitmapImage from a ushort array? and if so how?
At the minute I'm creating a Bitmap, converting it to a Bitmap array and displaying it, but this is too slow, I need to continuously update the image (live video feed), while each frame is being created the UI studders, this is making my app very slow when video is running. So I need to get my ushort[] into a BitmapImage as fast as possible
Thanks,
Eamonn
here you have an example of how to get a BitmapImage through a MemoryStream, this might help you
you can use BitConverter to convert ushorts to byte for input to MemoryStream
Assuming you're working with values between 0 and 255 you could cast it into an array of bytes and then load it into a MemoryStream:
// Please note that with values higher than 255 the program will throw an exception
checked
{
ushort[] values = { 200, 100, 30/*, 256*/ };
var bytes = (from value in values
select (byte)value).ToArray();
// Taken from: http://stackoverflow.com/questions/5346727/wpf-convert-memory-stream-to-bitmapimage
using (var stream = new MemoryStream(data))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
}
}

Load a byte[] into an Image at Runtime

I have a byte[] that is represented by an Image. I am downloading this Image via a WebClient. When the WebClient has downloaded the picture and I reference it using its URL, I get a byte[]. My question is, how do I load a byte[] into an Image element in WPF? Thank you.
Note: This is complementary to the question I asked here: Generate Image at Runtime. I cannot seem to get that approach to work, so I am trying a different approach.
Create a BitmapImage from the MemoryStream as below:
MemoryStream byteStream = new MemoryStream(bytes);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.EndInit();
And in XAML you can create an Image control and set the above image as the Source property.
You can use a BitmapImage, and sets its StreamSource to a stream containing the binary data. If you want to make a stream from a byte[], use a MemoryStream:
MemoryStream stream = new MemoryStream(bytes);
In .Net framework 4.0
using System.Drawing;
using System.Web;
private Image GetImageFile(HttpPostedFileBase postedFile)
{
if (postedFile == null) return null;
return Image.FromStream(postedFile.InputStream);
}
One way that I figured out how to do it so that it was both fast and thread safe was the following:
var imgBytes = value as byte[];
if (imgBytes == null)
return null;
using (var stream = new MemoryStream(imgBytes))
return BitmapFrame.Create(stream,BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
I threw that into a converter for my WPF application after running the images as Varbinary from the DB.

Categories

Resources