You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升屏幕分区平均像素颜色的获取速度?

优化屏幕分区平均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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 10:57:01