I have a class that creates an Image from a Byte[], along with some other logic, and I am trying to write a unit test that asserts that the Image instance returned from my class is the same as some fake Image I have in my unit test.
I cannot find a reliable way to:
Start with a fake Image \ Byte[] \ Resource \ something.
Pass a Byte[] representing the fake thing to my class.
Assert the Image returned from my class is the same as my fake thing.
Here's the code I have come up with so far:
Bitmap fakeBitmap = new Bitmap(1, 1);
Byte[] expectedBytes;
using (var ms = new MemoryStream())
{
fakeBitmap.Save(ms, ImageFormat.Png);
expectedBytes = ms.ToArray();
}
//This is where the call to class goes
Image actualImage;
using (var ms = new MemoryStream(expectedBytes))
actualImage = Image.FromStream(ms);
Byte[] actualBytes;
using (var ms = new MemoryStream())
{
actualImage.Save(ms, ImageFormat.Png);
actualBytes = ms.ToArray();
}
var areEqual =
Enumerable.SequenceEqual(expectedBytes, actualBytes);
Console.WriteLine(areEqual);
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
using(StreamWriter sw = new StreamWriter(Path.Combine(desktop, "expectedBytes.txt")))
sw.Write(String.Join(Environment.NewLine, expectedBytes));
using(StreamWriter sw = new StreamWriter(Path.Combine(desktop, "actualBytes.txt")))
sw.Write(String.Join(Environment.NewLine, actualBytes));
This always returns false.
Try this:
public static class Ext
{
public static byte[] GetBytes(this Bitmap bitmap)
{
var bytes = new byte[bitmap.Height * bitmap.Width * 3];
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
Marshal.Copy(bitmapData.Scan0, bytes, 0, bytes.Length);
bitmap.UnlockBits(bitmapData);
return bytes;
}
}
var bitmap = new Bitmap(#"C:\myimg.jpg");
var bitmap1 = new Bitmap(#"C:\myimg.jpg");
var bytes = bitmap.GetBytes();
var bytes1 = bitmap1.GetBytes();
//true
var sequenceEqual = bytes.SequenceEqual(bytes1);
I can imagine that Image.Save also updates the metadata inside the image, modifying the date/time of that picture which will change your byte-array.
Related
I am trying to generate a thumbnail from the base64string. I am storing the image in a table and am trying to generate a thumbnail from the base64string being stored.
I am able to generate the thumbnail if I provide a path to the image, but that will not work in my case.
This is the working solution of generating a thumbnail from an image path:
protected void GenerateThumbnail(object sender, EventArgs e)
{
string path = Server.MapPath("../src/img/myImage.png");
System.Drawing.Image image = System.Drawing.Image.FromFile(path);
using (System.Drawing.Image thumbnail = image.GetThumbnailImage(100, 100, new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero))
{
using (MemoryStream memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png);
Byte[] bytes = new Byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, (int)bytes.Length);
string base64String = Convert.ToBase64String(bytes, 0, bytes.Length);
Image2.ImageUrl = "data:image/png;base64," + base64String;
Image2.Visible = true;
}
}
}
Can anyone provide any advice on how to use the base64string instead of the image path the generate thumbnail?
Assuming, b64 is the base64 string, you can convert it to a byte array and use that to construct the starting image.
byte[] bytes = Convert.FromBase64String(b64);
using (MemoryStream ms = new MemoryStream(bytes))
{
Bitmap thumb = new Bitmap(100, 100);
using (Image bmp = Image.FromStream(ms))
{
using (Graphics g = Graphics.FromImage(thumb))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawImage(bmp, 0, 0, 100, 100);
}
}
// a picturebox to show/test the result
picOut.Image = thumb;
}
Be sure to dispose of the thumb when you are done with it.
Mixed few tricks found online. (one from #Plutonix)
string ThumbNailBase64 = ResizeBase64Image(YourBase64String,200, 300);
base64 Input => resize => base64 Output
You get the desired thumbnail with auto aspect ratio.
public static string ResizeBase64Image(string Base64String, int desiredWidth, int desiredHeight)
{
Base64String = Base64String.Replace("data:image/png;base64,", "");
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(Base64String);
using (MemoryStream ms = new MemoryStream(imageBytes))
{
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
var imag = ScaleImage(image, desiredWidth, desiredHeight);
using (MemoryStream ms1 = new MemoryStream())
{
//First Convert Image to byte[]
imag.Save(ms1, imag.RawFormat);
byte[] imageBytes1 = ms1.ToArray();
//Then Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes1);
return "data:image/png;base64,"+base64String;
}
}
}
public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
I have a code in form1 constructor:
ConvertedBmp = ConvertTo24(newest.FullName);
The function ConvertTo24 is:
private static Bitmap ConvertTo24(string inputFileName)
{
sw = Stopwatch.StartNew();
Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(converted))
{
g.PageUnit = GraphicsUnit.Pixel;
g.DrawImageUnscaled(bmpIn, 0, 0);
}
sw.Stop();
return converted;
}
The problem is how can i use the ConvertedBmp in this line:
backTexture = TextureLoader.FromFile(D3Ddev, #"D:\test.bmp");
TextureLoader have some properties and two of them are: Fromfile and it's getting device and string or FromStream and it's getting device and Stream.
I have the device object already but how can i use the ConvertedBmp(Bitmap type) with the TextureLoader ?
Bitmap class has a method called Save() which accepts a Stream (for example a MemoryStream object) and an ImageFormat, use that. After saved the Bitmap into a MemoryStream you can use that with TextureLoader.
Image.Save Method (Stream, ImageFormat)
I get below code from here:
http://www.java2s.com/example/csharp/system.drawing/bitmap-to-memory-stream.html
public static MemoryStream ToMemoryStream(this Bitmap b)
{
MemoryStream ms = new MemoryStream();
b.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms;
}
Work for my need
EDIT: Add method where error actually occurs...
I am opening an image and I want to be able to overwrite the original file as modification happens. I tryed both of the methods here
public ImgPro(String Path)
{
Bitmap bt1 = new Bitmap(Path);
Bitmap bt2 = new Bitmap(bt1.Width, bt1.Height, PixelFormat.Format24bppRgb);
var imgRec = new Rectangle(0, 0, bt1.Width, bt1.Height);
Graphics bt2G = Graphics.FromImage(bt2);
bt2G.DrawImage(bt1, imgRec);
bt1.Dispose();
this.bitmap = bt2;
}
And
public ImgPro(String Path)
{
Bitmap bt1 = new Bitmap(Path);
Bitmap bt2 = new Bitmap(bt1.Width, bt1.Height, PixelFormat.Format24bppRgb);
var imgRec = new Rectangle(0, 0, bt1.Width, bt1.Height);
BitmapData bt1Data = bt1.LockBits(imgRec, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bt2Data = bt2.LockBits(imgRec, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
// Create data array to hold bmpSource pixel data
int numBytes = bt1Data.Stride * (int)bt1.Height;
var srcData = new byte[numBytes];
var destData = new byte[numBytes];
Marshal.Copy(bt1Data.Scan0, srcData, 0, numBytes);
Array.Copy(srcData, destData, srcData.Length);
Marshal.Copy(destData, 0, bt2Data.Scan0, numBytes);
bt1.UnlockBits(bt1Data); bt2.UnlockBits(bt2Data);
bt1.Dispose();
this.bitmap = bt2;
}
But both options failed when I went to save the file I got this error.
An unhandled exception of type 'System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.dll
For this method:
public void Save(string filename)
{
bitmap.Save(filename, ImageFormat.Jpeg);
}
Since Bitmap locks the underlying stream, you could copy the file contents to a MemoryStream and base the Bitmap on that instead. This should prevent the file from being locked:
var bytes = File.ReadAllBytes(Path);
using (var stream = new MemoryStream(bytes)) // Don't dispose this until you're done with your Bitmap
{
Bitmap bt1 = new Bitmap(stream);
// ...
}
I use the following code to convert a BitmapSource to a byte array representing a png:
/// <summary>
/// Converts BitmapSource to a PNG Bitmap.
/// </summary>
/// <param name="source">The source object to convert.</param>
/// <returns>byte array version of passed in object.</returns>
public static byte[] ToPngBytes(this BitmapSource source)
{
// Write the source to the bitmap using a stream.
using (MemoryStream outStream = new MemoryStream())
{
// Encode to Png format.
var enc = new Media.Imaging.PngBitmapEncoder();
enc.Frames.Add(Media.Imaging.BitmapFrame.Create(source));
enc.Save(outStream);
// Return image bytes.
return outStream.ToArray();
}
}
I'm looking to do the same operation but convert a byte array that's a Jpeg without having to create a BitmapSource first.
Signature should look like this:
public static byte[] ToPngBytes(this byte[] jpegBytes)
This code works but seems inefficient as I've to use a Writeable Bitmap to do this:
private WriteableBitmap colorBitmap;
private byte[] GetCompressedImage(byte[] imageData, System.Windows.Media.PixelFormat format, int width, int height, int bytesPerPixel = sizeof(Int32))
{
// Initialise the color bitmap converter.
if (colorBitmap == null)
colorBitmap = new WriteableBitmap(width, height, 96.0, 96.0, format, null);
// Write the pixels to the bitmap.
colorBitmap.WritePixels(new Int32Rect(0, 0, width, height), imageData, width * bytesPerPixel, 0);
// Memory stream used for encoding.
using (MemoryStream memoryStream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
// Add the frame to the encoder.
encoder.Frames.Add(BitmapFrame.Create(colorBitmap));
encoder.Save(memoryStream);
// Get the bytes.
return memoryStream.ToArray();
}
}
Below is a snippet of code to take a byte array of any format and convert it to a byte array with a JPG format:
byte[] jpgImageBytes = null;
using (var origImageStream = new MemoryStream(image))
using (var jpgImageStream = new MemoryStream())
{
var jpgImage = System.Drawing.Image.FromStream(origImageStream);
jpgImage.Save(jpgImageStream, System.Drawing.Imaging.ImageFormat.Jpeg);
jpgImageBytes = jpgImageStream.ToArray();
jpgImage.Dispose();
}
Bit Late for the answer but this should help others. In this solution it doesn't matter which type of image it is(as long as it's an image you can convert it to any type.
public static byte[] ConvertImageBytes(byte[] imageBytes, ImageFormat imageFormat)
{
byte[] byteArray = new byte[0];
FileStream stream = new FileStream("empty." + imageFormat, FileMode.Create);
using (MemoryStream ms = new MemoryStream(imageBytes))
{
stream.Write(byteArray, 0, byteArray.Length);
byte[] buffer = new byte[16 * 1024];
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
byteArray = ms.ToArray();
stream.Close();
ms.Close();
}
return byteArray;
}
Use it like as below
ConvertImageBytes(imageBytes, ImageFormat.Png);
Using C#, is there a better way to convert a Windows Bitmap to a byte[] than saving to a temporary file and reading the result using a FileStream?
There are a couple ways.
ImageConverter
public static byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
This one is convenient because it doesn't require a lot of code.
Memory Stream
public static byte[] ImageToByte2(Image img)
{
using (var stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
return stream.ToArray();
}
}
This one is equivalent to what you are doing, except the file is saved to memory instead of to disk. Although more code you have the option of ImageFormat and it can be easily modified between saving to memory or disk.
Source: http://www.vcskicks.com/image-to-byte.php
A MemoryStream can be helpful for this. You could put it in an extension method:
public static class ImageExtensions
{
public static byte[] ToByteArray(this Image image, ImageFormat format)
{
using(MemoryStream ms = new MemoryStream())
{
image.Save(ms, format);
return ms.ToArray();
}
}
}
You could just use it like:
var image = new Bitmap(10, 10);
// Draw your image
byte[] arr = image.ToByteArray(ImageFormat.Bmp);
I partially disagree with prestomanifto's answer in regards to the ImageConverter. Do not use ImageConverter. There's nothing technically wrong with it, but simply the fact that it uses boxing/unboxing from object tells me it's code from the old dark places of the .NET framework and its not ideal to use with image processing (it's overkill for converting to a byte[] at least), especially when you consider the following.
I took a look at the ImageConverter code used by the .Net framework, and internally it uses code almost identical to the one I provided above. It creates a new MemoryStream, saves the Bitmap in whatever format it was in when you provided it, and returns the array. Skip the extra overhead of creating an ImageConverter class by using MemoryStream
You can also just Marshal.Copy the bitmap data. No intermediary memorystream etc. and a fast memory copy. This should work on both 24-bit and 32-bit bitmaps.
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = null;
try
{
bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
return bytedata;
}
finally
{
if (bmpdata != null)
bitmap.UnlockBits(bmpdata);
}
}
.
Save the Image to a MemoryStream and then grab the byte array.
http://msdn.microsoft.com/en-us/library/ms142148.aspx
Byte[] data;
using (var memoryStream = new MemoryStream())
{
image.Save(memoryStream, ImageFormat.Bmp);
data = memoryStream.ToArray();
}
Use a MemoryStream instead of a FileStream, like this:
MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
More simple:
return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Try the following:
MemoryStream stream = new MemoryStream();
Bitmap bitmap = new Bitmap();
bitmap.Save(stream, ImageFormat.Jpeg);
byte[] byteArray = stream.GetBuffer();
Make sure you are using:
System.Drawing & using System.Drawing.Imaging;
I believe you may simply do:
ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
Very simple use this just in one line:
byte[] imgdata = File.ReadAllBytes(#"C:\download.png");