SoFunction
Updated on 2025-04-06

Python uses its own module to achieve efficient operation of screen pixels

1. Get the screen zoom ratio

from ctypes import wintypes
import ctypes
 
 
HORZRES = 8
LOGPIXELSX = 118
 
 
def get_scale_factor() -> float:
    user32 = .user32
    gdi32 = .gdi32
 
    # Define HDC and UINT types    HDC = 
    UINT = 
 
    # Define the parameter type and return type of GetDC and GetDeviceCaps     = []
     = HDC
 
     = [HDC, UINT]
     = 
 
    # Get the device context    dc = (None)
    widthScale = (dc, HORZRES)
    width = (dc, LOGPIXELSX)
    scale = width / widthScale
    return scale

2. Get the pixel color at the specified coordinates of the screen

import ctypes
from ctypes import wintypes
from typing import Sequence, Generator
 
 
user32 = .user32
gdi32 = .gdi32
 
# Define the typeHWND = 
HDC = 
HBITMAP = 
 
 
class BITMAPINFOHEADER():
    _fields_ = [
        ("biSize", ),
        ("biWidth", ),
        ("biHeight", ),
        ("biPlanes", ),
        ("biBitCount", ),
        ("biCompression", ),
        ("biSizeImage", ),
        ("biXPelsPerMeter", ),
        ("biYPelsPerMeter", ),
        ("biClrUsed", ),
        ("biClrImportant", )
    ]
 
class BITMAPINFO():
    _fields_ = [
        ("bmiHeader", BITMAPINFOHEADER),
        ("bmiColors",  * 3)
    ]
 
 
def get_pixel_color(coords: Sequence[tuple[int, int]], hwnd: HWND) -> Generator[tuple[int, int, int], None, None]:
    rect = ()
    (hwnd, (rect))
    width =  - 
    height =  - 
 
    # Create a memory device context    hdc_src = (hwnd)
    hdc_dst = (hdc_src)
    bmp = (hdc_src, width, height)
    (hdc_dst, bmp)
 
    # Use BitBlt to copy window content to memory device context    (hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020)  # SRCCOPY
 
    # Get bitmap information    bmi = BITMAPINFO()
     = (BITMAPINFOHEADER)
     = width
     = -height  # Negative value indicates bottom-up     = 1
     = 32
     = 0
 
    # Create a buffer and get bitmap data    buffer = ctypes.create_string_buffer(width * height * 4)
    (hdc_dst, bmp, 0, height, buffer, (bmi), 0)
 
    # Free up resources    (bmp)
    (hdc_dst)
    (hwnd, hdc_src)
 
    # traverse the specified coordinates and return the pixel color    for x, y in coords:
        if 0 <= x < width and 0 <= y < height:
            offset = (y * width + x) * 4
            color = buffer[offset:offset + 4]
            yield color[2], color[1], color[0]  # BGR -> RGB
        else:
            yield (0, 0, 0)

3. A simple use case

from typing import Sequence, Generator, Tuple
from tkinter import ttk
import tkinter as tk
from ctypes import wintypes
import ctypes
import requests
from io import BytesIO
from PIL import Image, ImageTk
 
 
 
 
 
user32 = .user32
gdi32 = .gdi32
 
HWND = 
HDC = 
HBITMAP = 
 
 
class BITMAPINFOHEADER():
    _fields_ = [
        ("biSize", ),
        ("biWidth", ),
        ("biHeight", ),
        ("biPlanes", ),
        ("biBitCount", ),
        ("biCompression", ),
        ("biSizeImage", ),
        ("biXPelsPerMeter", ),
        ("biYPelsPerMeter", ),
        ("biClrUsed", ),
        ("biClrImportant", )
    ]
 
 
class BITMAPINFO():
    _fields_ = [
        ("bmiHeader", BITMAPINFOHEADER),
        ("bmiColors",  * 3)
    ]
 
 
def get_pixel_color(coords: Sequence[Tuple[int, int]], hwnd: HWND) -> Generator[Tuple[int, int, int], None, None]:
    rect = ()
    (hwnd, (rect))
    width =  - 
    height =  - 
 
    hdc_src = (hwnd)
    hdc_dst = (hdc_src)
    bmp = (hdc_src, width, height)
    (hdc_dst, bmp)
 
    (hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020)  # SRCCOPY
 
    bmi = BITMAPINFO()
     = (BITMAPINFOHEADER)
     = width
     = -height  # Negative value indicates bottom-up     = 1
     = 32
     = 0
 
    buffer = ctypes.create_string_buffer(width * height * 4)
    (hdc_dst, bmp, 0, height, buffer, (bmi), 0)
 
    (bmp)
    (hdc_dst)
    (hwnd, hdc_src)
 
    for x, y in coords:
        print(x, y, width, height)
        if 0 <= x < width and 0 <= y < height:
            offset = (y * width + x) * 4
            color = buffer[offset:offset + 4]
            yield color[2], color[1], color[0]  # BGR -> RGB
        else:
            yield (0, 0, 0)
 
 
def get_window_handle(window):
    window_name = window._w
    if not window_name.startswith("."):
        window_name = "." + window_name
    
    hwnd = .(None, ())
    if not hwnd:
        raise ValueError("Cannot get the window handle.")
    return hwnd
 
def download_image(url):
    response = (url)
    if response.status_code == 200:
        return (BytesIO())
    else:
        raise Exception(f"Failed to download image: HTTP {response.status_code}")
 
def display_image_in_label(image):
    photo = (image)
    label = (root, image=photo)
     = photo  # Keep references to PhotoImage to prevent garbage collection    ()
 
 
def show_color(event):
    hwnd = get_window_handle(root)
    x, y = , 
    # Note that the coordinates here are relative to the window's coordinates, and the sequence of multiple coordinate points passed in get_pixel_color should be    # In addition, in order to efficiently obtain the colors of multiple points in the same picture, I used the generator to lazy load here, so please traverse the iterator completely when obtaining data    result = get_pixel_color([(x, y)], hwnd)
    colors = [i for i in result]
    print(f"{, }: {colors}")
 
 
if __name__ == "__main__":
    root = ()
    width, height = 900, 500
    screenwidth = root.winfo_screenwidth()
    screenheight = root.winfo_screenheight()
    geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
    ("Test Sample")
    (geometry)
    ("<Motion>", show_color)
 
    image_url = "/th/id/R-C.475631ce281b88c3cd465761b37c5256?rik=ZFMiTYFwaPypTQ&riu=http%3a%2f%%2ffile%2f20180102%2f21532952_215949247000_2.jpg&ehk=9NnCJ9JG44zfdF2%2fr373s25s68H9vxLvyfMsKgEzAwc%3d&risl=&pid=ImgRaw&r=0"
    try:
        img = download_image(image_url)
        display_image_in_label(img)
    except Exception as e:
        print(f"Error: {e}")
        (root, text="Failed to load image.").pack()
 
    ()

4. Summary

The above method is much more efficient than the usual method of using PIL, because it is based on IO screenshot operations, and frequent IO operations make it very inefficient to simply access the screen pixels.

The above method uses BitBlt. BitBlt is an efficient bitmap operation method that can copy the contents of the window into the context of the memory device and then get the pixel color through GetPixel or directly accessing the bitmap data. Just like Su Access, its performance is significantly stronger than the former. For more details on Window API operations, please refer to the official documentation:

Windows GDI) (Bitmap Functions - Win32 apps | Microsoft Learn

This is the article about Python using its own module to achieve efficient operation of screen pixels. For more related content on Python screen pixels, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!