Error converting byte array to Image - c#

I've read images in and saved them in a database in byte[] format. Later I want to retrieve the images and convert them into Image format. I've written the following method:
private List<Image> ConvertByteArraysToImages(List<byte[]> byteDataList)
{
List<Image> retVal = new List<Image>();
int counter = 0;
foreach (byte[] byteData in byteDataList)
{
// Something is wrong here
MemoryStream memstr = new MemoryStream(byteData);
Image img = Image.FromStream(memstr);
retVal.Add(img);
memstr.Dispose();// Just added this
// This works fine, the images appear in the folder
System.IO.File.WriteAllBytes(String.Format(#"C:\dev\test{0}.png", counter), byteData);
counter++;
}
return retVal;
}
I'm calling this method from an action which adds the images to the ViewBag to use in the view.
public ActionResult ViewTicket(Incident ticket)
{
//Read the ticket via the web API
string serialisedJSON = JsonConvert.SerializeObject(ticket.ID);
string response = TicketUtilities.JSONRequestToAPI(serialisedJSON, "GetSingleIncident");
Incident retVal = JsonConvert.DeserializeObject<Incident>(response);
//Convert byte[] to Image and add to ViewBag
List<Image> ScreenshotImagesFullsize = ConvertByteArraysToImages(retVal.Screenshots);
ViewBag.Add(ScreenshotImagesFullsize); //Error here
return View(retVal);
}
When I try to add the images to the ViewBag I get the following error in the browser:
Cannot perform runtime binding on a null reference
Writing the byte arrays to file produces the correct output but I'm not getting a list of Images in my return value. Hovering over retVal in debug mode shows the following:
I passed in two byte arrays and I see 2 objects in retVal, but I also I see the error: "Cannot evaluate expression because the code of the current method is optimized". Why does this occur?
Update: I disabled JIT optimization and now I can see the following:
I can see that the object has correctly acquired properties such as the height and width but the actual data is null.

Do not dispose the stream and do keep at least one reference to it as long as you need the image.
"You must keep the stream open for the lifetime of the Image."
https://msdn.microsoft.com/de-de/library/1kcb3wy4(v=vs.110).aspx
Note that there is no need to manually call dispose on a MemoryStream because it does not have unmanaged resources

So I solved this, the problem turned out not to be the conversion but rather adding Image objects to the view. For some reason adding Image objects to the view does not work, to overcome this I converted the image to a Base64 string
using (MemoryStream m = new MemoryStream())
{
retVal.Img.Save(m, retVal.Img.RawFormat);
byte[] imageBytes = m.ToArray();
// Convert byte[] to Base64 String
string imreBase64Data = Convert.ToBase64String(imageBytes);
retVal.ImgB64 = string.Format("data:image/png;base64,{0}", imreBase64Data);
}

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.

Convert byte[] image to string

MediaFile ficheiro;
byte[] image = File.ReadAllBytes(ficheiro.Path);
var file = image.ByteArrayImageToBase64();
string[] x = new string[] { file };
MediaFile is a plugin to get image in gallery
Visually the code does not give me error. But I want to see another way that I can convert the bytes of the image to string.
What would be the most efficient way to convert an array of bytes from an image to a string. I intend to send this image later with a post method. But my difficulty is in conversion because trying to send says that the request is too long. due to ByteArrayImageToBase64

Xamarin IOS Encoding/Decoding Images from and to Base64

I am working on an application for IOS in Xamarin. I have a menu in which I request something called "Doublechecks". These doublechecks have a field with the name "Medication". In a previous working copy of the app, I simply used a string to fill this field, but now we had the idea to, instead of filling this medication field with a string, to fill it with an image. One of the tips I got was to convert a taken or chosen image to base64.
In the menu where I make a new doublecheck I have a button that pops up an actionsheet, where you can choose weather you want to pick an image from your PhotoLibrary, or take a new picture with your camera. When you've taken or chosen a picture, I use the following method to encode it to Base64:
var imageToSend = originalImage.AsJPEG (0.23f).GetBase64EncodedString (NSDataBase64EncodingOptions.None);
Where the originalImage is the image I took/chose. Now, when requesting all doublechecks, I use the following to decode it:
byte[] encodedDataAsBytes = System.Convert.FromBase64String (imageToDisplay);
string decoded = System.Text.Encoding.ASCII.GetString (encodedDataAsBytes);
NSData data = NSData.FromString (decoded, NSStringEncoding.ASCIIStringEncoding);
return UIImage.LoadFromData (data);
The method works up until the return. Somehow, the UIImage is not being constructed, and has its value null, even though the 'data' contains the decoded string.
I have searched several threads and fora, but without much avail. Any help would be much appreciated.
Why are you converting the base64 decoded data into a string ?
Since you already have the byte array you should be able to simply do:
byte[] encodedDataAsBytes = System.Convert.FromBase64String (imageToDisplay);
NSData data = NSData.FromArray (encodedDataAsBytes);
return UIImage.LoadFromData (data);
NSData method not always a good solution. You can also try following
var rawData = pixelMap.ToByteArray();
using (var provider = new CGDataProvider(rawData, 0, rawData.Length))
{
using (var colorSpace = CGColorSpace.CreateDeviceRGB())
{
var cgImage = new CGImage(
pixelMap.Width,
pixelMap.Height,
pixelMap.BitsPerComponent,
pixelMap.BytesPerPixel * ByteToBit,
pixelMap.BytesPerPixel * pixelMap.Width,
colorSpace,
CGBitmapFlags.ByteOrderDefault,
provider,
null,
true,
CGColorRenderingIntent.Default
);
return ImageSource.FromStream(() => UIImage.FromImage(cgImage).AsPNG().AsStream());
}
}
Check out my library and samples for more info about both Android and iOS bitmap from/to 2d pixel map.
https://github.com/enginkirmaci/Imaging-Library

My stream keeps throwing Read/Write Timeout exceptions

I am parsing a PowerPoint presentation using Open Office SDK 2.0. At one point in the program I'm passing a stream to a method that will return an image's MD5. However, there seems to be a problem in the stream, before it even gets to my MD5 method.
Here's my code:
// Get image information here.
var blipRelId = blip.Embed;
var imagePart = (ImagePart)slidePart.GetPartById(blipRelId);
var imageFileName = imagePart.Uri.OriginalString;
var imageStream = imagePart.GetStream();
var imageMd5 = Hasher.CalculateStreamHash(imageStream);
In debug, before I let it drop into Hasher.CalculateStreamHash, I check the imageStream properties. Immediately, I see that the ReadTimeout and WriteTimeout both have similar errors:
imageStream.ReadTimeout' threw an exception of type 'System.InvalidOperationException
imageStream.WriteTimeout' threw an exception of type 'System.InvalidOperationException
Here's a picture of the properties that I"m seeing during debug, in case it helps:
This code is running over a PowerPoint presentation. I'm wondering if the fact that it's zipped (a PowerPoint presentation is basically just a zipped up file) is the reason I'm seeing those timeout errors?
UPDATE: I tried taking the stream, getting the image and converting it to a byte array and sending that to the MD5 method as a memory stream, but I still get those same errors in the Read/Write Timeout properties of the stream. Here's the code as it is now:
// Get image information here.
var blipRelId = blip.Embed;
var imagePart = (ImagePart)slidePart.GetPartById(blipRelId);
var imageFileName = imagePart.Uri.OriginalString;
var imageStream = imagePart.GetStream();
// Convert image to memory stream
var img = Image.FromStream(imageStream);
var imageMemoryStream = new MemoryStream(this.imageToByteArray(img));
var imageMd5 = Hasher.CalculateStreamHash(imageMemoryStream);
For clarity, here's the signature for the CalculateStreamHash method:
public static string CalculateStreamHash([NotNull] Stream stream)
Mischief managed! I was able to overcome this problem by using a BufferedStream and adding an overloaded method to my MD5 method that accepted a BufferedStream as a parameter:
// Get image information here.
var blipRelId = blip.Embed;
var imagePart = (ImagePart)slidePart.GetPartById(blipRelId);
var imageFileName = imagePart.Uri.OriginalString;
// Convert image to buffered stream
var imageBufferedStream = new BufferedStream(imagePart.GetStream());
var imageMd5 = Hasher.CalculateStreamHash(imageBufferedStream);
...and:
public static string CalculateStreamHash([NotNull] BufferedStream bufferedStream)

What's the correct way to get an Image in to a Byte[] in C#

I'm trying to make use of an image pasted from the clipboard, like a screenshot.
Here is a snippet of code:
public void PasteImage(Image pasteImage)
{
MemoryStream image = new MemoryStream();
pasteImage.Save(image, pasteImage.RawFormat);
image.Position=0;
byte[] byteImage = image.ToArray();
On the line:
pasteImage.Save(image, pasteImage.RawFormat);
I get this error:
An unhandled exception of type 'System.ArgumentNullException' occurred
in System.Drawing.dll
Additional information: Value cannot be null.
I'm following previous stackoverflow posts I've found on this topic, such as How to compare two images using byte arrays, and How to convert image in byte array, and just can't get it to work.
I'm wondering if there is something specific about images retrieved from the clipboard. I'm using this code to fetch the image:
if (Clipboard.ContainsImage())
{
Image pasteImage = GetClipboardImage();
PasteImage(pasteImage);
}
EDIT
This is the code behind GetClipboardImage():
public Image GetClipboardImage()
{
System.Drawing.Image returnImage = null;
if (Clipboard.ContainsImage())
{
returnImage = Clipboard.GetImage();
}
return returnImage;
}
If I put a breakpoint on the line that errors I can see that both pasteImage and pastImage.RawFormat are not null:
This looks relevant as an example Ill have a crack at that and report back: How can I get an image out of the clipboard without losing the alpha channel in .NET?
Looking at the MSDN article for Save Image, you might want to try:
Instead of
pasteImage.Save(image, pasteImage.RawFormat);
try
pasteImage.Save(image, System.Drawing.Imaging.ImageFormat.RawFormat)

Categories