Convert a raw RGB byte array to a JPG encoded string base64 - c#

I am successfully converting a JPG image file into a base64 string, and then that string into a JPG image again.
string str64 = ImageToStrBase64(My.Resources.ImageJpg);
PictureBox1.Image = Base64StrToImage(str64);
ImageToStrBase64() and Base64StrToImage() are custom functions just to explain the idea, but I can place the code if necessary.
I am also converting a raw byte array (RGB or BGR, no matters) into a base64.
However, I do now need to convert a raw byte array into a JPG encoded base64 string. I am finding solutions that involves only an image file saving, but file management it's high time consuming. How to encode then a BGR byte array into a JPG byte array using, for example a memory stream?
Function use to convert jpg into a formatted byte array:
public static string ImagetoStrBase64(System.Drawing.Image imageToEncode)
{
string base64String = "";
using (System.Drawing.Image image = imageToEncode)
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
byte[] imageBytes = m.ToArray();
base64String = Convert.ToBase64String(imageBytes);
}
}
return base64String;
}
Update (regarding to chat)
Let's simplify it... We have a byte[] with single green pixel:
byte[] rawPixelArray = new byte[] {0, 255, 0};
Then, GetJpgBytes(rawPixelArray) must return an encoded byte[] for a Jpeg image of 1x1 pixel. This is more clear I guess :)

Before you read the answer, read the bullet points:
A real useful conversion, should return JPEG encoded image which is equivalent to content of a Jpeg file, which is not pixel data.
When you get an array of pixel data of a Jpeg image, you will have non of benefits of a Jpeg image. It's no more Jpeg encoded image, it's an array of pixel data which can be used to create a BitMap.
To better understand the point, for example for a 10K JPEG white image, the pixel data will be 2MB!
I've shared a few methods in this answer which are really useful standalone, and I've showed how you can combine them to get what you want. (While I cannot see any benefit in the conversion which you are trying to do.)
These are very useful pieces which you can put together to solve the puzzle:
ImageData: In addition to a byte[] of image pixels, you need to know about width, height and pixel format of the image. Then you can describe the image and create an Image from that data. Without having all these information a byte[] is meaningless on its own.
GetImageDataFromImage: Gets image data (width, height, pixel format, pixel data) from an Image
CreateImageFromImageData: Creates an Image from image data
ConvertImageToJpegImage: Converts an Image to Jpeg Image
ByteArrayToBase64: Converts a byte[] to Base64 string.
Then after having these pieces, you can achieve what you want.
Assuming you have a byte[] of pixel data, width, height and pixel format of your data, this is what you need:
// A 2x2 image having 24 bit RGB pixel format, all pixels are #FF0000 (Red)
var imageData = new ImageData()
{
Width = 2,
Height = 2,
PixelFormat = PixelFormat.Format24bppRgb,
PixelData = new byte[] {
0x0, 0x0, 0xFF, 0x0, 0x0, 0xFF,
0xF, 0x0, 0xFF, 0x0, 0x0, 0xFF
}
};
var image = CreateImageFromImageData(imageData);
var jpeg = ConvertImageToJpegImage(image);
var jpegImageData = GetImageDataFromImage(jpeg);
var jpegPixelsBase64 = ByteArrayToBase64(jpegImageData.PixelData);
Which result in AAD+AAD+AAD+AAD+ which is in fact an image having #FE0000 color!
Note: I didn't dispose images to keep it clear, in action, you need to dispose image and jpeg.
ImageData
Required information about an image, including width, height, pixel format, and pixel data array:
public class ImageData
{
public int Width { get; set; }
public int Height { get; set; }
public PixelFormat PixelFormat { get; set; }
public byte[] PixelData { get; set; }
}
GetImageDataFromImage
Gets image data from an image.
ImageData GetImageDataFromImage(Image image)
{
using (var bitmap = new Bitmap(image.Width, image.Height, image.PixelFormat))
{
using (var g = Graphics.FromImage(bitmap))
g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, bitmap.PixelFormat);
var rowLength = image.Width * Image.GetPixelFormatSize(image.PixelFormat) / 8;
var bytes = new byte[image.Height * rowLength];
var ptr = data.Scan0;
for (var i = 0; i < image.Height; i++)
{
Marshal.Copy(ptr, bytes, i * rowLength, rowLength);
ptr += data.Stride;
}
bitmap.UnlockBits(data);
return new ImageData
{
Width = bitmap.Width,
Height = bitmap.Height,
PixelFormat = bitmap.PixelFormat,
PixelData = bytes
};
}
}
We rely on LockBits to get or set image byte array.
CreateImageFromImageData
Creates image from image data:
Image CreateImageFromImageData(ImageData imageData)
{
var bitmap = new Bitmap(imageData.Width, imageData.Height, imageData.PixelFormat);
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, bitmap.PixelFormat);
var rowLength = imageData.Width * Image.GetPixelFormatSize(imageData.PixelFormat) / 8;
var bytes = new byte[imageData.Height * rowLength];
var ptr = data.Scan0;
for (var i = 0; i < imageData.Height; i++)
{
Marshal.Copy(imageData.PixelData, i * rowLength, ptr, rowLength);
ptr += data.Stride;
}
bitmap.UnlockBits(data);
return bitmap;
}
We rely on LockBits to get or set image byte array.
ConvertImageToJpegImage
Converts an image to Jpeg Image:
public Image ConvertImageToJpegImage(Image img)
{
using (var stream = new MemoryStream())
{
img.Save(stream, ImageFormat.Jpeg);
var bytes = stream.ToArray();
return (Image)new ImageConverter().ConvertFrom(bytes);
}
}
If you care about compression level use jpeg encoder.
ByteArrayToBase64
Conversion from byte[] to Base64String and is straightforward, but to have better readability of the answer and the code:
public string ByteArrayToBase64(byte[] bytes)
{
return Convert.ToBase64String(bytes);
}

