欢迎深入 SVG(Scalable Vector Graphics)的内核。在绝大多数计算机视觉(CV)任务中,图像是像素矩阵(Raster),但在我们的 SVG-MLLM 项目中,图像是代码。
对于人类设计师,SVG 是 Illustrator 或 Figma 画板上的图形;但对于大模型,SVG 首先是一串文本序列(Sequence of Tokens),其次是一棵结构化树(DOM Tree),最后才是通过渲染引擎呈现的视觉信号。
本章的目标不仅仅是教你“怎么写 SVG”,而是教你“如何从机器视角的理解 SVG”。我们需要解构 SVG 如何通过 XML 标签定义画布空间,如何通过数学指令描述几何拓扑,以及这些表达方式对模型训练意味着什么(例如:为什么相对坐标比绝对坐标更有利于模型学习形状的泛化?为什么贝塞尔曲线的控制点预测是生成的难点?)。如果你希望模型能生成可编辑、可渲染、结构合法的矢量图,就必须先掌握这门“几何语言”的语法规则。
SVG 本质上是一个 XML 文档。对于 MLLM 而言,第一步就是理解“世界在哪里”。
这是 SVG 中最核心的坐标映射概念,也是数据预处理中最容易出错的环节。
<svg width="800px" height="600px"> 定义。它决定了浏览器或渲染引擎在屏幕上开辟多大的矩形区域。对于模型生成而言,这通常是不重要的,因为矢量图可以无限缩放。<svg viewBox="min-x min-y width height"> 定义。这是模型真正“生活”的坐标系。关键概念:归一化(Normalization)
在训练 SVG 模型时,我们通常不希望模型去预测任意范围的浮点数(如 1234.56)。我们希望将所有数值限制在一个固定的范围(如 0-1 或 0-1024)。因此,理解 viewBox 是进行数据清洗的第一步。
[ 浏览器 / 屏幕物理像素区域 (Viewport) ]
+-------------------------------------------------------+
| (0,0) 物理原点 |
| +-----------------------------------------------+ |
| | SVG 画布 (viewBox="0 0 100 100") | |
| | | |
| | 逻辑坐标 (50, 50) 在这里 --------> X | |
| | 虽然只有 100 单位宽,但它被映射到 | |
| | 整个屏幕区域。对于模型,它只需输出 50。 | |
| | | |
| +-----------------------------------------------+ |
| |
+-------------------------------------------------------+
Rule-of-Thumb: 在将 SVG 喂给模型前,务必重写
viewBox为标准正方形(如0 0 24 24或0 0 1024 1024),并重新缩放内部所有的路径坐标。这能极大降低模型对“空间尺度”的学习负担。
SVG 提供了一组预定义的几何形状。虽然它们都可以用 <path> 来表示,但保留图元标签对 MLLM 具有极高的语义价值。
<rect x="10" y="10" width="50" height="50" rx="5" />
rx/ry)。<circle cx="50" cy="50" r="20" />
<path>,则需要 4 段贝塞尔曲线(约 24 个参数),不仅序列变长,而且模型很难精确画出完美的圆。polygon 隐含了“闭合”的几何约束,而 polyline 是开放的。模型设计决策: 在构建 Tokenizer 时,是否将
rect转换为path?
- 转换派: 统一词表,所有几何都是 path,模型只需学一种语法。
- 保留派: 保留高级语义,生成的 SVG 更容易被人类编辑(Human-readable)。
- 结论: 现代趋势倾向于保留。因为这属于“结构化思维”的一部分。
<path> 语言:几何的“汇编语言”<path> 是 SVG 的灵魂。其 d (data) 属性包含了一套极其紧凑的绘图指令微语言。这是生成式模型最难攻克的部分,因为它是一个状态机(State Machine)。
M 100 100 L 200 100): 坐标是相对于原点的。
m 100 100 l 100 0): 坐标是相对于“笔触当前位置”的增量。
l 100 0 l 0 100 l -100 0 z)都是一样的。这极大地帮助了模型的泛化。这是 SVG 生成中最具挑战性的部分。
C / c): 需要 4 个点:起点(当前笔触)、控制点1、控制点2、终点。
Q / q): 只有 1 个控制点。计算简单,但表达复杂曲线能力弱。S / s, T / t):
S x2 y2 x y: 这里省略了第一个控制点,它被默认计算为“上一段曲线第二个控制点的中心对称点”。S 指令意味着模型不仅要看当前的输入,还要“回忆”起上一段曲线的几何特征。这强依赖于 Transformer 的 Attention 机制。 (Start) P0 P1 (Control Point 1)
o-----------o
| \
| \ (The Curve)
| \ . . . . . .
| \ . .
Tangent | . .
Direction| . .
. o P2 (Control Point 2)
. /
. /
o----------------o
(End) P3
A / a) 的噩梦A rx ry rot large-arc-flag sweep-flag x y
弧线指令是参数最多的指令,包含两个布尔标志位(大弧/小弧,顺时针/逆时针)。
A 指令非常难以训练。大多数 SVG 深度学习工作(如 DeepSVG, Im2Vec)在预处理阶段都会将所有 Arc 转换为近似的 Cubic Bezier Curves。这是一个标准的 Rule-of-Thumb。SVG 允许在组 (<g>) 或元素级别应用变换:translate, scale, rotate, skewX/Y, matrix。
<g transform="rotate(45)"><rect transform="translate(10,0)" .../></g>SVG 的强大之处在于内容(Path)与表现(Style)的分离。
<linearGradient>, <radialGradient>)、图案 (<pattern>)。stroke, stroke-width, stroke-linecap (端点形状), stroke-linejoin (拐角形状), stroke-dasharray (虚线)。nonzero (默认): 基于射线法和路径方向(顺/逆时针)判定内部。evenodd: 仅仅计算射线穿过路径的次数,奇数为内,偶数为外。nonzero 规则下会出现意想不到的“镂空”或“填实”错误。viewBox 是逻辑坐标系,训练前必须将数据归一化到固定范围(如 0-1 或 0-256)。M, L, C 指令是理解 SVG 的关键。贝塞尔曲线通过控制点定义形状,这是参数化建模的重点。Arc 指令近似为 Cubic Bezier。Transform 烘焙进坐标点。<svg width="200" height="200" viewBox="0 0 100 100">。
如果在坐标 (50, 50) 处画一个点,请问它在屏幕上的物理位置是在 SVG 区域的中心,还是右下角?
M 10 10 h 20 v 20 h -20 z (注意大小写混合)。请描述这是一个什么形状,以及它的各个顶点坐标。
C 10 10 90 10 100 0 中,起点是 (0,0)。请问这条曲线在起点的切线斜率大致是多少?
S 指令都显式展开为 C 指令(补全那个隐含的控制点),这对模型的训练是有利还是有弊?
128 视为文本 "1", "2", "8" 三个 token。
B. 将坐标 128 视为一个整数 token <coord_128>。
哪种更适合 SVG 生成?为什么?
<path> 的 d 属性完全正确,stroke 是 red,stroke-width 是 5,但渲染出来依然什么都没有。除了 opacity 和 display,还有可能是 SVG 的哪个父级属性导致的?
在处理 SVG 数据集和构建模型时,以下是 90% 的开发者会踩的坑:
0.00001 输出为 1e-5。普通的文本 Tokenizer 可能会把它切分成 1, e, -, 5,导致模型困惑。M 10 20,M10,20,M 10, 20,M10.5.5(如果是小数,甚至可以省略空格)都是合法的。svgpathtools 或 svgelements 库解析成对象,再重新序列化为标准格式(例如:命令和数字间统一用空格,坐标对间统一用逗号)。d="M 10 10 20 20 30 30"。M 后面跟随多组坐标,后续的坐标会被隐式视为 L(画线)。M 10 10 L 20 20 L 30 30,否则模型会学到混乱的语法。Z 指令会将“当前点”重置为子路径的起点。如果在 Z 之后紧跟相对坐标指令(如 l 10 10),它是基于起点的位移,而不是基于 Z 之前的点的位移。fill-rule="nonzero"。如果你的数据集中包含大量依靠 evenodd 渲染的图标,但你没有显式把这个属性喂给模型,模型默认用 nonzero 渲染就会出错。evenodd 的路径转换为几何上等价的 nonzero 路径(通常涉及改变内孔洞的绕转方向)。