---
name: template-craft
description: 制作与调整 MAA 任务的识别资源：截模板图/抠模板、定位 roi/定坐标/量坐标、取色生成 ColorMatch 参数、调 maskRange/mask_ranges/掩码范围。用户提到截模板/裁模板/模板图、roi 坐标、取色、ColorMatch、给新活动或新界面做图像适配，或点名 ImageCropper/MaskRangeTool 时使用，即使用户没有明确说出工具名。
---

# 模板与识别资源制作（ImageCropper / MaskRangeTool）

- `tools/ImageCropper/`（截模板/定 roi/取色）：agent 用 CLI `cli.py`（无头）；GUI `main.py` 留给人拖框。
- `tools/MaskRangeTool/`（maskRange 调参，LUV/HSV/RGB）：直接 `import mask_utils` 调函数。
- CLI 子命令速查与 GUI 连设备/截 PC 窗口（需人操作）见 `tools/ImageCropper/README.md`；无头调用只需 opencv-python 与 numpy（MaskRangeTool 另需 matplotlib），MaaFw 仅 GUI 连设备用，依赖各见工具目录 `requirements.txt`。

## 坐标系（先于一切）

- CLI 所有坐标基于 ｢等比标准化到短边 720｣ 的图像，与 GUI 框选、MAA 运行时 roi 同一空间；对裁剪产物等小图二次 grid/crop 必须 `--raw`（否则被放大、坐标系改变）。注意源图宽高比非 16:9 时（如 4:3 窗口截图），标准化空间与 16:9 运行时空间不等比，roi 不可直接搬用集成。
- roi 格式 `x,y,w,h`（`--box` 收两点式 `x1,y1,x2,y2`）；产物默认写 `tools/ImageCropper/dst/`，stdout 打印绝对路径，建议 `-o` 指到本次工作目录。
- **读数口径（最高纪律）：图上数字即线号**。grid 默认切分渲染（图像切块排入表格，格间以 1px 红/蓝交替切缝相隔——供数线防串列/串行（奇偶校验），内容像素不被覆盖；N 格有 N+1 条线，边距标注 0-based 线号：顶/底边距标竖线号、左/右边距标横线号，线 c 的像素 = origin + c×cell_x、线 r = origin + r×cell_y（两轴格距可不同；stdout 打印生效 cell/origin 与换算式供核对）；`--line-color` 可单色覆盖线色）。只允许判断目标边缘夹在哪两根线之间——唯一合法读数形式是 ｢左/上边缘夹在线 a 与线 b 之间，右/下边缘夹在线 c 与线 d 之间｣；禁止 ｢压线 X/边界在线 X 上｣ 的精确断言（目测精度不足以支撑），禁止 ｢c4.3~7.9｣ 之类小数读数，禁止任何格内占比/比例目测（Read 展示端可能缩放 grid 图，占比目测一律不可靠，不得据此做格内偏移判断）。**像素换算只发生在工具内部**：裁剪传 `crop c1,r1,c2,r2 --in-lines`（线区间：左/上闭、右/下开，区间两端即目标边界；配 `--cell` 与 `--cell-origin`，默认 64 与 0,0），开窗传 grid `--region-lines c1,r1,c2,r2 --region-cell <上一层线距> --cell-origin <上一层 stdout origin>`（--region-cell 只管开窗换算、--cell 只管本层渲染，省略 --region-cell 则回落 --cell）。`--cell-origin` 同样只是 `--region-lines`/`--in-lines` 的开窗与换算基准：渲染图的线号原点恒为窗左上角，与本参数无关，禁止把它当 ｢跨窗统一线号原点｣ 传值。格距两轴独立：`--cell-x`/`--cell-y` 覆盖 `--cell` 对应轴，`--region-cell-x`/`--region-cell-y` 同理覆盖 `--region-cell` 对应轴（窄带窗 ｢读数轴细、扫描轴粗｣ 靠这两对覆盖实现）；stdout 打印实际生效的四轴值（本层 cell 与开窗换算 region-cell 的 x/y）与换算式，下一层开窗照抄，勿自行推算。任何 ｢线号×格距｣ 的像素心算一律禁止，--cell-origin 的数值从上一层 stdout 打印的 origin 直接抄写。输出硬限 1280×720——Read 展示端会把更大的图降采样到约 810p、标注全部糊掉，故绝不产出大图：显示步长两轴各自从 cell*zoom 出发在 720p 预算内自动双向调整（放不下每线序号就放大、超预算就缩小），字号 0.7→0.3 逐级自适应，stdout 打印实际 step。`--overlay` 画线叠加模式读边缘所贴基准线的线号（数字起始位置=线位），输出超 720p 时自动按比例缩小图像，线号语义不变（线 c 恒对应原图 origin + c×cell），stdout 打印实际 factor。大区域配细格放不下时直接报错退出，报错信息指引 ｢先在粗格全图上定位，再对 --region 小图做细格边界判断｣。

