Byte array or matrix to BitMap - c#

I am currently having the following problem: I want to convert a byte array that comes from a file with the following configuration:
Byte1: R color of pixel 0,0.
Byte2: G color of pixel 0,0.
Byte3: B color of pixel 0,0.
Byte4: R color of pixel 0,1.
...
ByteN: R color of pixel n,n.
So what I want to do is convert these bytes into a bitmap without having to set pixel by pixel with bitmap.setPixel because it takes too long.
Any suggestions? Thanks in advance!

If you have the byte[] of the pixels, and the width and height, then you can use BitmapData to write the bytes to the bitmap since you also know the format. Here's an example:
//Your actual bytes
byte[] bytes = {255, 0, 0, 0, 0, 255};
var width = 2;
var height = 1;
//Make sure to clean up resources
var bitmap = new Bitmap(width, height);
var data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
bitmap.UnlockBits(data);
This is a very fast operation.
You will need to import these three namespaces at the top of your C# file, at minimum:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

Related

Image processing : How to can i use this code like getpixel and setpixel

Hello guys i saw this code in microsoft docs and i need to use it like getpixel and setpixel , clean code examples thanks .
private void LockUnlockBitsExample(PaintEventArgs e){
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// 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[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
I need i way to use this code like getpixel and setpixel functions with good explanation and clean code thanks .

Converting RAW byte data to Bitmap

I am taking a screenshot of an android device using ADB and receiving the screenshot as raw byte data.
I know the raw byte data coming through is in the format rgba
Red is offset 0, Green offset 8, Blue offset 16, Alpha offset 24, each value is 1 byte. This makes up the entire byte array.
I am trying to convert this byte array to a Bitmap in C# and it is working for the most part, the image looks correct in every way apart from the fact that it is coming through with a 'blue hue' -- the coloring is off.
The following is the code I'm using to convert my raw byte data:
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
I've read in this post that it could be to do with the byte order of the actual rgba values. As you can see in the code above, I tried casting my bmpData.Scan0 to int* and it is still coming through with a blue hue.
I'm wracking my brain as to what I can do now to get this image to come through with the correct colors. I'm assuming it's reading red as blue and blue as red or vice versa.
I thought I could manipulate the raw byte data so that it is in the correct byte order when converting it to a bitmap, however I'm not sure how I can go about doing that.
Any suggestions?
So, in the end my solution was simple.
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
byte[] newData = new byte[imageData.Length];
for(int x = 0; x < imageData.Length; x+= 4)
{
byte[] pixel = new byte[4];
Array.Copy(imageData, x, pixel, 0, 4);
byte r = pixel[0];
byte g = pixel[1];
byte b = pixel[2];
byte a = pixel[3];
byte[] newPixel = new byte[] { b, g, r, a };
Array.Copy(newPixel, 0, newData, x, 4);
}
imageData = newData;
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
All I had to do was loop through the imageData and adjust the bytes that made up the pixels and re-order them so that they would suit the expected format for windows bitmap which is BGRA.
Obviously, I can still make some small optimisations in the for loop that shifts the bytes around, but it's working.
Did you have a look at this post ?
You're stating that every 'value' is 8 bytes, so 1 pixel is 4x8 = 32 bytes? But you are using 32bpp image format, so 32 bits per pixel -> 1 pixel = 4 bytes.
Also pay attention to little/big endian, if you're acquiring the image from another processor/network/...

Blue and red channels swapped during conversion from bitmap to bitmapsource

I need to copy a bitmap that I receive from a camera into a BitmapSource in order to show it in a WPF application. Image arrives in PixelFormat.Format24bppRgb with a negative stride. I got this working by following code
//NOTE: image is in PixelFormat.Format24bppRgb
var bitmap = imageBuffer.Bitmap;
Image = new WriteableBitmap(bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Rgb24, null);
var bitmapData = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
var rowSize = bitmapData.Stride < 0 ? -bitmapData.Stride : bitmapData.Stride;
var bitmapPtr = bitmapData.Scan0;
var bitmapLine = new Int32Rect(0, 0, bitmap.Width, 1);
for (int line = 0; line < bitmap.Height; line++)
{
Image.WritePixels(bitmapLine, bitmapPtr, rowSize, rowSize, 0, line);
bitmapPtr += bitmapData.Stride;
}
bitmap.UnlockBits(bitmapData);
The problem I am running into is that that blue and red channels seem to be swapped. I could resolve the issue by creating the BitmapSource as Bgr24 but since in application I need to also do some image processing prior to conversion I would prefer to have things in a correct format prior to that. Am I doing something wrong in the conversion or is this some GDI peculiarity?
Note that if I apply the camera bitmap directly to a WinForms picture box, the image is displayed correctly. Also WriteableBitmap is only recreated for the sake of code brevity.
If the image format you receive is RGB but the stride is negative, then the image format is BGR as it's being read backwards.
Negative stride means it's an image bottom-up instead of top-down, usually the draw operations of Graphics would handle these things for you, but the Image class WritePixels doesn't allow to specify a negative stride, so you must reverse the pixel format (BGR)
The correct conversion from System.Drawing.Imaging.PixelFormat.Format24bppRgb to System.Windows.Media.PixelFormat is PixelFormats.Bgr24.
Negative stride has nothing to do with with the pixel format, it only describes vertical rotation of the image - top down for positive and bottom up for negative. If the stride had any effect on the pixel format, then the code below would have stored the images with reversed stride with blue and red channel swapped. This is not the case and the only effect seen is that one image is vertically rotated.
var wholeImage = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
bitmapData = bitmap.LockBits(wholeImage, ImageLockMode.ReadOnly, bitmap.PixelFormat);
var reverseStride = -bitmapData.Stride;
var reversedStartPtr = bitmapData.Scan0 + bitmapData.Stride * (bitmapData.Height - 1);
var reverseStrideBitmap = new Bitmap(bitmapData.Width, bitmapData.Height,
reverseStride, bitmapData.PixelFormat, reversedStartPtr);
bitmap.Save("original.png");
reverseStrideBitmap.Save("reversedStride.png");

Converting bitmap to special gray scale byte array

I need to create an image in memory (can be huge image!) and to extract from it byte array in the size of width x height. Each byte must have value of 0-255 (256 gray scale values: 0 for white and 255 for black).
The part of creating the image is easy, here is a simple example of my code:
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
drawing.Clear(Color.Black);// paint the background
drawing.DrawString(text, font, Brushes.White, 0, 0);
Problem is to convert it to "my" special gray scale byte array. When I'm using any pixel format other then Format8bppIndexed, the byte array I'm getting from the bitmap is not in the size I need (width*length) so I need a conversion that takes too much time. When I'm using Format8bppIndexed I'm getting the byte array very fast and in the right size, but each byte/pixel is 0-15.
Changing the bitmap palette has no affect:
var pal = img.Palette;
for (int i = 1; i < 256; i++){
pal.Entries[i] = Color.FromArgb(255, 255, 255);
}
img.Palette = pal;
Any idea how to do it?
Edit: Full code:
// assume font can be Times New Roman, size 7500!
static private Bitmap DrawText(String text, Font font)
{
//first, create a dummy bitmap just to get a graphics object
var img = new Bitmap(1, 1);
var drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
var textSize = drawing.MeasureString(text, font);
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size (must be multiple of 4)
int width = (int) (textSize.Width/4) * 4;
int height = (int)(textSize.Height / 4) * 4;
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
// paint the background
drawing.Clear(Color.Black);
drawing.DrawString(text, font, Brushes.White, 0, 0);
var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var newBitmap = new Bitmap(width, height, bmpData.Stride, PixelFormat.Format8bppIndexed, bmpData.Scan0);
drawing.Dispose();
return newBitmap;
}
private static byte[] GetGrayscleBytesFastest(Bitmap bitmap)
{
BitmapData 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);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
You probably want to do this in two steps. First, create a 16bpp grayscale copy of your original image as described in Convert an image to grayscale.
Then, create your 8bpp image with the appropriate color table and draw the 16bpp grayscale image onto that image. That will do the conversion for you, converting the 16-bit grayscale values to your 256 different colors.
You should then have an 8bpp image with your 256 different shades of gray. You can then call LockBits to get access to the bitmap bits, which will be index values in the range 0 to 255.
I have solved this problem with ImageSharp
I calculate the gray value from the rgb values and then add it to the array.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
private static byte[] GetImageData(byte[] imageData)
{
using (var image = Image.Load<Rgba32>(imageData))
{
var buffer = new byte[image.Width * image.Height];
var index = 0;
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgba32> pixelRow = accessor.GetRowSpan(y);
for (int x = 0; x < pixelRow.Length; x++)
{
ref Rgba32 pixel = ref pixelRow[x];
buffer[index] = (byte)((pixel.R + pixel.G + pixel.B) / 3);
index++;
}
}
});
return buffer;
}
}

