macOS下Python钢琴块脚本如何实现更快的截图?
macOS下优化钢琴块脚本截图速度方案
你的核心问题是macOS上全屏截图效率太低,且你只需要检测几个固定坐标的像素,完全没必要截整个屏幕。以下是两种能把截图耗时压到0.1秒内的方案:
方案一:使用mss模块(推荐,代码改动小)
mss是专门针对快速屏幕捕获的库,支持多平台,在macOS上效率远高于PIL的ImageGrab,而且可以只截取你需要的小区域,进一步减少耗时。
首先安装mss:
pip install mss
修改后的代码:
from pynput.keyboard import Listener, KeyCode from pynput.mouse import Controller as MouseController, Button import mss import time mouse = MouseController() pixel_coords = [(590, 490), (492, 490), (678, 490), (766, 490)] running = True # 计算需要截取的最小区域:包含所有检测点的矩形 min_x = min(coord[0] for coord in pixel_coords) max_x = max(coord[0] for coord in pixel_coords) min_y = min(coord[1] for coord in pixel_coords) max_y = max(coord[1] for coord in pixel_coords) # 稍微扩大一点避免边缘问题 monitor = {"top": min_y - 2, "left": min_x - 2, "width": max_x - min_x + 4, "height": max_y - min_y + 4} def click(x, y): mouse.position = (x, y) mouse.click(Button.left) def play(): global running with mss.mss() as sct: while running: x = time.time() # 只截取指定区域 screen = sct.grab(monitor) # 转换坐标:检测点相对于截图区域的位置 for coord in pixel_coords: rel_x = coord[0] - monitor["left"] rel_y = coord[1] - monitor["top"] # 获取像素值,mss的像素是(x,y)顺序,返回(r,g,b) value = screen.pixel(rel_x, rel_y) # 注意mss返回的是3通道,所以去掉原判断里的alpha通道值 if value in [(0, 0, 0), (15, 20, 19), (13, 32, 41), (0, 161, 201)]: click(coord[0], coord[1]) print(f"Time taken: {round(time.time() - x, 4)}") def on_press(key): global running if key == KeyCode(char="q"): running = False print("Stopped") quit() with Listener(on_press=on_press) as listener: play() listener.join()
这个方案通过只截取包含所有检测点的小区域,加上mss的高效捕获,耗时能降到0.02秒以内,完全满足要求。
方案二:使用macOS原生Quartz框架(极致速度)
直接调用macOS的Quartz API读取单个像素,不需要截取任何图像区域,速度是最快的,适合只需要检测少量像素的场景。
首先安装pyobjc(用于调用Objective-C框架):
pip install pyobjc-framework-Quartz
修改后的代码:
from pynput.keyboard import Listener, KeyCode from pynput.mouse import Controller as MouseController, Button import Quartz import time mouse = MouseController() pixel_coords = [(590, 490), (492, 490), (678, 490), (766, 490)] running = True def get_pixel_color(x, y): # 获取主屏幕 screen = Quartz.CGMainDisplayID() # 创建上下文 context = Quartz.CGDisplayCreateImage(screen) # 获取像素数据 data = Quartz.CGDataProviderCopyData(Quartz.CGImageGetDataProvider(context)) # 计算像素在数据中的位置(macOS屏幕是RGBA格式,每行字节数是宽度*4) width = Quartz.CGImageGetWidth(context) byte_pos = (y * width + x) * 4 # 读取RGBA值 r = data[byte_pos] g = data[byte_pos+1] b = data[byte_pos+2] a = data[byte_pos+3] # 释放资源 Quartz.CGImageRelease(context) return (r, g, b, a) def click(x, y): mouse.position = (x, y) mouse.click(Button.left) def play(): global running while running: x = time.time() for coord in pixel_coords: value = get_pixel_color(coord[0], coord[1]) if value in [(0, 0, 0, 255), (15, 20, 19, 255), (13, 32, 41, 255), (0, 161, 201, 255)]: click(coord[0], coord[1]) print(f"Time taken: {round(time.time() - x, 4)}") def on_press(key): global running if key == KeyCode(char="q"): running = False print("Stopped") quit() with Listener(on_press=on_press) as listener: play() listener.join()
这个方案直接读取每个指定点的像素,不需要生成图像对象,耗时大概在0.01秒左右,极致高效。注意每次调用CGDisplayCreateImage会捕获整个屏幕的像素数据,但因为只读取几个点,整体还是很快。
关键优化点总结
- 避免全屏截图:只截取需要的区域,减少数据处理量
- 使用更高效的捕获库/API:放弃PIL的ImageGrab,改用专门的快速截图工具或系统原生API
- 减少不必要的图像操作:直接读取像素值,避免生成完整的图像对象
内容的提问来源于stack exchange,提问作者pirosow
相关产品推荐
相关产品推荐