## 工作流（第 0 步先验 + 网格逐层逼近、线区间逐层细化：每层只判断目标边缘夹在哪两根线之间，线区间端点即目标边界；禁止任何 ｢边缘在格内哪里｣ 式的回归判断，试错裁剪亦无必要）

**线数硬上限与逼近粒度选择**：任何单窗任一轴的法向线数不得超过 20 根（线数=该轴窗跨度÷本层该轴线距）——红蓝交替切缝+线号标注在 ≤12 根时基本无误读空间，20 是硬顶。每层窗的法向跨度=上一层线区间+两侧各 1 根上层线距的外延（外延覆盖上层读数漂移，目标须在窗内居中），按 ｢（带+外延）÷本层线距 ≤ 20｣ 选本层线距，级差尽量取 4~8 倍（为满足 ≤20 上限可缩小），典型链 64→16→8→2（末层 cell-x 2 时配 cell-y 16 的两轴窄带，读上下缘时对调）。

0. **第 0 步（读原图建目标先验）**：进入网格定位前先读一遍原图（不加网格），记录目标形态先验，后续每层读数与先验互校：形状（圆形/矩形；弧形边注定产生矩形模板的角部冗余，终点口径见 ｢弧形/非矩形目标口径｣ ）、颜色与背景对比（预判哪几层可辨，淡色边留给细层）、结构邻居（描边框/文字行/角标等 ｢长得像边界的邻居｣ 提前列出防误认）、尺寸感（目标跨度约几根线距）。**先验与交叉自检的线语义纪律**：先验、每层读数、跨层互检的描述一律使用线号/线区间/相对位置（夹在哪两根线之间、与哪条线相邻、在目标哪一侧），禁止像素值与像素算术；像素只允许出现在工具 stdout 换算式与产物文件名两处，最终 roi 以 `crop --in-lines` 产物文件名为唯一像素出处。
1. **L1（cell 64 全图粗格定带）**：`python cli.py grid <源图> --cell 64 -o <工作目录>/l1.png`（stdout 打印 cell/origin/实际 step，先看再读；全图 origin 恒 (0,0)），读出目标有没有、四边各夹在哪几线之间，得初始线区间带。**粗格读数的角色只是把搜索窗定到 ±1~2 线**：Read 缩放下跨线目测可能偏差 1~1.5 个线距甚至自相矛盾，属预期行为；精确边界由下层裁决，L1 读数与下层结果矛盾时以后者为准、不重读 L1。
2. **L2（读全目标线区间，线距按 20 根上限自选、典型 16）**：开窗= L1 线区间向四周各对称外延 1 线（单位=L1 线距 64px；外延量容忍 L1 的整线级偏差，本身保证目标在窗内），本层线距按 ｢（带+外延）÷本层线距 ≤ 20｣ 选：`python cli.py grid <源图> --region-lines <L1线区间各向外延1线> --region-cell 64 --cell <本层线距，典型16> --cell-origin <L1 stdout origin> -o <工作目录>/l2.png`（--region-cell 取上一层线距管开窗换算、--cell 为本层渲染格距，二者解耦）。**自检：目标必须在窗内大致居中，明显不居中即开窗方向算错**，回查上一层读数与外延方向。本层读全目标线区间：四边各夹在哪两线之间。开窗落空（窗内无目标）时回 L1 重定带或再外延 1 线重开。**淡色/低对比目标在粗层目测即可能失灵**（偏差可远超半个线距，如浅灰边框看错 10~30px）——不在粗层死磕，转 `color` 色值扫描定位：对行/列逐条开单条 roi（长度 ≥3 像素）逐条读值，聚类输出位置盲、长条 roi 反推不出边界位置。等距网格批量收割（图标阵/头像阵）：用 `color` 对候选边界行/列色值扫描定节距与原点（阈值如与背景任一通道差 >10），批量 roi 逐一回读验证；逐卡可有 ±2px 相位抖动，等距模型失配时退回逐卡标定。
3. **L3（按边窄带逐层逼近，法向典型 8→2）**：对每条边单独开窄带窗（不罩全目标），读哪条边就用哪条边法向轴的细 cell、沿边方向轴的粗 cell——读左/右缘（x 坐标）法向轴为 x（`--cell-x <法向线距> --cell-y 16`），读上/下缘（y 坐标）对调；沿边轴固定 16 不参与逼近。窗=该边上一层线区间法向两侧各对称外延 1 线（单位=上层法向线距），沿边方向罩住边的中段即可；本层法向线距按 ｢（带+外延）÷本层线距 ≤ 20｣ 选（典型 8），已可一步到 2 就直达末层；弧形边读数窗取中线附近跨度即可——弧的最远点在中线上，读最远点处的线区间不需要沿整弧布线。这是读数窗口径，与模板形态口径无关：模板产物仍必须是完整外接矩形、弧外靠 maskRange 排除（见 ｢弧形/非矩形目标口径｣ ）：`python cli.py grid <源图> --region-lines <该边法向区间外延 1 线、沿边取中段> --region-cell <上层法向线距> --cell-x <本层法向线距> --cell-y 16 --cell-origin <上层 stdout origin> -o <工作目录>/l3_<边>[_<线距>].png`（读上下缘时 --cell-x/--cell-y 对调；目标小、四边可合并一窗时仍须居中自检）。法向逐层细化，每层只在本层窗内读线区间，边界一律在末层窗内裁定，不拿粗层读数细分边界（低倍下淡色 halo 会被邻近内容干扰误读）。
4. **L4（末层复核与放大兜底）**：逼近链末层已把法向收到 2px 细格，本层逐边复核收口；基于末层窗重开时 `--region-cell-x`/`--region-cell-y` 抄末层 stdout 打印的两轴 cell（法向 2、沿边 16，随边向对调）。某边夹在哪两线之间仍难辨（淡灰抗锯齿/边缘光/低对比）时：先按靠外一线保守计入，再对该边截更小 region 提高 `--zoom`（初始显示步长倍数，step=cell*zoom，工具在 720p 预算内自动调整，小窗放得下高倍）按末层同参数重开窗放大看细节，放大后清晰即定；多次放大仍不可辨的按裁决纪律处理。口径四边一致；先验（如 ｢圆应宽高相等｣）与读数冲突时先怀疑读错、重读该窗。
5. **一次终裁 + 验证**：`python cli.py crop <源图> c1,r1,c2,r2 --in-lines --cell 2 --cell-origin <该层 stdout origin> -o <工作目录>`（线区间端点即像素边界，工具内部换算、产物文件名即像素 roi），另按产物文件名中的像素值裁 (x−2,y−2,w+4,h+4) 对照窗：产物四边恰为目标边界、2px 环带内无目标主体色残留（按口径被排除的边缘渐变不算失败），且复验含原点（first_read 与 final 可能同尺寸不同原点，只对 w/h 会漏误差）。失败即某层线区间读错，回该边所在层重读，不引入额外校验步骤，勿凭产物目测猜偏移。

