I have following code to create bitmap using array with data*
//Here create the Bitmap to the know height, width and format
Bitmap bmp = new Bitmap( 5,7,PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
//Unlock the pixels
bmp.UnlockBits(bmpData);
bmp.Save("BitMapIcon.bmp",ImageFormat.Bmp);
My input array (data ) :
byte[5] data = {0xff,0xff,0xff,0xff,0xff}
Question :
Can array of data* for bitmap input have only valu of each pixel (in this
format it is 0 and 1 ) ?
Why passing array of 0xff value some of
pixel are not black ?
Can width size be less than 1 byte ?
You have two issues:
First, the default palette for the indexed format uses 0 for black and 1 for white. So your code is actually trying to initialize a white bitmap, not a black one
Second, and more to the point of your question, is that you are not fully initializing the bitmap. This relates to your third question: the width of the bitmap is allowed to have less than 1 byte's worth of pixels, but the bitmap data itself could require more than that for a single scan line of the bitmap.
Indeed, due to the alignment requirements of a bitmap, your bitmap's "stride" is 4 bytes. So you need 28 bytes total in order to fully initialize the bitmap.
This code will initialize the bitmap the way you want:
//Here create the Bitmap to the know height, width and format
Bitmap bmp = new Bitmap(5, 7, PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
byte[] data = new byte[bmpData.Stride * bmpData.Height];
for (int i = 0; i < data.Length; i++)
{
data[i] = 0xff;
}
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
//Unlock the pixels
bmp.UnlockBits(bmpData);
Use 0x00 instead of 0xff if you actually wanted black pixels.
Related
I have an .rgb file that I attached. It is a valid rgb file that I verified through other software. As far as I understand this is a raw RGB data file.
I need to load the file into byte[] and display it in PictureBox.
From all my searches here I came to this solution that seems correct and that works for others. Unfortunately it displays nothing for me:
string imgPath = Path.Combine(mInstallPath, "squirrel-720x576x50.rgb\\squirrel-720x576x50.rgb");
byte[] imageData = File.ReadAllBytes(imgPath);
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, width * height * 3);//imageData.Length);
//bmpSource.CopyPixels((Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride);
bmp.UnlockBits(bmpData);
pictureBox.Image = bmp;
}
Any ideas why nothing is displayed in my pictureBox ?
I'm using a third party DLL which has as parameter a RGB buffer.
I have used the following code to read RGB buffer from Bitmap:
private byte[] GetBGRValues(Bitmap bmp)
{
// 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.ReadOnly, PixelFormat.Format24bppRgb);
// 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);
bmp.UnlockBits(bmpData);
return rgbValues;
}
The problem is that the generated RGB buffer is not correct. If I open this buffer in IrfanView, supplying correct parameters, the generated image is not correct (looks like it is shifted).
If a get a buffer that I read using C++ code it works.
I have noticed that bmpData.Stride is 1 unity greater than what I was expecting (width * channels). (I know that .NET uses 4 bytes alignment).
The question is: why is the RGB buffer not correct?
You noticed right - you need to take Stride into account. In general you cannot simply copy image in one Copy call. Stride include both row length and padding and could be greater then row length. So you need to copy only bytes you need from each row, ignore padding bytes and advance to next row by adding Stride.
I guess this is what you see with your code:
- original image and expected result
- invalid result without stride
Here is working code:
public static byte[] GetBGRValues(Bitmap bmp)
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
var imgBytes = bmp.Height * rowBytes;
byte[] rgbValues = new byte[imgBytes];
var ptr = bmpData.Scan0;
for (var i = 0; i < bmp.Height; i++)
{
Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);
ptr += bmpData.Stride; // next row
}
bmp.UnlockBits(bmpData);
return rgbValues;
}
More details you can read in this answer: Byte Array to Image Conversion
Also maybe this image will help you to understand Stride purpose:
You need to skip white area at the right when you getting bytes from Bitmap.
Be sure you watch that the order is B-G-R instead of R-G-B
You can try this unsafe code which converts the values to uint. So you have RGB converted to uint
/// <summary>
/// Locks a Bitmap into system memory.
/// </summary>
public unsafe void LockBits()
{
var width = this.Bitmap.Width;
var height = this.Bitmap.Height;
var imageLockMode = ImageLockMode.UserInputBuffer;
// Setting imageLockMode
imageLockMode = imageLockMode | ImageLockMode.ReadOnly;
imageLockMode = imageLockMode | ImageLockMode.WriteOnly;
// Save the bouunds
this._bounds = new Rectangle(0, 0, width, height);
// Create Pointer
var someBuffer = new uint[width*height];
// Pin someBuffer
fixed (uint* buffer = someBuffer) //pin
{
// Create new bitmap data.
var temporaryData = new BitmapData
{
Width = width,
Height = height,
PixelFormat = PixelFormat.Format32bppArgb,
Stride = width*4,
Scan0 = (IntPtr) buffer
};
// Get the data
this.BitmapData = this.Bitmap.LockBits(this._bounds, imageLockMode, PixelFormat.Format32bppArgb,
temporaryData);
// Set values
this.Buffer = someBuffer;
}
}
I remember a library I working on years ago - the colors were shifted strangely. The underlying Microsoft Library had a (feature) in that the RGB had been reversed inside the library - unbeknownst to us we tried a transform and discovered that little gem.
Also do not forget the Alpha Channel in your C# buffer.
Bitmap color channels in memory are represented in the order Blue, Green, Red and Alpha despite being commonly referred to by abbreviation ARGB!
http://softwarebydefault.com/2013/03/22/bitmap-swap-argb/
I am loading pixels from a file onto an array of bytes using this code:
Bitmap bmp = new Bitmap(filename);
var rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr ptr = bmpData.Scan0;
int numBytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[numBytes];
Marshal.Copy(ptr, rgbValues, 0, numBytes);
I am pretty sure that this is not the problem.
After I have loaded the file, I want to display manipulate the colors and then display in a WPF window.
So I create a bitmap source with the following line:
BitmapSource img = BitmapSource.Create(width, height, 96, 96, PixelFormats.Rgb24, null, pixels, stride);
The problem is that the red bytes are switched with the green bytes.
It is similar to this - Why color changes when save a BitmapSource to Bitmap for PixelFormat.Format48bppRgb? -
but i don't know how to fix it.
Then you may use PixelFormats.Bgr24 instead of PixelFormats.Rgb24 to create the BitmapSource.
var img = BitmapSource.Create(
width, height, 96, 96, PixelFormats.Bgr24, null, pixels, stride);
This puzzles me for last two hours. Reading an image file results in different pixel values between imread in Matlab and Image.FromFile in C#?
aa=imread('myfile.tif')
max(aa(:)) = 248 in matlab
In C#
var image2Array = imageToByteArray((Bitmap) Image.FromFile("myfile.tif"));
byte maxx = 0;
foreach(var a in image2Array)
{
maxx = Math.Max(maxx, a);
}
//maxx = 255
Futhermore, in Matlab,
aa(1,1) = 13,
aa(1,2) = 13
but in C#
image2Array[0]=17,
image2Array[1]=0
They should be the same.
BTW, in this case, pixel type is uint8. so there is no dimensional difference.
If you ask me how I got byte array from Image, I used MSDN document to make this method.
public byte[] imageToByteArray(Bitmap bmp)
{
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
rect,
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);
// Unlock the bits.
bmp.UnlockBits(bmpData);
return rgbValues;
}
What did I do wrong here? I suspect that they use different reading algorithms because two resulting images look same.
UPDATE:
I don't think there is anything wrong with what I was doing. I concluded that reading tif as a bitmap was the cause of the problem. To confirm this theory,
I displayed the two images and they looked exactly the same. So there is no mistake on my part, I think.
I tried to read the same file with opencv and its pixel values were exactly the same as the ones from matlab. This was surprisingly to me. I would very cautiously use Bitmap in C# from now on.
Your imageToByteArray method does return a byte array, but you can't assume each byte is a pixel. The PixelFormat determines how the pixel data is stored in the byte array.
The best site I've seen that documents this is Bob Powell's lockbits page.
If the PixelFormat is Format8bppIndexed, then this (untested) code should give you the color values for each pixel.
var bmp = (Bitmap)Bitmap.FromFile("myfile.tif");
// ******* Begin copying your imageToByteArray method
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
rect,
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[] imageData = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, imageData, 0, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// ******* End copying your imageToByteArray method
// Now loop through each pixel... The byte array contains extra bytes
// used for padding so we can't just loop through every byte in the array.
// This is done by using the Stride property on bmpData.
for (int y = 0; y < bmpData.Height; y++)
{
for (int x = 0; x < bmpData.Width; x++)
{
var offset = (y * bmpData.Stride) + x;
// The byte in the image array gives the offset into the palette
var paletteIndex = imageData[offset];
// Given the offset, find the matching color in the palette
var color = bmp.Palette.Entries[offset];
// Look at the color value here...
}
}
TIFF has many formats, you are attempting to read it as a bitmap.
I suggest reading it using a proprietary TIFF reader instead : Good Tiff library for .NET
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;
}