Python利用自带模块实现屏幕像素高效操作

2025-02-10 04:50

本文主要是介绍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利用自带模块实现屏幕像素高效操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153369

相关文章

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1

python获取网页表格的多种方法汇总

《python获取网页表格的多种方法汇总》我们在网页上看到很多的表格,如果要获取里面的数据或者转化成其他格式,就需要将表格获取下来并进行整理,在Python中,获取网页表格的方法有多种,下面就跟随小编... 目录1. 使用Pandas的read_html2. 使用BeautifulSoup和pandas3.

PyQt6/PySide6中QTableView类的实现

《PyQt6/PySide6中QTableView类的实现》本文主要介绍了PyQt6/PySide6中QTableView类的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学... 目录1. 基本概念2. 创建 QTableView 实例3. QTableView 的常用属性和方法

Python装饰器之类装饰器详解

《Python装饰器之类装饰器详解》本文将详细介绍Python中类装饰器的概念、使用方法以及应用场景,并通过一个综合详细的例子展示如何使用类装饰器,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. 引言2. 装饰器的基本概念2.1. 函数装饰器复习2.2 类装饰器的定义和使用3. 类装饰

PyQt6/PySide6中QTreeView类的实现

《PyQt6/PySide6中QTreeView类的实现》QTreeView是PyQt6或PySide6库中用于显示分层数据的控件,本文主要介绍了PyQt6/PySide6中QTreeView类的实现... 目录1. 基本概念2. 创建 QTreeView 实例3. QTreeView 的常用属性和方法属性

Python 交互式可视化的利器Bokeh的使用

《Python交互式可视化的利器Bokeh的使用》Bokeh是一个专注于Web端交互式数据可视化的Python库,本文主要介绍了Python交互式可视化的利器Bokeh的使用,具有一定的参考价值,感... 目录1. Bokeh 简介1.1 为什么选择 Bokeh1.2 安装与环境配置2. Bokeh 基础2

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型