Related

SharpAvi Image to ByteArray to Frame to AVI

I'm having some trouble converting an image to a video using the SharpAVI.dll.
I have managed to produce a video file using a randomly generated byte array by using the documentation on SharpAVI's website:
Getting Started with SharpAVI
So the next step I thought I would take was to take an Image, create a Bitmap image, convert the bitmap to a byte array and then simply save the byte array to each frame of the video file. When I run the program, I get no errors or anything and a video file of an appropriate file size is produced however the video file is unreadable and will not open. I'm really struggling to see why this won't work. Any help would be greatly appreciated!
My Code:
private void GenerateSingleImageVideo()
{
string imagePath = textBoxImagePath.Text;
Bitmap thisBitmap;
//generate bitmap from image file
using (Stream BitmapStream = System.IO.File.Open(imagePath, FileMode.Open))
{
Image img = Image.FromStream(BitmapStream);
thisBitmap = new Bitmap(img);
}
//convert the bitmap to a byte array
byte[] byteArray = BitmapToByteArray(thisBitmap);
//creates the writer of the file (to save the video)
var writer = new AviWriter(textBoxFileName.Text + ".avi")
{
FramesPerSecond = int.Parse(textBoxFrameRate.Text),
EmitIndex1 = true
};
var stream = writer.AddVideoStream();
stream.Width = thisBitmap.Width;
stream.Height = thisBitmap.Height;
stream.Codec = KnownFourCCs.Codecs.Uncompressed;
stream.BitsPerPixel = BitsPerPixel.Bpp32;
int numberOfFrames = ((int.Parse(textBoxFrameRate.Text)) * (int.Parse(textBoxVideoLength.Text)));
int count = 0;
while (count <= numberOfFrames)
{
stream.WriteFrame(true, byteArray, 0, byteArray.Length);
count++;
}
writer.Close();
MessageBox.Show("Done");
}
private byte[] BitmapToByteArray(Bitmap img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
You're wrong in assuming that you should pass a Bitmap object to WriteFrame method. It expects pixel data in bottom to top 32bpp format. See example in
// Buffer for pixel data
var buffer = new byte[width * height * 4];
...
// Copy pixels from Bitmap assuming it has expected 32bpp pixel format
var bits = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length);
bitmap.UnlockBits(bits);
You can see code of a sample app as a reference
https://github.com/baSSiLL/SharpAvi/blob/master/Sample/Recorder.cs

Reading the pixel values of a .jpg image into a byte array

