如何提升屏幕分区平均像素颜色的获取速度?
优化屏幕分区平均RGB计算速度的方案
针对你的显示器背光项目,当前0.8秒的计算延迟可以通过以下几个关键优化大幅降低:
1. 用Numpy替代纯Python像素遍历
原代码中逐个像素累加RGB值的纯Python循环是最大性能瓶颈,Numpy的向量化运算能把这部分速度提升几十倍。
修改get_average_color方法,直接对图像数组的区域进行求和与平均:
import numpy as np # ... 其他代码保持不变 ... def get_average_color(self, box): # 将PIL图像转为numpy数组,形状为 (高度, 宽度, 3) img_array = np.array(self.pixels) # 提取区域:y1到y2,x1到x2 x1, y1, x2, y2 = box region = img_array[y1:y2, x1:x2] # 计算区域内RGB的平均值 avg_r = int(np.mean(region[:, :, 0])) avg_g = int(np.mean(region[:, :, 1])) avg_b = int(np.mean(region[:, :, 2])) # 格式化补零,直接用f-string更高效 return f"{avg_r:03d}{avg_g:03d}{avg_b:03d}"
2. 替换截图库为mss(更快的屏幕捕获)
PIL.ImageGrab在多系统下的性能不算最优,mss库直接调用系统原生API,截图速度能提升2-3倍,且支持多显示器。
先安装mss:
pip install mss
修改grab方法和初始化代码:
import mss # ... 其他代码保持不变 ... class Ordinator: def __init__(self, sw, sh): self.screenW = sw self.screenH = sh # 初始化mss捕获器 self.sct = mss.mss() # 定义屏幕区域(mss用top, left, width, height格式) self.monitor = {"top": 0, "left": 0, "width": sw, "height": sh} # 定义分区(保持原坐标逻辑) self.top_left = (0, 0, int(0.33 * sw), int(0.33 * sh)) self.top_middle = (int(0.33 * sw), 0, int(0.66 * sw), int(0.33 * sh)) self.top_right = (int(0.66 * sw), 0, sw, int(0.33 * sh)) self.middle_left = (0, int(0.33 * sh), int(0.33 * sw), int(0.66 * sh)) self.middle_right = (int(0.66 * sw), int(0.33 * sh), sw, int(0.66 * sh)) self.bottom_left = (0, int(0.66 * sh), int(0.33 * sw), sh) self.bottom_middle = (int(0.33 * sw), int(0.66 * sh), int(0.66 * sw), sh) self.bottom_right = (int(0.66 * sw), int(0.66 * sh), sw, sh) def grab(self): # 用mss捕获屏幕,直接转为PIL图像 sct_img = self.sct.grab(self.monitor) self.pixels = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX")
3. 优化字符串拼接逻辑
原代码中get_all_colors的字符串逐个相加效率低,改用列表收集结果后一次性拼接:
def get_all_colors(self): self.grab() # 用列表收集所有分区的颜色字符串,再一次性拼接 color_parts = [ self.get_average_color(self.top_right), self.get_average_color(self.top_middle), self.get_average_color(self.top_left), self.get_average_color(self.middle_left), self.get_average_color(self.bottom_left), self.get_average_color(self.bottom_middle), self.get_average_color(self.bottom_right), self.get_average_color(self.middle_right) ] return "".join(color_parts)
4. 可选:降低截图分辨率进一步提速
如果对颜色精度要求不是极高,可以将截图缩放到更小的尺寸再计算平均,能进一步减少计算量:
def grab(self): sct_img = self.sct.grab(self.monitor) self.pixels = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX") # 缩放为原尺寸的1/4,可根据需求调整比例 self.pixels = self.pixels.resize((self.screenW//4, self.screenH//4)) # 更新分区坐标以匹配缩放后的尺寸 self.screenW = self.screenW//4 self.screenH = self.screenH//4
完整优化后的代码
整合以上所有优化点后的完整代码:
import serial, time, tkinter as tk, numpy as np, mss from PIL import Image root = tk.Tk() screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() root.destroy() lastx = 23 class Ordinator: """获取屏幕8个分区的平均颜色,减少Arduino端计算量""" def __init__(self, sw, sh): self.screenW = sw self.screenH = sh self.sct = mss.mss() self.monitor = {"top": 0, "left": 0, "width": sw, "height": sh} # 定义屏幕分区 self.top_left = (0, 0, int(0.33 * sw), int(0.33 * sh)) self.top_middle = (int(0.33 * sw), 0, int(0.66 * sw), int(0.33 * sh)) self.top_right = (int(0.66 * sw), 0, sw, int(0.33 * sh)) self.middle_left = (0, int(0.33 * sh), int(0.33 * sw), int(0.66 * sh)) self.middle_right = (int(0.66 * sw), int(0.33 * sh), sw, int(0.66 * sh)) self.bottom_left = (0, int(0.66 * sh), int(0.33 * sw), sh) self.bottom_middle = (int(0.33 * sw), int(0.66 * sh), int(0.66 * sw), sh) self.bottom_right = (int(0.66 * sw), int(0.66 * sh), sw, sh) def grab(self): sct_img = self.sct.grab(self.monitor) self.pixels = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX") # 可选:缩放截图以提速 # self.pixels = self.pixels.resize((self.screenW//4, self.screenH//4)) # self.screenW = self.screenW//4 # self.screenH = self.screenH//4 def get_average_color(self, box): img_array = np.array(self.pixels) x1, y1, x2, y2 = box region = img_array[y1:y2, x1:x2] avg_r = int(np.mean(region[:, :, 0])) avg_g = int(np.mean(region[:, :, 1])) avg_b = int(np.mean(region[:, :, 2])) return f"{avg_r:03d}{avg_g:03d}{avg_b:03d}" def get_all_colors(self): self.grab() color_parts = [ self.get_average_color(self.top_right), self.get_average_color(self.top_middle), self.get_average_color(self.top_left), self.get_average_color(self.middle_left), self.get_average_color(self.bottom_left), self.get_average_color(self.bottom_middle), self.get_average_color(self.bottom_right), self.get_average_color(self.middle_right) ] return "".join(color_parts) Ord = Ordinator(screen_width, screen_height) # ser = serial.Serial('/dev/tty.usbmodem14101', 9600) while True: start = time.time() x = Ord.get_all_colors() if x != lastx: # ser.write(x.encode()) print(x) lastx = x print(f"End: {time.time() - start:.3f}s") time.sleep(0.1)
这些优化后,单轮循环的耗时应该能降到0.1秒以内,基本消除背光延迟。
内容的提问来源于stack exchange,提问作者Seaver Olson
相关产品推荐
相关产品推荐

