display an image retrieved from its URI - c#

I have some problems while trying to serialize an object containing a BitmapSource field.
In fact, at the beginning, I had an ImageSource but it seems not to be serializable.
So I tried to use the associated property to store a string and to convert & convert back the image to/from the string.but no image is displayed now :-(
here is the XAML image tag:
<Image
x:Name="bookCover"
Grid.Row="0"
Grid.RowSpan="1"
Grid.Column="0"
MaxWidth="200"
MaxHeight="320"
Margin="5"
Source="{Binding Image}" SizeChanged="bookCover_SizeChanged" >
</Image>
here is the Image property & field in the model class:
public string _image;
public BitmapSource Image
{
get => Base64ToImage(_image);
set
{
_image =ImageToBase64(value);
OnPropertyChanged("Image");
}
}
and their associated methods:
public static string ImageToBase64(BitmapSource bitmap)
{
var encoder = new JpegBitmapEncoder();
var frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = new MemoryStream())
{
encoder.Save(stream);
return Convert.ToBase64String(stream.ToArray());
}
}
public static BitmapSource Base64ToImage(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
using (var stream = new MemoryStream(bytes))
{
return BitmapFrame.Create(stream);
}
}
and here my program retrieves an image from internet via its URI and stores it:
var myUri=new Uri(book0.LargeImage.URL);
bookToInsert.Image = new BitmapImage(myUri);
thank you.

When you create a BitmapSource from a stream that is closed immediately after creation, you have to set BitmapCacheOption.OnLoad. Otherwise the stream would have to be kept open until the BitmapSource is actually used, e.g. displayed in an Image element.
public static BitmapSource Base64ToImage(string base64)
{
using (var stream = new MemoryStream(Convert.FromBase64String(base64)))
{
return BitmapFrame.Create(
stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}
A couple of notes:
base64 encoding and decoding seems unnecessary. Better serialize a byte array.
Do not initially create a BitmapImage from the URL. Better directly get the image buffer by a web request.
You can bind the Source property of an Image element not only to an ImageSource, but also to a string, Uri or byte array. So your property may be declared as byte[] Image.
Try a much simpler view model like this:
public class ViewModel : INotifyPropertyChanged
{
private byte[] image;
public byte[] Image
{
get => image;
set
{
image = value;
OnPropertyChanged(nameof(Image));
}
}
}
and set the Image property like this:
var webClient = new WebClient();
viewModel.Image = webClient.DownloadData(book0.LargeImage.URL);
Or asynchronously:
var httpClient = new HttpClient();
viewModel.Image = await httpClient.GetByteArrayAsync(book0.LargeImage.URL);

Related

Why Image.Save() in C# does not always give the same serialized result and how to serialize an image in a deterministic way?

I serialize images using the following code:
public static string SerializeImage(Image image)
{
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream, image.RawFormat);
return Convert.ToBase64String(memoryStream.ToArray());
}
}
and deserialize the images by doing the following
public static Image DeserializeImage(string serializedImage)
{
byte[] imageAsBytes = Convert.FromBase64String(serializedImage);
using (MemoryStream memoryStream = new MemoryStream(imageAsBytes, 0, imageAsBytes.Length))
{
memoryStream.Write(imageAsBytes, 0, imageAsBytes.Length);
return Image.FromStream(memoryStream, true);
}
}
If I have an image and does
string serializedImage1 = SerializeImage(image);
Image deserializedImage = DeserializeImage(serializedImage1);
string serializedImage2 = SerializeImage(deserializedImage );
Then
serializedImage1 == serializedImage2;
as expected. But it is not always the case.
If I serialize an image on Process 1, and then redeserialize and reserialize it on Process 2, then the result of the reserialization on Process 2 is not the same as on the Process 1. Everything works, but a few bytes in the beginning of the serialization are different.
Worst, if I do the same thing on 2 different dll (or thread, I'm not sure), it seems the serialization result is not the same too. Again, the serialization/deserialization works, but a few bytes are different.
The image the first time is loaded with the following function :
public static Image GetImageFromFilePath(string filePath)
{
var uri = new Uri(filePath);
var bitmapImage = new BitmapImage(uri);
bitmapImage.Freeze();
using (var memoryStream = new MemoryStream())
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapImage));
pngBitmapEncoder.Save(memoryStream);
Image image = Image.FromStream(memoryStream);
return image;
}
}
Note however that it happens even if the image is loaded twice with the DeserializeImage() function.
The tests I have done are with ImageFormat.Jpeg and ImageFormat.Png.
First question, why it does this ? I would have expected the result to be always the same, but I suppose some salt is used when doing the Image.Save().
Second question : I want to have a deterministic way to serialize an image, keeping the image format intact. The goal is to save the image in a DB and also to compare serialized images to know if it already exists in the system where this function is used.
Well, I discovered while trying to solve this that a png or jpeg Image object inside C# has some metadata associated to it and doing what I was doing is just not a reliable way to compare images.
The solution I used was derived from this link
https://insertcode.wordpress.com/2014/05/13/compare-content-of-two-files-images-in-c/
So what I do finally is save the images inside the system with the SerializeImage(Image image) function previously described, and when I want to consume it I deserialize it with the DeserializeImage(string serializedImage) function previously described. But when I want to compare images I use the following functions
public static bool ImagesAreEqual(Image image1, Image image2)
{
string image1Base64Bitmap = GetImageAsBase64Bitmap(image1);
string image2Base64Bitmap = GetImageAsBase64Bitmap(image2);
return image1Base64Bitmap.Equals(image2Base64Bitmap);
}
public static string GetImageAsBase64Bitmap(Image image)
{
using (var memoryStream = new MemoryStream())
{
using (var bitmap = new Bitmap(image))
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
}
return Convert.ToBase64String(memoryStream.ToArray());
}
}
That convert the image to raw bitmap before comparing them.
This does a perfect job for me in all my needed cases : the formats of the images are saved/restored correctly, and I can compare them between them to check if they are the same without having to bother with the possibly different serialization.

