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
Related
The method for editing Bitmap using BitmapData and Lock and UnlockBits taken from the MSDN seems to work very differently on Bitmaps with with different sizes. It only does this on PixelFormat.Format24bppRgb and changing it seems to fix it, but I'd like to know what's going on.
Here's the method for reference:
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);
}
It works just fine for Bitmap(200, 200) or Bitmap(200,250). But when it's used on Bitmap(50, 50) or most of the other sizes it goes completely crazy...
The rgbValues array has unexpected length of 7600. (The expected length would be Width * Height * Bytes per pixel = 50 * 50 * 3 = 7500)
This causes it to throw IndexOutOfRange Exception in most cases
I tried to change the value added to the counter but none seems to work
for (int counter = 10; counter + 10 < rgbValues.Length; counter += n)
rgbValues[counter] = 255;
//Doesn't work for any number put for n from 2 to 10
//I also limited it from beginning and end just to try
//if it's not changing some crucial value in the bytes
//(I know it's a nonsense but I tried)
If 3 is put for n (referring to the code above) it yields a picture with 1st row red, 2nd blue, 3rd green, repeat
If 1 is put there it actually makes the whole picture white
Can someone please explain why is this happening? And how can this be fixed? (besides changing the format)
Note: I'm not keen on this format but I'd like to know what's actually going on.
I am using BinaryReader to read the bytes of an image, I am having some issues trying to read the ARGB values of a bitmap image using BinaryReader. Can anyone suggest a way I could get the byte value for each pixel in a bitmap image?
Thanks in advance
Easy way is to use unsafe context and lock some bits. Oversimplified sample:
unsafe
{
var bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* first = (byte*)bitmapData.Scan0;
byte a = first[0];
byte r = first[1];
byte g = first[2];
byte b = first[3];
...
bmp.UnlockBits(bitmapData);
}
However, if you still need to use BinaryReader and you know how many bytes per pixel there are, It is possible to just skip the header (you can find it's length in #Bradley_Ufffner 's link) and access the bytes.
You will need to study the BMP file format available here: http://en.wikipedia.org/wiki/BMP_file_format
Reading the file correctly will involve figuring out the pixel format from the header and parsing the data correctly based on that. The file may be palatalized, in which case you will need to read out the color table data and use it to map pixels to actual colors. Pixel data may also be compressed and will have to be extracted based on values in the header.
This won't be a simple project, things like this are the reason graphics libraries were invented.
If you need to read a bitmap's pixel data using BinaryReader, try UnmanagedMemoryStream:
Bitmap bmp = new Bitmap("img.bmp");
var bits = bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try{
unsafe{
using(Stream bmpstream = new UnmanagedMemoryStream((byte*)bits.Scan0, bits.Height*bits.Stride))
{
BinaryReader reader = new BinaryReader(bmpstream);
for(int y = 0; y < bits.Height; y++)
{
bmpstream.Seek(bits.Stride*y, SeekOrigin.Begin);
for(int x = 0; x < bits.Width; x++)
{
byte b = reader.ReadByte();
byte g = reader.ReadByte();
byte r = reader.ReadByte();
byte a = reader.ReadByte();
}
}
}
}
}finally{
bmp.UnlockBits(bits);
}
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 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);
}
I have a doubt in c#. How to read a jpeg or bmp file using c#? and how to store the pixel's RGB values in array? Then how to check whether the value is already exist or not?
James Schek has it, but beware that GetPixel is extremely, incredibly slow.
Here's a complete sample using lockbits:
/*Note unsafe keyword*/
public unsafe Image ThresholdUA(float thresh)
{
Bitmap b = new Bitmap(_image);//note this has several overloads, including a path to an image
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*This time we convert the IntPtr to a ptr*/
byte* scan0 = (byte*)bData.Scan0.ToPointer();
for (int i = 0; i < bData.Height; ++i)
{
for (int j = 0; j < bData.Width; ++j)
{
byte* data = scan0 + i * bData.Stride + j * bitsPerPixel / 8;
//data is a pointer to the first byte of the 3-byte color data
}
}
b.UnlockBits(bData);
return b;
}
There's another way to do it using marshaling though. Here's the same thing, but with marshaling:
/*No unsafe keyword!*/
public Image ThresholdMA(float thresh)
{
Bitmap b = new Bitmap(_image);
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
/* GetBitsPerPixel just does a switch on the PixelFormat and returns the number */
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*the size of the image in bytes */
int size = bData.Stride * bData.Height;
/*Allocate buffer for image*/
byte[] data = new byte[size];
/*This overload copies data of /size/ into /data/ from location specified (/Scan0/)*/
System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);
for (int i = 0; i < size; i += bitsPerPixel / 8 )
{
double magnitude = 1/3d*(data[i] +data[i + 1] +data[i + 2]);
//data[i] is the first of 3 bytes of color
}
/* This override copies the data back into the location specified */
System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);
b.UnlockBits(bData);
return b;
}
Read the file using the Bitmap class.
Lock pixels.
Retrieve bytes from array.
Alternatively, you can use GetPixel if you just need one or two.
You can use Image.FromFile (http://msdn.microsoft.com/en-us/library/system.drawing.image.fromfile.aspx) to create an Image object from an image on disk.
As it was already mentioned, the fastest way to retrieve pixels is to use LockBits(), however, there's a way to do it without Marshal.Copy or unsafe code.
First, you'll need to compute Stride of your image:
var stride = ComputeStride(img.Width, format);
it is width*bytesPerPixel value rounded up to be divisible by 4. See formulas here.
Then you'll need to initialize an array of the required size:
var pixels = new byte[img.Height*stride]
Then you'll need to retrieve an unmanaged pointer to the beginning of this array.
You may use Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0), but it's safer to pin the array in memory:
var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
var scan0 = pixels.AddrOfPinnedObject();
You'll need to create BitmapData structure:
var bData = new BitmapData{Width = img.Width, height = img.Height, Stride = stride, Scan0 = scan0};
Then you'll pass it to LockBits method while setting ImageLockMode.UserInputBuffer flag.
img.LockBits(area, ImageLockMode.Readonly | ImageLockMode.UserInputBuffer, format, bData);
Voila! Pixels are stored in pixels array. But you'll need to unpin your buffer:
handle.Free();
This may seem cumbersome, but this is the fastest way, since only one copying of data is required.