I am trying to get all the pixels from a set image using LockBits and iterating through every pixel via for. But i am getting incorrect pixels. I exhilarate more in a second.
Code:
Bitmap bmp = new Bitmap(ImagePath);
pictureBox1.Image = bmp;
Rectangle bmpRec = new Rectangle(0, 0,
bmp.Width, bmp.Height); // Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec,
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb); // Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; // Set pointer
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; // Gets array size
byte[] rgbValues = new byte[DataBytes]; // Creates array
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); // Copies of out memory
StringBuilder Pix = new StringBuilder(" ");
// pictureBox1.Image = bmp;
StringBuilder EachPixel = new StringBuilder("");
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
var pixel = rgbValues[i + j * Math.Abs(bmpData.Stride)];
Pix.Append(" ");
Pix.Append(Color.FromArgb(pixel));
}
}
Now I have created a 2x2 pixel image of pure blue. My output should be
255 0 0 255 255 0 0 255 255 0 0 255 255 0 0 255
(A R G B)
but i get something like
Color [A=0, R=0, G=0, B=255] Color [A=0, R=0, G=0, B=255] Color [A=0, R=0, G=0, B=0] Color [A=0, R=0, G=0, B=0]
Where am I going wrong? Sorry if I can't explain exactly whats wrong. Basically the pixel output is incorrect and doesn't match up with the input bmp.
I'm not sure what exactly you're trying to do here... I think you misunderstand how Scan0 and Stride work. Scan0 is a pointer to the beginning of the image in memory. Stride is the length of each row in memory in bytes. You've already locked the image into memory with bmp.LockBits, you don't have to Marshal it.
Bitmap bmp = new Bitmap(ImagePath);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
StringBuilder sb = new StringBuilder();
unsafe
{
for (int y = 0; y < bmp.Height; y++)
{
byte* row = (byte*)bmpData.Scan0 + (y * bmpData.Stride);
for (int x = 0; x < bmp.Width; x++)
{
byte B = row[(x * 4)];
byte G = row[(x * 4) + 1];
byte R = row[(x * 4) + 2];
byte A = row[(x * 4) + 3];
sb.Append(String.Format("{0} {1} {2} {3} ", A, R, G, B);
}
}
}
Fixed the problem by changing what and how it outputs.
I now use Color ARGB = Color.FromArgb(A, R, G, B) I also now use a pixel array.
byte B = row[(x * 4)];
byte G = row[(x * 4) + 1];
byte R = row[(x * 4) + 2];
byte A = row[(x * 4) + 3];
Related
When using DiffSetPixel it's adding the different pixels to the new bitmap diffBM when i'm using the second method DiffLockBits it's adding the different pixels to the existing bitmap.
what i want to do is in the first method the DiffSetPixel to be able to add the different pixels to existing bitmap any existing bitmap and not only to the diffBM and in the DiffLockBits i want to add it also to the diffBM.
and in both methods i want to be able to change the different pixels colors for example to red or green or any color and not only grayscale.
DiffSetPixel method
public static Bitmap DiffSetPixel(Bitmap src1, Bitmap src2, int x1, int y1, int x2, int y2, int width, int height)
{
Bitmap diffBM = new Bitmap(width, height, PixelFormat.Format24bppRgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
//Get Both Colours at the pixel point
Color col1 = src1.GetPixel(x1 + x, y1 + y);
Color col2 = src2.GetPixel(x2 + x, y2 + y);
// Get the difference RGB
int r = 0, g = 0, b = 0;
r = Math.Abs(col1.R - col2.R);
g = Math.Abs(col1.G - col2.G);
b = Math.Abs(col1.B - col2.B);
// Invert the difference average
int dif = 255 - ((r + g + b) / 3);
// Create new grayscale RGB colour
Color newcol = Color.FromArgb(dif,dif,dif);
diffBM.SetPixel(x, y, newcol);
}
}
return diffBM;
}
DiffLockBits method
public static Bitmap DiffLockBits(Bitmap bmp1 , Bitmap bmp2)
{
Bitmap diffBM = new Bitmap(bmp1.Width, bmp1.Height, PixelFormat.Format24bppRgb);
//Lock bitmap's bits to system memory
Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
BitmapData bmpData = bmp1.LockBits(rect, ImageLockMode.ReadWrite, bmp1.PixelFormat);
Rectangle rect1 = new Rectangle(0, 0, bmp2.Width, bmp2.Height);
BitmapData bmpData1 = bmp2.LockBits(rect1, ImageLockMode.ReadWrite, bmp2.PixelFormat);
//Scan for the first line
IntPtr ptr = bmpData.Scan0;
IntPtr ptr1 = bmpData1.Scan0;
//Declare an array in which your RGB values will be stored
int bytes = Math.Abs(bmpData.Stride) * bmp1.Height;
byte[] rgbValues = new byte[bytes];
int bytes1 = Math.Abs(bmpData1.Stride) * bmp2.Height;
byte[] rgbValues1 = new byte[bytes1];
//Copy RGB values in that array
Marshal.Copy(ptr, rgbValues, 0, bytes);
Marshal.Copy(ptr1, rgbValues1, 0, bytes1);
for (int i = 0; i < rgbValues.Length; i += 3)
{
if (rgbValues[i] != rgbValues1[i])
{
//Set RGB values in a Array where all RGB values are stored
byte gray = (byte)(rgbValues[i] * .21 + rgbValues[i + 1] * .71 + rgbValues[i + 2] * .071);
rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] = gray;
}
}
//Copy changed RGB values back to bitmap
Marshal.Copy(rgbValues, 0, ptr, bytes);
//Unlock the bits
bmp1.UnlockBits(bmpData);
bmp2.UnlockBits(bmpData1);
return bmp1;
}
I am new in working with Bitmap and using 16 bits per pixel Format16bppRgb555;
I want to Extract RGB Values from Bitmap Data. Here is my code
static void Main(string[] args)
{
BitmapRGBValues();
Console.ReadKey();
}
static unsafe void BitmapRGBValues()
{
Bitmap cur = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format16bppRgb555);
//Capture Screen
var screenBounds = Screen.PrimaryScreen.Bounds;
using (var gfxScreenshot = Graphics.FromImage(cur))
{
gfxScreenshot.CopyFromScreen(screenBounds.X, screenBounds.Y, 0, 0, screenBounds.Size, CopyPixelOperation.SourceCopy);
}
var curBitmapData = cur.LockBits(new Rectangle(0, 0, cur.Width, cur.Height),
ImageLockMode.ReadWrite, PixelFormat.Format16bppRgb555);
try
{
byte* scan0 = (byte*)curBitmapData.Scan0.ToPointer();
for (int y = 0; y < cur.Height; ++y)
{
ulong* curRow = (ulong*)(scan0 + curBitmapData.Stride * y);
for (int x = 0; x < curBitmapData.Stride / 8; ++x)
{
ulong pixel = curRow[x];
//How to get RGB Values from pixel;
}
}
}
catch
{
}
finally
{
cur.UnlockBits(curBitmapData);
}
}
LockBits can actually convert your image to a desired pixel format, meaning no further conversion should be needed. Just lock the image as Format32bppArgb and you can simply take your colour values from single bytes.
BitmapData curBitmapData = cur.LockBits(new Rectangle(0, 0, cur.Width, cur.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 stride = curBitmapData.Stride;
Byte[] data = new Byte[stride * cur.Height];
Marshal.Copy(curBitmapData.Scan0, data, 0, data.Length);
cur.UnlockBits(curBitmapData);
With this code, you end up with the byte array data, which is filled with your image data in ARGB format, meaning the colour component bytes will be in there in the order [B, G, R, A]. Note that the stride is the amount of bytes to skip to get to a next line on the image, and since this is not always equal to "width * bytes per pixel", it should always be taken into account.
Now you got that, you can do whatever you want with it...
Int32 curRowOffs = 0;
for (Int32 y = 0; y < cur.Height; y++)
{
// Set offset to start of current row
Int32 curOffs = curRowOffs;
for (Int32 x = 0; x < cur.Width; x++)
{
// ARGB = bytes [B,G,R,A]
Byte b = data[curOffs];
Byte g = data[curOffs + 1];
Byte r = data[curOffs + 2];
Byte a = data[curOffs + 3];
Color col = Color.FromArgb(a, r, g, b);
// Do whatever you want with your colour here
// ...
// Increase offset to next colour
curOffs += 4;
}
// Increase row offset
curRowOffs += stride;
}
You can even edit the bytes, and then build a new image from them if you want.
Context: I have a program that gets the ArGB of any image. Throws it into a Color ARGBFormat = Color.FromArgb(alpha, red, green, blue); Now I would like to put that into a PictureBox. I do not have a complete Pixel Array (It is more or less scattered).
Code:
Bitmap bmp = new Bitmap(ImagePath);
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0;
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
StringBuilder EveryPixel = new StringBuilder(" ");
int PixelSize = 4;
Color ARGBFormat;
Bitmap ImageOut = new Bitmap(bmp.Width, bmp.Height);
unsafe
{
for (int y = 0; y < bmpData.Height; y++)
{
byte* row = (byte*)bmpData.Scan0 + (y * bmpData.Stride);
for (int x = 0; x < bmpData.Width; x++)
{
int offSet = x * PixelSize;
// read pixels
byte blue = row[offSet];
byte green = row[offSet + 1];
byte red = row[offSet + 2];
byte alpha = row[offSet + 3];
ARGBFormat = Color.FromArgb(alpha, red, green, blue);
ImageOut.SetPixel(x, y, ARGBFormat); //Slow
EveryPixel.Append(ARGBFormat);
}
}
}
I want to use that code that I have been working on ^ to be displayed into a PictureBox without using SetPixels. I want to use LockBits as it is the most optimize method.
Here you go
initialization code before the loop
BitmapData bitmapData = ImageOut.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte[] pixels = new byte[bmp.Width * bmp.Height * 4]; //4 means 1 byte for each a r g b
IntPtr ptr = bitmapData.Scan0;
Marshal.Copy(ptr , pixels, 0, pixels.Length);
update the pixels within the loop
pixels[offSet] = blue;
pixels[offSet + 1] = green;
pixels[offSet + 2] = red;
pixels[offSet + 3] = alpha;
finally to cleanup the code
bitmapData.UnlockBits();
so for me code look like
unsafe
{
BitmapData bitmapData = ImageOut.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
byte[] pixels = new byte[bmp.Width * bmp.Height * 4]; //4 means 1 byte for each a r g b
IntPtr ptr = bitmapData.Scan0;
Marshal.Copy(ptr , pixels, 0, pixels.Length);
for (int y = 0; y < bmpData.Height; y++)
{
byte* row = (byte*)bmpData.Scan0 + (y * bmpData.Stride);
for (int x = 0; x < bmpData.Width; x++)
{
int offSet = x * PixelSize;
// read pixels
byte blue = row[offSet];
byte green = row[offSet + 1];
byte red = row[offSet + 2];
byte alpha = row[offSet + 3];
//copy to target
pixels[offSet] = blue;
pixels[offSet + 1] = green;
pixels[offSet + 2] = red;
pixels[offSet + 3] = alpha;
}
}
bitmapData.UnlockBits();
}
//set to picturebox
pictureBox.Image = ImageOut;
I hope this work for you.
I am trying to extract R G B values from a pixel in the following code:
for ( int i=0; i < pixeldata.length; i++)
{
IntPtr ptr = bmd.Scan0+i;
byte* pixel = (byte*)ptr;
//here is the problem :O
float r = pixel[1];
float g = pixel[2];
float b = pixel[3];
}
....
where bmd is an array of pixels data:
BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, source.PixelFormat);
and source is the Bitmap of my input, which is an image.
I am trying to avoid the use of Color object. I have already done that and it works, I want to use this other way, but the issue is that ptr is a number and I have to extract the R G B from it.
This is the solution that gives you the right answer.
Bitmap source = new Bitmap(image);
Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);
BitmapData bmd = source.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int totalPixels = rect.Height * rect.Width;
int[] pixelData = new int[totalPixels];
for (int i = 0; i < totalPixels; i++)
{
byte* pixel = (byte*)bmd.Scan0;
pixel = pixel + (i * 4);
byte b = pixel[0];
byte g = pixel[1];
byte r = pixel[2];
int luma = (int)(r * 0.3 + g * 0.59 + b * 0.11);
pixelData[i] = luma;
}
If you have a format that stores R, G, and B as one byte each linearly in memory in that order, the code to extract the RGB values should look like
byte r = pixel[0];
byte g = pixel[1];
byte b = pixel[2];
Note that the index offset begins at 0, and that the values returned are byte not float (though you can certainly cast if you wish).
Additionally you would have to increment i by 3 rather than 1 because 3 adjacent bytes represent a single pixel.
You would be wise to test that source.PixelFormat indeed uses the format you are assuming.
You also have to compile with the /unsafe switch in order to use pointers in C#.
UPDATE
Per #Don's comment as well as your own, the order in linear memory would be ABGR. That means the code would be:
for ( int i=0; i < pixeldata.length; i+=4)
{
IntPtr ptr = bmd.Scan0+i;
byte* pixel = (byte*)ptr;
byte a = pixel[0]; // You can ignore if you do not need alpha.
byte b = pixel[1];
byte g = pixel[2];
byte r = pixel[3];
}
Ok, this was interesting, and I have written some code to play with. Assuming that your image has pixels in format Format24bppRgb (more info about formats here: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx). This format store B, G, R values in 24 bits one after another.
Below code which will parse some d:\\24bits.bmp image from your hard drive and creates new identical one "d:\\24bits_1.bmp" using information B, G, R information from bytes array of first image data.
unsafe private static void TestBMP()
{
Bitmap bmp = new Bitmap("d:\\24bits.bmp");
// Ensure that format is Format24bppRgb.
Console.WriteLine(bmp.PixelFormat);
Bitmap copyBmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// Copy all pixels of initial image for verification.
int pixels = bmp.Height * bmp.Width;
Color[,] allPixels = new Color[bmp.Height, bmp.Width];
for (int i = 0; i < bmp.Height; i++)
for (int j = 0; j < bmp.Width; j++)
allPixels[i, j] = bmp.GetPixel(j, i);
// 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,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
byte* stream = (byte*)ptr;
for (int y = 0; y < bmp.Height; y++)
for (int x = 0; x < bmp.Width; x++)
{
int byteIndex = y * bmpData.Stride + x * 3;
byte r = stream[byteIndex + 2];
byte g = stream[byteIndex + 1];
byte b = stream[byteIndex];
Color c = allPixels[y, x];
if (r != c.R || g != c.G || b != c.B)
{
Console.WriteLine("This should never appear");
}
copyBmp.SetPixel(x, y, Color.FromArgb(255, r, g, b));
}
// Save new image. It should be the same as initial one.
copyBmp.Save("d:\\24bits_1.bmp");
}
I am using C#, and having an image stored in the object Bitmap.
Now I would like to convert this image into 8bit greyscale, then into a 4-bit greyscale image.
Do you have any tips how this can be made?
In the .NET Bitmap formats, there are no such thing as a 8 or 4 bit grayscale image. The supported formats are enumerated by the PixelFormat enumeration. You can, however, create a 4 or 8 bit image by creating a indexed image (8bppIndexed or 4bppIndexed), where each entry in the palette is a greyscale value.
This code takes a Bitmap and creates a copy as a 8bpp indexed image with greyscale values:
public static Bitmap BitmapToGrayscale(Bitmap source)
{
// Create target image.
int width = source.Width;
int height = source.Height;
Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed);
// Set the palette to discrete shades of gray
ColorPalette palette = target.Palette;
for(int i = 0 ; i < palette.Entries.Length ; i++)
{
palette.Entries[i] = Color.FromArgb(0,i,i,i);
}
target.Palette = palette;
// Lock bits so we have direct access to bitmap data
BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
for(int r = 0 ; r < height ; r++)
{
byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
for(int c = 0 ; c < width ; c++)
{
byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11));
*pTarget = colorIndex;
pTarget++;
pSource += 3;
}
}
}
target.UnlockBits(targetData);
source.UnlockBits(sourceData);
return target;
}
In order to make a 4Bpp image instead, you would need to create the target with PixelFormat.Format4bppIndexed, and then set the ColorPalette to 16 discrete shades of gray. Finally, in the loop you should normalize values 2 be between 0-15 and pack each 2 pixel values into a single byte.
This is the modified code to make a 4bpp greyscale image:
public static Bitmap BitmapToGrayscale4bpp(Bitmap source)
{
// Create target image.
int width = source.Width;
int height = source.Height;
Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed);
// Set the palette to discrete shades of gray
ColorPalette palette = target.Palette;
for(int i = 0 ; i < palette.Entries.Length ; i++)
{
int cval = 17*i;
palette.Entries[i] = Color.FromArgb(0,cval,cval,cval);
}
target.Palette = palette;
// Lock bits so we have direct access to bitmap data
BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
unsafe
{
for(int r = 0 ; r < height ; r++)
{
byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
byte prevValue = 0;
for(int c = 0 ; c < width ; c++)
{
byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)) / 16);
if (c % 2 == 0)
prevValue = colorIndex;
else
*(pTarget++) = (byte)(prevValue | colorIndex << 4);
pSource += 3;
}
}
}
target.UnlockBits(targetData);
source.UnlockBits(sourceData);
return target;
}