Using SkiaSharp.SKBitmap image as the ImageSource for a Button in Xamarin

I am attempting to use/convert the SKBitmap image as/to an ImageSource object in order to use the aforementioned image in a Button by assignment to its ImageSource property but, for the life of me, cannot figure out how to convert an SKBitmap object to an ImageSource.
Searching the web only yielded the articles/issues on converting an ImageSource to SKBitmap but not the converse.
Thanks in advance.
You could try this:
SKBitmap bitmap = ...;
// create an image COPY
//SKImage image = SKImage.FromBitmap(bitmap);
// OR
// create an image WRAPPER
SKImage image = SKImage.FromPixels(bitmap.PeekPixels());
// encode the image (defaults to PNG)
SKData encoded = image.Encode();
// get a stream over the encoded data
Stream stream = encoded.AsStream();
img.Source = ImageSource.FromStream(()=> stream);
The ImageSource property of the Button class is of ImageSource type, whose value can be set to that of a string representing the path of an image either via assigning the string to the property or using the ImageSource.FromFile() method. As the ImageSource type cannot be used with SKBitmap images, the image represented by the SKBitmap object can be saved to the disk (preferably in the application cache) and the path to the saved image can be used to initialize the concerned ImageSource object.
SKBitmap bitmap;
SKImage image = SKImage.FromBitmap(bitmap);
SKData encodedData = image.Encode(SKEncodedImageFormat.Png, 100);
string imagePath = Path.Combine(FileSystem.CacheDirectory, "image.png");
bitmapImageStream = File.Open(imagePath,
FileMode.Create,
FileAccess.Write,
FileShare.None);
encodedData.SaveTo(bitmapImageStream);
bitmapImageStream.Flush(true);
bitmapImageStream.Dispose();
ImageSource imgSrc;
imgSrc = ImageSource.FromFile(imagePath);
// or imgSrc = imagePath;

Is there a solution to convert an ImageSource to an Android Bitmap and vice-versa in Xamarin.Forms

