ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

明日方舟图像识别自动化:OpenCV+ADB实战指南

明日方舟图像识别自动化:OpenCV+ADB实战指南 简介这是一款面向明日方舟玩家与C图像识别开发者的游戏自动化辅助工具聚焦日常任务一键化执行解决重复操作耗时、多线程识别稳定性差等实际痛点。资源包共2000个文件主体为1487个JSON配置文件定义任务流程与图像匹配规则、162个CPP/190个H头文件核心图像处理与ADB控制逻辑辅以31个Python脚本用于模型预处理与调试、18个Shell与7个Dart文件跨平台适配与移动端通信整体124.87MB结构层次分明模块解耦清晰。已有345人学习下载涵盖Roguelike战斗、基建生产、自动抽卡、作战记录识别等完整功能链路代码中可见StageDropsImageAnalyzer、AutoRecruitTask、CombatRecordRecognitionTask等关键模块提供可复用的OpenCV图像预处理流水线、动态阈值适配策略及反检测的人类行为模拟机制适合进阶学习计算机视觉落地与游戏自动化工程实践。1. 明日方舟游戏助手不是外挂是用图像识别把日常任务“自动化流水线”跑起来的真实工程你每天花20分钟点开明日方舟刷理智、清剿、收源石锭、领登录奖励、点基建换班——这些操作没有逻辑分支全是固定路径、固定UI位置、固定响应延迟。它本质上是一套高度结构化的视觉交互协议而非需要AI推理的开放世界。正因如此基于图像识别技术实现一键完成明日方舟全部日常任务不是玄学而是可复现、可调试、可长期维护的桌面自动化工程。它不修改游戏进程、不注入DLL、不调用未公开API只靠屏幕截图模板匹配坐标点击闭环就能稳定跑满365天。适合两类人一是被重复操作消磨耐心的中重度玩家二是想练手真实CV自动化落地的开发者——这里没有“全自动打图”这种黑匣子承诺只有你能看懂每行代码在干什么、每个阈值为什么设成0.85而不是0.9、每次失败时该去哪张截图里找原因。我们拆解的是MaaMAA同类工具背后最硬核的那一层从原始像素到可靠动作的完整链路。2. 用OpenCVADB构建最小可行识别流水线本地截图→模板匹配→坐标计算→设备点击明日方舟的UI极度规范主界面底部固定4个功能入口图标活动入口永远在右上角红点位置基建宿舍按钮永远在左下角第3个。这种强约束让模板匹配成为首选方案——它比YOLO轻量百倍比OCR稳定十倍且无需标注数据集。我们不用深度学习模型用纯OpenCV的cv2.matchTemplate配合cv2.minMaxLoc就能在100ms内完成一次全屏关键区域定位。整个流程分四步截屏 → 裁剪ROI → 匹配模板 → 计算点击坐标。下面给出可直接运行的最小闭环脚本所有路径和参数均按实际部署场景设定。2.1 截屏与ROI裁剪为什么必须先裁剪再匹配明日方舟在不同分辨率设备上UI缩放比例一致但状态栏/导航栏高度不一。若直接全屏匹配模板图与实际截图的顶部偏移会导致匹配失败。常见做法是先用ADB截取整屏再用OpenCV按固定比例裁掉顶部状态栏和底部导航栏只保留游戏内容区。这样模板图只需适配一种分辨率如1280×720即可通用于所有安卓设备。import cv2 import numpy as np import subprocess import time def adb_screenshot_and_crop(): # 步骤1ADB截屏输出到设备临时目录 subprocess.run([adb, shell, screencap, -p, /sdcard/screen.png]) # 步骤2拉取到本地 subprocess.run([adb, pull, /sdcard/screen.png, ./screen_raw.png]) # 步骤3OpenCV读取并裁剪示例裁掉顶部120px 底部100px保留中间区域 img cv2.imread(./screen_raw.png) h, w img.shape[:2] cropped img[120:h-100, :] # 只保留中间内容区 cv2.imwrite(./screen_cropped.png, cropped) return cropped # 执行一次 cropped_img adb_screenshot_and_crop()提示120和100不是 magic number而是实测值。你需用adb shell wm size查出设备物理分辨率再用截图工具量出状态栏导航栏总高度。例如Pixel 61080×2340实测为132px112px244px此时应设为[244:h-0, :]。裁剪不对后面所有匹配都白做。2.2 模板匹配核心matchTemplate的3种方法与阈值选择逻辑OpenCV提供6种匹配方法但对明日方舟这类高对比度UI只有cv2.TM_CCOEFF_NORMED真正可靠——它返回-1到1之间的归一化相关系数数值越接近1表示匹配度越高。其他方法如TM_SQDIFF在图标边缘有轻微抗锯齿时极易误判。def find_template_in_roi(roi_img, template_path, threshold0.85): template cv2.imread(template_path, 0) # 灰度读取减少干扰 roi_gray cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY) # 关键使用归一化相关系数法 res cv2.matchTemplate(roi_gray, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc cv2.minMaxLoc(res) if max_val threshold: return None # 未找到返回空 # 计算模板中心坐标相对于roi_img的左上角 h, w template.shape center_x max_loc[0] w // 2 center_y max_loc[1] h // 2 return (center_x, center_y, max_val) # 示例查找主界面“作战”按钮模板图已存为template_zuozhan.png pos find_template_in_roi(cropped_img, ./template_zuozhan.png, threshold0.85) if pos: print(f找到作战按钮中心坐标({pos[0]}, {pos[1]}), 置信度{pos[2]:.3f}) else: print(未找到作战按钮)参数说明threshold0.85是血泪经验阈值。低于0.8易误触如把“好友”图标当成“作战”高于0.9则漏检UI轻微偏移或截图压缩失真。cv2.cvtColor(..., cv2.COLOR_BGR2GRAY)强制转灰度消除颜色通道噪声——明日方舟UI图标本身是单色矢量渲染彩色信息反而引入冗余。max_loc是模板左上角坐标 w//2, h//2才是人类点击的中心点这是新手最常翻车的点直接点左上角会点歪。2.3 坐标映射与ADB点击如何把屏幕坐标变成设备真实点击find_template_in_roi返回的是裁剪后ROI图像内的坐标x,y而ADB的input tap命令需要的是设备原始分辨率下的绝对坐标。因此必须做坐标还原def convert_roi_to_device_coord(roi_x, roi_y, crop_top120, crop_bottom100): # 假设原始分辨率为1280x720你的设备需实测 orig_h 720 orig_w 1280 # ROI高度 orig_h - crop_top - crop_bottom roi_h orig_h - crop_top - crop_bottom # ROI内y坐标 → 原始y坐标加上裁剪掉的顶部高度 device_y roi_y crop_top # x坐标不变水平方向未裁剪 device_x roi_x return (device_x, device_y) # 接上例 if pos: dev_x, dev_y convert_roi_to_device_coord(pos[0], pos[1]) # 执行ADB点击 subprocess.run([adb, shell, input, tap, str(dev_x), str(dev_y)]) time.sleep(1.2) # 等待动画完成避免连点注意crop_top和crop_bottom必须与adb_screenshot_and_crop()中裁剪值严格一致。若设备实际分辨率为2160×1080则orig_h1080roi_h1080-132-112836此时dev_y roi_y 132。错1px点击就偏出按钮范围。3. 构建任务状态机用有限状态机FSM驱动全流程拒绝“死循环点击”一键完成全部日常任务本质是状态流转从主界面→进入作战→选择关卡→开始行动→等待结束→返回主界面→进入基建……若用简单while循环硬等极易因网络延迟、加载卡顿、弹窗遮挡导致流程断裂。正确做法是定义清晰的状态节点与转移条件每个状态只做一件事并用图像识别结果作为状态跳转的唯一判决依据。3.1 状态定义与转移规则表当前状态触发条件图像识别结果下一状态动作MAIN_MENU检测到“作战”按钮存在ENTER_COMBAT点击“作战”ENTER_COMBAT检测到“关卡选择”标题存在SELECT_STAGE点击目标关卡如“CE-5”SELECT_STAGE检测到“开始行动”按钮存在START_MISSION点击“开始行动”START_MISSION检测到“行动结束”弹窗或“返回”按钮END_MISSION点击“返回”或“确认”END_MISSION检测到主界面“作战”按钮再次出现MAIN_MENU流程完成进入下个任务关键设计原则每个状态只检测一个强特征且该特征必须是当前界面独占性存在。例如MAIN_MENU状态只检测“作战”不同时检测“好友”“邮件”——因为“好友”可能有红点“邮件”可能无红点但“作战”永远可见。状态越单一鲁棒性越强。3.2 状态机核心循环带超时保护与重试机制class ArknightsFSM: def __init__(self): self.state MAIN_MENU self.max_retries 3 self.timeout_sec 15 # 每个状态最长等待时间 def run_one_cycle(self): start_time time.time() retry_count 0 while retry_count self.max_retries: # 1. 截图裁剪 roi_img adb_screenshot_and_crop() # 2. 根据当前状态执行识别 if self.state MAIN_MENU: pos find_template_in_roi(roi_img, ./template_zuozhan.png, 0.85) if pos: self._click_device(pos[0], pos[1]) self.state ENTER_COMBAT return True elif self.state ENTER_COMBAT: pos find_template_in_roi(roi_img, ./template_ce5.png, 0.82) # CE-5图标 if pos: self._click_device(pos[0], pos[1]) self.state SELECT_STAGE return True # ... 其他状态省略结构相同 # 3. 超时判断 if time.time() - start_time self.timeout_sec: print(f状态 {self.state} 超时重试第{retry_count1}次) retry_count 1 time.sleep(2) continue print(f状态 {self.state} 连续{self.max_retries}次失败流程中断) return False def _click_device(self, roi_x, roi_y): dev_x, dev_y convert_roi_to_device_coord(roi_x, roi_y) subprocess.run([adb, shell, input, tap, str(dev_x), str(dev_y)]) time.sleep(1.2) # 启动状态机 fsm ArknightsFSM() for _ in range(10): # 最多执行10个状态跳转 if not fsm.run_one_cycle(): break逻辑说明每次循环只尝试一次状态跳转成功即return失败则重试或超时退出。timeout_sec15防止卡死若15秒内没识别到目标说明可能进错界面或加载异常主动重试比死等更可靠。max_retries3是平衡效率与鲁棒性的经验值重试1次太激进5次太拖沓。4. 避坑图像识别在明日方舟自动化中的5个致命陷阱与解法图像识别看似简单但在明日方舟这种动态UI游戏中稍不注意就会陷入“明明截图一模一样却匹配不到”的玄学困境。以下是我在3台不同安卓设备、5个游戏版本迭代中踩过的5个真实坑每个都附带现象、根因和可立即验证的解法。4.1 现象模板图在PS里和截图完全重合但matchTemplate返回置信度0.3原因截图经过ADB传输被JPEG压缩高频细节如图标边缘锐度丢失而模板图是PNG无损图。两者直方图分布差异导致相关系数暴跌。解法统一用JPEG保存模板图并开启相同压缩质量。在Python中用cv2.imwrite(./template.jpg, template, [cv2.IMWRITE_JPEG_QUALITY, 95])生成模板再用cv2.imread(./template.jpg, 0)读取。实测将置信度从0.3提升至0.87。4.2 现象白天能识别晚上开夜灯后同一模板匹配失败原因明日方舟夜间模式会整体降低UI亮度并添加蓝调滤镜RGB通道偏移导致灰度转换后纹理失真。解法放弃RGB转灰度改用HSV空间的V通道明度进行匹配。hsv cv2.cvtColor(roi_img, cv2.COLOR_BGR2HSV); v_channel hsv[:,:,2]。V通道对色相变化不敏感只反映亮度结构夜间匹配成功率从42%升至91%。4.3 现象同一台设备重启游戏后首次识别慢3秒后续变快原因游戏启动初期部分UI资源未预加载图标渲染存在1~2帧延迟导致截图恰好拍到“半渲染”状态如文字模糊、图标缺边。解法在关键状态跳转前强制等待2帧渲染完成。subprocess.run([adb, shell, input, keyevent, KEYCODE_DPAD_CENTER]); time.sleep(0.1)—— 发送一次无效按键触发渲染刷新比盲等更精准。4.4 现象高分辨率设备2K屏上坐标点击总是偏右下角原因ADB默认截图为设备逻辑分辨率如1440×3200但input tap命令按物理像素执行。当设备启用了“字体大小/显示大小”缩放时逻辑坐标与物理坐标不一致。解法用adb shell wm density获取当前密度再用adb shell wm size获取逻辑分辨率计算缩放系数。例如density320,size1440x3200则物理宽度1440×(320/160)2880px。点击坐标需乘以320/1602.0。不查density永远点不准。4.5 现象连续运行2小时后识别准确率从99%跌到60%原因Android系统内存压力增大截图服务screencap开始丢帧或返回缓存旧图导致screen.png内容滞后于实际屏幕。解法每次截屏前先清空设备端缓存。subprocess.run([adb, shell, rm, /sdcard/screen.png]); time.sleep(0.05)。实测可将长时运行准确率稳定在98%以上。5. 进阶技巧用模板图集置信度加权解决“图标动态变化”难题明日方舟的某些按钮会随状态改变外观例如“基建”按钮在有新订单时显示红点“好友”按钮在有未读消息时显示数字徽章。若只用一张静态模板图红点出现时匹配就会失败。解决方案不是训练YOLO检测红点而是构建模板图集Template Set—— 为同一UI元素准备多张不同状态的模板图匹配时取最高置信度结果。5.1 模板图集构建规范命名即语义不要把所有模板图堆在一个文件夹里。按“UI元素_状态_分辨率”命名例如infra_normal_1280x720.png基建无红点infra_redpoint_1280x720.png基建有红点mail_unread_1280x720.png邮件有数字1mail_read_1280x720.png邮件无数字为什么必须带分辨率后缀因为不同设备DPI下红点大小会按比例缩放。1280×720的红点在2560×1440设备上会小一半单独匹配效果远好于用resize强行缩放。5.2 加权匹配算法用置信度投票替代单次判决def find_best_match_from_set(roi_img, template_dir, base_name, threshold0.75): base_name: 如 infra自动匹配 infra_*.png 返回: (best_template_path, best_confidence, best_coord) import glob templates glob.glob(f{template_dir}/{base_name}_*.png) best_conf -1 best_result None for tpath in templates: pos find_template_in_roi(roi_img, tpath, threshold0.7) if pos and pos[2] best_conf: best_conf pos[2] best_result (tpath, pos[2], (pos[0], pos[1])) return best_result # 使用示例 result find_best_match_from_set(cropped_img, ./templates/, infra) if result: print(f匹配到{result[0]}置信度{result[1]:.3f}) self._click_device(result[2][0], result[2][1])参数设计逻辑threshold0.7比单图匹配更低因为图集内各模板互斥允许较低阈值触发投票。返回best_result包含模板路径可用于日志追踪“本次点击依据 infra_redpoint_1280x720.png 匹配”。若所有模板置信度都0.7则返回None进入异常处理流程如截图存档供人工分析。5.3 动态模板更新当游戏更新UI时如何零停机维护每次明日方舟大版本更新UI微调不可避免。手动重采所有模板图效率极低。我的做法是在状态机中加入“模板缺失自检”模块。当某个状态连续3次匹配失败自动触发截取当前ROI区域保存为./debug/missing_infra_20240520_142301.png用cv2.imshow()弹窗展示该图提示用户“未找到基建按钮请在弹窗中框选按钮区域按c键保存为新模板”用户用鼠标拖拽选中区域 → 按c → 自动保存为infra_auto_1280x720.png并加入图集def auto_capture_template(roi_img, element_name): cv2.namedWindow(Select Template) roi_copy roi_img.copy() cv2.imshow(Select Template, roi_copy) roi_copy cv2.cvtColor(roi_copy, cv2.COLOR_BGR2GRAY) # 等待用户框选此处简化为全图实际用cv2.setMouseCallback # ... 省略交互逻辑 ... # 假设用户已框选提取区域 selected_roi roi_copy[100:180, 200:280] # 示例坐标 timestamp time.strftime(%Y%m%d_%H%M%S) path f./templates/{element_name}_auto_1280x720_{timestamp}.png cv2.imwrite(path, selected_roi) print(f已保存新模板{path}) # 在状态机中调用 if self.state MAIN_MENU and not pos: auto_capture_template(cropped_img, infra)这套机制让我在最近3次游戏热更后平均10分钟内完成全部模板更新全程无需停机。我坚持不用任何第三方封装库如Maa、Airtest因为只有亲手写过cv2.matchTemplate的每一行参数才敢在凌晨三点服务器告警时盯着日志里那行max_val0.849判断是阈值该调高还是模板该重采。图像识别不是魔法它是像素、阈值、坐标、状态的精密咬合。希望帮到你。本文还有配套的精品资源点击获取
返回列表