arrowout/arrowout_java_研发文档.md
2026-04-11 19:43:52 +08:00

223 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Arrow Out 自动求解研发文档Java输入为图片
## 1. 目标与范围
- 输入:一张关卡截图(包含 UI、背景、彩色箭头线条与箭头头部
- 输出:
- `List<Arrow>`:每个箭头的 `id`、离散坐标 `(gx, gy)`、方向 `dir(8方向)`
- `List<Integer>`:一条可消除序列(按 `id` 返回)。
- 不在范围:
- 自动点击/自动操作游戏。
- 复杂的通用 OCR/UI 识别;本方案仅提取箭头(位置+方向)。
## 2. 规则抽象(用于建模与求解)
### 2.1 箭头属性
每个箭头只保留两类信息:
- 位置:`(x, y)`(工程上最终用离散坐标 `(gx, gy)`)。
- 朝向8 方向之一(`N, NE, E, SE, S, SW, W, NW`)。
> 颜色、线条粗细、形状风格不影响解题,忽略。
### 2.2 “可消除”判定
对任意箭头 `A`
-`A` 的箭头尖端tip沿其朝向作射线。
- 若该射线上存在任意其他箭头(不论颜色与距离),则 `A` **不可消除**
- 若该射线直到边界都没有遇到任何箭头,则 `A` **可消除**
消除后会改变其他箭头的判定,因为阻挡关系会变化。
## 3. 总体架构
分两层:
- `Vision`(识别层):图片 → 箭头列表(像素坐标 tip + 方向)→ 离散化网格坐标 `(gx, gy)`
- `Solver`(求解层):箭头列表 `(gx,gy,dir)` → 消除序列。
接口建议:
- `VisionExtractor.extract(Mat screenshot) -> List<Arrow>`
- `ArrowOutSolver.solve(List<Arrow> arrows) -> List<Integer>`
- `solveFromImage(String imagePath) -> List<Integer>`(端到端封装)
## 4. Vision从图片提取 `(x,y,dir)`OpenCV Java
### 4.1 坐标系约定
- OpenCV 图像坐标:`x` 向右增大,`y` 向下增大。
- 求解模型可以沿用该坐标系,也可以转换为数学坐标系(`y` 向上增大)。
- **要求:方向枚举与坐标系必须一致**。
建议:内部统一用 OpenCV 坐标系方向定义也按“y 向下”为正。
### 4.2 预处理:裁剪棋盘 ROI去 UI
目的:减少误检(顶部文字/按钮/底部工具栏等)。
推荐方案(鲁棒):利用“彩色线条像素”的饱和度/亮度特征定位主区域。
1) 转 HSV`cvtColor(bgr, hsv, COLOR_BGR2HSV)`
2) 前景掩码:`mask = (S > s0) && (V > v0)`(只抓彩色高饱和区域)
3) 形态学:`close` 填洞、`open` 去噪
4) 找最大连通区域或最大外接矩形 contour取其外接矩形为 `boardRoi`
输出:`Mat board`(棋盘区域图像)。
### 4.3 二值化:生成箭头像素掩码
目标:得到只包含箭头线条/箭头头部的 `arrowMask`
-`board` 上按 HSV 阈值生成二值图(仍然只依赖 S/V
- `arrowMask = (S > s0) && (V > v0)`
-`arrowMask``morphology close/open`,让箭头头部轮廓更连续。
输出:`Mat arrowMask`0/255
### 4.4 检测箭头头部:定位 tip 与方向
核心思想:箭头头部通常呈“尖角三角形/箭头尖”,几何特征稳定;方向可由“尖点指向”得到。
步骤:
1) `findContours(arrowMask, ...)` 获取轮廓集合
2) 对每个 contour 过滤:
- 面积在 `[amin, amax]`(避免把长线条当头部,或把噪声当头部)
- `approxPolyDP` 多边形近似
3) 取 tip尖点
- 对多边形各顶点计算内角,选择 **内角最小** 的顶点作为 `tip`
4) 取中心 `center`
-`moments` 计算轮廓质心
5) 方向向量
- `vec = tip - center`
- `angle = atan2(vec.y, vec.x)`(按 OpenCV 坐标系)
6) 角度量化到 8 方向
- 每 45° 一档;误差超过 22.5° 的标记为“不确定”进入兜底
7) 去重/聚类
-`tip` 的欧式距离把相近候选聚成一簇(阈值 5~15 像素)
- 每簇保留面积最大的候选
输出(像素级):`List<ArrowPixel>{ tipX, tipY, dir }`
### 4.5 离散化:从像素坐标到网格坐标 `(gx, gy)`
求解层需要稳定的“同一行/列/对角线”关系,建议把像素 tip 坐标离散化到网格。
推荐做法:对 `x``y` 分别做 1D 容差聚类。
1) 收集全部 `tipX`,排序
2) 从左到右扫描:相邻差值 < `tx` 的归为同一簇
3) 每簇中心取均值 `cx`簇序号即 `gx`
4) `tipY` 同理得到 `gy`
参数建议
- `tx/ty` 可取箭头头部平均宽度 0.5~1
- 或先验经验值需按分辨率调参10~25 像素
输出`List<Arrow>{ id, gx, gy, dir }`
### 4.6 识别质量校验(必须做)
- 方向置信度量化前角度与目标方向夹角 > 22.5° 视为不可信。
- 唯一性:同一 `(gx,gy)` 不应出现多个箭头(出现则说明去重/聚类失败)。
- Debug 可视化:在 `board` 上画 tip 点与方向短线,输出调试图,便于快速调参。
### 4.7 兜底方案(可选增强)
若几何检测不稳:
- 模板匹配8 个方向头部模板,匹配得到方向与 tip。
- 轻量检测模型:训练在 Python推理可用 ONNXRuntime Java 或 OpenCV DNN。
## 5. Solver由 `(gx,gy,dir)` 求消除序列
### 5.1 blocker/blockedBy 核心结构
对每个箭头 `i` 维护:
- `blocker[i]`:沿自身方向最近的箭头 id若不存在为 `-1`
- `blockedBy[i]`:所有当前把 `i` 当作 blocker 的箭头集合(反向索引,用于增量更新)。
- `ready`:所有 `blocker == -1` 且未删除的箭头队列。
流程:不断从 `ready` 取出可消除箭头删除,并只更新受影响的箭头(即 `blockedBy[removed]`)。
### 5.2 最近阻挡者查询4 类直线索引)
8 方向只会落在 4 类直线:
- 行:`y = const`,按 `x` 排序
- 列:`x = const`,按 `y` 排序
- 主对角线:`x - y = const`,按 `x` 排序
- 副对角线:`x + y = const`,按 `x` 排序
建议索引结构:
- `Map<Integer, TreeMap<Integer, Integer>> rows; // y -> (x -> id)`
- `Map<Integer, TreeMap<Integer, Integer>> cols; // x -> (y -> id)`
- `Map<Integer, TreeMap<Integer, Integer>> diags; // (x-y) -> (x -> id)`
- `Map<Integer, TreeMap<Integer, Integer>> antiDiags; // (x+y) -> (x -> id)`
`computeBlocker(i)`
1) 根据 `dir` 选择索引与外层 key
2) 使用 `TreeMap.higherEntry(index)``lowerEntry(index)` 找最近邻
3) 找到则返回其 id否则返回 `-1`
删除箭头时同步从 4 套索引中移除,确保后续查询不会返回已删除对象。
### 5.3 多候选时的选择策略
- 优先消除 `blockedBy[u].size()` 最大的箭头(更可能解锁更多箭头)。
- 或用简单队列先做一版;若出现卡死再引入回溯(见 6
## 6. 无解与回溯(增强项)
纯“可消除就删”的贪心可能在某些关卡卡死(`ready` 为空但仍有剩余)。
可选策略:
- `A先落地`:贪心卡死即返回“无解/需回溯”。
- `B增强`:在 `ready` 多候选时做 DFS 回溯;需实现状态回滚(操作日志优于全量拷贝)。
- `C启发式`:结合入度优先等启发式减少回溯概率。
## 7. Java 类型与接口建议
### 7.1 数据结构
- `enum Direction { N, NE, E, SE, S, SW, W, NW }`
- `class Arrow { int id; int gx, gy; Direction dir; boolean removed; }`
### 7.2 核心类
- `class VisionExtractor { List<Arrow> extract(Mat screenshot); }`
- `class ArrowOutSolver { List<Integer> solve(List<Arrow> arrows); }`
- `class Pipeline { List<Integer> solveFromImage(String path); }`
## 8. 测试与验收标准
### 8.1 Vision 层
- 能从给定截图中输出合理数量的箭头(不漏检、不明显误检)。
- 输出的 `(gx,gy)` 唯一且稳定(同一箭头在同一位置)。
- 输出方向 `dir` 与肉眼一致(允许少量不确定,但需可追踪)。
### 8.2 Solver 层
- 对构造的小规模数据集:`solve` 输出序列长度等于箭头数量。
- 删除序列合法:每一步删除的箭头在当步满足 `blocker == -1`
- 若卡死:能明确返回失败(或触发回溯策略)。
## 9. 调参建议(研发流程)
1) 先固定 ROI或实现自动 ROI
2) 调 `s0/v0` 使 `arrowMask` 只保留彩色线条
3) 调形态学核大小使箭头头部轮廓连续
4) 调 `amin/amax` 让头部候选稳定
5) 调 `tx/ty` 让网格离散化不抖动
6) 用 debug 图逐轮验证 tip 与方向