Byte of pixel data to image - c#

I have learnt how to export pixel data of an image to byte array, here is my code
void Button2Click(object sender, EventArgs e)
{
Bitmap img = new Bitmap (#"24x30.bmp");
var BitmapData = img.LockBits( new Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadOnly,img.PixelFormat);
var length = BitmapData.Stride * BitmapData.Height;
MessageBox.Show(BitmapData.Width.ToString());
byte[] bytes = new byte[length];
Marshal.Copy(BitmapData.Scan0, bytes, 0, length);
img.UnlockBits(BitmapData);
string test = ByteArrayToBinary(bytes);
}
I convert the bytes to string bit but lets ignore it. What I want to know is, how can I convert the byte of pixel data to an image? Please share the code and the reference.
I have read many references but I don't get it until now.
EDIT:
This summary of my case, i have Stride, Width, Height, and Byte[] of Pixel data. How can i reconstruct it to image again thanks

Same code, but copy the other way. (You can read a dummy image or use another image constructor.)

Obviously, you first step should be to somehow convert your text back to a byte array, but then you'll see that you can't actually create an image from just that data.
As you mentioned in comments already, your dumped binary block is missing all header data. You need the original width, height and pixel format before you can convert the data back to an image.
Also, if this is an indexed image, there will be a colour palette, and you don't save that either.
And finally, you copy all data in the image memory. Images are kept in memory per line of pixels, but these lines are usually rounded to the next multiple of four bytes (except in some indexed pixel formats, I think). This means that unless your image uses four bytes per pixel (32 bits per pixel), the bytes you end up with may contain junk data at the end of each line. You don't trim that off in any way, meaning you not only need the width and height, but the stride as well, before you can reconstruct the image.
As for how to build the image, the method is pretty much the same. You make a new image with the correct dimensions and pixel format, open its backing memory using LockBits but in WriteOnly mode, and copy your data into it.
I posted a full method for that on this site before, so feel free to check it out.

Related

Capture image from touch of PixelSense-compatible PC and save it as .bmp

I'm using Microsoft Surface 2.0 SDK with SUR40 PixelSense compatible computer. I need to capture image from it's touch and save it as .bmp. Since Surface SDK comes with RawImageVisualizer example, which displays picture from touch on the screen, I've tried to modify program for writing picture to HDD. The problem is, I get ArgumentException: Parameter is invalid during building Image from byte array captured from touch.
This is how I retrieve byte array with image data from FrameReceivedEventArgs on FrameReceived event:
event.UpdateRawImage(
ImageType.Normalized,
normalizedImage,
0, 0,
InteractiveSurface.PrimarySurfaceDevice.WorkingAreaWidth,
InteractiveSurface.PrimarySurfaceDevice.WorkingAreaHeight);
And that's how I try to write bytes as .bmp to disk:
System.Drawing.Image img;
using (System.Drawing.Image raw = System.Drawing.Image.FromStream(new MemoryStream(normalizedImage)))
{
img = raw.Clone() as System.Drawing.Bitmap;
}
img.Save("C:/img.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
So I get the exception trying to create Image from stream. Nevertheless this byte array works totally fine with Texture2D and SpriteBatch which displays it. How can I fix ArgumentException?
i've just realized, that UpdateRawImage does not return a byte representation of PNG file, but only an array of pixels. So, to build an image from it, one have to write all other parts of file structure to the array: header and color table (if needed). In many cases this can be simply done with one of System.Drawing.Bitmap constructors:
public Bitmap(
int width,
int height,
int stride,
PixelFormat format,
IntPtr scan0
)
But I was not so lucky, because UpdateRawImage returns 8bpp grayscale pixels, and PixelFormat enum doesn't support them (the most close is Format16bppGrayScale, but it uses 2 bytes for pixel, not one). So, in this particular situation, there are two obvious solutions. The first is making a new array of pixels, which meets one of PixelFormat standards (that was my choice, because I need 24-bit RGB image, despite it's actually black-white with only 256 shades). The second is writing BMP headers manually (and it's not very difficult due to open specs).

What Does the Byte[] Representation of an Image Actually Mean?

I loaded a 1 pixel image into a bitmap and then converted it to a byte[]
_Image = "test.jpg";
Bitmap testImage = new Bitmap(_Image);
ImageConverter converter = new ImageConverter();
byte[] byteTestImage = (byte[])converter.ConvertTo(testImage,typeof(byte[]));
The single pixel has RGB values (255, 116, 25). Each of these can be represented by a byte,
so I assumed that byteTestImage would correspond to this. But, byteTestImage is 635 elements in total.
What is the relationship between those bytes and the 1 pixel image?
The file you loaded is a JPG. It has certain additional information (width, height, EXIF data) not just colors. Look at https://en.wikipedia.org/wiki/JPEG
Try opening it in a hex editor. You might even be able to read info about the camera used to take it.
There is no always RGB format for single pixel in Bitmap. It all depends on format. You can have a alfa component, you can have a palette to which martix of pixels refers to and more...
Check out: Bitmap format

OpenCV create Mat from byte array

In my C++ dll I am creating Mat from byte array:
BYTE * ptrImageData; //Image data is in this array passed to this function
Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);
The image is created with some gray shade not the original one.
Is this the proper way of creating Mat from byte array?
Please see code
ptrImageData is passed to the C++ dll from C# code.
C# code to pass the image data
System.Drawing.Image srcImage //Has the image
MemoryStream ms = new MemoryStream();
Marshal.FreeHGlobal(ptrImageData);
srcImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] imgArray = ms.ToArray();
ms.Dispose();
int size1 = Marshal.SizeOf(imgArray[0]) * imgArray.Length;
IntPtr ptrImageData = Marshal.AllocHGlobal(size1);
Marshal.Copy(imgArray, 0, ptrImageData, imgArray.Length);
//Calling C++ dll function
ProcessImage(ptrImageData, srcImage.Width, srcImage.Height);
Marshal.FreeHGlobal(ptrImageData);
The C++ code appears ok, in that this creates a matrix wrapper for the supplied image data, assuming the buffer is in the conventional RGB8 format. Note that this constructor does not copy the buffer, so the buffer must remain valid for the duration of this Mat instance (or be copied).
Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);
It appears the problem lies in Your C# code. I am not a C# developer, but I will do my best to help. You are creating a memory stream and using the JPEG codec to write a compressed version of the image into the buffer as if it were a file. But that is not the data format that cv::Mat is expecting, so you will basically see garbage (compressed data interpreted as uncompressed).
Given a System.Image.Drawing.Image instance, you can create a wrapper Bitmap object directly (or maybe use as, since it is a simple downcast). Then you can just use the Bitmap.LockBits() method tog obtain a pointer to the underlying image data.
Bitmap bmp = new Bitmap(sourceImage);
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbBuffer = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbBuffer, 0, bytes);
// Do your OpenCV processing...
// ...
// Unlock the bits.
bmp.UnlockBits(bmpData);
and then you can pass the rgbBuffer to OpenCV.
I'm not convinced that the memory management in the original code is entirely correct either, but anyway the above will work provided the scope of the buffer ownership is within the lock and unlock method calls. If the image data is to outlive this code block, you will have to copy the buffer.
Be careful with your pixel formats too - you need to make sure the Image/Bitmap instance really contains RGB8 data. OpenCV's cv::Mat has various flags so you can work with a variety of in-memory image formats. But note that these are not the same as the on-disk (typically compressed) formats, such as PNG, TIFF, and so forth.
Yes, this is one way to create a Mat from a byte array. You just have to be careful that your array contains what you think it does.
The image is created with some gray shade not the original one.
So you are getting an image in newImg? What was the pixel format of the original data?
Maybe you've switched the red and blue channels. The following line will swap the channels:
cv::cvtColor(newImg,swappedImg,CV_RGB2BGR);
Here is link to docs: http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-mat
In general you should take care about two things:
When you pass external data into matrix constructor, the external data is not automatically deallocated, so you should take care of it. If you want OpenCV matrix to care about memory, then you should copy matrix (you can do it in many ways, e.g. using Mat::clone or Mat::copyTo methods.
External data may not be continuous, i.e. size of row may be bigger than width multiplied by number of channels multiplied by size of data element. So you may want specify "step" as last argument of constructor. If you allocate external data manually and 100% sure that it is continuous, then you may not pass step and rely on automatic step calculation.
I am not familiar with C#, but it seems to me that you release data right after ProcessImage call. So if ProcessImage is asynchronous or somehow caches your matrix (i.e. lifetime of matrix is longer that ProcessImage call), then you should care about memory management.

how to insert/ extract transparent channel from a bitmap in C#?

I want to extract the transparent channel from a bitmap to a 2D array, do some things with it, and then return it back to the bitmap.
How do I extract it/ insert it?
I assume you're using System.Drawing.Bitmap that has a PixelFormat value of Format32bppArgb.
You'll want to call LockBits to so that you can operate on the bitmap bits directly.
The returned BitmapData instance contains information about the bitmap, including the Scan0 property, which is the address of the first pixel in the bitmap. The Alpha channel is the most significant byte of each pixel.
Note that Scan0 is an IntPtr. The bits are in a 1-dimensional array. You'll have to write your own indexing code that treats the 1D array as a 2D array. Be sure to take the Stride into account.

C# Getting the pixel data efficiently from System.Drawing.Bitmap

I have several (~2GB) raw 24bpp RGB files on HDD.
Now I want to retrieve a portion of it and scale it to the desired size.
(The only scales allowed are 1, 1/2, 1/4, 1/8, ..., 1/256)
So I'm currently reading every line from the rectangle of interest into an array, which leaves me with a bitmap which has correct height but wrong width.
As the next step I'm creating a Bitmap from the newly created array.
This is done with by using a pointer so there is no copying of data involved.
Next I'm calling GetThumbnailImage on the Bitmap, which creates a new bitmap with the correct dimensions.
Now I want to return the raw pixel data (as a byte array) of the newly created bitmap.
But to achieve that I'm currently copying the data using LockBits into a new array.
So my question is: Is there a way to get the pixel data from a Bitmap into a byte array without copying it?
Something like:
var bitmapData = scaledBitmap.LockBits(...)
byte[] rawBitmapData = (byte[])bitmapData.Scan0.ToPointer()
scaledBitmap.UnlockBits(bitmapData)
return rawBitmapData
I'm well aware that this doesn't work, it is just an example to what I basically want to achieve.
I think this is your best bet.
var bitmapData = scaledBitmap.LockBits(...);
var length = bitmapData.Stride * bitmapData.Height;
byte[] bytes = new byte[length];
// Copy bitmap to byte[]
Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
scaledBitmap.UnlockBits(bitmapData);
You have to copy it, if you want a pass around a byte[].
You don't have to delete the bytes that were allocated, you just need to Dispose of the original Bitmap object when done as it implements IDisposable.

Categories