Bitmap <-> JPEG Conversion - c#

I have an application that use the image captured by the mobile camera and sends it to a webservice. Currently I am putting the image in a byte[] which then will be transmitted. This is done by:
filename = cameracapturedialog.FileName;
FileStream fs = new FileStream(filename, FileMode.Open);
byte[] ImageByte = new byte[fs.Length]; //file to send
fs.Read(ImageByte, 0, Convert.ToInt32(fs.Length));
But now I would like to perform some processing (resizing), hence I had to put the image into a BITMAP object, and after the processing I will convert it back to JPEG.
Is there a way to convert a JPEG into Bitmap and then back to JPEG without having no changes in the pixels (for testing I will perform no processing on the Bitmap)? Hence if I compare the first JPEG with the second JPEG I need that the files will be exactly the same.
What do you think the best solution is? Can I use something else instead of Bitmap. Any suggestion with some code will be greatly appreciated.

JPG is a lossy format. It will ALWAYS lose information because of the way the encoding algorithm works. So you'll never get the original image from a jpg, no matter what encoder you use.

No. You can save it with Quality=100 which would be almost like the original image. However, the resulting file will be huge.

Related

Is it possible to store image into pdf417 barcode?

Is it possible to store(Encode) image/Pictures into pdf417 barcode? if so is there any tutorial or sample code?
The barcode cannot just hold a reference to an image in a database. The customer also expect to be able to store any image he wants.
Thank you.
As ssasa mentionned you could store the image as a byte array:
public static byte[] GetBytes(Image image)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
// you may want to choose another image format than PNG
image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
... or, if it MUST be a string, you could base64 encode it:
public static string GetBase64(Image image)
{
Image yourImage;
// using the function from the first example
var imageBytes = GetBytes(yourImage);
var encodedString = Convert.ToBase64String(imageBytes);
return Encoding.UTF8.GetBytes(encodedString);
}
Remember, though: a PDF417 barcode allows storing up to 2710 characters. While this is more than enough for most structures you'd ever want to encode, it's rather limitating for an image. It may be enough for small-sized bitmaps, monochrome images and/or highly compressed JPEGs, but don't expect being able to do much more than that, especially if you want to be able to store other data along.
If your customers expects to be able to store, as you say, any picture they want, you'd better be lowering their expectations as soon as possible before writing any code.
If it's an option, you may want to consider using QR Codes instead. Not that you'll work miracles with those either but you may like the added storage capacity.
Yes, Department of Defense Common Access Cards (CAC) store JPEG image of the cardholder:

How to "include" a .PNG image in a binary file?

I'm doing some resource management code where I take a bunch of different resources (image positions etc.) along with the actual images and make a single binary file out of them. Now, how do I actually include the .PNG file in a binary file and how do I read it back again? I would like to retain the .PNG compression.
I use BinaryWriter to write the data into a file, and BinaryReader to read it back. Here's an example of the format I'm using:
BinaryWriter writer = new BinaryWriter(new FileStream("file.tmp"));
writer.Write(name);
writer.Write(positionX);
writer.Write(positionY);
// Here should be the binary data of the PNG image
writer.Close();
BinaryReader reader = new BinaryReader(new FileStream("file.tmp"));
string name = reader.ReadString();
float posX = reader.ReadSingle();
float posY = reader.ReadSingle();
Bitmap bitmap = ... // Here I'd like to get the PNG data
reader.Close();
There is some other data too, both before and after the PNG data. Basically I will merge multiple of PNG files into this one binary file.
You will need to use so sort of Prefix (int) Followed by a Length Indicator (int) followed by your Payload (variable length) or if you know this will be the last thing in your file, then you can skip the prefix/size and just read until end of stream.
Then when you save your various parts, you write your prefix, then your length and then your data.
Ideally you could use some of the serialisers like protobuf to do a lot of the serialising for you, then you can just load your class back. I do this in one of my projects for Plugin Installers. The final File is a Zip, but the structures to generate the file "filenames, description, actual file locations, etc" are stored in a custom file like what you are describing.
if you are doing this all in memory then you could Serialise your PNG Image to a MemoryStream (get the size), then write the Size to the FileStream (file.tmp) followed by the MemorySteam Buffer
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms);
writer.Write(ms.Length);
ms.Position = 0;
ms.CopyTo(writer.BaseStream);
}
Basically Paul Farry's answer is what you need to do. Read up on binary formats like the PNG format (see file header, chunks), the ZIP format (file headers, data descriptor), which implement something -- on a more elaborate level than you need -- the mechanism of storing different chunks of data in one file.

Save bitmap to file has zero in image size field