How did I get a Stride less than the Bitmap width?

I am copying from one 1bpp bitmap to a smaller 1bpp bitmap. I just want to clip out a region so I can count the number of black pixels.
I use the following to make the copies:
private Bitmap Copy(Bitmap srcBitmap, Rectangle section)
{
BitmapData SourceLockedData;
BitmapData DestLockedData;
Rectangle DestRect;
byte[] SrcImageData;
byte[] DestImageData;
int ByteCount;
int WidthCount = 0;
int CurrentLine = 0;
int DestStride;
int SrcStride = 0;
// Create the new bitmap and associated graphics object
Bitmap Newbmp = new Bitmap(section.Width, section.Height, PixelFormat.Format1bppIndexed);
Newbmp.SetResolution(srcBitmap.HorizontalResolution, srcBitmap.VerticalResolution);
//Lock the bits
SourceLockedData = srcBitmap.LockBits(section, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
SrcStride = SourceLockedData.Stride;
//Get a count of the number of bytes to copy. Remember, bytes are not pixels.
ByteCount = SourceLockedData.Stride * SourceLockedData.Height;
//Initialize the source byte array
SrcImageData = new byte[ByteCount];
//Copy the data to the source byte array
Marshal.Copy(SourceLockedData.Scan0, SrcImageData, 0, ByteCount);
//Unlock the bits
srcBitmap.UnlockBits(SourceLockedData);
//Set a rectangle to the size of the New bitmap
DestRect = new Rectangle(new Point(0, 0), Newbmp.Size);
//Lock the bits
DestLockedData = Newbmp.LockBits(DestRect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
DestStride = DestLockedData.Stride;
//Get a count of the number of bytes to copy. Remember, bytes are not pixels.
ByteCount = DestLockedData.Stride * DestLockedData.Height;
//Initialize the source byte array
DestImageData = new byte[ByteCount];
//Copy the data to the destination byte array
Marshal.Copy(DestLockedData.Scan0, DestImageData, 0, ByteCount);
//Unlock for now
Newbmp.UnlockBits(DestLockedData);
for (int ArrayIndex = 0; ArrayIndex < ByteCount; ArrayIndex++)
{
if (WidthCount == Newbmp.Width)
{
//increment the line and push the index by the stride
ArrayIndex = (++CurrentLine) * DestStride;
continue;
}
DestImageData[ArrayIndex] = SrcImageData[ArrayIndex];
WidthCount++;
}
//Lock the bits again
DestLockedData = Newbmp.LockBits(DestRect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
//Copy data from byte array to IntPtr
Marshal.Copy(DestImageData, 0, DestLockedData.Scan0, ByteCount);
//Unlock bits
Newbmp.UnlockBits(DestLockedData);
// Return the bitmap
return Newbmp;
}
The biggest problem I am having is that both the SourceLockedData.Stride and DestLockedData.Stride are smaller than the width of the respective images. How can that be? From everything I know about stride it's the number of bits from one scan line of data to the next scan line of data. How is it mathematically possible for this to be less than the width?
Am I using LockBits or BitmapData wrong? Can BitmapData not be trusted? Should I calculate the stride by hand?
Tom P.
I figured out that the stride can be less than the width if you are dealing with RLE bitmaps. Since the bitmaps that I am loading are TIFFs they are RLE8 encoded.

Categories

Resources