'How to get the color of a pixel in python as fast as possible?

I'm on ubuntu and I want to check every 0.1sec the color of a specific pixel on my screen. How can I do that?

I know about PIL but this would need a full screenshot every 0.1sec just for one single pixel.

Then I found this method using ctypes.windll : Faster method of reading screen pixel in Python than PIL?

But this wont work because I'm not on Windows. Any other idea?

Edit: Solved thanks to b_c

from Xlib import display, X
from PIL import Image #PIL


def getColor(x,y):
    W, H = 1, 1
    dsp = display.Display()
    root = dsp.screen().root
    raw = root.get_image(x, y, W, H, X.ZPixmap, 0xffffffff)
    image = Image.frombytes("RGB", (W, H), raw.data, "raw", "BGRX")
    print image.getpixel((0, 0))
    time.sleep(0.01)


Solution 1:[1]

PIL and other similar programs usually allow you to specify a boundary box to grab smaller amounts

PyAutoGui allows you to take smaller sections

as referenced here https://pyautogui.readthedocs.io/en/latest/screenshot.html code such as

pyautogui.screenshot(region=(0,0, 300, 400))

could be useful

https://pillow.readthedocs.io/en/4.2.x/reference/Image.html

could be useful as well, bbox allows you to only observe a small area.

Solution 2:[2]

raw.data is sometimes returned as a string, for example when the color is black. This causes a:

TypeError: a bytes-like object is required, not 'str'

A dirty workaround for the code you posted is:

from Xlib import display, X
from PIL import Image #PIL

def getColor(x,y):
    W, H = 1, 1
    dsp = display.Display()
    root = dsp.screen().root
    raw = root.get_image(x, y, W, H, X.ZPixmap, 0xffffffff)
    if isinstance(raw.data,str):
        bytes=raw.data.encode()
    else:
        bytes=raw.data
    image = Image.frombytes("RGB", (W, H), bytes, "raw", "BGRX")
    print image.getpixel((0, 0))
    time.sleep(0.01)

Solution 3:[3]

Hi I know how to solve your problem. I created a script which loops checking if the pixel or pixels change color.

This script uses only PIL to do this.

The time it takes to do its verification is 33ms.

Because that 33 or 32ms is the time it takes to capture the screen, so no matter how many pixels you need to check, the time will always be 33ms. Unfortunately I haven't found any other package that is faster.

import time
import keyboard
from PIL import Image, ImageGrab


while not keyboard.is_pressed('ctrl'): # press ctrl to stop.
    start_time = time.time()

    px = ImageGrab.grab().load()
    color1 = px[439, 664] # pixel 1
    color2 = px[575, 664] # pixel 2
    color3 = px[706, 664] # pixel 3
    color4 = px[842, 664] # pixel 4
    
    print(color1)

    print(f"Finish in: {round(1000 * (time.time() - start_time))} ms ") # how much  he takes to finish

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 martineau
Solution 2 Steve V.
Solution 3