**拆窗纪律**：两轴网格（读数轴细、扫描轴粗）下 720p 预算撞线已基本消失，预算内无解时工具直接报错退出，按报错指引调整参数重开即可；按边多窗仍存在，各窗 origin 不同、线号不通用，读数连同各窗 stdout 换算式（origin 与 cell）记录，终裁前统一到单一格系——只有同一 origin 下的读数才能拼用；换算到目标格系即线号平移——目标格系线号 = 窗内线号 + (窗 origin − 目标 origin) ÷ cell，逐轴代入各窗 stdout 值，仅用线号代数，禁止像素算术（如窗 origin (144,220) cell 2 的线号 8 → 目标 origin (112,176) 格系线号 8+(144−112)/2=24）。

**裁决纪律**：背景永远排除；目标自身渐变（抗锯齿/光晕）计入与否按匹配场景定——背景固定可含入，可能随主题/底色变化则把边界收进渐变内侧、小一圈换鲁棒。四边同口径优先于组件间等尺寸：同款组件间 1~2px 的渐变尾差异如实保留，集成侧需要等尺寸时统一收紧口径。先验（如 ｢圆应宽高相等｣）与读数冲突时先怀疑读数有误、重读该窗，重读仍冲突才以图为准。

**弧形/非矩形目标口径**：产物=外接矩形模板，图上弧外区域直接涂纯黑（像素 0），任务配 `"maskRange": [1, 255]` 排除涂黑像素——匹配时模板转灰度，纯黑（灰度 0）落在区间外不参与匹配（仓库实践即 ｢maskRange [1, 255] + 背景涂黑｣ ，见 `docs/zh-cn/protocol/task-schema.md` 的 maskRange 字段）。模板必须完整外接矩形、无缺角缺段；涂黑范围按第 0 步先验的目标形状划定（如圆形按外接矩形内圆外区域）。

