How can I calculate Total of an Image; the overall total of all elements.
The source Image is ARGB format, Each color channel can be treated as single 2D matrix. Is this approach right?
Pseudo code:
Total[ImageData[img],2] (*Mathematica code*)
Below is the C# code I have tried; However, I dont understand how to convert the Byte value to Real in C#. Real is Double data type in C#.
// Calculate total of Image; Total[ImageData[img]];
Rectangle rect = new Rectangle(0, 0, MskImg3G.Width, MskImg3G.Height);
System.Drawing.Imaging.BitmapData bmpData =
MskImg3G.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, MskImg3G.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * MskImg3G.Height;
byte[] TotalValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, TotalValues, 0, bytes);
// do something with the array
//Console.WriteLine(rgbValues.Length);
int sum = 0; foreach (byte b in TotalValues) sum += b;
Console.WriteLine(sum);//Total[ImageData[img, "Byte"], 2]
double vOut = Convert.ToDouble(sum);//Byte value - original ImageType - Real(MMA type)
Console.WriteLine(vOut);//Total[ImageData[img, "Byte"], 2]
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(TotalValues, 0, ptr, bytes);
MskImg3G.UnlockBits(bmpData);
//-------------------------TEST----------------------------------//
Both the console line outputs give: 2774762 is the Byte value, how can I get its Double(real) value?
> 2774762
> 2774762
How can achieve this in C# ?
Load the image :
Bitmap bitmap = Bitmap.FromFile("C:\\Temp\\img.bmp", true);
Get and process the pixels:
for for (int i=0;i<bitmap .Height;i++) for (int j=0;j<bitmap .width;j++)
{
Color c=bitmap .GetPixel(i,j) ;
// Do your totalisation(s) here using c.R, c.G and c.B
}
Related
hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(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;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.
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/
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 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.
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;
}