本文主要是介绍Python利用自带模块实现屏幕像素高效操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下...
1、获取屏幕放缩比例
from ctypes import wintypes import ctypes HORZRES = 8 LOGPIXELSX = 118 def get_scale_factor() -> float: user32 = ctypes.windll.user32 gdi32 = ctypes.windll.gdi32 # 定义 HDC 和 UINT 类型 HDC = wintypes.HDC UINT = wintypes.UINT # 定义 GetDC 和 GetDeviceCaps 的参数类型和返回类型 user32.GetDC.argtypes = [wintypes.HWND] user32.GetDC.restype = HDC gdi32.GetDeviceCaps.argtypes = [HDC, UINT] gdi32.GetDeviceCaps.restype = wintypes.INT # 获取设备上下文 dc = user32.GetDC(None) widthScale = gdi32.GetDeviceCaps(dc, HORZRES) width = gdi32.GetDeviceCaps(dc, LOGPIXELSX) scale = width / widthScale return scale
2、获取屏幕指定坐标处像素颜色
import ctypes from ctypes import wintypes from typing import Sequence, Generator user32 = ctypes.windll.user32 gdi32 = ctypes.windll.gdi32 # 定义类型 HWND = wintypes.HWND HDC = wintypes.HDC HBITMAP = wintypes.HBITMAP class BITMAPINFOHEADER(ctypes.Structure): _fields_ = [ ("biSize", wintypes.dwORD), ("biWidth", wintypes.LONG), ("biHeight", wintypes.LONG), ("biPlanes", wintypes.WORD), ("biBitCount", wintypes.WORD), ("biCompression", wintypes.DWORD), ("biSizeImage", wintypes.DWORD), ("biXPelsPerMeter", wintypes.LONG), ("biYPelsPerMeter", wintypes.LONG), ("biClrUsed", wintypes.DWORD), ("biClrImportant", wintypes.DWORD) ] class BITMAPINFO(ctypes.Structure): _fields_ = [ ("bmiHeader", BITMAPINFOHEADER), ("bmiColors", wintypes.DWORD * 3) ] def get_pixel_color(coords: Sequence[tuple[int, int]China编程], hwnd: HWND) -> Generator[tuple[int, int, int], None, None]: rect = wintypes.RECT() user32.GetClientRect(hwnd, ctypes.byref(rect)) width = rect.right - rect.left height = rect.bottom - rect.top # 创建内存设备上下文 hdc_src = user32.GetDC(hwnd) hdc_dst = gdi32.CreateCompatibleDC(hdc_src) bmp = gdi32.CreateCompatibleBitmap(hdc_src, width, height) gdi32.SelectObject(hdc_dst, bmp) # 使用 BitBlt 复制窗口内容到内存设备上下文 gdi32.BitBlt(hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020) # SRCCOPY # 获取位图信息 bmi = BITMAPINFO() bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) bmi.bmiHeader.biWidth = width bmi.bmiHeader.biHeight = -height # 负值表示自底向上 bmi.bmiHeader.biPlanes = 1 bmi.bmiHeader.biBitCount = 32 bmi.bmiHeader.biCompression = 0 # 创建缓冲区并获取位图数据 buffer = ctypes.create_string_buffer(width * height * 4) gdi32.GetDIBits(hdc_dst, bmp, 0, height, buffer, ctypes.byref(bmi), 0) # 释放资源 androidgdi32.DeleteObject(bmp) gdi32.DeleteDC(hdc_dst) user32.ReleaseDC(hwnd, hdc_src) # 遍历指定坐标并返回像素颜色 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、一个简单的使用案例
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 = ctypes.windll.user32 gdi32 = ctypes.windll.gdi32 HWND = wintypes.HWND HDC = wintypes.HDC HBITMAP = wintypes.HBITMAP class BITMAPINFOHEADER(ctypes.Structure): _fields_ = [ ("biSize", wintypes.DWORD), ("biWidth", wintypes.LONG), ("biHeight", wintypes.LONG), ("biPlanes", wintypes.WORD), ("biBitCount", wintypes.WORD), ("biCompression", wintypes.DWORD), ("biSizeImage", wintypes.DWORD), ("biXPelsPerMeter", wintypes.LONG), ("biYPelsPerMeter", wintypes.LONG), ("biClrUsed", wintypes.DWORD), ("biClrImportant", wintypes.DWORD) ] class BITMAPINFO(ctypes.Structure): _fields_ = [ ("bmiHeader", BITMAPINFOHEADER), ("bmiColors", wintypes.DWORD * 3) ] def get_pixel_color(coords: Sequence[Tuple[int, int]], hwnd: HWND) -> Generator[Tuple[int, int, int], None, None]: rect = wintypes.RECT() user32.GetClientRect(hwnd, ctypes.byref(rect)) width = rect.right - rect.left height = rect.bottom - rect.top hdc_src = user32.GetDC(hwnd) hdc_dst = gdi32.CreateCompatibleDC(hdc_src) bmp = gdi32.CreateCompatibleBitmap(hdc_src, width, height) gdi32.SelectObject(hdc_dst, bmp) gdi32.BitBlt(hdc_dst, 0, 0, width, height, hdc_src, 0, 0, 0x00CC0020) # SRCCOPY bmi = BITMAPINFO() bmi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER) bmi.bmiHeader.biWidth = width bmi.bmiHeader.biHeight = -height # 负值表示自底向上 bmi.bmiHeader.biPlanes = 1 bmi.bmiHeader.biBitCount = 32 bmi.bmiHeader.biCompression = 0 buffer = ctypes.create_string_buffer(width * height * 4) gdi32.GetDIBits(hdc_dst, bmp, 0, height, buffer, ctypes.byref(bmi), 0) gdi32.DeleteObject(bmp) gdi32.DeleteDC(hdc_dst) user32.ReleaseDC(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 = ctypes.windll.user32.FindWindowW(None, window.title()) if not hwnd: raise ValueError("Cannot get the window handle.") return hwnd def download_image(url): responjsse = requests.get(url) if response.status_code == 200: return Image.open(BytesIO(response.content)) else: raise Exception(f"Failed to download image: HTTP {response.status_code}") def display_image_in_label(image): photo = ImageTk.PhotoImage(image) label = ttk.Label(root, image=photo) label.image = photo # 保持对 PhotoImage 的引用,防止被垃圾回收 label.pack() def show_color(event): hwnd = get_window_handle(root) x, y = event.x, event.y # 注意这里的坐标是相对于窗口的坐标,且传入get_pixel_color的应该是包含多个坐标点的序列 # 此外,为了高效获取同一个画面多个点的颜色,此处我使用了生成器进行懒加载,因此获取数据时请完整遍历迭代器 result = get_pixel_color([(x, y)], hwnd) colors = [i for i in result] print(f"{event.x, event.y}: {colors}") if __name__ == "__main__": root = tk.Tk() width, height = 900, 500 screenwidth = root.winfo_screenwidth() screenheight = root.winfo_screenheight() geometry = '%dx%d+%d+%d' % (width, height, (screenwidth 编程China编程- width) / 2, (screenheight - height) / 2) root.title("测试样例") root.geometry(geometry) root.bind("<Motion>", show_color) image_url = "https://ts1.cn.mm.bing.net/th/id/R-C.475631ce281b88c3cd465761b37c5256?rik=ZFMiTYFwaPypTQ&riu=http%3a%2f%2fpic.ntimg.cn%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}") ttk.Label(root, text="Failed to load image.").pack() root.mainloop()
4、总结
上述方法比通常使用PIL的Image.ImageGrab方法要高效非常多,因为Image.ImageGrab是基于IO截屏操作的,频繁的IO操作使单纯进行屏幕像素访问十分低效。
而上述方法采用的是BitBlt。BitBlt 是一种高效的位图操作方法,可以将窗口的内容复制到内存设备上下文中,然后通过 GetPixel 或直接访问位图数据来获取像素颜色。就像素访问而言其性能显著强于前者。更多关于Window的API操作详见官方文android档:
Windows GDI) (位图函数 - Win32 apps | Microsoft Learn
到此这篇关于python利用自带模块实现屏幕像素高效操作的文章就介绍到这了,更多相关Python屏幕像素操作内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于Python利用自带模块实现屏幕像素高效操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!