**交付边界**：止于产出最终模板图与 roi/ColorMatch/maskRange 数值，整理产物绝对路径与数值回报用户；写入 `resource/template/` 与 `resource/tasks/*.json` 属任务集成步骤，由调用场景另行决定。

## ColorMatch 取色

`color` 子命令对 roi 聚类主色，输出可直接并入任务 JSON 的 ColorMatch 参数草稿（method 4 = RGB 空间）；`lower`/`upper` 为按聚类嵌套的三通道向量，`count` 为各簇阈值内的命中数（int；簇间可重叠、总和可超 roi 像素数，仅作主导色存在性参考，不可当精确计数或位置信息）。`--connected` 要求命中连成片（实心色块状态判断用），默认按像素计数。stdout 会先打 ｢Note: …｣ 标准化警告再输出 JSON，脚本化解析前需过滤。GUI 的 C/c 输出（仅首个聚类、向量单层展开）与 CLI 草稿结构不同，勿交叉对照。

## MaskRangeTool

`main.py` 是写死示例路径的演示脚本，agent 直接调 `mask_utils`（base_mask_ranges 预设见 `main.py` 顶部；`color` 支持 luv/hsv/rgb）：

```python
import sys
sys.path.insert(0, r"<仓库根>\tools\MaskRangeTool")
import cv2
from mask_utils import generate_mask_ranges, compare_2_image_with_mask_ranges
# 自动推荐 maskRanges；show=False 无头拿返回值，save 存直方图+mask 预览供回读
ranges = generate_mask_ranges(img, "luv", base_mask_ranges, show=False, save="preview.png")
# 两组图在给定 ranges 下的差异对比，验证 maskRange 区分度
compare_2_image_with_mask_ranges(img1, img2, ranges, "luv", show=False, save="cmp.png")
```
