拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python图色识别实战:条件运算符与多线程窗口颜色监控

这次我们来看一个很典型的 Python 图色实战题目怎么用条件运算符在“不使用大漠插件”的前提下面向游戏窗口截图做颜色状态识别再配合多线程把监控逻辑跑起来。标题里的演示对象是《永恒之塔》塔2某岛地图但这篇文章的重点并不在具体打怪或寻路而是把一套可以复用的 Python 图色代码思路沉淀下来截窗口、找颜色、算比例、用条件运算符做状态分支、用 threading 同时处理截图与日志。先说结论这个项目属于“零基础到实战”教程系列中的第 1.10 节前面如果已经掌握 Python 基础语法、Pillow 基本操作和 window 窗口枚举那么这一节可以轻松跟完。本节最关键的三件事是第一用纯 Python 方案替代大漠插件完成“区域截屏与颜色匹配”第二理解条件运算符在图色状态判断里的正确写法与容易踩的坑第三把单线程识别改成多线程监控验证 Event、Lock、共享字典这些并发工具在真实截图场景中的表现。需要提前说明的是游戏自动化有明确的使用边界。本文只做“窗口截图 颜色统计 状态输出”不提供任何后台绑定、自动战斗、绕过反作弊机制的实现。如果要把这套代码用在在线游戏环境中请先确认是否符合游戏用户协议和相关法规否则只能用于本地 UI 自动化学习、自动化测试或自建训练环境。这一点会在后面单独展开。1. 核心能力速览与技术选型在动手之前先用一张表把项目基本情况说清楚。这样你看完就知道这一节要不要继续跟。能力项说明项目定位Python 图色实战系列第 1.10 节条件运算符 图色识别 多线程是否使用大漠插件否纯 Python 方案编程语言Python 3.10 及以上版本兼容以本机环境为准主要依赖Pillow、numpy、pywin32、标准库 threading可选依赖opencv-python用于后续模板匹配扩展操作系统Windows 10 / Windows 11窗口模式前台窗口截图不处理硬件加速后台绑定识别方式截取窗口客户区域统计指定颜色附近的像素数量或占比多线程能力支持使用 threading Event Lock 控制API 服务本节不涉及不开启 HTTP 接口批量任务面向多个监控区域可扩展本节以单窗口多状态演示适合场景Python 图色入门、UI 自动化测试、游戏窗口状态数据分析从项目标题可以推断作者强调“非大漠插件”说明这个系列并不打算依赖付费组件或第三方图色 DLL。大漠插件在旧版图色脚本里确实很常见很多老教程会直接用 Dm.FindColor、Dm.GetColor 这类接口但它的免费版有功能限制商业版需要授权而且实现原理偏黑盒并不适合零基础学习者建立图色识别的底层认知。换成 Pillow numpy 之后图色识别的逻辑变得透明我们能看到截图数据是一张 RGB 图像找色本质是在二维像素数组里做数值匹配条件运算符负责把匹配结果转成业务状态。这种思路换到任何窗口、任何 UI 控件、任何自动化测试项目里都能复用。缺点是纯 Python 截图方案很难处理 DirectX 或硬件加速渲染的游戏后台窗口这一点后面会重点讲。2. 环境准备与依赖安装这一节我们先把运行环境准备好。本机需要安装 Python然后安装四个库Pillow 负责截图和图像读取numpy 负责像素批量计算pywin32 负责枚举窗口和获取窗口矩形opencv-python 是可选的如果后续要做“找图”模板匹配可以一并装上。在命令行中执行pip install pillow numpy pywin32 opencv-python如果命令行提示 pip 不是内部命令说明 Python 没有加入 PATH需要重新运行 Python 安装包勾选“Add Python to PATH”或者使用python -m pippython -m pip install pillow numpy pywin32 opencv-python安装完成后检查 Pillow 和 numpy 是否能正常导入python -c from PIL import Image; import numpy; print(ok)Windows 高分屏也要处理。很多程序在 DPI 缩放不是 100% 时win32gui 拿到的窗口坐标和 ImageGrab 截到的图像会产生偏移截出来的区域会错位。所以在做窗口截图之前建议把当前进程设为 DPI 感知。在 Python 脚本顶部写入import ctypes try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: ctypes.windll.user32.SetProcessDPIAware()需要注意这段代码必须在创建任何窗口和截图之前执行并且只在 Windows 上有意义。如果你运行的是普通桌面应用或者远程桌面DPI 设置还可能不一样截图前最好先用一个小程序把目标窗口的坐标打印出来确认。3. 不用大漠也能做图色截屏、找色、区域统计大漠插件被很多老脚本使用是因为它把“截屏、找色、找图、OCR、绑定窗口”等能力打包成一个 DLL调用简单。但它的缺点也很明显不同版本兼容性差异大在 Win10/Win11 下经常出现绑定窗口失败免费版本功能受限部分安全软件会把它当作潜在风险程序处理。换成 Python 原生方案之后我们要自己完成三件事。3.1 获取指定窗口的客户区坐标图色脚本最常见的操作是绑定一个窗口然后只截取窗口内部区域。不使用大漠时我们可以通过 win32gui 找到窗口句柄再转换成屏幕坐标。下面的函数先获取当前前台窗口的客户区矩形再通过 ClientToScreen 把客户区坐标转换成屏幕坐标import win32gui def get_foreground_client_bbox(): hwnd win32gui.GetForegroundWindow() if hwnd 0: raise RuntimeError(没有获取到前台窗口) # GetClientRect 返回的是客户区左上角(0,0)到右下角的相对坐标 left, top, right, bottom win32gui.GetClientRect(hwnd) # 把客户区左上角和右下角转换成屏幕坐标 left, top win32gui.ClientToScreen(hwnd, (left, top)) right, bottom win32gui.ClientToScreen(hwnd, (right, bottom)) return hwnd, (left, top, right, bottom)这里的核心是理解 Windows 的坐标体系GetWindowRect 返回的是整个窗口在屏幕上的矩形包括标题栏和边框GetClientRect 返回的是绘图区域的大小不包括标题栏和边框。如果截图只需要客户区就必须用 ClientToScreen 换算。如果目标窗口没有出现在前台我们也可以先枚举所有窗口再按标题匹配import win32gui def find_window_by_title(title_part: str): result [] def callback(hwnd, extra): if win32gui.IsWindowVisible(hwnd): title win32gui.GetWindowText(hwnd) if title_part.lower() in title.lower(): result.append(hwnd) win32gui.EnumWindows(callback, None) return result用标题匹配窗口时不要只匹配一次因为同名窗口可能有多个。图色脚本更稳妥的做法是先让用户拖拽选择窗口或者列出候选窗口列表再让用户指定其中一个。本节为了保持代码简洁采用前台窗口作为默认演示对象。3.2 用 ImageGrab 截取窗口区域拿到窗口矩形之后可以直接调用 ImageGrab.grab 截图from PIL import ImageGrab def capture_client_region(): hwnd, bbox get_foreground_client_bbox() img ImageGrab.grab(bboxbbox).convert(RGB) return hwnd, imgImageGrab.grab 的 bbox 参数必须接收屏幕坐标并且这个窗口必须处于可见状态。如果窗口被其他窗口完全遮挡、最小化或处于 DirectX 独占全屏模式ImageGrab 通常只能截到其他窗口或空白内容这是不依赖大漠做“后台截图”的首要限制。对于常见桌面程序、窗口化游戏和 UI 自动化测试只要窗口保持在前台这种截图方案就足够稳定。如果确实需要后台截图并且目标程序使用普通 GDI 绘制后续可以尝试 win32ui 配合 CreateCompatibleDC 的方案但复杂度明显更高放在后续章节再展开。3.3 用 numpy 做颜色匹配对图色脚本来说“找色”并不是把每个像素用 for 循环遍历一遍尤其是截图区域有几十万像素时Python 纯循环会很慢。更高效的做法是把图片转成 numpy 数组然后用向量化运算统计颜色。下面的函数会统计一张截图里接近指定颜色的像素数量import numpy as np def count_near_color(img, target_color, tolerance30): img: PIL Image 对象RGB 模式 target_color: (r, g, b) 元组 tolerance: 每个通道允许的误差 arr np.array(img).astype(np.int16) target np.array(target_color, dtypenp.int16) diff np.abs(arr - target) mask ( (diff[:, :, 0] tolerance) (diff[:, :, 1] tolerance) (diff[:, :, 2] tolerance) ) return int(mask.sum())这里用 int16 是为了防止做减法时出现负数溢出。如果 arr 是 uint8减去一个较大的 target 值可能变成很大的正数导致 abs 判断失效。“找色”在实际项目里经常不是只找一个点而是找一条状态条的占比。例如一个血量条由左到右从空到满满了之后红色区域占 80%残血时红色区域只占 10%。通过统计红色像素数量占区域总像素数量的比例就能判断当前状态。区域统计代码如下def count_color_ratio(img, target_color, tolerance30): arr np.array(img).astype(np.int16) target np.array(target_color, dtypenp.int16) diff np.abs(arr - target) mask ( (diff[:, :, 0] tolerance) (diff[:, :, 1] tolerance) (diff[:, :, 2] tolerance) ) total img.size[0] * img.size[1] return int(mask.sum()) / total4. 条件运算符的图色写法与易错点基础图色函数已经准备好接下来看这一节的主角条件运算符。4.1 条件运算符基础语法Python 的条件运算符也叫三元表达式语法为值1 if 条件 else 值2执行逻辑是条件成立时整个表达式取值 1条件不成立时取值 2。在图色项目中最常见的是把识别结果转成状态字符串。例如统计到血条区域红色像素比例超过 0.1就认为需要危险提示danger_ratio 0.15 status danger if danger_ratio 0.1 else safe print(status)4.2 用条件运算符压缩多状态分支条件运算符可以嵌套使用用于处理多状态。图色脚本经常会遇到类似逻辑红色比例高是“危险”蓝色比例高是“警告”绿色比例高是“安全”。嵌套写法如下def decide_status(red_ratio, blue_ratio, green_ratio): return ( danger if red_ratio 0.1 else warning if blue_ratio 0.1 else safe if green_ratio 0.1 else unknown )这个写法可以运行但可读性一般。如果状态分支超过三层更推荐用判断表或 if/elif 结构而不是强行嵌套。条件运算符适合“二选一”或“三选一”的场景超过三个状态时代码维护成本会明显上升。4.3 图色脚本里常见的错误写法初学者在写图色脚本时容易把条件运算符写成传统的condition and a or b这是历史遗留写法。在 Python 2 时代因为缺少三元表达式有人会用a and b or c来模拟但当 b 是 0、空字符串、空列表等假值时结果会出错。例如hp_ratio 0.0 action (hp_ratio 0.1) and 喝药 or 正常输出 print(action) # 正常输出这段代码在 hp_ratio 小于 0.1 时会得到“正常输出”看似没问题。但如果把两个分支对调action (hp_ratio 0.1) and 喝药 or 正常输出 print(action)当第二个分支是空字符串或其他假值时逻辑就会出错。所以图色状态判断不要使用and/or组合冒充条件运算符直接写a if condition else b。另一个容易错的是条件优先级。条件运算符的优先级非常低如果和比较运算符混在一起最好用括号包裹status (danger if red_ratio 0.1 else safe) _ str(int(red_ratio * 100))不包裹时字符串拼接的优先级会先执行导致状态判断结果和后面的内容拼接到一起运行结果可能变成0_danger并不符合直觉。5. 实战一单线程窗口状态颜色监控前面内容偏基础现在写一个完整可运行的单线程版本。这个程序会每 0.5 秒截取一次当前前台窗口客户区左上角的 200x10 区域统计红色、绿色、蓝色像素占比并用条件运算符输出当前状态。先看完整代码import ctypes import time import numpy as np import win32gui from PIL import ImageGrab ctypes.windll.shcore.SetProcessDpiAwareness(1) REGION_WIDTH 200 REGION_HEIGHT 10 RED (40, 40, 240) GREEN (30, 220, 80) BLUE (30, 180, 230) def get_foreground_client_bbox(): hwnd win32gui.GetForegroundWindow() if hwnd 0: raise RuntimeError(没有获取到前台窗口) left, top, right, bottom win32gui.GetClientRect(hwnd) left, top win32gui.ClientToScreen(hwnd, (left, top)) right, bottom win32gui.ClientToScreen(hwnd, (right, bottom)) return hwnd, (left, top, right, bottom) def count_near_color(img, target_color, tolerance30): arr np.array(img).astype(np.int16) target np.array(target_color, dtypenp.int16) diff np.abs(arr - target) mask ( (diff[:, :, 0] tolerance) (diff[:, :, 1] tolerance) (diff[:, :, 2] tolerance) ) return int(mask.sum()) def analyze_foreground_status(): hwnd, bbox get_foreground_client_bbox() img ImageGrab.grab(bboxbbox).convert(RGB) # 在客户区左上角截取一小块区域作为测试条 test_region img.crop((0, 0, REGION_WIDTH, REGION_HEIGHT)) total REGION_WIDTH * REGION_HEIGHT red_ratio count_near_color(test_region, RED) / total green_ratio count_near_color(test_region, GREEN) / total blue_ratio count_near_color(test_region, BLUE) / total return red_ratio, green_ratio, blue_ratio def decide_status(red_ratio, green_ratio, blue_ratio): return ( danger if red_ratio 0.1 else warning if blue_ratio 0.1 else safe if green_ratio 0.1 else unknown ) if __name__ __main__: for i in range(10): red, green, blue analyze_foreground_status() status decide_status(red, green, blue) print( f[{i}] red{red:.2f} green{green:.2f} blue{blue:.2f} status{status}, flushTrue, ) time.sleep(0.5)这段代码没有做任何后台绑定也没有模拟键鼠操作只是一个前台窗口颜色采样器。运行后把目标窗口切换到前台比如打开一个显示红、绿、蓝状态条的窗口或测试页面控制台会持续输出颜色占比和状态。验证时可以先打开一个窗口把目标区域左上角故意显示成接近红色观察输出是否从 safe 变成 danger。如果一直是 unknown说明截取到的区域颜色和三种预设颜色都不接近可以通过打印某个像素值来调试print(np.array(test_region)[0, 0])运行到这里可以发现这个思路本质上并不复杂截图数据是数组颜色判断是数组统计条件运算符负责把统计结果映射成业务状态。真正的难点在于窗口区域定位、颜色阈值调整和多线程状态同步。6. 实战二多线程截图与状态消费单线程版本里截图、颜色统计、打印输出都在同一个循环中完成。如果颜色统计耗时长打印和状态展示就会被阻塞。为了模拟真实的“边截图、边读取状态”场景下面引入 threading。多线程版本设计如下一个线程专门负责截图和颜色统计另一个线程负责读取共享状态并打印日志主线程负责控制程序运行时长和退出。这里使用 threading.Event 作为停止信号使用 threading.Lock 保护共享字典。因为 ImageGrab 和 win32gui 的调用要在同一个线程里串行执行避免多个线程同时调用 Windows GDI 接口引发不稳定所以这里只有 capture_worker 线程会操作句柄和截图。完整代码import ctypes import threading import time import numpy as np import win32gui from PIL import ImageGrab ctypes.windll.shcore.SetProcessDpiAwareness(1) REGION_WIDTH 200 REGION_HEIGHT 10 RED (40, 40, 240) GREEN (30, 220, 80) BLUE (30, 180, 230) def get_foreground_client_bbox(): hwnd win32gui.GetForegroundWindow() if hwnd 0: raise RuntimeError(没有获取到前台窗口) left, top, right, bottom win32gui.GetClientRect(hwnd) left, top win32gui.ClientToScreen(hwnd, (left, top)) right, bottom win32gui.ClientToScreen(hwnd, (right, bottom)) return hwnd, (left, top, right, bottom) def count_near_color(img, target_color, tolerance30): arr np.array(img).astype(np.int16) target np.array(target_color, dtypenp.int16) diff np.abs(arr - target) mask ( (diff[:, :, 0] tolerance) (diff[:, :, 1] tolerance) (diff[:, :, 2] tolerance) ) return int(mask.sum()) def decide_status(red_ratio, green_ratio, blue_ratio): return ( danger if red_ratio 0.1 else warning if blue_ratio 0.1 else safe if green_ratio 0.1 else unknown ) def capture_worker(stop_event, shared_state, state_lock, interval1.0): while not stop_event.is_set(): try: hwnd, bbox get_foreground_client_bbox() img ImageGrab.grab(bboxbbox).convert(RGB) test_region img.crop((0, 0, REGION_WIDTH, REGION_HEIGHT)) total REGION_WIDTH * REGION_HEIGHT red_ratio count_near_color(test_region, RED) / total green_ratio count_near_color(test_region, GREEN) / total blue_ratio count_near_color(test_region, BLUE) / total status decide_status(red_ratio, green_ratio, blue_ratio) with state_lock: shared_state[red_ratio] red_ratio shared_state[green_ratio] green_ratio shared_state[blue_ratio] blue_ratio shared_state[status] status shared_state[last_update] time.time() shared_state[error] None except Exception as exc: with state_lock: shared_state[error] str(exc) time.sleep(interval) def log_worker(stop_event, shared_state, state_lock): while not stop_event.is_set(): with state_lock: if shared_state.get(error): print(error:, shared_state[error], flushTrue) else: print( status, shared_state.get(status), red, round(shared_state.get(red_ratio, 0), 2), green, round(shared_state.get(green_ratio, 0), 2), blue, round(shared_state.get(blue_ratio, 0), 2), flushTrue, ) time.sleep(0.5) if __name__ __main__: stop_event threading.Event() state_lock threading.Lock() shared_state {status: unknown, last_update: 0.0} t1 threading.Thread( targetcapture_worker, args(stop_event, shared_state, state_lock, 1.0), daemonTrue, ) t2 threading.Thread( targetlog_worker, args(stop_event, shared_state, state_lock), daemonTrue, ) t1.start() t2.start() try: time.sleep(10) finally: stop_event.set() t1.join(timeout3) t2.join(timeout1) print(已停止)运行这个程序会出现两个输出规律不同的事件流capture_worker 每 1 秒刷新一次共享状态log_worker 每 0.5 秒读一次并打印。即使某个时刻截图耗时变长log_worker 也不会被截图过程卡住因为它读取的是上一次截图完成后保存的状态。多线程方案的核心收益是“职责分离”。截图线程只关心画面采集和颜色统计日志线程只关心状态展示主线程负责整体退出。后续如果要把状态写到队列、写到数据库或者通过 HTTP 接口暴露只需要再加一个消费线程即可不需要改动截图逻辑。但也要注意多线程不是只有好处。共享状态如果不上锁多个线程同时读写字典时可能读到半个写操作的结果。日志线程如果负责动作执行而动作执行本身需要窗口在前台那么不同线程抢窗口焦点会产生严重冲突。这里建议在图色脚本中用 Windows 消息或队列机制串行化所有需要操作窗口的动作不要在多个线程里同时调用前台键盘鼠标操作。7. 资源占用、常见问题与排查方法图色脚本除了看功能能不能跑通还要看资源占用是否可控以及出问题时怎么定位。7.1 截图频率和 CPU 占用ImageGrab.grab 在每次调用时都会从屏幕读取整块像素截图区域越大耗时越长。本节代码只截取前台窗口客户区的一部分实际 CPU 占用会明显低于全屏 1080p 截图。但不同机器性能差异很大并不能给一个通用数字。更稳妥的判断方法是先提高循环间隔。第一次测试时把 interval 设为 2 秒或 3 秒观察 CPU 占用和输出是否连贯。如果程序长时间运行仍然稳定再逐步缩短间隔。颜色统计使用 numpy 向量化后耗时主要集中在图片截取和 RGB 转换而不是 for 循环遍历。如果需要进一步降低占用可以限制识别区域不要在整张窗口上做颜色统计。比如血条只出现在固定坐标区域就只截那个区域。截图前先确认区域大小比截图后裁剪更高效。7.2 常见问题排查下面是本节代码运行中比较容易遇到的问题以及排查思路问题现象可能原因排查方式解决方案控制台一直输出 unknown测试区域颜色和预设颜色不匹配打印测试区域某像素的 RGB 值调整目标颜色或 tolerance窗口始终截不到内容窗口在后台、最小化或被遮挡检查前台窗口句柄切换窗口到前台或改用窗口枚举截图区域偏移DPI 缩放不为 100%打印窗口矩形和截图尺寸开启进程 DPI 感知多线程运行后程序关不掉线程没有收到退出信号检查 stop_event 是否正确 set使用 Event join 控制线程退出CPU 占用过高截图频率太高或区域太大分步测量截图耗时加大 interval缩小识别区域ImageGrab 截图为黑色窗口使用硬件加速渲染打开窗口时切换成窗口化渲染普通程序无此问题游戏需要按实际环境处理颜色匹配不稳定颜色容差太小或屏幕色彩管理影响打印像素值并观察抖动范围适当增大 tolerance多个线程同时使用 win32gui线程间抢用 GDI 资源加日志观察卡顿位置只在一个线程里执行窗口操作图色脚本里最值得重视的是“窗口后台截图”的边界。ImageGrab 依赖 Windows GDI 从屏幕画面取数据对许多现代游戏和硬件加速渲染的窗口前台窗口可能没问题但后台窗口大概率截不到真实画面。有些游戏还会使用反作弊保护禁止外部程序读取窗口绘制内容。遇到这种情况不建议继续尝试做后台绑定因为除了技术难度高还可能违反软件使用协议。7.3 批量监控区域的扩展思路本节代码只监控了一个 200x10 的区域。实际项目里一个窗口可能同时有血量、蓝量、目标状态等多个颜色区域。批量处理时不要为每个区域各开一个截图线程因为多个 ImageGrab 并发执行会重复占用 GDI反而更慢。更合理的方案是把所有监控区域配置成一个列表monitor_regions [ { name: hp_bar, rect: (0, 0, 200, 10), target_color: (40, 40, 240), threshold: 0.1, }, { name: mp_bar, rect: (0, 15, 200, 10), target_color: (30, 180, 230), threshold: 0.1, }, ]截图线程只截一次整个窗口然后根据配置项分别裁出不同区域做统计。这样可以显著减少截图次数颜色统计也能并发执行。虽然 Python 的 GIL 会影响纯计算线程的真实并行但由于 numpy 底层会释放 GIL多个 numpy 统计任务在适当拆分后仍能获得一定加速。8. 合规使用边界与最佳实践图色脚本是一个非常敏感的技术方向因为同样一套代码用在自动化测试里是效率工具用在在线游戏里就是外挂脚本。本文给出的所有示例只完成了“读取颜色状态”这一层没有编写自动键鼠动作也没有尝试绕过硬件的窗口绑定限制。如果你是在学习 Python 图色最安全的练手窗口是记事本、浏览器页面或自建的 PyQt 测试程序。只要窗口里能渲染出不同颜色的区域就能验证截图、找色、条件运算符和多线程逻辑不需要真的打开游戏。等代码逻辑稳定后再决定是否把识别结果接入自己的合法 UI 测试流程。对于在线游戏我建议直接认定为高风险场景。原因有两个第一多数游戏用户协议明确禁止使用第三方工具执行自动操作第二使用按键模拟或内存读取可能违反平台规则甚至带来账号安全风险。本文不提供任何绕过游戏保护机制的内容也不鼓励读者把“多线程非大漠插件”理解成用来做自动战斗的高可用外挂方案。工程实践上图色脚本项目应该遵守几个基本原则识别和动作分离截图线程只负责状态分析动作执行必须独立排队并经过人工确认权限最小化只截图和保存日志不读取与业务无关的数据保留审计日志每一次识别结果和动作来源都要可追溯定期检查道德边界不要把图色能力用于未经授权的人脸识别、验证码绕过、批量注册或侵犯他人隐私的活动。如果要把这套代码接入自动化测试平台建议再加上日志文件输出、异常重试、配置文件隔离。代码里的窗口标题、监控区域、阈值颜色都不应该硬编码在函数里而是放到配置文件或命令行参数里便于不同环境下切换。9. 总结与下一步这一节从零写完了两个版本单线程窗口颜色监控和多线程截图状态消费。核心收获有三个一是知道不用大漠插件也能做图色识别底层就是“截屏 numpy 数组统计 条件运算符判断”二是熟悉了条件运算符在 Python 里的正确写法避免使用 and/or 这种历史遗留写法导致逻辑出错三是理解了 threading 中 Event 和 Lock 怎么配合保证共享状态在截图线程和日志线程之间安全传递。如果你准备继续深入这个系列下一步最值得验证的是两件事第一把颜色统计改成“区域平均颜色”或“模板匹配找图”让识别不再依赖固定的 RGB 阈值第二把共享状态从普通字典换成 queue.Queue让截图线程产生的状态变更按顺序进入队列日志或动作线程按队列消费。这样能解决更复杂的“多监控点 多动作”任务调度问题。最容易踩的坑是随意缩短截图间隔。刚跑通代码时建议先从 1 秒到 2 秒间隔开始确认 CPU 占用可接受后再逐步提高频率。不要在没做区域裁剪的情况下把整块屏幕都读进来否则后面加再多颜色逻辑也容易卡在截图这一环。建议把本文代码保存成一个screen_color_monitor.py文件同时写好注释和区域配置后续加入更多监控项时只需要修改配置不必反复重写线程逻辑。然后尝试用自建测试窗口做一次完整的“颜色变化识别”实验确认多线程能够及时反应。跑通之后再谈扩展。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门