I'm trying to write a program in c# to send a frame via ethernet.
Currently I have .jpg test images in 1920x1080 resolution and very different sizes in bytes.
I am trying to convert a .jpg image to a byte array, I looked for similar answers but when I tried them I got byte arrays including 437, 1030, 1013 bytes for each image. Considering that the images are in HD resolution, this does not make sense. How can I convert an image file to form a 1920*1080*3 (RGB) byte array? Please keep in mind that I am trying to develop a real time application that should be able to send frames at a high rate so this code cannot be slow.
Thanks in advance.
Tunc
to read Image bytes to byte array:
Image image = ...;
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
if (ms.Length == 0)
{
ms.Close();
throw new Exception("Bad Image File");
}
ms.Position = 0;
byte[] baImageBytes = new byte[ms.Length];
ms.Read(baImageBytes , 0, (int)ms.Length);
ms.Close();
to create image from byte array:
byte[] baImageBytes =...
Image myImage = Image.FromStream(new MemoryStream(baImageBytes ));
JPG is a compressed format, that's why its size (and size of the corresponding Byte array)
will be usually far less than 1920*1080*3. In order to get Byte array from JPG you can use
streams:
Image myImage;
...
byte[] result;
using (MemoryStream ms = new MemoryStream()) {
myImage.Save(ms, ImageFormat.Jpeg);
result = ms.ToArray();
}
If all you want are pixels in a form of Byte array you have to convert your JPG into BMP (or other raw, uncompressed format)
Bitmap myImage;
...
byte[] rgbValues = null;
BitmapData data = myImage.LockBits(new Rectangle(0, 0, myImage.Width, myImage.Height), ImageLockMode.ReadOnly, value.PixelFormat);
try {
IntPtr ptr = data.Scan0;
int bytes = Math.Abs(data.Stride) * myImage.Height;
rgbValues = new byte[bytes];
Marshal.Copy(ptr, rgbValues, 0, bytes);
}
finally {
myImage.UnlockBits(data);
}
}

Is it possible to reduce the size of an image comes from byte array