I find when I use the Bitmap.Save method with format ImageFormat.Bmp that the bitmap header info in the file has zero in the biSizeImage field.
From what I can see this is ok according to the specs, but unfortunately the crappy embedded device I am forced to work with insists on it being set correctly.
So the question is how to go about getting this header info field always set when the bitmap is saved?
The issue, as you suspect, is that 0 is a perfectly allowable value for the image size field of an RGB bitmap. The MSDN documentation for the BITMAPINFOHEADER structure confirms this fact:
biSizeImage
The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
If biCompression is BI_JPEG or BI_PNG, biSizeImage indicates the size of the JPEG or PNG image buffer, respectively.
So the implementation in the .NET Framework is simply doing things the standard, documented way. Since you need something else, you'll have to handle the save to file process yourself, which will allow you to manually modify the biSizeImage member of the BITMAPINFOHEADER structure.
A pretty comprehensive example is available here: Saving a Control Image to a Bitmap File. Of course, it's written for the Compact Framework, but most of the individual components are still applicable. You'll need to P/Invoke several functions from the Windows API (visit www.pinvoke.net for the definitions), and work with a DC.
Taking the drawing into your own hands will probably also be faster, because you're using the GDI subsystem, rather than GDI+. An article on that general approach is available here: 1bpp in C#, and it appears to demonstrate taking matters into your own hands with the BITMAPINFOHEADER struct as well.
If you understand unmanaged C++ code, there's a tutorial available here that might give you some idea of what you'd have to implement in C# to do the same thing: Loading and Saving Bitmaps. As you can see, the code required really isn't that long, albeit still more than the one line Bitmap.Save method.
I encountered with this problem too and it solved with a simple trick.
save the bitmap, then open it as a file stream and change the biSizeimage field manually.
picbpp.Save("pic.bmp", ImageFormat.Bmp);
string path = "pic.bmp";
FileStream fs = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.None);
// Change biSizeimage field manually.
Byte[] info = new byte[] {0x10,0x59};
fs.Seek(34, SeekOrigin.Begin);
fs.Write(info, 0, info.Length);
fs.Close();

passing the right BytesArray into the MemoryStream for Image

there a way to determine that I am passing the right byte array to the MemoryStream if I want to create an Image out of byte array.
MemoryStream mStream = new MemoryStream();
mStream.Write(byteArray, 0, byteArray.Lenth);
Image imgObj = Image.FromStream(mStream);
How can I, if possible Correct the byteArray that it is a valid byteArray for an Image?
This is a really ominous question, surely you must know where you are reading your data from? When you create an image using Image.FromStream, an ArgumentException will be thrown if it cannot recognise the format. Why don't you use that mechanism for identifying an incorrect stream of data, rather than re-invent the wheel?
I've done a bit of programatic image manipulation myself. The thing you'll want to do is find the spec for the image format that you are modifying and make sure you do everythign you should. For example png files are chunked and have checksums on each section so if you change something in that chunk you have to recalculate the checksum at the end of the section.
After reading your questions and your comments, i think what you're trying is to manipulate the image by manipulating the byte array before you put it into the Image class. And now you claim that your byte array is corrupt for this image format and how you can correct it.
So the answer to this question would be: You corrupted it, you'll fix it.
But to really solve your problem, if your goal is to manipulate the picture itself, just load it into an interims Image and use the Graphics class to manipulate your picture. Afterwards put the result into the real image object you like. Ready, without any hassle about working on the byte array.
here's the answer Image Processing for Dummies with C# and GDI+
OT: i don't know how to put links on comments so I put it in the answers.

How can I take a byte array of a TIFF image and turn it into a System.Drawing.Image object?

I have a byte[] array, the contents of which represent a TIFF file (as in, if I write out these bytes directly to a file using the BinaryWriter object, it forms a perfectly valid TIFF file) and I'm trying to turn it into a System.Drawing.Image object so that I can use it for later manipulation (feeding into a multipage TIFF object)
The problem I'm having is that the commonly accepted code for this task:
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms, true);
return returnImage;
}
doesn't work for me. The second line of the above method where it calls the Image.FromStream method dies at runtime, saying
Parameter Not Valid
I believe that the method is choking on the fact that this is a TIFF file but I cannot figure out how to make the FromStream method accept this fact.
How do I turn a byte array of a TIFF image into an Image object?
Also, like I said the end goal of this is to have a byte array representing a multipage TIFF file, which contains the TIFF files for which I have byte array objects of right now. If there's a much better way to go about doing this, I'm all for it.
OK, I found the issue, and it was from a part of the code unrelated to the part of the code I was asking about. The data was being passed as a string, I was converting it to a byte array (this was a test rig so I was trying to simulate the byte array that I get in the main app), then converting that to a MemoryStream, then making an Image from that.
What I failed to realize was that the string was Base64 encoded. Calling Convert.FromBase64String() caused it to turn into a byte array which wouldn't kill the Image.FromStream() method.
So basically it boiled down to a stupid mistake on my part. But hey, the code above is still useful and this page will probably serve as a Google result as to how to avoid this mistake to someone else.
Also, I found an easy way to construct a Multi-Page TIFF from my byte arrays here.
Edit: The assumption below is not correct, I had a chance to fire up my IDE later and tested with and without Write and both populated the MemoryStream correctly.
I think you need to write to your MemeoryStream first.
As if my memory (no pun intended) serves me correctly this:
MemoryStream ms = new MemoryStream(byteArrayIn);
Creates a memory stream of that size.
You then need to write your byte array contents to the memory stream:
ms.Write(byteArrayIn, 0, byteArrayIn.Length);
See if that fixes it.
All these were clues that helped me figure out my problem which was the same problem as the question asks. So i want to post my solution which i arrived at because of these helpful clues. Thanks for all the clues posted so far!
As Time Saunders posted in his answer, that Write method to actually write the bytes to the memory stream is essential. That was my first mistake.
Then my data was bad TIFF data too, but in my case, i had an extra character 13 at the beginning of my image data. Once i removed that, it all worked fine for me.
When i read about some basic TIFF file format specs, i found that TIFF files must begin with II or MM (two bytes with values of either 73 or 77). II means little-endian byte order ('Intel byte ordering') is used. MM means big-ending ('Motorola byte ordering') is used. The next two bytes are a two byte integer value ( = Int16 in .NET) of 42, binary 101010.
Thus a correct TIFF stream of bytes begins with the decimal byte values of: 73, 73, 42, 0 or 77, 77, 0, 42. I encourage anyone with the same problem that we experienced to inspect your TIFF data byte stream and make sure your data is valid TIFF data!
Thanks Schnapple and Tim Saunders!!

Categories