I'm currently working on an application that modifies a picture that is currently located in the PCL project. And I'm currently having trouble with converting the DataTypes.
I'm currently trying to figure out how I can convert an ImageSource to a bitmap. I've read some answers on the internet but they didn't seem to work for me.
I call the platform-specific code with a DependencyService and pass the ImageSource as a parameter.
The function signature looks like this:
public ImageSource BlurImage(ImageSource ImageSource)
{
return null;
}
This function should Create a bitmap from the ImageSource first and once all the logic has been done it should convert back to an ImageSource.
Can someone explain to me how I should convert ImageSource to bitmap and vice-versa?
Thanks in advance,
Tom
You can use the below function to convert ImageSource to Bitmap:
private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource, Context context)
{
IImageSourceHandler handler;
if (imageSource is FileImageSource)
{
handler = new FileImageSourceHandler();
}
else if (imageSource is StreamImageSource)
{
handler = new StreamImagesourceHandler(); // sic
}
else if (imageSource is UriImageSource)
{
handler = new ImageLoaderSourceHandler(); // sic
}
else
{
throw new NotImplementedException();
}
var originalBitmap = await handler.LoadImageAsync(imageSource, context);
return originalBitmap;
}
And the Next one for Bitmap to ImageSource:
public async Task<ImageSource> GetBytesFromImage(Bitmap bitmap)
{
ImageSource imgSource;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream); // You can change the compression asper your understanding
imgSource=ImageSource.FromStream(stream);
}
return imgSource;
}

Convert System.Windows.Media.ImageSource to ByteArray

is there a way to convert a ImageSource object to byte array?
I have an ImageSource object bound to a WPF window, i can convert a byte array from the data base and convert it to ImageSource but i can't do it the reverse way.
Thx in advance.
Edit: I tried to convert ImageSource as BitmapImage but got a null object.
Even if your ImageSource is not a BitmapImage you may still successfully cast it to BitmapSource, which is the base class of all WPF bitmap classes like BitmapImage, BitmapFrame, WriteableBitmap, RenderTargetBitmap etc. (see here).
So in case your ImageSource is actually a BitmapSource (and not a DrawingImage or a D3DImage), the following method converts it to a byte array by using the specified BitmapEncoder (e.g. a PngBitmapEncoder):
public byte[] ImageSourceToBytes(BitmapEncoder encoder, ImageSource imageSource)
{
byte[] bytes = null;
var bitmapSource = imageSource as BitmapSource;
if (bitmapSource != null)
{
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
bytes = stream.ToArray();
}
}
return bytes;
}

How to convert type 'system.windows.media.imagesource' to 'system.drawing.image' or byte[] in C#?

I am doing a project in which a built in class for DICOM giving me the ImageSource,I want to use that ImageSource in My silverlight Image control. This conversion I am doing through WCF services. I found WCF does not support ImageSource, so I need to convert the output of built in class into Image or else in byte[]. So that I can send that output to Silverlight and in Silverlight client I'll reconvert it to ImageSource and can assign it to Image Control easily.
I googled for this but I did not find any help in there. Can anybody help me to fix this problem or provide me any alternate solution for this. Any help will be appreciated, Thanks in advance.
Note:- I do not have any permission for code modification on the built in class. As its a third party library.
UPDATE:-
Brief Description:
I have a class let say GetImageSource and in that I have a method say giveImgSource(). Now my questions is:
In WCF I have to call this method and after getting ImageSource from this method I need to pass it to my silverlight Client. As WCF doesn't know about ImageSource, so I need to convert the output of this method to some one out of the following or any alternate if you knows:
byte[]
Image
FileStream
MemoryStream etc
Is it a png image? Then use this to convert to byte[]:
var image = (BitmapSource)value;
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
return ms.ToArray();
}
UPDATE:
Decoding:
var bytes = (byte[])value;
var image = new BitmapImage();
image.BeginInit();
if (bytes == null)
{
// Processing null case
}
else
{
using (var ms = new MemoryStream(bytes))
{
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
}
}
return image;
Please refer to the below links for converting ImageSource to byte[]. They use the BitmapSource and WriteableBitmap classes which are available under PresentationCore library.
(1) How to Convert ImageSource to byte[]?
(2) How to Convert ImageSource to byte[] and back to ImageSource?
Hope that it will solve your problem.
The following two helper methods should be able to do the trick:
public BitmapImage ImageFromBuffer(Byte[] bytes)
{
MemoryStream stream = new MemoryStream(bytes);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.EndInit();
return image;
}
public Byte[] BufferFromImage(BitmapImage imageSource)
{
Stream stream = imageSource.StreamSource;
Byte[] buffer = null;
if (stream != null && stream.Length > 0)
{
using (BinaryReader br = new BinaryReader(stream))
{
buffer = br.ReadBytes((Int32)stream.Length);
}
}
return buffer;
}

Categories