I have a byte array that contains a jpeg image. I was just wondering if it is possible to reduce its size?
Edit: Ok. I acknowledged my mistake. Then my question is how can I reduce the quality of the image comes from a byte array.
Please understand that there is no free lunch. Decreasing the size of a JPEG image by increasing the compression will also decrease the quality of the image. However, that said, you can reduce the size of a JPEG image using the Image class. This code assumes that inputBytes contains the original image.
var jpegQuality = 50;
Image image;
using (var inputStream = new MemoryStream(inputBytes)) {
image = Image.FromStream(inputStream);
var jpegEncoder = ImageCodecInfo.GetImageDecoders()
.First(c => c.FormatID == ImageFormat.Jpeg.Guid);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, jpegQuality);
Byte[] outputBytes;
using (var outputStream = new MemoryStream()) {
image.Save(outputStream, jpegEncoder, encoderParameters);
outputBytes = outputStream.ToArray();
}
}
Now outputBytes contains a recompressed version of the image using a different JPEG quality.
By decreasing the jpegQuality (should be in the range 0-100) you can increase the compression at the cost of lower image quality. See the Encoder.Quality field for more information.
Here is an example where you can see how jpegQuality affects the image quality. It is the same photo compressed using 20, 50 and 80 as the value of jpegQuality. Sizes are 4.99, 8.28 and 12.9 KB.
Notice how the text becomes "smudged" even when the quality is high. This is why you should avoid using JPEG for images with uniformly colored areas (images/diagrams/charts created on a computer). Use PNG instead. For photos JPEG is very suitable if you do not lower the quality too much.
Please try:
// Create a thumbnail in byte array format from the image encoded in the passed byte array.
// (RESIZE an image in a byte[] variable.)
public static byte[] CreateThumbnail(byte[] PassedImage, int LargestSide)
{
byte[] ReturnedThumbnail;
using (MemoryStream StartMemoryStream = new MemoryStream(),
NewMemoryStream = new MemoryStream())
{
// write the string to the stream
StartMemoryStream.Write(PassedImage, 0, PassedImage.Length);
// create the start Bitmap from the MemoryStream that contains the image
Bitmap startBitmap = new Bitmap(StartMemoryStream);
// set thumbnail height and width proportional to the original image.
int newHeight;
int newWidth;
double HW_ratio;
if (startBitmap.Height > startBitmap.Width)
{
newHeight = LargestSide;
HW_ratio = (double)((double)LargestSide / (double)startBitmap.Height);
newWidth = (int)(HW_ratio * (double)startBitmap.Width);
}
else
{
newWidth = LargestSide;
HW_ratio = (double)((double)LargestSide / (double)startBitmap.Width);
newHeight = (int)(HW_ratio * (double)startBitmap.Height);
}
// create a new Bitmap with dimensions for the thumbnail.
Bitmap newBitmap = new Bitmap(newWidth, newHeight);
// Copy the image from the START Bitmap into the NEW Bitmap.
// This will create a thumnail size of the same image.
newBitmap = ResizeImage(startBitmap, newWidth, newHeight);
// Save this image to the specified stream in the specified format.
newBitmap.Save(NewMemoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
// Fill the byte[] for the thumbnail from the new MemoryStream.
ReturnedThumbnail = NewMemoryStream.ToArray();
}
// return the resized image as a string of bytes.
return ReturnedThumbnail;
}
// Resize a Bitmap
private static Bitmap ResizeImage(Bitmap image, int width, int height)
{
Bitmap resizedImage = new Bitmap(width, height);
using (Graphics gfx = Graphics.FromImage(resizedImage))
{
gfx.DrawImage(image, new Rectangle(0, 0, width, height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
}
return resizedImage;
}
The byte array size can always be reduced but you will lose data about your image. You could reduce the quality of the jpeg then it would take up less data as a byte array.
Think of the JPEG bytes as being a very compressed representation of a much larger number of bytes.
So if you try to apply some function to reduce the number of bytes it would be like trying to compress an already compressed thing.

Convert a image to a monochrome byte array

I am writing a library to interface C# with the EPL2 printer language. One feature I would like to try to implement is printing images, the specification doc says
p1 = Width of graphic Width of graphic in bytes. Eight (8) dots = one (1) byte of data.
p2 = Length of graphic Length of graphic in dots (or print lines)
Data = Raw binary data without graphic file formatting. Data must be in bytes. Multiply the width in bytes (p1) by the number of print lines (p2) for the total amount of graphic data. The printer automatically calculates the exact size of the data block based upon this formula.
I plan on my source image being a 1 bit per pixel bmp file, already scaled to size. I just don't know how to get it from that format in to a byte[] for me to send off to the printer. I tried ImageConverter.ConvertTo(Object, Type) it succeeds but the array it outputs is not the correct size and the documentation is very lacking on how the output is formatted.
My current test code.
Bitmap i = (Bitmap)Bitmap.FromFile("test.bmp");
ImageConverter ic = new ImageConverter();
byte[] b = (byte[])ic.ConvertTo(i, typeof(byte[]));
Any help is greatly appreciated even if it is in a totally different direction.
If you just need to convert your bitmap into a byte array, try using a MemoryStream:
Check out this link: C# Image to Byte Array and Byte Array to Image Converter Class
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
As SLaks said I needed to use LockBits
Rectangle rect = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = null;
byte[] bitVaues = null;
int stride = 0;
try
{
bmpData = Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, Bitmap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
stride = bmpData.Stride;
int bytes = bmpData.Stride * Bitmap.Height;
bitVaues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, bitVaues, 0, bytes);
}
finally
{
if (bmpData != null)
Bitmap.UnlockBits(bmpData);
}

How to create a bmp file from byte[] in C#

I have a byte[] array received in TCP Client.The array contains a 24-bit RGB Bitmap file.How to create that bitmap file with given Width ,Height and data?
In C++ I use this
int WriteBitmapFile(const char *filename, int width, int height, unsigned char *imageData)
{
FILE *filePtr; // file pointer
BITMAPFILEHEADER bitmapFileHeader; // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader; // bitmap info header
DWORD imageIdx; // used for swapping RGB->BGR
unsigned char tempRGB; // used for swapping
// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
return 0;
// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 32; // 24-bit
bitmapInfoHeader.biCompression = BI_RGB; // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 4; // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width; // bitmap width
bitmapInfoHeader.biHeight = height; // bitmap height
// switch the image data from RGB to BGR
for(imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=4)
{
tempRGB = imageData[imageIdx];
imageData[imageIdx] = imageData[imageIdx + 2];
imageData[imageIdx + 2] = tempRGB;
}
// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);
// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);
// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);
// close our file
fclose(filePtr);
// Success
return 1;
}
How could I do that in C#?
If the array actually contains a bitmap file, then you can just save the bytes as a file:
File.WriteAllBytes(fileName, imageData);
If the array contains only raw pixel data, you can create a Bitmap object using the data:
unsafe {
fixed (byte* ptr = imageData) {
using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr))) {
image.Save(fileName);
}
}
}
The stride value is the number of bytes between the scan lines. If there is no padding between the scan lines, it's width * 3 for a 24bpp format.
This method uses the data in the array without creating another copy of the entire image in memory (which is why it needs the stride value).
If the bitmap data is stored upside down in the array, the stride value should be negative, and the pointer should be the start of the last scan line in memory (ptr + stride * (height - 1)).
I can't test it using the stream you will be receiving, but this should work.
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
I'd recommend making a Bitmap in C#, and letting it save itself.
For an example, see this post. (Particularly, the last response is correct.)
this is one way of doing it, here i have created a custom Event args that contains the size at which the image was stored as a byte array. You may not need to bother with this, this was code i created to retreive images from a byte array that a gige camera was storing to so for me this made sence.
public Bitmap ShowImage(byte[] sender, EventImageParams e)
{
Bitmap bitmap = new Bitmap(e.width, e.height, PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(sender, 0, pNative, (e.width * e.height * 3));
//
bitmap.UnlockBits(bmData);
return bitmap;
}

Categories