I have code that serializes/deserializes bitmap objects. For some image files (especially png files) I found out that the size of the file is reduced after deserializing it, and the image quality is reduced. And if I serialize the deserialized image again, the resulting byte array is different compared to what it was before. Has anyone experienced the same problem? If so, how were you able to solve it?
Here is my code snippet:
private byte[] GetBytes(Bitmap image)
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
formatter.Serialize(ms, image);
byte[] _bytes = ms.ToArray();
return _bytes;
}
}
private Bitmap GetImage(byte[] _bytes)
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(_bytes))
{
Bitmap bmp = (Bitmap)formatter.Deserialize(ms);
return bmp;
}
}
PS: Using an Image object instead of Bitmap seems to solve the problem, but I'd rather not change the object type due to external dependencies.
Related
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.
I have a web app that runs on Azure. The user can upload an image and I save that image for them. Now when I receive the image, I have it as byte[]. I would like to save it as JPG to save space. The source image could be anything such as PNG, BMP or JPG.
Is it possible to do so? This needs to run on Azure and I am using WebApps/MVC5/C#.
Thanks for any help.
Get the memorystream and then use System.Drawing
var stream = new MemoryStream(byteArray)
Image img = new Bitmap(stream);
img.Save(#"c:\s\pic.png", System.Drawing.Imaging.ImageFormat.Png);
The last line there where you save the file, you can select the format.
So the answers by #mjwills and Cody was correct. I still thought to put the two methods I exactly needed:
public static Image BitmapToBytes(byte[] image, ImageFormat pFormat)
{
var imageObject = new Bitmap(new MemoryStream(image));
var stream = new MemoryStream();
imageObject.Save(stream, pFormat);
return new Bitmap(stream);
}
Also:
public static byte[] ImgToByteArray(Image img)
{
using (MemoryStream mStream = new MemoryStream())
{
img.Save(mStream, img.RawFormat);
return mStream.ToArray();
}
}
Cheers everyone.
How do I convert an object of BitmapImage type to a byte array?
There's plenty of examples out on the web but all of them are using methods that no longer exist for windows Store app.
best solution I've found is this, however I get an exception on the first code line when trying to run it
public static byte[] ConvertBitmapToByteArrayAsync(WriteableBitmap bitmap)
{
using (var stream = bitmap.PixelBuffer.AsStream())
{
MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
exception:
A first chance exception of type 'System.AccessViolationException'
occurred in System.Runtime.WindowsRuntime.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
from what I can gather it may have to do with the size of the stream but I haven't been able to figure out how to fix it.
I hope this can help you:
// Converting Bitmap to byte array
private byte[] ConvertBitmapToByteArray(Bitmap imageToConvert)
{
MemoryStream ms = new System.IO.MemoryStream();
imageToConvert.Save(ms, ImageFormat.Png);
return ms.ToArray();
}
// Converting byte array to bitmap
private Bitmap ConvertBytesToBitmap(byte[] BytesToConvert)
{
MemoryStream ms = new MemoryStream(BytesToConvert);
try { return new Bitmap(ms); }
catch { return null; }
}
I have a wcf service which returns a bmp in byte[]. However Silverlight's Image control doesnt support displaying bmp's so i need to convert the bmp byte[] to png or jpg byte[]. Is there a library out there which does this conversion? Or any other way of displaying the bmp byte[] on the silverlight client?
Thanks!
Update1
In order to achieve the conversion I would have done something like this in .NET
private byte[] ConvertBmpToJpeg(byte[] bmp)
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(new MemoryStream(bmp)))
{
MemoryStream ms = new MemoryStream();
image.Save(ms, ImageFormat.Jpeg);
return ms.ToArray();
}
}
Since System.Drawing is not available in Silverlight, how do I achieve what the code does above in Silverlight?
Answer
using the library mentioned by dj kraze below-
ExtendedImage img = new ExtendedImage();
var bd = new BmpDecoder();
var je = new JpegEncoder();
bd.Decode(img, new MemoryStream(bitmapBytes));
MemoryStream ms = new MemoryStream();
je.Encode(img, ms);
BitmapImage bi = new BitmapImage();
bi.SetSource(new MemoryStream(ms.ToArray()));
display_ScreenShot.Source = bi;
Here is an even easier way of doing it..
This site may help out a lot
Image Converting
im currently trying out GD-Sharp and wanted to convert the graphic into bitmap without saving it to image file.
GD-Sharp method to save to stream is
bool GB.Save(Stream outStream);
for saving using stream is
using(FileStream fs = File.OpenWrite(#"stream1.jpg"))
{
image.Save((System.IO.Stream)fs);
fs.Close();
}
since bitmap supports stream, how do it convert GD-Sharp to Bitmap ? thanks.
You can use a MemoryStream, something like
gdsBitmap.Save(memStream);
memStream.Seek(0);
gdiBitmap = Bitmap.FromStream(memStream);
i did something like this.
MemoryStream memStream = new MemoryStream();
gdimg.Save(memStream);
Bitmap bmp2 = new Bitmap(memStream);