Get Pixel Color Fastest Way

Get Pixel color fastest way?

I found a perfect way which is clearly faster than the GetPixel one:

HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;

while(true)
{
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;

hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);

for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];

x = i / (4 * MAX_HEIGHT);
y = i / (4 * MAX_WIDTH);

if (red == 255 && green == 0 && blue == 0)
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}

I hope this could help someone else.

c# faster way of setting pixel colours

Note: The following information was originally created by Bob Powell. The original link is no longer functional, so I have copied this information from the Internet Archive at https://web.archive.org/web/20120330012542/http://bobpowell.net/lockingbits.htm. It's a bit long, but I think it's worth preserving.

I'm not sure if this will serve as a direct answer to your question, but perhaps it will help you in finding a solution.


Using the LockBits method to access image data

Many image processing tasks and even file type conversions, say from 32 bit-per-pixel to 8 bit-per-pixel can be speeded up by accessing the pixel data array directly, rather than relying on GetPixel and SetPixel or other methods.

You will be aware that .NET is a managed code system which most often uses managed data so it's not often that we need to gain access to bytes stored in memory anymore however, image manipulation is one of the few times when managed data access is just too slow and so we need to delve once again into the knotty problems of finding the data and manipulating it.

Before I start on the subject in hand, I'll just remind you that the methods used to access any unmanaged data will be different depending on the language in which your program is written. C# developers have the opportunity, via the unsafe keyword and use of pointers, to access data in memory directly. Visual basic programmers should access such data through the Marshal class methods which may also show a small performance loss.

Lock up your bits

The Bitmap class provides the LockBits and corresponding UnlockBits methods which enable you to fix a portion of the bitmap pixel data array in memory, access it directly and finally replace the bits in the bitmap with the modified data. LockBits returns a BitmapData class that describes the layout and position of the data in the locked array.

The BitmapData class contains the following important properties;

  • Scan0 The address in memory of the fixed data array
  • Stride The width, in bytes, of a single row of pixel data. This width
    is a multiple, or possibly sub-multiple, of the pixel dimensions of
    the image and may be padded out to include a few more bytes. I'll
    explain why shortly.
  • PixelFormat The actual pixel format of the data. This is important
    for finding the right bytes
  • Width The width of the locked image
  • Height The height of the locked image

The relationship of Scan0 and Stride to the array in memory is shown in figure1.

Sample Image

The Stride property, as shown in figure 1, holds the width of one row in bytes. The size of a row however may not be an exact multiple of the pixel size because for efficiency, the system ensures that the data is packed into rows that begin on a four byte boundary and are padded out to a multiple of four bytes. This means for example that a 24 bit per pixel image 17 pixels wide would have a stride of 52. The used data in each row would take up 317 = 51 bytes and the padding of 1 byte would expand each row to 52 bytes or 134 bytes. A 4BppIndexed image of 17 pixels wide would have a stride of 12. Nine of the bytes, or more properly eight and a half, would contain data and the row would be padded out with a further 3 bytes to a 4 byte boundary.

The data carrying portion of the row, as has been suggested above, is laid out according to the pixel format. A 24 bit per pixel image containing RGB data would have a new pixel every 3 bytes, a 32 bit per pixel RGBA every four bytes. Pixel formats that contain more than one pixel per byte, such as the 4 bit per pixel Indexed and 1 bit per pixel indexed, have to be processed carefully so that the pixel required is not confused with it's neigbour pixels in the same byte.

Finding the right byte.

