首页 / 资讯中心 / 文章详情

基于OpenCV模板匹配的本地截图核对与进度统计工具

基于OpenCV模板匹配的本地截图核对与进度统计工具 ★ FEATURED ARTICLE
这类“原神代肝一单枫丹水神瞳无前置收集已完成感谢老板信任”的接单文案在社交平台和二手交易区几乎每天都能刷到。表面上看它是一笔游戏服务交易但如果拆开来看它本质上是一种很典型的重复性工作外包接单方需要按照地图区域逐个核对点位记录哪些已经完成、哪些还没有做避免遗漏和重复最后给出一份让委托人放心的“进度凭证”。这套流程放到工程视角里其实对应三件事点位数据的结构化管理、截图信息的自动核对以及进度的批量统计与导出。这三件事恰好是很多做本地小工具的人日常都在写的内容。不过有个边界必须先说清楚。本文不会教你如何自动化操作游戏客户端也不讨论账号共享、代登录、脚本注入、内存读写、反检测绕过这类内容。账号外借和第三方代操作所带来的封号、盗号、支付绑定泄露、实名信息被滥用等风险远比省下来的那点时间成本高得多。本文要讲的是另一条路把“收集进度记录”这件事放回本地用自己的截图、自己的数据做一个纯离线的核对与统计工具。整个过程不接触游戏进程、不读取客户端内存、不上传任何数据、不共享账号。这个思路的适用范围其实比游戏广得多。任何“大量截图 固定目标 需要人工核对”的场景都能套用比如仓库盘点照片核对、设备巡检照片归档、财务报表截图归档、纸质单据拍照入库。核心逻辑是一样的先定义目标长什么样再从截图里找它找到之后更新一条结构化记录最后按维度出统计。本文会给出完整的实现路径包括数据结构设计、截图像素级比对、进度看板接口、批量任务调度、资源占用观察和排查清单代码可以直接改一改用到自己的业务里。如果你只是想知道“这东西值不值得自己写一个”可以先看一个判断标准当你需要核对的条目超过 50 条、并且需要反复核对第二遍第三遍的时候手工表格就已经不可靠了这时候上工具才有收益如果只是十几条用记事本记一下反而更快。下面的内容按“先能跑起来再谈优化”的顺序组织。1. 核心能力速览先给一张速览表把本文这套本地核对工具的能力边界列清楚。需要说明的是表中不涉及具体的游戏版本号、点位总数、地图坐标这类会随版本变化的数据这些必须以你自己当前的数据源为准任何写死的数字都会很快过期。能力项说明项目类型本地离线的截图核对与进度统计工具Python核心依赖Python 3.9、OpenCV、NumPy、Flask、SQLite主要功能截图目标匹配、点位状态标记、按区域统计进度、批量扫描、结果导出推荐硬件普通办公本即可纯 CPU 运行不强制要求独立显卡显存占用本方案不涉及 GPU 推理显存占用为 0若替换为深度学习检测模型需按模型版本实测内存占用与单张截图分辨率和批量并发数正相关需按实际环境测试支持平台Windows / Linux / macOS只要有 Python 环境即可启动方式命令行启动 Flask 服务浏览器或脚本访问本地端口是否支持 API支持提供本地 HTTP 接口是否支持批量任务支持按目录遍历 失败重试数据存储单文件 SQLite便于备份和迁移适合场景个人进度记录、截图归档核对、巡检照片比对、批量截图信息提取不适合场景任何需要读写游戏客户端、自动化操作、账号代管的需求这张表里最值得强调的一点是“不强制要求独立显卡”。很多人一看到“识别”“匹配”就默认要上 GPU其实模板匹配这类传统计算机视觉方法在 CPU 上跑得飞快一张 1080P 截图做一次全图匹配通常在几十毫秒量级具体取决于模板尺寸和截图分辨率。只有在目标尺度变化大、光照变化剧烈、需要语义级识别的时候才需要考虑换模型那时候才谈得上显存。2. 适用场景与使用边界2.1 这套工具真正适合谁第一类是“自己玩、自己想记清楚”的玩家。地图上的收集物动辄上百个靠脑子记必然会漏靠 Excel 手填又容易错行。用一个本地工具把截图丢进去自动识别、自动打勾成本很低而且数据完全在自己机器上不用担心隐私。第二类是做数据整理的人手里有一批结构相似的截图需要把里面的固定元素找出来并计数比如盘点照片里的标签、巡检照片里的仪表、报表截图里的固定表头。第三类是想练手计算机视觉工程化的人模板匹配是入门成本最低的一条路径从读图、匹配、去重、落库到出接口是一条完整的链路。这三类场景有个共同点数据是自己的处理是本地的结果是给自己看的。只要满足这三条工具就有价值。反过来说如果你的需求是“让别人替我做”那就不是技术问题而是交易问题本文不涉及。2.2 明确不建议碰的几件事账号共享和代登录是第一条红线。把账号密码交给陌生人等于把实名信息、支付方式、社交关系链全部暴露出去出了问题很难追责。第二条是任何形式的客户端读写包括内存扫描、进程注入、修改本地文件、模拟输入。这类操作轻则触发风控重则直接封号而且从技术上讲它和“本地处理自己的截图”完全是两回事不要混为一谈。第三条是把工具做成对外服务一旦对外提供“帮你查进度”的能力就会牵扯到数据归属、隐私合规、以及平台规则的问题收益和风险完全不成比例。本文的实现严格限制在本地只读你自己放在./inputs目录下的图片文件只写你自己机器上的 SQLite 数据库服务只监听127.0.0.1不对外暴露端口。这几点在后面的启动命令里会明确体现不要随意改成0.0.0.0。3. 环境准备与前置条件3.1 软件与硬件清单软件方面Python 版本建议 3.9 及以上太老的版本在类型标注和部分库的兼容性上会踩坑。依赖库主要是四个opencv-python负责图像读取和模板匹配numpy负责数组运算flask提供本地 HTTP 接口sqlite3是 Python 标准库自带不需要额外安装。如果要做结果导出成 Excel再加一个openpyxl就够了。不建议一上来就装一堆深度学习框架那会把环境搞得很重而且对这个方案没有帮助。硬件方面没有特殊要求。截图分辨率越高、单批处理的图片越多内存占用越大这是唯一的变量。磁盘空间主要看你的截图量一张 1080P 的 PNG 大概 1 到 3MB几千张就是几个 GB建议单独留一个盘位或者目录。端口方面默认用 7860如果这个端口已经被别的服务占了换个 7861、8080 都行后面会讲怎么改。3.2 目录结构规划动手写代码之前先把目录规划好这一步偷懒后面会很痛苦。核心原则是“输入、模板、数据、输出、日志”五个目录分开任何一个都不要混在一起。输入目录只放原始截图不做任何修改模板目录只放目标小图数据目录只放数据库文件输出目录放导出结果日志目录放失败记录。这样后面做批量任务的时候清理和重跑都很简单。# 推荐的项目目录结构目录名可以按自己习惯改 game-progress-tool/ ├── app.py # Flask 服务入口 ├── matcher.py # 图像匹配逻辑 ├── init_db.py # 数据库初始化脚本 ├── config.json # 运行参数配置 ├── inputs/ # 原始截图只读不改 ├── templates/ # 目标模板小图 ├── data/ │ └── progress.db # SQLite 数据库 ├── outputs/ # 导出的统计结果 └── logs/ # 失败记录与运行日志模板图这一块要多说一句。模板图的质量直接决定匹配准确率理想情况是从你自己的截图里裁剪出来的一小块目标区域尺寸在 32×32 到 96×96 像素之间比较合适。太小容易误匹配太大则对缩放和遮挡很敏感。裁剪的时候尽量选背景干净、目标完整、没有半透明遮罩的那一帧这样阈值好调很多。4. 安装部署与启动方式4.1 依赖安装建议用虚拟环境隔离避免和系统里其他项目的依赖打架。下面这条命令在三个平台上都能用Windows 下把source换成对应的激活脚本即可。# 创建并激活虚拟环境 python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # 安装最小依赖集 pip install --upgrade pip pip install opencv-python numpy flask openpyxl安装完成后先做个自检确认 OpenCV 能正常导入并且版本符合预期。这一步看起来多余但实际上能提前发现“装了两个 OpenCV 版本互相覆盖”这类经典问题。import cv2 import numpy as np import flask print(OpenCV:, cv2.__version__) print(NumPy:, np.__version__) print(Flask:, flask.__version__)如果cv2.__version__报错说找不到模块八成是装了opencv-python-headless和opencv-python两个包冲突了先pip uninstall干净再重装一次。4.2 数据库初始化数据结构是整个工具的地基。这里设计两张表就够了point表存点位定义和完成状态shot表存每张截图的处理记录。点位表用point_code做唯一约束保证同一个点位不会被重复插入区域字段单独建索引后面按区域统计会快很多。时间字段统一用 UTC 字符串避免时区问题。import sqlite3 from pathlib import Path DB Path(./data/progress.db) DB.parent.mkdir(parentsTrue, exist_okTrue) SCHEMA CREATE TABLE IF NOT EXISTS point ( id INTEGER PRIMARY KEY AUTOINCREMENT, region TEXT NOT NULL, point_code TEXT NOT NULL UNIQUE, x REAL, y REAL, done INTEGER NOT NULL DEFAULT 0, updated_at TEXT ); CREATE TABLE IF NOT EXISTS shot ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_name TEXT NOT NULL, region TEXT, hit_count INTEGER DEFAULT 0, created_at TEXT ); CREATE INDEX IF NOT EXISTS idx_point_region ON point(region); CREATE INDEX IF NOT EXISTS idx_shot_region ON shot(region); with sqlite3.connect(DB) as conn: conn.executescript(SCHEMA) print(数据库初始化完成:, DB.resolve())表结构定好之后先把点位数据灌进去。灌数据这一步建议单独写个脚本从你自己的记录文件或者手动整理的表里读不要硬编码在业务代码里。这样换区域、补点位的时候只需要重跑导入脚本。4.3 服务启动服务启动就一条命令重点在host参数必须是127.0.0.1。很多人图方便写成0.0.0.0结果整个局域网都能访问本地工具变成了公开服务这就完全没必要了。端口选 7860 是因为它不容易和常见服务冲突如果你的机器上这个端口被占了改成 7861 或者 8081 都可以。# 启动本地服务仅监听回环地址 python app.py # 如果端口冲突用环境变量覆盖 PORT7861 python app.py启动之后浏览器访问http://127.0.0.1:7860/api/progress能看到一个 JSON 数组就说明服务起来了。如果浏览器转圈半天没反应先看终端有没有报错再看端口有没有被占具体排查在第八节。5. 功能测试与效果验证功能测试不要一上来就丢几百张图进去跑那样出了问题根本不知道是哪一环。正确的顺序是单图匹配 → 阈值调优 → 单批扫描 → 全量扫描 → 结果导出。每一环都确认通过再进下一步。5.1 截图目标匹配测试这是最核心的一环代码要处理两个问题一是找到所有匹配位置二是在阈值附近会有大量重叠框必须做去重。下面的实现用TM_CCOEFF_NORMED归一化相关系数取值范围在 -1 到 1 之间越接近 1 越相似去重时按得分从高到低排序保留互相距离超过阈值的那些点。import cv2 import numpy as np def match_icon(screen_path: str, template_path: str, threshold: float 0.85): screen cv2.imread(screen_path, cv2.IMREAD_COLOR) template cv2.imread(template_path, cv2.IMREAD_COLOR) if screen is None or template is None: raise FileNotFoundError(截图或模板读取失败检查路径与文件名) result cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) ys, xs np.where(result threshold) hits [ {x: int(x), y: int(y), score: round(float(result[y, x]), 4)} for x, y in zip(xs, ys) ] return dedup(hits) def dedup(hits, min_dist: int 12): kept [] for h in sorted(hits, keylambda i: i[score], reverseTrue): if all( abs(h[x] - k[x]) min_dist or abs(h[y] - k[y]) min_dist for k in kept ): kept.append(h) return kept if __name__ __main__: res match_icon(./inputs/test_01.png, ./templates/icon.png, 0.85) print(f命中数量: {len(res)}) for item in res[:10]: print(item)测试的时候先拿一张你确认过“大概有几个目标”的截图跑看命中数量对不对。如果命中数量明显偏多说明阈值太低把 0.85 往上调到 0.90 试试如果明显偏少说明阈值太高或者模板图裁剪得不干净先换模板再调阈值。这一步的耐心程度决定了后面的返工量。5.2 不同分辨率与缩放的稳定性测试模板匹配对缩放非常敏感这是它最大的短板。同一张模板图在 1080P 截图里能匹配上换到 2K 截图里可能一个都找不到因为目标的绝对像素尺寸变了。解决办法有两个一是把截图统一缩放到固定宽度再匹配二是对模板做多尺度缩放逐个尺度试。前者成本低后者准一点但慢。建议先做统一缩放把截图都 resize 到宽度 1920模板图按同样比例裁剪。实测下来这条路能覆盖大多数情况而且处理速度稳定。如果某些截图本身比例特殊比如带黑边的、带额外 UI 层的就单独归到一个异常目录里人工处理不要为了几个特例把整体逻辑搞复杂。判断这一环是否通过的标准很简单随机抽 20 张不同分辨率、不同场景的截图人工标一遍正确答案再和工具结果对比准确率能到 90% 以上就算可用。剩下的误差人工复核一遍成本可以接受。5.3 进度统计与导出测试匹配跑通之后要让结果真正落到业务上。最直接的方式是按区域统计总点位、已完成数、完成率。这个查询在 SQLite 里一条GROUP BY就够注意SUM(done)在没有任何已完成记录时会返回NULL代码里要做空值兜底。import sqlite3 def progress_by_region(db_path: str, region: str ): sql ( SELECT region, COUNT(*) AS total, SUM(done) AS done FROM point WHERE region LIKE ? GROUP BY region ORDER BY region ) with sqlite3.connect(db_path) as conn: rows conn.execute(sql, (f%{region}%,)).fetchall() return [ { region: r[0], total: r[1], done: r[2] or 0, percent: round((r[2] or 0) / r[1] * 100, 2) if r[1] else 0.0, } for r in rows ] if __name__ __main__: for item in progress_by_region(./data/progress.db, 枫丹): print(item)导出成 Excel 的时候建议把“未完成清单”单独放一个 sheet。核对进度的人真正关心的是“还差哪些”而不是“已经做了多少”这一点在实际使用中差别很大。导出文件按日期命名放到outputs目录下避免覆盖上一版。5.4 单批扫描的稳定性验证单图测试通过之后做一次 50 到 100 张的小批量扫。这一环的重点不是准确率而是稳定性看进程会不会崩、内存会不会涨、失败的图有没有被记录下来。跑完之后检查logs/failed.json如果失败记录是空的并且总耗时在你可接受的范围内这一环就算过了。如果发现某几张图稳定失败先把它们单独拎出来看大概率是分辨率异常或者截图不完整不是代码逻辑问题。6. 接口 API 与批量任务6.1 本地接口服务实现接口这一块用 Flask 写最小实现就够了三个路由一个查进度、一个标记完成、一个扫描图片。参数校验必须做尤其是image路径绝对不要直接拼接用户输入去读文件至少要判断路径是否在允许的目录范围内。虽然这是本地服务但基本的输入校验习惯要有。import sqlite3 from flask import Flask, request, jsonify from matcher import match_icon app Flask(__name__) DB ./data/progress.db ALLOWED_DIR ./inputs def query(sql, args()): with sqlite3.connect(DB) as conn: return conn.execute(sql, args).fetchall() app.route(/api/progress, methods[GET]) def api_progress(): region request.args.get(region, ) sql ( SELECT region, COUNT(*), SUM(done) FROM point WHERE region LIKE ? GROUP BY region ) rows query(sql, (f%{region}%,)) return jsonify([ {region: r[0], total: r[1], done: r[2] or 0} for r in rows ]) app.route(/api/point/code/done, methods[POST]) def api_mark_done(code): with sqlite3.connect(DB) as conn: cur conn.execute( UPDATE point SET done 1, updated_at datetime(now) WHERE point_code ?, (code,), ) conn.commit() if cur.rowcount 0: return jsonify({ok: False, msg: point_code 不存在}), 404 return jsonify({ok: True, point_code: code}) app.route(/api/scan, methods[POST]) def api_scan(): payload request.get_json(forceTrue, silentTrue) or {} image payload.get(image, ) threshold float(payload.get(threshold, 0.85)) if not image.startswith(ALLOWED_DIR): return jsonify({ok: False, msg: 路径不在允许目录内}), 400 try: hits match_icon(image, ./templates/icon.png, threshold) except FileNotFoundError as e: return jsonify({ok: False, msg: str(e)}), 400 return jsonify({ok: True, hit_count: len(hits), hits: hits[:20]}) if __name__ __main__: app.run(host127.0.0.1, port7860, debugFalse)接口起来之后先用curl验证一遍别急着写脚本。很多时候是接口本身没通脚本只是替接口背了锅。# 查询指定区域的进度 curl http://127.0.0.1:7860/api/progress?region枫丹 # 标记某个点位为已完成 curl -X POST http://127.0.0.1:7860/api/point/FD-0127/done # 扫描单张截图 curl -X POST http://127.0.0.1:7860/api/scan \ -H Content-Type: application/json \ -d {image: ./inputs/test_01.png, threshold: 0.85}6.2 批量任务脚本与失败重试批量任务的核心不是“跑完”而是“跑完之后还知道哪些失败了”。下面这个脚本做了三件事遍历输入目录、逐张调用接口、把失败的文件名和原因写进日志。加了sleep是为了避免短时间打满本地服务虽然本地调用压力不大但保留这个习惯没坏处。重试逻辑放在外层失败的图单独放一个列表跑完之后可以对着列表二次重跑。import json import time from pathlib import Path import requests BASE http://127.0.0.1:7860 INPUT_DIR Path(./inputs) LOG_FILE Path(./logs/failed.json) LOG_FILE.parent.mkdir(parentsTrue, exist_okTrue) images sorted(INPUT_DIR.glob(*.png)) failed [] for idx, img in enumerate(images, 1): for attempt in range(1, 3): try: resp requests.post( f{BASE}/api/scan, json{image: f./inputs/{img.name}, threshold: 0.85}, timeout30, ) resp.raise_for_status() data resp.json() print(f[{idx}/{len(images)}] {img.name} 命中 {data[hit_count]} 个) break except Exception as e: if attempt 2: failed.append({file: img.name, error: str(e)}) print(f[{idx}/{len(images)}] {img.name} 最终失败: {e}) else: time.sleep(1) time.sleep(0.05) LOG_FILE.write_text( json.dumps(failed, ensure_asciiFalse, indent2), encodingutf-8 ) print(f完成失败 {len(failed)} 个详情见 {LOG_FILE})6.3 配置文件管理参数不要散在代码里统一放一个 JSON 配置文件改参数的时候不用动代码也方便以后做多套配置切环境。下面这份配置基本覆盖了日常需要调整的项batch_size在本地服务场景下建议保持 1并发上去之后内存和日志会变乱收益不明显。{ input_dir: ./inputs, output_dir: ./outputs, template_dir: ./templates, db_path: ./data/progress.db, region: 枫丹, threshold: 0.85, resize_width: 1920, batch_size: 1, retry: 2, timeout: 30, log_dir: ./logs }7. 资源占用与性能观察这一块很多人会跳过但实际用起来最容易出问题的就是这里。这套方案是纯 CPU 的没有显存概念所以要观察的是 CPU 占用和内存占用。观察方式很简单Linux 下用ps看常驻内存和 CPU 百分比Windows 下用 PowerShell 看工作集大小。# Linux查看 Python 服务的 CPU 与内存占用 ps -o pid,%cpu,%mem,rss,cmd -p $(pgrep -f app.py) # 持续观察内存变化用于判断是否有泄漏 watch -n 2 ps -o rss -p $(pgrep -f app.py)# Windows PowerShell查看 Python 进程资源占用 Get-Process python | Select-Object Id, CPU, WorkingSet64, StartTime影响性能的变量主要有四个。第一是截图分辨率2K 图的像素量是 1080P 的两倍多匹配耗时基本线性增长。第二是模板尺寸模板越大滑动窗口的计算量越大。第三是批量并发数并发高了内存会翻倍而且日志交错反而不好排查。第四是磁盘 IO如果截图放在机械硬盘或者网络盘上读图时间可能比计算时间还长。降低资源占用的实用做法有三个。一是先把截图统一 resize 到固定宽度这一步能砍掉很大一部分计算量二是按区域分批跑跑完一批清一次内存不要让进程一直持有大数组三是及时释放OpenCV 读进来的numpy数组在函数返回后正常会被回收但如果你在循环里把所有结果都塞进一个大列表内存就上去了该落库的落库该写的写文件。还有一个容易被忽略的点进程残留。反复启动服务的时候如果上一次的进程没退干净新进程会因为端口被占而启动失败但错误日志可能被刷过去了。养成习惯启动前先确认端口空闲Windows 下用netstat -ano | findstr :7860Linux 下用lsof -i:7860确认没进程占用再启动。8. 常见问题与排查方法排查的核心原则是分层定位先确认进程活着再确认端口通再确认接口返回正常最后才怀疑算法逻辑。绝大多数“跑不通”其实卡在前两层只是大家习惯直接怀疑代码。问题现象可能原因排查方式解决方案启动报端口占用上次进程未退出或端口被其他服务占用lsof -i:7860或netstat -ano结束残留进程或换端口浏览器访问无响应服务未启动或监听地址不对看终端是否有启动日志确认host为127.0.0.1且进程存活cv2导入失败装了多个 OpenCV 包冲突pip listgrep opencv读图返回None路径错误或文件损坏打印绝对路径用看图软件打开修正路径或重新导出截图匹配结果全为空阈值过高或模板不匹配把阈值降到 0.5 看是否有命中重新裁剪模板图统一缩放尺寸匹配结果大量重复未做重叠框去重打印命中坐标分布加距离去重调min_dist接口返回 400参数缺失或路径不在允许目录看返回的msg字段检查请求体 JSON 和路径前缀接口返回 404point_code不存在查数据库确认编码先导入点位数据再标记批量任务中途卡住某张图超大或服务已挂看最后输出的文件名跳过异常图重启服务重跑批量任务失败率高截图分辨率差异大抽样对比命中结果统一 resize 宽度后重跑内存持续上涨结果未落库一直在内存累积观察rss随时间变化分批处理处理完及时写库统计数为 0done字段全为 NULL直接查表看done值代码里做or 0空值兜底这张表里最值得提前预防的是“匹配结果全为空”和“结果大量重复”这两条它们出现频率最高而且都和模板图质量强相关。判断方法很直接把阈值临时降到 0.3 跑一次如果能命中一堆乱七八糟的位置说明模板本身有辨识度问题如果降到 0.3 还是空的那就是尺寸对不上先查缩放比例。9. 最佳实践与使用建议第一永远先用小样本验证。拿 5 张图跑通全流程比拿 500 张图跑崩了再回头找问题要省时间得多。第二保留一套最小可运行配置也就是本文这套一个模板、一张表、三个接口。不要一上来就搞多模板、多区域、多数据源复杂度上去之后调试成本是指数级的。第三目录管理要严格。输入目录只读不改这一点很重要很多人为了省事直接原地覆盖后面想回滚就回不去了。数据库文件建议定期复制一份到别的目录SQLite 单文件备份成本极低但真丢了会很麻烦。第四批量任务必须有日志和失败重跑机制没有日志的批量任务等于黑盒出了问题只能全量重跑。第五合规和隐私这条线要一直记住。这套工具处理的都是本机截图不涉及上传、不涉及账号、不涉及第三方数据。如果哪天你想把它改成“帮别人处理数据”的服务先想清楚数据授权、隐私保护、平台规则这三件事不要因为技术上能实现就直接做。涉及他人账号、他人截图、他人信息的内容一律先拿到明确授权再谈实现。第六结果一定要人工复核一遍再交付。工具的作用是把 90% 的重复工作自动化剩下 10% 的判断仍然需要人。指望工具 100% 准确最后返工的成本反而更高。核对的时候优先看“未完成清单”这个列表越短交付质量越高。10. 总结与下一步回到最开始那条接单文案。它之所以能成为一个生意是因为“重复核对”这件事本身确实费时间而且容易出错。但解决这个问题的路径不止一条一条是把账号交出去承担封号、盗号、隐私泄露的全部风险另一条是把核对这件事本身工具化让重复劳动变成一次性的脚本开发。后者前期要多花几个小时写代码但之后每接一批新数据成本几乎是零而且数据始终在自己手里。如果你打算动手建议优先验证两件事一是模板匹配在你自己的截图上准确率能不能到 90%这决定了这条路走不走得通二是把数据库和接口跑起来确认“扫一张图 → 更新一条记录 → 查一次进度”这条链路是通的。这两件事验证完剩下的都是工程量不涉及技术不确定性。最容易踩的坑有三个模板图裁剪不干净导致误匹配、截图分辨率不统一导致命中率骤降、批量任务没有日志导致失败无法定位。这三个坑对应三个动作模板从自己截图里裁、处理前统一缩放、批量必须写失败日志。把这三件事做扎实这套工具就能稳定用起来。后续如果还想往前走可以往两个方向扩一是把模板匹配换成更鲁棒的检测模型代价是需要 GPU 和显存得先测清楚自己机器的上限二是把统计结果做成可视化的进度看板按区域、按批次对比完成率变化。这两个方向的代码量都不大但收益取决于你的实际使用频率。建议先用最小版本跑一段时间确认自己真的会天天用再考虑加功能。
阅读完成 · 觉得有帮助?
咨询建站