360 Secure SkillHub
360 安全技能中心

栅格参考图转SVG规格

针对栅格参考图(图标表、牌面、UI 截图)提供像素级复刻流程:网格定位、逐区域取色、连通域测量并换算 viewBox 坐标、描边与对比度修正,再用并排对照页和结构计数断言验证;文中引用的配套测量脚本未随包提供。

oracis
oracis
热度 12.92
最近更新 2026-09-20 08:44:03
分类:创作依赖:代码执行 / 读写文件 / 联网浏览搜索应用场景:图像生成 / UI 原型 / 代码生成

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add ref-image-to-svg-spec
Metadata
name
ref-image-to-svg-spec
slug
ref-image-to-svg-spec
displayName
栅格参考图转SVG规格
version
1.0.0
visibility
public
category
design-media
subCategories
["design-visual-asset","design-ui"]
description
把一张栅格参考图(图标表、牌面表、UI 截图、设计稿切图)复刻成 SVG / Canvas 手绘图形时,用像素级测量代替目测:自动切分网格、按色系采样真色、连通域量出每个元素的位置尺寸并换算成 viewBox 坐标,最后生成「参考图 vs 本项目」并排对照页验证,再用结构计数断言(n 个点 = n 个圆)把结果钉死。当用户说「按这张图的图案改」「照着这个图做图标」「复刻这套牌面 / 表情 / 图标」「颜色不对,按参考图调」,或给出的参考图是低分辨率截图(有压缩噪点)时使用。
agent_created
true

栅格参考图 → SVG 规格

何时用

用户丢来一张图说「按这个改图案/图标」。肉眼估色估位必偏,而且低分辨率截图 (聊天软件压过的、缩放过的小图)会让「看着像」和「实际色值」差很远。全程用像素测量。

五步流程

1. 网格定位,不要手填坐标

参考图通常是等距排布的元素表。用浅灰分框线(低饱和 + 中亮度)做行/列投影找边界:

def is_line(r,g,b):
    mx,mn = max(r,g,b), min(r,g,b)
    return mx-mn < 16 and 170 <= mx <= 240      # 浅灰框线
# 对整列像素计数 → 峰值即竖线位置;相邻很近的两条合并(同一根线的两沿)

坑:别用 --window-size 之外的方式硬编码坐标——不同图的边距差几个像素, 换算到 viewBox 就是几度偏差。也别一次全图统一阈值,某些行的框线更淡会整行漏掉, 必要时逐行检测。

2. 配色:腐蚀一次取量化众数

# 分类:max-min < 50 或 s < 0.42 的算灰/白,不参与配色
# 腐蚀:只保留「上下左右四邻都是同色系」的像素 = 色块/笔画内部
mode.setdefault(family, Counter())[px] += 1
# 再按 8 级量化合并(压缩噪点会把同一个色散成好几个近似值),取众数

不要用「最饱和 N% 像素的中位数」——两个坑:

  • 被混色暗边带偏(暗色像素的 HSV 饱和度反而更高,越暗越"饱和");
  • 被更粗的元素带偏。实测一张麻将牌面参考图上,万字那一行的蓝是偏紫的 #241464,而筒点是 #3c5474,根本不是同一个色。所以要逐行/逐区域统计, 混在一起只会得到一个谁都不像的中间值。

3. 元素测量:连通域 + 换算

4/8 连通域扫出每个图形元素,输出 bbox / 中心 / 尺寸,再线性换算到 viewBox:

vbx = cx / cell_w * 100            # viewBox 宽 100
vby = cy / cell_h * 138            # viewBox 高 138 —— 注意用**各自的高度**换算

关键:参考图的单格宽高比往往和目标不同(例:参考 84×105 = 0.80,实际牌面 42×58 = 0.72)。所以 x 按宽度百分比、y 按高度百分比分别映射,牌面会被"拉高", 这正是真实牌的样子;千万不要按同一个比例缩放(元素会变形或对不齐)。

连通域会碎片化(细笔画的环被走线切断),所以:

  • 先按中心坐标聚类再读位置,而不是指望每根笔画出一个完整框;
  • 同一元素被拆成 2~3 块时,取它们 bbox 的并集。

4. 比例关系靠「剖面」而不是目测

同心圆、环、渐变这类结构,沿半径统计彩色像素占比就能读出每层半径:

for r in [i/2 for i in range(0, 34)]:
    hit = sum(1 for a in range(72) if colored(cx + r*cos(a), cy + r*sin(a)))
    print(r, hit*100/72)      # 100=纯色环,≈0=白环

实测得到「实心圆 → 白 0.63R → 彩 0.50R → 白 0.24R → 彩芯 0.13R」这种精确比例, 比"看起来大概是一半"靠谱得多。

5. 验证:并排对照页 + 结构计数断言

(a) 并排对照页——用 CSS background-position 裁参考图,和本项目渲染并排:

<div class="ref" style="
  background-image:url('ref.png');
  background-size:1066px 536px;              /* 原图尺寸 × 放大系数 */
  background-position:-74px -32px;           /* 单格左上角坐标 × 系数,负值 */
  padding-top:125%;"></div>                  /* 单格高/宽 百分比,保持比例 */

整页截图后一眼就能看出「哪一张对不上」。这是唯一能证伪的手段—— DOM 断言只能证明 SVG 里有这个字符串,证明不了它画出来像不像。

(b) 结构计数断言——把「语义」变成可数的结构,写进测试:

n + '筒' → 恰好 n 个圆点(每点 5 层同心圆)→ 数 <circle>
n + '条' → 恰好 n 根竹                    → 数 <rect>
六条应是 3 列(不是 2 列)→ new Set(x 坐标).size === 3
九条中间一列为红 / 九筒中间一行为红 → 数色值出现次数

这类错误肉眼极难发现(六条排成 2×3 看着也挺自然),必须靠计数兜住。

新断言必须亲手改坏一次确认它会失败。 验证时注意:改「点的排列顺序」不会 触发列数断言(列数没变),要真的改列数。永远通过的新断言等于没有断言。

描边宽度的亚像素陷阱

SVG 单位 ≠ 屏幕像素:牌面 42px 宽时缩放比只有 0.42,viewBox 里 3 个单位的描边 只有 ~1.26px,再细就会被抗锯齿糊成浅色。所以:

  • 描边宽度设下限:max(3, min(w,h) * 0.24);
  • 参考图上很亮的颜色(对比度 < 4.5:1)直接用会显得"没画上去", 按目标尺寸适当压暗并记录理由(本项目把参考图的绿 #3c844c(4.23:1) 压到 #16663a(6.27:1))。

配套脚本

项目里留一份 scripts/read_ref_tiles.py(同类项目可直接抄):网格定位 + 逐行配色 + 逐格连通域测量,输出已换算好的 viewBox 坐标。

工作流顺序(省时间的排法)

  1. 先跑测量脚本,把全部尺寸色值拿到手(别边看边猜);
  2. 按测量值一次性写完绘制代码,node --check 过语法;
  3. 生成对照页截图 → 修正偏差;
  4. 补结构计数断言 + 负向验证;
  5. 最后才做真机尺寸(1:1)可读性检查:用「外层 div 精确裁剪 + 内层 iframe 负 margin」 绕过无头浏览器视口钳制(Windows 无头最小视口宽 ~504),才能看到真实像素。