I am trying to get all the pixels in an image using a Bitmap and GetPixels. Now I know that it is very inefficient so I have been looking into LockBits. I have successfully made what I think locks the bits but I can not get each and every pixels. My Code so far is...
//Creates Rectangle for holding picture
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr Pointer = bmpData.Scan0; //Scans the first line of data
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
string Pix = " ";
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
for (int p = 0; p < DataBytes; p++)
{
Pix += " " + rgbValues[p];
}
I would like to use Lockbits, as it is the best way to get pixels. Any help?
Thank you.
When accessing the data directly from LockBits, the storage order is B-G-R, not RGB... that is why you are getting the values in the reverse order.
Related
I'm trying to use the SDK of a sensor for intraoral x-ray. I managed to save the image, but the image doubled.
This is the function to save the image.
private void SaveImage(short[] data, int widht, int height)
{
Bitmap pic = new Bitmap(widht, height);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic.Save(#"C:\Users\WIM\Desktop\teste\teste\teste.jpeg", ImageFormat.Jpeg);
}
And this is the image saved
I'm not used to coding in C#. So I'm not sure if I do this right, and if the problem is on the function where i save the image or on the way as populated short array (short[] data);
[Update]
As suggested, I added PixelFormat.Format16bppGrayScale. and the function was like this:
private void SaveImage(short[] data, int widht, int height)
{
Bitmap pic = new Bitmap(widht, height, PixelFormat.Format16bppGrayScale);
Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat) ;
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(data, 0, pixelStartAddress, data.Length);
pic.UnlockBits(picData);
pic.Save(#"C:\Users\WIM\Desktop\teste\teste\teste.jpeg", ImageFormat.Jpeg);
}
And I got this error:
System.Runtime.InteropServices.ExternalException: 'GDI+ generic error.'
In this line:
pic.Save(#"C:\Users\WIM\Desktop\teste\teste\teste.jpeg", ImageFormat.Jpeg);
[Update]
I tried to modify the pixel format, and I tested all the other 16bpp. This is the result:
Format16bpp565
Format16bpp555
And for Format16bppArgb1555 result a transparent image.
The pixel format Format16bppGrayScale exists in System.Drawing, but it is not actually implemented or supported. That's why it gives an error when trying to save it.
If this is indeed 16 bpp greyscale data, the only way to solve this in System.Drawing namespace is to reduce your data from 16-bit to 8-bit, treat it as an indexed (paletted) image, and just give it a grey fade from [0,0,0] to [255,255,255] as colour palette.
public static void SaveImage(short[] data, int width, int height, string path)
{
int dataLen = data.Length;
// This might not equal the width.
int inputStride = dataLen / height;
byte[] sourceData = new byte[dataLen];
// Reduce Int16 to bytes by downshifting the data by 8 bits.
// Effectively this divides the number by 256.
for (int i = 0; i < dataLen; ++i)
sourceData[i] = (Byte) (data[i] >> 8);
// Make an 8bpp image
using (Bitmap pic = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
{
BitmapData picData = pic.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pic.PixelFormat);
// Get actual stride from BitmapData
Int32 targetStride = picData.Stride;
Int64 scan0 = picData.Scan0.ToInt64();
// Stride is ALWAYS rounded up to the next multiple of 4, so copy line by line to avoid issues.
for (int y = 0; y < height; ++y)
Marshal.Copy(sourceData, y * inputStride, new IntPtr(scan0 + y * targetStride), width);
pic.UnlockBits(picData);
// Requesting Bitmap.Palette makes a COPY, so get it out, edit it, and then reassign it.
ColorPalette pal = pic.Palette;
for (int i = 0; i < 256; ++i)
pal.Entries[i] = Color.FromArgb(i, i, i);
pic.Palette = pal;
pic.Save(path, ImageFormat.Png);
}
}
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 .
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/...
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.
I have been trying to set the alpha values manually for a Bitmap with the format of Format32bppArgb . In this code example, I am setting them all to 0.5f, however, they will be different values in the future and not all 0.5f/128 (as this is my test case to just get this working). How can I properly set the alpha values for a bitmap quickly? I could use SetPixel(), however, SetPixel() is horrifically slow for large images compared to just locking/unlocking the bitmap.
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);
for (int counter = 0; counter < rgbValues.Length; counter += 4)
{
rgbValues[counter] = 255;
rgbValues[counter + 1] = 255;
rgbValues[counter + 2] = 255;
rgbValues[counter + 3] = 128;
}
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
The best way to do this, providing you want to have the same alpha value on your entire bitmap, is to use a ColorMatrix. Check out this example by Microsoft:
http://msdn.microsoft.com/en-us/library/w177ax15(v=vs.71).aspx