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

Python 图色识别实战:条件运算符与多线程屏幕监控解析

这次我们来看一个 Python 图色识别实战教程“1.10 python 图色实战之条件运算符——永恒之塔 塔 2 *xian岛 零基础到实战多线程非大漠插件”。这套教程和常见按键精灵、大漠插件思路不同核心是纯 Python 实现图色识别不依赖大漠插件的注册、绑定和收费验证。整个系列从变量、循环、条件运算符一路深入到多线程属于“零基础到实战”的路径设计。第 1.10 节重点解决两个问题条件运算符怎么写才能让颜色判断代码更短、更清晰。单线程循环取色容易卡死怎么用多线程切到后台监控模式。如果你正在学习 Python 自动化、想理解屏幕取色和像素匹配的基本原理或者想知道不装大漠插件怎么处理图色逻辑这篇可以直接往下看。下面会给出环境准备、代码示例、测试方法和常见问题排查。1. 核心能力速览能力项说明项目类型Python 图色识别教程、游戏场景自动化练习关键技术条件运算符、Pillow 屏幕取色、threading 多线程插件依赖无不使用大漠插件核心使用方式读取窗口/屏幕指定坐标像素匹配目标颜色范围后执行动作语言版本Python 3Pillow、pywin32 均支持 3.8支持平台Windows 为主涉及窗口坐标和截图是否支持批量任务支持可通过多线程实现对多个坐标点或任务队列的循环检测是否支持 API教程本身无 API核心逻辑可封装成模块供其他脚本调用显存要求无CPU 运行即可适合人群Python 入门者、自动化脚本爱好者、游戏界面自动化测试人员2. 适用场景与使用边界这类项目首先要说清楚边界。图色识别本身是一类通用技术可以用在自动化测试、UI 状态检测、辅助工具开发等场景。教程选择“永恒之塔”里的塔 2、*xian岛 类固定地图场景作为实战对象是为了有一个背景稳定、坐标相对固定的界面环境方便演示颜色匹配和区域判断。从技术学习角度它适合想理解“截屏 - 取色 - 判断 - 动作”这条完整链路的人。想封装一套不依赖第三方商业插件的 Python 屏幕检测工具。想搞清楚多线程怎么在不阻塞主逻辑的前提下持续检查画面状态。需要提醒的是任何针对在线游戏、含对抗性玩家场景的自动化行为都可能违反游戏用户协议。这个系列更适合放在本地客户端界面、模拟器测试环境、或自己搭建的测试窗口中进行学习验证不建议直接拿到真实游戏环境里做影响其他玩家体验的操作。代码本身是技术练习怎么用是使用者的责任。3. Python 环境准备与依赖安装3.1 检查 Python建议使用 Python 3.8 或更高版本。Windows 下先确认命令可用python --version如果提示找不到命令需要到 Python 官网下载安装包安装时勾选“Add Python to PATH”安装完成后重新打开终端。3.2 安装依赖库这个教程主要需要三个库Pillow负责屏幕截图与像素读取。pywin32负责 Windows 窗口句柄、窗口坐标转换也用于鼠标/键盘行为扩展。numpy可选处理大批量像素比对时提速。安装命令pip install pillow pywin32 numpy国内网络环境可以加镜像pip install pillow pywin32 numpy -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后验证python -c from PIL import ImageGrab; print(Pillow OK) python -c import win32api; print(pywin32 OK)如果 pywin32 导入失败一般需要重启终端或者检查 Python 位数与系统位数一致。教程本身的取色核心逻辑依赖 Pillow 就能跑通pywin32 主要用来做窗口定位和键鼠动作扩展。3.3 推荐项目目录结构不管是一键脚本还是多线程封装建议先按目录整理python-color-practice/ ├── main.py # 主逻辑入口 ├── screen_utils.py # 截屏、取色、找色封装 ├── task_thread.py # 多线程任务调度 ├── config.py # 坐标点、颜色阈值配置 ├── logs/ # 运行日志目录 └── screenshots/ # 测试截图保存目录这个结构的好处是取色函数和任务逻辑分离后面接 API、接批量任务、换游戏场景时只需要改配置文件。4. 条件运算符在 Python 图色判断中的用法4.1 条件运算符基础Python 的三元表达式结构是结果 满足条件时的值 if 条件 else 不满足条件时的值例如判断某个像素的红色通道是否在 100 到 140 范围内color_r 125 if color_r 100 and color_r 140: result 红色通道匹配 else: result 红色通道不匹配 print(result)用条件运算符压缩写法color_r 125 result 红色通道匹配 if 100 color_r 140 else 红色通道不匹配 print(result)代码短了一个分支逻辑顺序却更直白先看条件再分别给两种取值。4.2 图色判断中的复合条件实际截屏返回的像素是一个包含(R, G, B)或(R, G, B, A)的元组。判断“是不是目标颜色”通常要同时比较三个通道还要允许容差。经典写法def is_color_close(pixel, target, tolerance30): r, g, b pixel[0], pixel[1], pixel[2] tr, tg, tb target if abs(r - tr) tolerance and abs(g - tg) tolerance and abs(b - tb) tolerance: return True return False大量if-return很容易腻条件运算符可以压缩返回值逻辑def is_color_close(pixel, target, tolerance30): r, g, b pixel[0], pixel[1], pixel[2] tr, tg, tb target return ( abs(r - tr) tolerance and abs(g - tg) tolerance and abs(b - tb) tolerance )函数最终返回的是布尔表达式本身不需要条件运算符再包一层。真正的三元结构更多用在“想根据匹配结果取不同值”的场景比如记录状态名称state_name 目标区域内血量条可见 if is_color_close(pixel, (200, 50, 50), 15) else 目标区域未检测到红色血条这一行的可读性很强条件、结果、默认值都在同一行里。4.3 条件运算符与多个坐标点融合在塔 2 这类固定场景里只判断一个点容易误判。常规做法是判断多个坐标点子结果汇总后做最终决策points { point_a: (250, 180, (180, 200, 200)), point_b: (310, 180, (220, 230, 230)), point_c: (280, 220, (60, 60, 80)), } # 先用常规循环统计 match_count 0 for name, (x, y, target_rgb) in points.items(): pixel_rgb get_pixel(x, y) if is_color_close(pixel_rgb, target_rgb, 25): match_count 1 scene_ready match_count 2条件运算符可以写统计值的累加match_count sum( 1 for (x, y, target_rgb) in points.values() if is_color_close(get_pixel(x, y), target_rgb, 25) ) scene_ready match_count 2这里没有强行使用三元表达式但逻辑被压缩成了单项表达式从“先取像素、再判断、再累加”变成“符合条件就贡献 1”在实战代码中很实用。5. 图色识别功能测试与效果验证5.1 屏幕像素读取封装先给出一个最简化的screen_utils.pyfrom PIL import ImageGrab def get_pixel(x, y): 读取屏幕指定坐标的颜色值 image ImageGrab.grab(bbox(x, y, x 1, y 1)) return image.getpixel((0, 0))[:3] def get_screen_region(left, top, right, bottom): 截取屏幕指定区域并返回 Image 对象 return ImageGrab.grab(bbox(left, top, right, bottom)) if __name__ __main__: print(当前鼠标位置的像素颜色, get_pixel(100, 100))如果你是做窗口内自动化建议用 pywin32 获取窗口句柄和客户区坐标把“窗口坐标”换算成“屏幕坐标”。教程中提到的永恒之塔属于全屏或窗口化游戏窗口模式下客户区坐标换算尤为重要。示例import win32gui def get_window_rect_by_title(title_keyword): 根据窗口标题关键字获取窗口在屏幕上的矩形区域 def enum_callback(hwnd, result): if win32gui.IsWindowVisible(hwnd): window_title win32gui.GetWindowText(hwnd) if title_keyword in window_title: result.append((hwnd, window_title)) return True windows [] win32gui.EnumWindows(enum_callback, windows) return windows得到窗口句柄后win32gui.GetWindowRect(hwnd)能拿到窗口相对于屏幕的位置再用这个位置做 ImageGrab 的截图区域。这种做法的好处是手动拖动窗口后坐标依然正确。5.2 验证目标颜色是否存在教程中比较关键的功能是通过若干个固定坐标点的颜色状态判断当前界面处于哪个状态。测试场景可以设计成打开一张固定背景的截图运行脚本记录每个坐标点的颜色输出到屏幕点 (250, 180) 的颜色为 (95, 210, 120)最近的目标色是 (100, 200, 120)匹配结果True 点 (310, 180) 的颜色为 (30, 40, 60)最近的目标色是 (220, 230, 230)匹配结果False判断成功的标准不是“某个像素一模一样”而是所有关键点状态组合是否符合预期逻辑。例如3 个坐标点都是亮色 → 判断为“待机状态”。3 个坐标点里至少 2 个进入暗红色 → 判断为“危险状态”。坐标点超出游戏窗口边界 → 截图返回的是桌面颜色肯定不匹配说明窗口被遮挡或者位置变了。5.3 常见误判排查颜色匹配偶尔不通过的原因主要是游戏画面有动态光影同一位置颜色会小幅变化。这时调大tolerance例如从 10 调到 30。截图坐标是窗口坐标不是屏幕坐标。需要叠加窗口的 (left,top) 偏移。电脑开了 HDR、色彩增强或者高对比度导致截屏颜色与肉眼看到的不一致。可关闭系统的颜色增强后再测。显示器缩放导致坐标偏移。Windows 默认 125% 或 150% 缩放时win32gui.GetWindowRect返回的可能是虚拟坐标需要在代码里做 DPI 自适应。6. 多线程任务调度与批量监控6.1 为什么不能用单线程死循环简单取色脚本经常这样写while True: pixel get_pixel(x, y) if is_color_close(pixel, target_color): do_something() time.sleep(0.2)单线程的问题很明显do_something()比较耗时的时候后面的截屏会被阻塞而游戏画面是持续变化的卡住期间可能错过关键变化。它还会占用当前终端进程没法同时执行其他任务。多线程可以解决这类问题一个线程持续做画面取色另一个线程负责动作处理或日志写入。6.2 用 threading 实现监控线程下面给一套可运行的简化模型用队列传递取色结果import threading import time import queue from collections import deque # from screen_utils import get_pixel # 按实际项目导入 # 共享任务队列 task_queue queue.Queue() # 停止事件 stop_event threading.Event() def color_detect_worker(stop_event, task_queue): 取色线程循环检查多个坐标点把命中的坐标写入队列 detect_points [ (250, 180, (180, 200, 200)), (310, 180, (220, 230, 230)), (280, 220, (60, 60, 80)), ] while not stop_event.is_set(): hit_points [] for (x, y, target_rgb) in detect_points: pixel_rgb get_pixel(x, y) if is_color_close(pixel_rgb, target_rgb, 30): hit_points.append((x, y)) if hit_points: task_queue.put(hit_points) time.sleep(0.2) def action_worker(stop_event, task_queue): 动作线程消费队列中的命中结果 while not stop_event.is_set(): try: hit_points task_queue.get(timeout0.5) except queue.Empty: continue print(检测到关键点, hit_points) # 这里可以调用封装好的键鼠操作或记录日志 task_queue.task_done()主函数启动detect_thread threading.Thread( targetcolor_detect_worker, args(stop_event, task_queue), ) action_thread threading.Thread( targetaction_worker, args(stop_event, task_queue), ) detect_thread.start() action_thread.start() try: while True: time.sleep(1) except KeyboardInterrupt: print(捕获 CtrlC准备退出...) stop_event.set() detect_thread.join() action_thread.join() print(线程已退出)核心点是取色线程不直接执行动作只把结果放进队列动作线程从队列取任务再处理。这样取色循环不会因为一次动作卡住而错过后续帧。6.3 多线程的共享状态控制线程之间不要直接共享普通变量来传递状态queue.Queue、threading.Event、threading.Lock是更稳的选择。计数器或状态标记用Lock保护state_lock threading.Lock() current_state unknown def update_state(new_state): global current_state with state_lock: current_state new_state教程中“非大漠插件”的重点就在这里大漠插件往往提供绑定窗口后的取色、找图、键鼠操作一体 API而纯 Python 方案需要自己处理线程同步、窗口坐标、动作调用。学习成本更高但代码完全透明也方便在非 Windows 环境部分复用。7. 资源占用与性能观察7.1 如何观察资源占用纯 Python 截屏取色的性能主要取决于ImageGrab.grab截屏区域大小。每轮循环检测的坐标点数量。单点像素读取是否需要额外转换 RGB。Windows 下可以用任务管理器观察进程 CPU 和内存也可以用 Python 内部统计每轮耗时import time loop_times deque(maxlen60) while not stop_event.is_set(): start time.perf_counter() # 执行一轮取色判断逻辑 run_detect_once() cost_ms (time.perf_counter() - start) * 1000 loop_times.append(cost_ms) avg_cost sum(loop_times) / len(loop_times) print(f单轮平均耗时: {avg_cost:.1f} ms, 最近一轮: {cost_ms:.1f} ms) time.sleep(0.1)如果单轮平均耗时达到 200ms 以上说明截屏区域太大或点坐标过多需要缩小bbox或调高time.sleep。7.2 CPU 推理和 GPU 推理的差异图色识别主要是像素比较CPU 足够不需要 GPU。只有在做大范围模板匹配、图像相似度计算或接入深度学习目标检测模型时才需要考虑 GPU。本项目定位是基础图色CPU 模式下也不会占用大量显存。7.3 性能优化思路需要监控的坐标点较多时可以先把整个目标区域截成一张 Image 对象再通过img.getpixel((x - left, y - top))读取相对坐标而不是对每个点单独ImageGrab.grab。一次截屏加多个getpixel比多次截屏快很多。对大区域批量找色可以用 numpy 加速import numpy as np from PIL import ImageGrab def find_color_in_screen(region, target_rgb, tolerance30): img ImageGrab.grab(bboxregion) arr np.array(img.convert(RGB)) r_min, g_min, b_min (target_rgb[0] - tolerance, target_rgb[1] - tolerance, target_rgb[2] - tolerance) r_max, g_max, b_max (target_rgb[0] tolerance, target_rgb[1] tolerance, target_rgb[2] tolerance) mask ( (arr[:, :, 0] r_min) (arr[:, :, 0] r_max) (arr[:, :, 1] g_min) (arr[:, :, 1] g_max) (arr[:, :, 2] b_min) (arr[:, :, 2] b_max) ) coords np.column_stack(np.where(mask)) return coords这段代码可帮助你定位所有符合条件的连续色块。输出是坐标数组方便统计色块面积和中心点。8. 常见问题与排查方法问题现象可能原因排查方式解决方案pip install安装失败镜像源慢、网络不通检查 pip 源和超时换国内镜像源或用--timeout参数Pillow 导入报错Python 版本不匹配或安装损坏执行pip show pillow卸载重装pip uninstall pillow pip install pillowImageGrab.grab返回全黑图片截图权限问题或显卡加速兼容问题先保存一张截图到本地查看更新显卡驱动或改用pywin32的截图方案颜色始终不匹配窗口被遮挡、坐标偏移太大打印当前像素颜色和期望颜色对比用打印结果校正坐标或增大容差窗口坐标错乱DPI 缩放导致检查 Windows 缩放比例在脚本中调用SetProcessDPIAware()或手动除以缩放系数多线程启动后界面卡死主线程被占用或共享资源没有加锁确认主线程是否一直在 sleep把耗时逻辑全部放入工作线程主线程只做轮询和退出控制task_queue.task_done()报错调用了太多次 task_done检查是否每次get后都调用一次保证配对调用或在 finally 中处理循环 CPU 占用过高sleep 时间太短、循环内做了大量截图观察每轮耗时和任务管理器用一次截屏循环取点方案替代逐个截屏增大 sleep 到 0.1 秒以上运行位置不对脚本直接使用全局像素坐标确认当前是否前置了指定程序窗口获取目标窗口句柄以窗口客户区为基准计算坐标9. 最佳实践与合规使用建议9.1 图色逻辑建议先做配置化把坐标点、目标颜色、容差、延迟时间抽到配置模块不要写死在判断函数里。实测阶段可能要反复调坐标配置化之后直接改一份config.py即可。# config.py 示例 DETECT_POINTS [ {name: 中心区域, pos: (250, 180), rgb: (180, 200, 200), tolerance: 30}, {name: 右侧区域, pos: (310, 180), rgb: (220, 230, 230), tolerance: 30}, ] LOOP_INTERVAL 0.2 # 单位秒 SCREENSHOT_REGION (0, 0, 1920, 1080)9.2 保留测试截图调试阶段建议把检测场景截图保存下来import os from PIL import ImageGrab os.makedirs(screenshots, exist_okTrue) def save_screenshot(tag): img ImageGrab.grab() filename fscreenshots/{tag}_{time.strftime(%Y%m%d_%H%M%S)}.png img.save(filename) print(截图已保存, filename)有了截图后面反复调参时就不用反复启动目标程序窗口。9.3 多线程尽量做“灾备退出”写 while 循环时一定要有一个停止事件不能只依赖CtrlC。可以在脚本根目录放一个标志文件线程检测到标志文件出现后自动退出def should_stop_by_flag(stop_flag_path): return os.path.exists(stop_flag_path)这样脱离终端运行时也能手动控制。9.4 监控中应有操作频率上限任何自动化动作都应该设置合理的延时避免高频点击。高频操作不仅会给本机带来负载也很容易在正常使用场景中造成误操作。把动作频率限制在一定间隔以下是工程上最基本的安全策略。9.5 合法授权与个人隐私如果后续把这类图色能力扩展成人脸识别、内容自动阅读、模拟器自动化、文档批量识别就必须同步检查数据来源的授权问题。处理真人信息、用户账号、涉及他人隐私的素材时只能使用已获得授权的测试数据。涉及游戏界面的自动化要提前确认该软件的用户协议是否允许使用辅助脚本不要把在线竞技类场景作为测试环境。10. 总结与下一步这套教程最有价值的点是把 Python 语言基础里看似简单的条件运算符放到了真实图色判断场景中三元表达式压缩匹配分支、复合比较一次性比较 RGB 三通道、条件运算符配合 sum 函数统计多个命中点。代码量不大但每一步都在解决实际问题。建议先把 5.1 的像素读取封装跑通再改造成 5.2 的多点状态判断最后才加多线程。最容易踩的坑是显示器 DPI 缩放和窗口坐标换算出现颜色不匹配时先打印坐标实际值再调容差和偏移。如果想要进一步扩展可以把这套图色逻辑封装成独立 Python 模块通过 HTTP API 对外提供输入截图和坐标返回检测结果这样就能接入自己的自动化测试平台或批量任务系统。从零基础到多线程这条路本身就是一步一步把 Python 基础语法和 Windows 自动化能力打通的过程。
分享:

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

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