Quickly getting the color of some pixels on the screen in Python on Windows 7
Asked Answered
M

5

19

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.

Masuria answered 27/9, 2010 at 0:40 Comment(0)
E
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.

Edgeworth answered 27/9, 2010 at 11:27 Comment(2)
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.Masuria
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.Edgeworth
V
16

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

Velarium answered 12/6, 2011 at 9:10 Comment(5)
ImageGrab is Windows only.Damascus
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?Fluffy
@Mihkel: Read up on class range(start, stop[, step])Forerun
@PéturIngiEgilsson Where do you get that? I just imported PIL, ImageGrab on Linux.Antagonistic
@YanKingYin buddy, it's been 5 years since that comment was posted.Postglacial
M
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.

Masuria answered 28/9, 2010 at 4:23 Comment(1)
PIL is abandoned (its main developer died), Pillow is a maintained fork of PIL, and the Pillow equivalent is called PIL.ImageGrab.grabHighjack
C
8

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)
Chronicle answered 30/8, 2013 at 16:5 Comment(0)
E
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.

Edgeworth answered 27/9, 2010 at 11:27 Comment(2)
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.Masuria
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.Edgeworth
C
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))
Colman answered 12/1, 2023 at 19:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.