Because the stride is the width of a row, to index any given row or Y coordinate you can multiply the stride by the Y coordinate to get the beginning of a particular row. Finding the correct pixel within the row is possibly more difficult and depends on knowing the layout of the pixel formats. The following examples show how to access a particular pixel for a given pixel format.

  • Format32BppArgb Given X and Y coordinates, the address of the first
    element in the pixel is Scan0+(y * stride)+(x*4). This Points to the
    blue byte. The following three bytes contain the green, red and alpha
    bytes.
  • Format24BppRgb Given X and Y coordinates, the address of the first
    element in the pixel is Scan0+(yStride)+(x3). This points to the
    blue byte which is followed by the green and the red.
  • Format8BppIndexed Given the X and Y coordinates the address of the
    byte is Scan0+(y*Stride)+x. This byte is the index into the image
    palette.
  • Format4BppIndexed Given X and Y coordinates the byte containing the
    pixel data is calculated as Scan0+(y*Stride)+(x/2). The corresponding
    byte contains two pixels, the upper nibble is the leftmost and the
    lower nibble is the rightmost of two pixels. The four bits of the
    upper and lower nibble are used to select the colour from the 16
    colour palette.
  • Format1BppIndexed Given the X and Y coordinates, the byte containing
    the pixel is calculated by Scan0+(y*Stride)+(x/8). The byte contains
    8 bits, each bit is one pixel with the leftmost pixel in bit 8 and
    the rightmost pixel in bit 0. The bits select from the two entry
    colour palette.

Iterating through the pixels

For pixel formats with one or more bytes per pixel, the formula is simple and can be accomplished by looping through all Y and X values in order. The code in the following listings sets the blue component of a 32 bit per pixel image to 255. In both cases bm is a bitmap previously created.

BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);

int PixelSize=4;

for(int y=0; y<bmd.Height; y++)
{
byte* row = (byte *)bmd.Scan0+(y*bmd.Stride);
for(int x = 0; x<bmd.Width; x++)
{
row[x * PixelSize] = 255;
}
}

In VB this operation would be treated a little differently because VB has no knowledge of pointers and requires the use of the marshal class to access unmanaged data.

Dim x As Integer
Dim y As Integer
Dim PixelSize As Integer = 4

Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat)

For y = 0 To bmd.Height - 1
For x = 0 To bmd.Width - 1
Marshal.WriteByte(bmd.Scan0, (bmd.Stride * y) + (4 * x) , 255)
Next
Next

Sub-byte pixels.

The Format4BppIndexed and Format1BppIndexed pixel formats mentioned earlier both have more than one pixel stored in a byte. In such cases, it's up to you to ensure that changing the data for one pixel does not effect the other pixel or pixels held in that byte.

The method for indexing a 1 bit per pixel image relies on using bitwise logical operations And and Or to reset or set specific bits in the byte. After using the formula shown above for 1 bit per pixel images, the lower 3 bits of the X coordinate is used to select the bit required. The listings below show this process in C# and VB. In both examples bmd is a bitmap data extracted from a 1 bit per pixel image.

C# code uses pointers and requires compiling with unsafe code

byte* p=(byte*)bmd.Scan0.ToPointer();

int index=y*bmd.Stride+(x>>3);

byte mask=(byte)(0x80>>(x&0x7));

if(pixel)
p[index]|=mask;
else
p[index]&=(byte)(mask^0xff);

VB code uses the marshal class

Dim mask As Byte = 128 >> (x And 7)

Dim offset As Integer = (y * bmd.Stride) + (x >> 3)

Dim currentPixel As Byte = Marshal.ReadByte(bmd.Scan0, offset)

If pixel = True Then
Marshal.WriteByte(bmd.Scan0, offset, currentPixel Or mask)
Else
Marshal.WriteByte(bmd.Scan0, offset, CByte(currentPixel And (mask Xor 255)))
End If

Note, it's quite valid to use the Marshal class from C# code. I used pointers because it offers the best performance.

Accessing individual pixels in a 4 bit per pixel image is handled in a similar manner. The upper and lower nibble of the byte must be dealt with separately and changing the contents of the odd X pixels should not effect the even X pixels. The code below shows how to perform this in C# and VB.

C#

int offset = (y * bmd.Stride) + (x >> 1);

byte currentByte = ((byte *)bmd.Scan0)[offset];

