I am doing some testing for a future project, in which I will be creating an image encryptor. Right now I just wanted to get together a way of converting a bitmap to a byte array, save it to a text file, reload it, and resave it under a different name. I got it to work...there is just a problem with the file size afterwards. My converted image(The one that read from a byte array to create the image) is showing a larger file size than the original image. Here is my code:
public class Program
{
public static void Main( string[] args )
{
/**
* Load the bitmap and convert it to a byte array
* then save the file to the desktop
*/
byte[] imageBytes = ImageToByte( new Bitmap( "C:/Users/Krythic/Desktop/NovaEngine.png" ) );
File.WriteAllBytes( "C:/Users/Krythic/Desktop/NovaImageData.txt" , imageBytes );
/**
* Load the saved image bytes, then convert them back into an image and save it to the
* desktop under a new name.
*/
byte[] convertedImageBytes = File.ReadAllBytes("C:/Users/Krythic/Desktop/NovaImageData.txt");
Bitmap image = ConvertToBitmap(convertedImageBytes);
image.Save("C:/Users/Krythic/Desktop/ConvertedImage.png");
}
public static byte[] ImageToByte( Bitmap img )
{
ImageConverter converter = new ImageConverter();
return ( byte[] )converter.ConvertTo( img , typeof( byte[] ) );
}
private static Bitmap ConvertToBitmap( byte[] imagesSource )
{
ImageConverter imageConverter = new ImageConverter();
Image image = ( Image )imageConverter.ConvertFrom( imagesSource );
return new Bitmap( image );
}
}
I think the problem is due to the image.Save(); function, which...I think...is not picking the optimal compression for the image. Maybe I am wrong? Here is a picture of the property pages for both images:
You will also notice that the saved byte array version of the original image is showing a larger file size. Why is this? Shouldn't the size of the file remain constant across the entire span of conversion?
Update: I'm pretty sure that the functions that I am using to convert the image are using poor conversion techniques. This would explain why the size of the original png differs from the byte-array-file version, which should be equal. So to solve this, I need an efficient, or correct way of doing the same thing that those two functions do.
Most likely the original file was somehow compressed , and re-writing the file ends up saving an un-compressed file because C# is not very smart about that.
and if C# was updated somehow and got better from the last time I used it, it is possible because you are converting a PNG into a bitmap (the bitmap is not size efficient) and then you save the bitmap to a PNG
Related
I would like to access the value of each individual pixel value of a 16UC1-formatted png image, which I receive as a byte[].
I am realtively new to image processing in C# and I got stuck at this problem for days now.
I can work with a "typical" bgr8-formatted jpg/png byte array simply by:
private static Bitmap getBitmap(byte[] array)
{
return new Bitmap(new MemoryStream(array));
}
I tried many things for the 16UC1-format. The furthest i got is:
private Bitmap getBitmap(byte[] array)
{
var bitmap = new Bitmap(640,480,PixelFormat.Format16bppRgb555);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, 640, 480), ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb555);
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, array, 0, array.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
this at least returns a bitmap, though it is completely black.
Trying PixelFormat.Format16bppGrayScale instead of PixelFormat.Format16bppRgb555 gives me a "General error in GDI+".
When writing the byte array to a file by e.g. by
File.WriteAllBytes(filename, array);
I can see the image with image viewers like IrfanView, though Windows photo viewer fails.
Reading the file as a Bitmap is not required. I want to avoid file operations for performance reasons. I simply want to access each individual xy-pixel of that image.
Update:
I started using Emgu.CV and applying imdecode as Dan suggested below.
private Bitmap getCompressedDepthBitmap(byte[] data)
{
Mat result = new Mat(new Size(640, 480), DepthType.Cv16U, 1);
CvInvoke.Imdecode(data,LoadImageType.AnyDepth, result);
return result.Bitmap;
}
This again gives me a black image. (By saving the byte array via WriteAllBytes I see useful contents.) I also tried
Image<Gray, float> image = result.ToImage<Gray, float>();
image.Save(Path.Combine(localPath, "image.png"));
which as well gave me a black image.
I am planning to normalize the Mat now somehow, maybe this helps...
Thank you for your interest and your support!
After hours and hours of wasted working time and despair I finally found the solution...
One important thing I was missing in my description above is that the image data byte[] is coming from of a ROS sensor_msgs/CompressedImage.msg.
The data byte array, which is supposed to contain the png data, sometimes starts with a 12byte header; seemingly only if the data is (1 channel) compressedDepth image. I acciendently found this info here.
Removing these awesomely obnoxous 12 bytes and continung as usual does the job:
var bitmap = new Bitmap(new MemoryStream(dataSkip(12).ToArray()));
i have a application where i need to check my scanner generates blank tif file or not.
here i share my example code
private void button1_Click(object sender, EventArgs e)
{
string path2 = #"F:\3333.tif";
string path = #"F:\Document Scanned # 1-blank.tif";
System.Drawing.Image img = System.Drawing.Image.FromFile(path);
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);
bytes = ms.ToArray();
}
System.Drawing.Image img2 = System.Drawing.Image.FromFile(path2);
byte[] bytes2;
using (MemoryStream ms3 = new MemoryStream())
{
img2.Save(ms3, System.Drawing.Imaging.ImageFormat.Tiff);
bytes2 = ms3.ToArray();
}
bool t = false;
t = bytes.SequenceEqual(bytes2);
}
Note: Blank tif image meance white page .
In above bool t always returns true why ? i used two diff images solved
Essentially, you are comparing the bytes of two TIF files (with the indirection of using Image). This can fail for various reasons:
Dimension: If the two images do not have the exact same height and width, the byte sequences will -of course- be different even if both are completely white.
Metadata: As far as I know, the TIF format contains various metadata. Therefore, two files may be different even if they have the same pixels. I would recommend manually checking all pixel values (e.g. Bitmap.GetPixel) and comparing them to white (Color.FromArgb(255,255,255,255)).
Noise: Are you sure that a blank file is always pure white (255,255,255)? Maybe some random pixels have slightly different values such as (255,254,255)...
this is related with test instrument but need to more help in C# code.
i did more explain here about my question.
i send command to instrument and i just receive data from instrument.
received data is real format (binary) and i just put in to string variable.
here i captured what's inside in string..
http://i.stack.imgur.com/UcYqV.png
then what i want do is i want this string to convert byte array. because my goal is make png file in my pc. instrument manual said this returned data is gif format but returned real type.'
i believe the problem point is when i convert to byte array,,, there are problem...
does anyone has this kind of experiance,.????
/// below is just send command to instrument that i want " Returns an image of the display in .gif format "
my6705B.WriteString("hcop:sdump:data?", true);
string image_format = my6705B.ReadString();
/// what's inside string image_format ??![i attached screenshot png file. this is what i received from instrument. (manual said this is Returns an image of the display in .gif format)][1] ![png file][2]
http://i.stack.imgur.com/UcYqV.png
/// now here i think i did something wrong,
/// the goal is i want change this return data to gif or png or image file.
/// any solution is ok for me
/// i just try that change this data to byte array and then try to change image file.
/// i think some error here because my code success to convert byte array then create image,,, error
//// i believe that i did something wrong in convert byte array.....
System.Text.UnicodeEncoding encode = new System.Text.UnicodeEncoding();
byte[] byte_array22 = encode.GetBytes(image_format);
MemoryStream ms4 = new MemoryStream(byte_array22);
Image image = Image.FromStream(ms4); //// error point
image.Save(#"C:\Users\Administrator\Desktop\imageTest.png");
i believe that my explain is in comment.
let me explain again that my goal is convert gif data to image file.
instrument give us Returns an image of the display in .gif format.
i received this data to string array. < i dont know this is correct or not but for now i put to string array > then i just want to png or jpg file with this gif data.
please advice,.
Joseph Choi
Please Try ImageFormat Like
image.Save(#"C:\Users\Administrator\Desktop\imageTest.png", System.Drawing.Imaging.ImageFormat.Png);
Update:
byte[] byte_array22 = Encoding.Unicode.GetBytes(image_format);
MemoryStream ms4 = new MemoryStream(byte_array22);
Image image = Image.FromStream(ms4, true, true);
image.Save(#"C:\Users\Administrator\Desktop\imageTest.png",System.Drawing.Imaging.ImageFormat.Png);
Hmm ... here are a couple of methods to transform an image to Base64 format (string), and back.
private string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)
{
using (MemoryStream ms = new MemoryStream())
{
// Convert Image to byte[]
image.Save(ms, format);
byte[] imageBytes = ms.ToArray();
// Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes);
return base64String;
}
}
private Image Base64ToImage(string base64String)
{
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(base64String);
using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
{
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
return image;
}
}
You can use them and play with them to see if they suit you.
I'm not sure that taking an image and simply dumping it into string array will help you much.
I know I can use
string base64Encoded = ...;
byte[] byteArray = Convert.FromBase64String(this.base64Encoded); // array size [31591]
var memoryStream = new MemoryStream(byteArray);
var bitmap = new Bitmap(memoryStream);
//byte[,] im = new byte[a.Width*a.Height,3];
// array size [891998, 3] - why this array is 90 times bigger?
but I want to do it manually.
What I really need is to know how from byteArray I can create 3dim pixel array [bitmap.width * bitmap.hight, 3 {Red,Green,Blue}]
1- size of byteArray is different from size of bitmap since bitmap is actually an uncompressed 24 bit image without a header, but byteArray is a compressed (RLE most likely) bitmap file.
2- you can use libbmp or another image processing library to load and manipulate pixels. These libraries are much better at handling that kind of stuff.
3- most bitmaps are compressed using RLE compression and bitmap is a very simple format. You can actually write a bitmap reader to read it to a byte array intead of Bitmap object.
I have the following codes to convert an image(bitmap) to byte array:
public byte[] ConvertImageToByteArray(Image imageToConvert, ImageFormat formatOfImage)
{
byte[] Ret;
try
{
using (MemoryStream ms = new MemoryStream())
{
imageToConvert.Save(ms, formatOfImage);
Ret = ms.ToArray();
}
}
catch (Exception)
{
throw;
}
return Ret;
}
and Convert byte array back to image(bitmap):
public Bitmap ConvertByteArrayToImage(byte[] myByteArray)
{
Image newImage;
using (MemoryStream ms = new MemoryStream(myByteArray, 0, myByteArray.Length))
{
ms.Write(myByteArray, 0, myByteArray.Length);
newImage = Image.FromStream(ms, true);
}
return newImage;
}
Here's my Main Program:
byte[] test = ConvertImageToByteArray(Image.FromFile("oldImage.bmp"), ImageFormat.Bmp);
Bitmap bmp = ConvertByteArrayToImage(test);
bmp.Save("newImage.bmp");
But when I compare both of the image files(old & new bitmap images), their checksum appeared to be different. Any reason for that happening? How to fix it to maintain its integrity?
Basically, there are many ways an identical image can be encoded in a BMP file. If I try your example on a random image I found, I see the .NET Bitmap class saves the file without filling the biSizeImage field in the BITMAPINFOHEADER structure in the BMP header (but the original image produced by IrfanView has it filled), which is a completely correct and documented possibility. (“This may be set to zero for BI_RGB bitmaps.”)
And this is definitely not the only variable thing in the BMP format. For instance, there are multiple possible orderings of pixel data in the image (top-to-bottom, bottom-to-top), specified in the header. (“If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.”)
So, if you receive any BMP file from a source not under your control and really need to produce an image using exactly the same BMP variant, you have a lot work to do, and I don’t think you could use the standard .NET helper classes for that.
See also this question: Save bitmap to file has zero in image size field
After chatting a bit, you solution comes down to reading and writing bytes, take the image object out the equation and just deal with the raw bytes.
To read the file:
MemoryStream ms = new MemoryStream(File.ReadAllBytes("filename"));
To write the file:
File.WriteAllBytes("outputfile", ms.ToArray());