20

I need to get the color of some pixels on the screen or from the active window, and I need to do so quickly. I've tried using win32gui and ctypes/windll, but they're much too slow. Each of these programs gets the color of 100 pixels:

import win32gui
import time
time.clock()
for y in range(0, 100, 10):
    for x in range(0, 100, 10):
        color = win32gui.GetPixel(win32gui.GetDC(win32gui.GetActiveWindow()), x , y)
print(time.clock())

and

from ctypes import windll
import time
time.clock()
hdc = windll.user32.GetDC(0)
for y in range(0, 100, 10):
    for x in range(0, 100, 10):
        color = windll.gdi32.GetPixel(hdc, x, y)
print(time.clock())

Each of these takes about 1.75 seconds. I need a program like this to take less than 0.1 seconds. What's making it so slow?

I'm working with Python 3.x and Windows 7. If your solution requires I use Python 2.x, please link me to an article showing how to have Python 3.x and 2.x both installed. I looked, but couldn't figure out how to do this.

5 Answers 5

17

This is better than using getpixel all the time and works faster.

import ImageGrab

px = ImageGrab.grab().load()
for y in range(0, 100, 10):
    for x in range(0, 100, 10):
        color = px[x, y]

Reference: Image.load

Sign up to request clarification or add additional context in comments.

3 Comments

Hello, I know this is old but I was wondering what the (0, 100, 10) means in for y in range and for x in range. I was thinking from where to take the pixels but why would there be 3 numbers? What does each of them mean?
@YanKingYin buddy, it's been 5 years since that comment was posted.
12

Thanks to Margus' direction, I focused on getting the image before extracting the pixel information. Here's a workable solution using the Python Imaging Library (PIL), which requires Python 2.x.

import ImageGrab
import time
time.clock()
image = ImageGrab.grab()
for y in range(0, 100, 10):
    for x in range(0, 100, 10):
        color = image.getpixel((x, y))
print(time.clock())

I don't think it gets any simpler than that. This takes (on average) 0.1 seconds, which is a little slower than I'd like but fast enough.

As for having Python 3.x and 2.x both installed, I separated that into a new question. I'm still having some trouble with it, but it's generally working.

1 Comment

PIL is abandoned (its main developer died), Pillow is a maintained fork of PIL, and the Pillow equivalent is called PIL.ImageGrab.grab
7

Disabling Windows Desktop Composition speeds pixel up reading A LOT.

Computer -> Properties -> Advanced system settings -> Performance -> desktop composition [ ] (warning this disables Windows's transparency effects)

Python 2.7 (Should be same for 3.x)

win32gui.GetPixel()     #1.75s => 20ms
ctypes.windll.gdi32.GetPixel() #1.75s => 3ms (fastest)
image.getpixel()        # 0.1s => 50ms
px[]                    # 0.1s => 50ms

AutoIt for comparison

$timer = TimerInit()

For $x = 0 To 100 Step 10
    For $y = 0 To 100 Step 10
        PixelGetColor($x,$y) ;slow => 1ms
    Next
Next

ConsoleWrite("Time: " & TimerDiff($timer)/1000 & @CRLF)

1 Comment

While this may seem to work, it is actually the wrong approach. The correct approach is to read all the required pixels at once. It’s easy to get data into the GPU, but harder to get it out
3

I had this same exact problem, and solved it (in Java, in C#). The main idea behind the solution is GetPixel from screen is slow, and you can't fix that. But as you need some pixels, you can get a bunch of them all at once.

The time that it took to get 64 pixels was 98 times faster.

2 Comments

So it sounds like you're suggesting that I capture the whole screen (say as a bitmap) then get the pixel values from that. This sounds like it might be a job for Python Imaging Library (PIL) (which requires python 2.x) or ImageMagick (which I'm not sure what what version of python it requires). If anyone has suggestions, feel free to chime in.
Yes, basically you need CopyFromScreen method equivalent from .net, with Bitmap class where you can access single pixel by its coordinates. And yes, people have done this before and there are lots of libraries that you can import, choose what fits you best.
0

try using the pyautogui library

import pyautogui

r, g, b = pyautogui.pixel(x, y)
print("The cursor is currently at: " + str(x) + ", " + str(y))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.