if((x&1) == 1)
{
currentByte &= 0xF0;
currentByte |= (byte)(colorIndex & 0x0F);
}
else
{
currentByte &= 0x0F;
currentByte |= (byte)(colorIndex << 4);
}

((byte *)bmd.Scan0)[offset]=currentByte;

VB

Dim offset As Integer = (y * bmd.Stride) + (x >> 1)

Dim currentByte As Byte = Marshal.ReadByte(bmd.Scan0, offset)

If (x And 1) = 1 Then
currentByte = currentByte And &HF0
currentByte = currentByte Or (colorIndex And &HF)
Else
currentByte = currentByte And &HF
currentByte = currentByte Or (colorIndex << 4)
End If

Marshal.WriteByte(bmd.Scan0, offset, currentByte)

Using LockBits and UnlockBits

The LockBits method takes a rectangle which may be the same size or smaller than the image being processed, a PixelFormat which is usually the same as that of the image being processed and a ImageLockMode value that specifies whether the data is read-only, write-only, read-write or a user allocated buffer. This last option cannot be used from C# or VB because the method overload for LockBits that specifies a user buffer is not included in the GDI+ managed wrapper.

It is very important that when all operations are complete the BitmapData is put back into the bitmap with the UnlockBits method. The snippet of code below illustrates this.

Dim bmd As BitmapData = bm.LockBits(New Rectangle(0, 0, 10, 10), ImageLockMode.ReadWrite, bm.PixelFormat)

' do operations here

bm.UnlockBits(bmd)

Summary

That just about covers the aspects of accessing the most popular and most difficult pixel formats directly. Using these techniques instead of the GetPixel and SetPixel methods provided by Bitmap will show a marked performance boost to your image processing and image format conversion routines.

Fastest way to get screen pixel color in C#

Via use of the timespan class recording the time it takes to grab just ONE SINGLE pixel from the screen between GDI+ and Managed DirectX it turns out that GDI+ is actually a lot faster.

Both methods were tested by using:

TimeSpan ts = new TimeSpan();

for (int tCount = 0; tCount < 1001; tCount++)
{
DateTime then = DateTime.Now;

METHOD_TO_TEST()

DateTime nNow = DateTime.Now;
TimeSpan tt = nNow - then;
ts += tt;
}

return (float)ts.Ticks / (float)(tCount - 1);

which would report the average amount of ticks that each operation would take over 1000 iterations

When comparing :

//global scope
Bitmap screenPixel = new Bitmap(1, 1);
Color c = Color.Black

//method to test
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(hWnd))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, xVal, 540, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();

}
}
c = screenPixel.GetPixel(0, 0);

GDI+, to :

//global scope
Color c = Color.Black
PresentParameters parameters = new PresentParameters();
parameters.Windowed = true;
parameters.SwapEffect = SwapEffect.Discard;
Device d = new Device(0, DeviceType.Hardware, hWnd, CreateFlags.HardwareVertexProcessing, parameters);
Surface s = d.CreateOffscreenPlainSurface(Manager.Adapters.Default.CurrentDisplayMode.Width, Manager.Adapters.Default.CurrentDisplayMode.Height, Format.A8R8G8B8,
Pool.Scratch);

//method to test
d.GetFrontBufferData(0, s);

GraphicsStream gs = s.LockRectangle(LockFlags.None);
byte[] bu = new byte[4];
gs.Position = readPos;
gs.Read(bu, 0, 4);
int r = bu[2];
int g = bu[1];
int b = bu[0];
c = return Color.FromArgb(r, g, b);

s.UnlockRectangle();
s.ReleaseGraphics();

Managed DirectX

GDI+ ran for average(s) of 20831.1953, 18611.0566, and 20761.1914 ticks for a total average of 20,067.814433333333333333333333333 ticks over 3000 iterations

while Managed DirectX ran for average(s) of 489297.969, 496458.4, and 494268.281 ticks for a total average of 493,341.55 ticks over 3000 iterations

