How can I get ImageSource from MemoryStream in WPF using c# ? or convert MemoryStream to ImageSource to display it as image in wpf ?
using (MemoryStream memoryStream = ...)
{
var imageSource = new BitmapImage();
imageSource.BeginInit();
imageSource.StreamSource = memoryStream;
imageSource.EndInit();
// Assign the Source property of your image
image.Source = imageSource;
}
Additional to #Darin Dimitrov answer if you disposed the stream before assigning to Image.Source nothing will show, so be careful
For example, the Next method will not work, From one of my projects using LiteDB
public async Task<BitmapImage> DownloadImage(string id)
{
using (var stream = new MemoryStream())
{
var f = _appDbManager.DataStorage.FindById(id);
f.CopyTo(stream);
var imageSource = new BitmapImage {CacheOption = BitmapCacheOption.OnLoad};
imageSource.BeginInit();
imageSource.StreamSource = stream;
imageSource.EndInit();
return imageSource;
}
}
you can not use returned imageSource from the last function
But this implementation will work
public async Task<BitmapImage> DownloadImage(string id)
{
// TODO: [Note] Bug due to memory leaks, if we used Using( var stream = new MemoryStream()), we will lost the stream, and nothing will shown
var stream = new MemoryStream();
var f = _appDbManager.DataStorage.FindById(id);
f.CopyTo(stream);
var imageSource = new BitmapImage {CacheOption = BitmapCacheOption.OnLoad};
imageSource.BeginInit();
imageSource.StreamSource = stream;
imageSource.EndInit();
return imageSource;
}
Related
I'm developing a small application which will get RAW image files, convert them to low quality JPEG's then load those JPEG's as thumbnails into a Grid.
My problem: I am having issues with the UI getting blocked while the images are being converted. I am dynamically adding controls to host those images in the grid just after the conversion is taking place for each image. Also I am binding those images to my Image control's Source with my ControlProperties ViewModel in code-behind.
My coding:
Here I am creating a new instance of my ControlProperties view model and inside I am doing the image conversion at the ImageSource.
cp = new ControlProperties()
{
ImageId = controlCount += 1, ImageSource = ThumbnailCreator.CreateThumbnail(imagePath)
};
My question:
Seeing as the images take a while to load, I am in need to get full control of my UI while they are being converted and added into my grid, but I am not getting it right at all. Can someone please help me with some advice or coding snippets to get me going please?
My ThumbnailCreator class
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace SomeProjName
{
public class ThumbnailCreator
{
private static string imageLocation;
private static int currentImage;
public static BitmapImage CreateThumbnail(string oldImagePath)
{
ConvertHighQualityRAWImage(oldImagePath);
if (imageLocation != string.Empty && imageLocation != null)
return OpenImage(imageLocation);
else return null;
}
//Creates low quality JPG image from RAW image
private static void ConvertHighQualityRAWImage(string oldImagePath)
{
BitmapImage image = new BitmapImage(new Uri(oldImagePath));
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(image));
using (var filestream = new FileStream(GetImageLocation(), FileMode.Create))
encoder.Save(filestream);
image.UriSource = null;
image.StreamSource = null;
image = null;
GC.WaitForPendingFinalizers();
GC.Collect();
}
//Returns low quality JPG thumbnail to calling method
private static BitmapImage OpenImage(string imagePath)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.DecodePixelWidth = 283;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(imagePath, UriKind.Relative);
image.EndInit();
DeleteImage();
return image;
}
private static string GetImageLocation()
{
imageLocation = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "thumbnails")).FullName + GetCurrentImage();
return imageLocation;
}
private static string GetCurrentImage()
{
return "\\" + (currentImage += 1).ToString() + ".jpg";
}
private static void DeleteImage()
{
if (File.Exists(imageLocation))
File.Delete(imageLocation);
}
}
}
You don't need to save your thumbnails to file. Use a MemoryStream instead:
public class ThumbnailCreator
{
public static BitmapImage CreateThumbnail(string imagePath)
{
BitmapFrame source;
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
source = BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
var encoder = new JpegBitmapEncoder() { QualityLevel = 17 };
encoder.Frames.Add(BitmapFrame.Create(source));
var bitmap = new BitmapImage();
using (var stream = new MemoryStream())
{
encoder.Save(stream);
stream.Position = 0;
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
return bitmap;
}
The intermediate encoding and decoding pass doesn't even seem to be necessary, so you could simply write this:
public class ThumbnailCreator
{
public static BitmapImage CreateThumbnail(string imagePath)
{
var bitmap = new BitmapImage();
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
return bitmap;
}
}
If you want to call the CreateThumbnail method asynchronously, use Task.Run():
cp.ImageSource = await Task.Run(() => ThumbnailCreator.CreateThumbnail(fileName));
Final Solution:
I just want to add this comment to Clemens solution. I also use the Garbage Collector to stop massive memory usage build-up when loading a lot of images. Here is the final method that I use to create the thumbnails.
public static BitmapImage CreateThumbnail(string imagePath)
{
var bitmap = new BitmapImage();
using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read))
{
bitmap.BeginInit();
bitmap.DecodePixelWidth = 283;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
}
bitmap.Freeze();
GC.WaitForPendingFinalizers();
GC.Collect();
return bitmap;
}
I have to convert BitmapImage (JPEG inside) to byte[] and then back.
Here is my code:
using System.IO;
using System.Windows.Media.Imaging;
public class CImageConverter
{
//
public BitmapImage ByteArray_To_BitmapImage(byte[] _binaryData)
{
BitmapImage _bitmapImage = new BitmapImage();
//
_bitmapImage.BeginInit();
_bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
_bitmapImage.StreamSource = new MemoryStream(_binaryData);
_bitmapImage.EndInit();
//
return _bitmapImage;
}
//
public byte[] BitmapImage_To_ByteArray(BitmapImage _bitmapImage)
{
byte[] bytes;
//
using(MemoryStream ms = new MemoryStream())
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(_bitmapImage));
encoder.Save(ms);
bytes = ms.ToArray();
}
//
return bytes;
}
}
It seems that BitmapImage_To_ByteArray working properly.
But ByteArray_To_BitmapImage throwing NotSupportedException in EndInit() method.
The message is (translated with web-translator):
Could not locate the image processing component which is suitable to
complete this operation.
I found similar questions on the internet, but the answers do not work. The usual answer is "I tried your code - I have everything working fine."
I also found this suggestion, but did not understood, how to use it.
Thanks for your help!
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;
}
}
Have you tried this?
I'm displaying an image from a base 64 string that came from an API. The problem is, the image is not being displayed.
Here's the code:
profilePictureImg.Source = GetUserImage(user.MobileNumber);
private BitmapImage GetUserImage(string phoneNumber)
{
BitmapImage bitmapImage = new BitmapImage();
var baseAddress = "http://192.168.0.103/vchatapi/api/Images/" + phoneNumber;
var http = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(new System.Uri(baseAddress));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "GET";
var response = http.GetResponse();
var stream = response.GetResponseStream();
var sr = new StreamReader(stream);
var content = sr.ReadToEnd();
var y ="";
var x = y.FromJson(content);
byte[] binaryData = Convert.FromBase64String(x);
using (MemoryStream ms = new MemoryStream(binaryData, 0, binaryData.Length))
{
ms.Write(binaryData, 0, binaryData.Length);
bitmapImage.StreamSource = ms;
}
return bitmapImage;
}
Any Ideas?? Thanks!
EDIT:
Got the fix. For some reason, it requires to call BeginInit and EndInit.
The image may be decoded as shown in this answer:
var binaryData = Convert.FromBase64String(x);
var bitmapImage = new BitmapImage();
using (var stream = new MemoryStream(binaryData))
{
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
The reason why you have to use BeginInit and EndInit is explained in the Remarks section of the BitmapImage MSDN documentation:
BitmapImage implements the ISupportInitialize interface to optimize
initialization on multiple properties. Property changes can only occur
during object initialization. Call BeginInit to signal that
initialization has begun and EndInit to signal that initialization has
completed. After initialization, property changes are ignored.
This may be one of those cases where it pays not to dispose the stream too eagerly; also, the Write here is unnecessary: you already added the data via the constructor. So just:
bitmapImage.StreamSource = new MemoryStream(binaryData);
return bitmapImage;
does that work?
You can try the following
byte[] binaryData = Convert.FromBase64String(x);
using (MemoryStream ms = new MemoryStream(binaryData))
{
bitmapImage = (Bitmap)Image.FromStream(ms);
}
I am having trouble converting an image resource into byte[].
For example, I have the following resource:
pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg
in my program. How do I convert this into a byte[].
I've tried using a BitMapImage, but it's ImageSource ends up being null after initialised.
This seems to work:
var info = Application.GetResourceStream(uri);
var memoryStream = new MemoryStream();
info.Stream.CopyTo(memoryStream);
return memoryStream.ToArray();
A general solution to convert a BitmapSource into a byte[] would look like this:
public byte[] GetImageBuffer(BitmapSource bitmap, BitmapEncoder encoder)
{
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return stream.ToArray();
}
}
You would use it like shown below, with any of the BitmapEncoders that are available in WPF.
var uri = new Uri("pack://application:,,,/AppName;component/Assets/Images/sampleimage.jpg");
var bitmap = new BitmapImage(uri);
var buffer = GetImageBuffer(bitmap, new JpegBitmapEncoder());
I am using visual studio 2010, (desktop application) and using LINQ to SQL to save image/video or audio files to database in dataType VarBinary (MAX). This I can do... Problem is, I can't get them and display them in xaml because I can't get the converting part correct. Here is what I have so far (though its not working);
private void bt_Click (object sender, RoutedEventArgs e)
{
databaseDataContext context = new databaseDataContext();
var imageValue = from s in context.Images
where s.imageID == 2
select s.imageFile;
value = imageValue.Single().ToString();
//convert to string and taking down to next method to get converted in image
}
public string value { get; set; }
public object ImageSource //taking from http://stackoverflow.com/
{
get
{
BitmapImage image = new BitmapImage();
try
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(value, UriKind.Absolute);
image.EndInit();
Grid.Children.Add(image);
}
catch { return DependencyProperty.UnsetValue; } return image;
}
}
I not even sure if I am on the correct track? And I am assuming that video or audio is quite similar methods?
Since your image is stored in binary format in the database, you want to "stream" this into an image object by leveraging the MemoryStream object.
Looking at your code, your solution will look something like this:
BitmapImage bmpImage = new BitmapImage();
MemoryStream msImageStream = new MemoryStream();
msImageStream.Write(value, 0, value.Length);
bmpCardImage.BeginInit();
bmpCardImage.StreamSource = new MemoryStream(msImageStream.ToArray());
bmpCardImage.EndInit();
image.Source = bmpCardImage;
It's very easy, if you have a binary data and want to create an Image object, use this code:
public Image BinaryToImage(byte[] binaryData)
{
MemoryStream ms = new MemoryStream(binaryData);
Image img = Image.FromStream(ms);
return img;
}
If you already have the bytes, to verify that what you saved is correct you can save the bytes to a file and open it....
string tempFile = Path.GetTempFileName();
MemoryStream ms = new MemoryStream(bytes); //bytes that was read from the db
//Here I assume that you're reading a png image, you can put any extension you like is a file name
FileStream stream = new FileStream(tempFile + ".png", FileMode.Create);
ms.WriteTo(stream);
ms.Close();
stream.Close();
//And here we open the file with the default program
Process.Start(tempFile + ".png");
And later you can use the answer of Dillie-O and stream....
Dillie-O's code makes for a very nice extension method:
// from http://stackoverflow.com/questions/5623264/how-to-convert-varbinary-into-image-or-video-when-retrieved-from-database-in-c:
public static BitmapImage ToImage(this Binary b)
{
if (b == null)
return null;
var binary = b.ToArray();
var image = new BitmapImage();
var ms = new MemoryStream();
ms.Write(binary, 0, binary.Length);
image.BeginInit();
image.StreamSource = new MemoryStream(ms.ToArray());
image.EndInit();
return image;
}