sandwichwasher
sandwichwasher

Reputation: 13

ctypes gdi32.GetPixel() returning inaccurate values - python

I am trying to grab the color of a pixel on screen. GetPixel() is giving me something close to the actual color, but different every time.

I've tried setting the dpi awareness but that has not solved the issue.

This is my current code:

import time
import ctypes
DC = ctypes.windll.user32.GetDC(0)
ctypes.windll.shcore.SetProcessDpiAwareness(2)

def getpixel(x, y):
    return tuple(int.to_bytes(ctypes.windll.gdi32.GetPixel(DC,x,y), 3, "little"))

while True:
    time.sleep(1)
    print(getpixel(0, 0))

This is what it outputs:

(116, 106, 99)
(100, 91, 85)
(113, 104, 98)
(114, 105, 99)
(100, 92, 87)
(114, 105, 100)
(113, 103, 96)
(100, 92, 87)
(110, 100, 94)

Upvotes: 0

Views: 413

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 178179

The OP code worked for me as is, but it does have the risk that the HDC returned could be truncated if it doesn't fit in 32 bits (the default return value if .restype is not defined for the function.

Here's a fully-specified example that should work properly on a 32- or 64-bit Windows OS. It's overkill, but guarantees all the types are converted correctly between C and Python. It's extended to read the color at the cursor position:

import time
import ctypes as ct
import ctypes.wintypes as w

HRESULT = w.LONG

class COLORREF(ct.Structure):
    _fields_ = (('r', ct.c_ubyte),
                ('g', ct.c_ubyte),
                ('b', ct.c_ubyte),
                ('unused', ct.c_ubyte))
    def __repr__(self):
        return f'COLORREF(r={self.r}, g={self.g}, b={self.b})'

user32 = ct.WinDLL('user32')
GetDC = user32.GetDC
GetDC.argtypes = w.HWND,
GetDC.restype = w.HDC
GetCursorPos = user32.GetCursorPos
GetCursorPos.argtypes = w.PPOINT,
GetCursorPos.restype = w.BOOL

shcore = ct.WinDLL('shcore')
SetProcessDpiAwareness = shcore.SetProcessDpiAwareness
SetProcessDpiAwareness.argtypes = ct.c_int,
SetProcessDpiAwareness.restype = HRESULT

gdi32 = ct.WinDLL('gdi32')
GetPixel = gdi32.GetPixel
GetPixel.argtypes = w.HDC, ct.c_int, ct.c_int
GetPixel.restype = COLORREF

hdc = GetDC(None)
SetProcessDpiAwareness(2)
p = w.POINT()
while True:
    time.sleep(.1)
    GetCursorPos(ct.byref(p))
    print(f'({p.x}, {p.y}) -> {GetPixel(hdc, p.x, p.y)}')

Upvotes: 0

Related Questions