Meaning, that the Managed DirectX setup I was using takes about 24 times longer to do what the GDI+ setup does

Now, things to note...
It is entirely possible there are more efficient ways to pull screen data with DirectX. I tried looking at pulling data from the back buffer instead of the front buffer, but for this particular example, the back buffer yielding nothing of value (it was essentially just a black screen). Another thing to note is the way that I implement my device handle, I'm pretty sure it captures the whole desktop. This might be less efficient than just grabbing the front buffer data for whatever specific window I am trying to capture... The only reasons I didn't do this were because all attempts to figure this out on my own resulted in failure (a DirectX invalid call exception somewhere in device instantiation), and because nobody I talked to from any resource knew a thing about managed DirectX, let alone how to use it for my purpose.

One more thing to note, I've heard and read that it is possible to hook into the DirectX api of an already running program. This may yield much faster results and be a better solution for others, but because of the often malicious nature of such a hook and the measures that the program from which I'm trying to capture uses to prevent it, this was not applicable to my solution.

In the end, it seems that for this particular case of capturing only a single screen pixel GDI+'s BitBlt, is faster that Managed DirectX or at least my implementation of it.

Fast method for getting pixel color?

I have not worked with this API before, but I found this website: Adobe Photoshop CS5 Object Library.

I did some looking and there is a SaveAs method that can be used on the Document object. You can use this method to save the document as an image (let's say a BITMAP).

From there, you can use several sources online that show how to extract pixel information quickly from a BMP.

Here is one particular article: Work with Bitmaps faster in C#

I realize that I am not providing copy/pasteable code for you to use, but I hope this points you in the correct direction.

Faster alternative for getPixel and getPixel in Android Bitmap?

Use getPixels() to get all the pixels, modify the values in the byte[], then call setPixels() to store all the pixels at once. The overhead of calling setPixel() on each individual pixel is killing your performance.

If it's still not fast enough, you can pass the array to an NDK function.

If you're doing live processing of Camera images, you can get even fancier.

Fastest way to get pixel color in Python or C++?

You may wish to take a look at the Python Image Library you could call something like this

import Image
im = Image.open("name_of_file.jpg")
list_of_pixels = list(im.getdata())
print list_of_pixels[0]

That would output the very first pixel in a RGB format like (255,255,255)
It is pretty fast but it wont ever beat C++ I believe

Faster than GetPixel()?

I would suggest you retrieve a pointer to the bitmap's pixel data (assuming you have a HBITMAP handle).

This is done via GetObject(), which should return you a BITMAP structure. This is the field you are interested in:

bmBits:
A pointer to the location of the bit values for the bitmap. The
bmBits member must be a pointer to an array of character (1-byte)
values.

Then you can run your checking logic per pixel on the buffers. That would be way faster from using GetPixel.

What is the fastest method to lookup pixel RGB data in C++?

Yes, GetPixel and SetPixel are relatively slow API calls. Manipulating individual pixels requires copying the contents of the DC (Device Context) into a temporary bitmap, mapping and locating the pixel, retrieving (or setting) its color value, and then (if the pixel's color is being set) blitting the temporary bitmap back to the device context. Once you understand all the work that is going on behind the scenes for such an apparently simple operation, you can see why it is slow. Worse, the overhead must be paid for each call to GetPixel and/or SetPixel, so you can see why these are not commonly used for painting operations.

The replacement approach involves the use of GetDIBits, which is the fastest possible way of determining the values of multiple pixels. This function essentially copies the contents of the device context (DC) into a temporary device-independent bitmap (DIB), which you can then view or manipulate as desired. Determining the color value of an individual pixel becomes as simple as indexing into that pixel's location in memory. With this approach, you can literally process millions of pixels per second. The overhead of retrieving the pixel color values is paid only once, at the very beginning.

To simulate SetPixel, you'd manipulate the desired pixel values, then call SetDIBits to update the device context (e.g., the screen).



Related Topics



Leave a reply



Submit