Original Scenario
I massively misunderstood my own code, and this scenario is invalid.
This is way out of my normal wheelhouse, so I'm going to explain best
I can.
I have a user-set color code. Example:
int R = 255;
int G = 255;
int B = 255;
And I have a lot of large images where I need to check the color of
pixels at certain sets of coordinates against the user-set color. I
can successfully get the byte* of any pixel in an image, and get the
values I expect.
I do this using BitmapData from Bitmap.LockBits(...). My
understanding is that locking will be important to performance
reasons. There will be a great many instances of this being used
across a very large collections of images, so performance is a major
consideration.
For those same performance reasons I'm trying to avoid converting the
retrieved pixel-colors represented by unsafe bytes to integers - I'd
much rather convert my int to a byte one time and use that for the
likely millions of pixels this will be run against each time it is
invoked.
However... I cannot figure out how to get any of my user-set integers
into an unsafe byte (byte*) and compare it to the unsafe byte
retrieved from a pixel.
The unsafe byte (byte*) was the 8-bit pointer of the data of the pixel (at least, that's how I understand it) but I am getting the individual colors as regular old bytes.
byte* pixel = //value here pulled from image;
pixel[2] //red value byte
pixel[1] //green value byte
pixel[0] //blue value byte
So I don't need to convert my ints to unsafe bytes ...pointers?..., just a simple Converter.ToByte(myInt).
The real question
But since I think this is still possibly a valid question outside my scenario, I'm going to leave this part up for someone to answer and hopefully help someone in the future:
How do you take any given int in C# and compare it to an "unsafe byte" pointer 'byte*'?
You would just want to dereference the byte pointer and compare it to the integer.
unsafe void Main()
{
byte x = 15;
int y = 15;
Console.WriteLine(AreEqual(&x, y)); // True
}
public unsafe bool AreEqual(byte* bytePtr, int val) {
var byteVal = *bytePtr;
return byteVal == val;
}
Let us open a open a bitmap and process each pixel
//Note this has several overloads, including a path to an image
//Use the proper one for yourself
Bitmap b = new Bitmap(_image);
//Lock(and Load baby)
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
//Bits per pixel, obviously
byte bitsPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat);
//Gets the address of the first pixel data in the bitmap.
//This can also be thought of as the first scan line in the bitmap.
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
//Do your magic here, compare your RGB values here
byte R = *b; //Dereferencing pointer here
byte G = *(b+1);
byte B = *(b+2);
}
}
//Unlocking here is important or memoryleak
b.UnlockBits(bData);
Related
I have an image sensor board for embedded development for which I need to capture a stream of images and output them in 8-bit monochrome / grayscale format. The imager output is 12-bit monochrome (which takes 2 bytes per pixel).
In the code, I have an IntPtr to a memory buffer that has the 12-bit image data, from which I have to extract and convert that data down to an 8-bit image. This is represented in memory something like this (with a bright light activating the pixels):
As you can see, every second byte contains the LSB that I want to discard, thereby keeping only the odd-numbered bytes (to put it another way). The best solution I can conceptualize is to iterate through the memory, but that's the rub. I can't get that to work. What I need help with is an algorithm in C# to do this.
Here's a sample image that represents a direct creation of a Bitmap object from the IntPtr as follows:
bitmap = new Bitmap(imageWidth, imageHeight, imageWidth, PixelFormat.Format8bppIndexed, pImage);
// Failed Attempt #1
unsafe
{
IntPtr pImage; // pointer to buffer containing 12-bit image data from imager
int i = 0, imageSize = (imageWidth * imageHeight * 2); // two bytes per pixel
byte[] imageData = new byte[imageSize];
do
{
// Should I bitwise shift?
imageData[i] = (byte)(pImage + i) << 8; // Doesn't compile, need help here!
} while (i++ < imageSize);
}
// Failed Attempt #2
IntPtr pImage; // pointer to buffer containing 12-bit image data from imager
imageSize = imageWidth * imageHeight;
byte[] imageData = new byte[imageSize];
Marshal.Copy(pImage, imageData, 0, imageSize);
// I tried with and without this loop. Neither gives me images.
for (int i = 0; i < imageData.Length; i++)
{
if (0 == i % 2) imageData[i / 2] = imageData[i];
}
Bitmap bitmap;
using (var ms = new MemoryStream(imageData))
{
bitmap = new Bitmap(ms);
}
// This also introduced a memory leak somewhere.
Alternatively, if there's a way to do this with a Bitmap, byte[], MemoryStream, etc. that works, I'm all ears, but everything I've tried has failed.
Here is the algorithm that my coworkers helped formulate. It creates two new (unmanaged) pointers; one 8-bits wide and the other 16-bits.
By stepping through one word at a time and shifting off the last 4 bits of the source, we get a new 8-bit image with only the MSBs. Each buffer has the same number of words, but since the words are different sizes, they progress at different rates as we iterate over them.
unsafe
{
byte* p_bytebuffer = (byte*)pImage;
short* p_shortbuffer = (short*)pImage;
for (int i = 0; i < imageWidth * imageHeight; i++)
{
*p_bytebuffer++ = (byte)(*p_shortbuffer++ >> 4);
}
}
In terms of performance, this appears to be very fast with no perceivable difference in framerate.
Special thanks to #Herohtar for spending a substantial amount of time in chat with me attempting to help me solve this.
I am trying to find if the image is clipped from the bottom and if it is, then I will divide it in two images from the last white pixel row. Following are the simple methods I created to check clipping and get the empty white pixel rows. Also, as you can see this is not a very good solution. This might cause performance issues for larger images. So if anyone can suggest me better ways then it will be a great help:
private static bool IsImageBottomClipping(Bitmap image)
{
for (int i = 0; i < image.Width; i++)
{
var pixel = image.GetPixel(i, image.Height - 1);
if (pixel.ToArgb() != Color.White.ToArgb())
{
return true;
}
}
return false;
}
private static int GetLastWhiteLine(Bitmap image)
{
for (int i = image.Height - 1; i >= 0; i--)
{
int whitePixels = 0;
for (int j = 0; j < image.Width; j++)
{
var pixel = image.GetPixel(j, i);
if (pixel.ToArgb() == Color.White.ToArgb())
{
whitePixels = j + 1;
}
}
if (whitePixels == image.Width)
return i;
}
return -1;
}
IsImageBottomClipping is working fine. But other method is not sending correct white pixel row. It is only sending one less row. Example image:
In this case, row around 180 should be the return value of GetLastWhiteLine method. But it is returning 192.
All right, so... we got two of subjects to tackle here. First, the optimising, then, your bug. I'll start with the optimising.
The fastest way is to work in memory directly, but, honestly, it's kind of unwieldy. The second-best choice, which is what I generally use, is to copy the raw image data bytes out of the image object. This will make you end up with four vital pieces of data:
The width, which you can just get from the image.
The height, which you can just get from the image.
The byte array, containing the image bytes.
The stride, which gives you the amount of bytes used for each line on the image.
(Technically, there's a fifth one, namely the pixel format, but we'll just force things to 32bpp here so we don't have to take that into account along the way.)
Note that the stride, technically, is not just the amount of bytes used per pixel multiplied by the image width. It is rounded up to the next multiple of 4 bytes. When working with 32-bit ARGB content, this isn't really an issue, since 32-bit is 4 bytes, but in general, it's better to use the stride and not just the multiplied width, and write all code assuming there could be padded bytes behind each line. You'll thank me if you're ever processing 24-bit RGB content with this kind of system.
However, when going over the image's content you obviously should only check the exact range that contains pixel data, and not the full stride.
The way to get these things is quite simple: use LockBits on the image, tell it to expose the image as 32 bit per pixel ARGB data (it will actually convert it if needed), get the line stride, and use Marshal.Copy to copy the entire image contents into a byte array.
Int32 width = image.Width;
Int32 height = image.Height;
BitmapData sourceData = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 stride = sourceData.Stride;
Byte[] data = new Byte[stride * height];
Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
image.UnlockBits(sourceData);
As mentioned, this is forced to 32-bit ARGB format. If you would want to use this system to get the data out in the original format it has inside the image, just change PixelFormat.Format32bppArgb to image.PixelFormat.
Now, you have to realise, LockBits is a rather heavy operation, which copies the data out, in the requested pixel format, to new memory, where it can be read or (if not specified as read-only as I did here) edited. What makes this more optimal than your method is, quite simply, that GetPixel performs a LockBits operation every time you request a single pixel value. So you're cutting down the amount of LockBits calls from several thousands to just one.
Anyway, now, as for your functions.
The first method is, in my opinion, completely unnecessary; you should just run the second one on any image you get. Its output gives you the last white line of the image, so if that value equals height-1 you're done, and if it doesn't, you immediately have the value needed for the further processing. The first function does exactly the same as the second, after all; it checks if all pixels on a line are white. The only difference is that it only processes the last line.
So, onto the second method. This is where things go wrong. You set the amount of white pixels to the "current pixel index plus one", rather than incrementing it to check if all pixels matched, meaning the method goes over all pixels but only really checks if the last pixel on the row was white. Since your image indeed has a white pixel at the end of the last row, it aborts after one row.
Also, whenever you find a pixel that does not match, you should just abort the scan of that line immediately, like your first method does; there's no point in continuing on that line after that.
So, let's fix that second function, and rewrite it to work with that set of "byte array", "stride", "width" and "height", rather than an image. I added the "white" colour as parameter too, to make it more reusable, so it's changed from GetLastWhiteLine to GetLastClearLine.
One general usability note: if you are iterating over the height and width, do actually call your loop variables y and x; it makes things a lot more clear in your code.
I explained the used systems in the code comments.
private static Int32 GetLastClearLine(Byte[] sourceData, Int32 stride, Int32 width, Int32 height, Color checkColor)
{
// Get color as UInt32 in advance.
UInt32 checkColVal = (UInt32)checkColor.ToArgb();
// Use MemoryStream with BinaryReader since it can read UInt32 from a byte array directly.
using (MemoryStream ms = new MemoryStream(sourceData))
using (BinaryReader sr = new BinaryReader(ms))
{
for (Int32 y = height - 1; y >= 0; --y)
{
// Set position in the memory stream to the start of the current row.
ms.Position = stride * y;
Int32 matchingPixels = 0;
// Read UInt32 pixels for the whole row length.
for (Int32 x = 0; x < width; ++x)
{
// Read a UInt32 for one whole 32bpp ARGB pixel.
UInt32 colorVal = sr.ReadUInt32();
// Compare with check value.
if (colorVal == checkColVal)
matchingPixels++;
else
break;
}
// Test if full line matched the given color.
if (matchingPixels == width)
return y;
}
}
return -1;
}
This can be simplified, though; the loop variable x already contains the value you need, so if you simply declare it before the loop, you can check after the loop what value it had when the loop stopped, and there is no need to increment a second variable. And, honestly, the value read from the stream can be compared directly, without the colorVal variable. Making the contents of the y-loop:
{
ms.Position = stride * y;
Int32 x;
for (x = 0; x < width; ++x)
if (sr.ReadUInt32() != checkColVal)
break;
if (x == width)
return y;
}
For your example image, this gets me value 178, which is correct when I check in Gimp.
This is a bit puzzling here. The following code is part of a little testing application to verify that code changes didn't introduce a regression. To make it fast we used memcmp which appears to be the fastest way of comparing two images of equal size (unsurprisingly).
However, we have a few test images that exhibit a rather surprising problem: memcmp on the bitmap data tells us that they are not equal, however, a pixel-by-pixel comparison doesn't find any difference at all. I was under the impression that when using LockBits on a Bitmap you get the actual raw bytes of the image. For a 24 bpp bitmap it's a bit hard to imagine a condition where the pixels are the same but the underlying pixel data isn't.
A few surprising things:
The differences are always single bytes that are 00 in one image and FF in the other.
If one changes the PixelFormat for LockBits to Format32bppRgb or Format32bppArgb, the comparison succeeds.
If one passes the BitmapData returned by the first LockBits call as 4th argument to the second one, the comparison succeeds.
As noted above, the pixel-by-pixel comparison succeeds as well.
I'm a bit stumped here because frankly I cannot imagine why this happens.
(Reduced) Code below. Just compile with csc /unsafe and pass a 24bpp PNG image as first argument.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
class Program
{
public static void Main(string[] args)
{
Bitmap title = new Bitmap(args[0]);
Console.WriteLine(CompareImageResult(title, new Bitmap(title)));
}
private static string CompareImageResult(Bitmap bmp, Bitmap expected)
{
string retval = "";
unsafe
{
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var resultData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var expectedData = expected.LockBits(rect, ImageLockMode.ReadOnly, expected.PixelFormat);
try
{
if (memcmp(resultData.Scan0, expectedData.Scan0, resultData.Stride * resultData.Height) != 0)
retval += "Bitmap data did not match\n";
}
finally
{
bmp.UnlockBits(resultData);
expected.UnlockBits(expectedData);
}
}
for (var x = 0; x < bmp.Width; x++)
for (var y = 0; y < bmp.Height; y++)
if (bmp.GetPixel(x, y) != expected.GetPixel(x, y))
{
Console.WriteLine("Pixel diff at {0}, {1}: {2} - {3}", x, y, bmp.GetPixel(x, y), expected.GetPixel(x, y));
retval += "pixel fail";
}
return retval != "" ? retval : "success";
}
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int memcmp(IntPtr b1, IntPtr b2, long count);
}
Take a look at this, which pictorially illustrates a LockBits buffer - it shows the Rows of Strides and where Padding can appear at the end of the Stride (if it's needed).
https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx
http://supercomputingblog.com/graphics/using-lockbits-in-gdi/
A stride is probably aligned to the 32bit (i.e. word) boundary (for efficiency purposes)...and the extra unused space at the end of the stride is to make the next Stride be aligned.
So that's what's giving you the random behaviour during the comparison...spurious data in the Padding region.
When you are using Format32bppRgb and Format32bppArgb that's naturally word aligned, so I guess you don't have any extra unused bits on the end, which is why it works.
Just an educated guess:
24 bits (3 bytes) is a little bit awkward on 32/64 bit hardware.
With this format there are bound to be buffers that are flushed out to a multiple of 4 bytes, leaving 1 or more bytes as 'don't care' . They can contain random data and the software doesn't feel obliged to zero them out. This will make memcmp fail.
The image is of big size and I used getPixel and and setPixel methods to access bits but found out that it was way too slow so I went to implement lock and unlock bits but could not get my head around it. I also went through tutorials of Bob Powell but the tutorials but could not understand. So, I am asking for some help here to get GLCM from the image.
GLCM is generally a very computationally intensive algorithm. It iterates through each pixel, for each neighbor. Even C++ image processing libraries have this issue.
GLCM does however lend itself quite nicely to parallel (multi-threaded) implementations as the calculations for each reference pixel are independent.
With regards to using lock and unlock bits see the example code below. One thing to keep in mind is that the image can be padded for optimization reasons. Also, if your image has a different bit depth or multiple channels you will need to adjust the code accordingly.
BitmapData data = image.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadOnly, PixelFormat.Gray8);
byte* dataPtr = (byte*)data.Scan0;
int rowPadding = data.Stride - (image.Width);
// iterate over height (rows)
for (int i = 0; i < height; i++)
{
// iterate over width (columns)
for (int j = 0; j < width; j++)
{
// pixel value
int value = dataPtr[0];
// advance to next pixel
dataPtr++;
// at the end of each column, skip extra padding
if (rowPadding > 0)
{
dataPtr += rowPadding;
}
}
image.UnlockBits(data1);
So, I am very confused over a quick test that I just ran. I am doing some image processing in C#. Get/SetPixel() have proven to be too slow, so I am using LockBits to get at the raw data.
However, I seem to have hit a situation which I can't figure out. While scanning the image, it seems that each pixel is laid out as Bgra, that is, blue byte, green byte, red byte, and alpha, in that order. I was under the impression that they would be laid out in Argb order. here is a sample of the code that I am using.
BitmapData baseData =
m_baseImage.LockBits(new Rectangle(new Point(0, 0), m_baseImage.Size),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Bitmap test = new Bitmap(m_baseImage.Width, m_baseImage.Height);
byte* ptr = (byte*)baseData.Scan0;
for (int y = 0; y < m_baseImage.Height; ++y)
{
for (int x = 0; x < m_baseImage.Width; ++x)
{
// this works, image is copied correctly
Color c1 = Color.FromArgb(*(ptr + 3), *(ptr + 2), *(ptr + 1), *ptr);
// below does not work! Bytes are reversed.
//Color c1 = Color.FromArgb(*ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3));
test.SetPixel(x, y, c1);
ptr += 4;
}
}
m_baseImage.UnlockBits(baseData);
pictureBox1.Image = m_baseImage;
pictureBox2.Image = test;
The first line which grabs the color of the base image works, the second does not. I am pretty sure that I am missing something very obvious here.
Not only are the colors reversed BGRA, but the rows are reversed as well - the bottom of the image is the first in memory. It's just the way Windows has always worked.
The little-endian explanation seems obvious, but I don't think it's the truth. If you look at the definition of COLORREF in the Windows API, you'll notice that Red is the low order byte and Blue is the higher order; if you stored this as a single integer value, it would be RGB0.
ARGB refers to the byte order in words fetched as words. If you fetch the bytes one at a time, you'll receive em low to hi as IBM PC's are little-endian