关于 vecline
Vecline 是面向开发者和创作者的本地优先图像与文档工具集,提供命令行、库和浏览器版 Studio。它结合格式转换与矢量化,并测量输出精度,支持经过验证的逐像素精确模式。
主要功能
- 01在栅格格式与 SVG 间转换
- 02以精确或可测量模式进行矢量化
- 03测量图像相似度和转换精度
- 04渲染并转换 PDF 和 Office 文档
- 05导出 DXF、EPS 和 G-code
适用场景
- 01将徽标和图标转换为可编辑矢量
- 02在不同图像格式间转换文件
- 03检查转换结果是否逐像素一致
- 04为制作加工准备指定切割尺寸的设计
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y veclineVECLINE_SOFFICEOptional. Path to a LibreOffice `soffice` binary, used only by the Office conversion tools. Left unset, they are auto-detected; if LibreOffice is absent the affected tools report themselves as unavailable rather than failing.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
16收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | enum | 否 | auto(默认)根据图像选择;pixel/lossless 用于平面图像时可实现逐位精确;trace 使用路径进行近似;embed 无损封装原始栅格图像auto | lossless | pixel | trace | embed |
| input | string | 是 | 源栅格图像的路径 |
| colors | number | 否 | 轨迹模式的调色板大小(2–256) |
| output | string | 否 | 可选的 .svg 输出路径 |
| preset | enum | 否 | 轨迹路径的调优设置;在像素和嵌入模式下忽略auto | logo | lineart | poster | photo | detailed | pixelart | exact |
| verify | boolean | 否 | 将结果栅格化并进行测量。返回包含 ssim、psnr、meanDeltaE、pixelIdentical 和 svgBytes 的 JSON,而非 SVG 文本。会额外执行一次渲染。 |
详细介绍
Vecline
可测量的栅格 ⇄ SVG 转换,以及全面的图像与文档工具集。 支持 10 种栅格格式,每种都能转换为其他所有格式,并实际测量每次转换的准确度,而非仅作宣称;还支持 PDF 和 Office(docx/xlsx/pptx)渲染与转换、图像 → PDF、面向创作者的实际物理切割尺寸 DXF,以及 EPS 和 G-code、中心线描摹和内容感知裁剪——其中大部分功能都在零依赖核心中实现,并已通过 CI 验证可打包用于浏览器。
对于平面图稿——徽标、图标、UI、截图、像素画——输出逐位精确:SSIM 1.0000、PSNR ∞、差异像素数为零;对于真实徽标,输出还比 imagetracerjs 或 vtracer 体积更小,同时仍只做到近似——24 KB,而它们分别为 60 KB 和 64 KB。这是因为系统识别出精确编码图像的成本比近似编码更低,而非曲线拟合更出色。现在描摹默认会输出真正的曲线——已启用亚像素边缘定位,这是曲线拟合得以运行的必要条件;此前默认输出的真实图稿是阶梯状的,曲线命令数为零。代价是 gzip 压缩后的字节数约为原来的 2.35 倍,因为坐标脱离整数格点会增加压缩成本;使用 --no-subpixel 即可恢复旧版较小的输出。像素模式和精确模式不受影响,仍然逐位精确。potrace 的输出比所有其他方案都小,但准确度低得多,这是实实在在的取舍,本 README 不会避而不谈。对于真实照片,在各工具的默认设置下,它在 SSIM 上领先其他三种方案 0.05–0.18;vtracer 的一个有文档记录的标志可缩小部分差距。它并非速度最快——在测过的每个样例中,imagetracerjs 都更快。本 README 中的每个数字都可复现:node scripts/fetch-corpus.mjs && npm run compare。(语料是获取的,而非提交到仓库的——这些照片不属于我们,无法重新分发。)
▶ Vecline Studio — vecline.xyz — 在浏览器中使用完整工具集。拖入图像、PDF,或浏览器无法读取的 TGA/PNM/ICO;调整参数;实时查看 SSIM / PSNR / CIEDE2000;导出 SVG、实际切割尺寸 DXF、EPS、PDF、G-code、React/Vue/Svelte/Solid 组件、分色、精灵图、网站图标、BlurHash,或者只是在 10 种栅格格式之间重新编码。免费、无限制、无需注册、可离线使用,而且不会上传任何内容——每次转换都在你的浏览器标签页中运行,而非服务器上。(对比情况 · 开发者试验场)
vecline vectorize logo.png --verify
✓ logo.svg 400×300 pixel mode 4 shapes 2.1 KB → 7.5 KB 228 ms
· Auto-selected pixel mode: 4 distinct colours, 1.0% run density. Output is bit-exact.
Accuracy bit-exact (lossless)
PSNR ∞
SSIM 1.000000 (luma 1.000000)
RMSE 0.0000
无损保证
vecline vectorize anything.png --lossless
--lossless 返回位精确的 SVG,否则就会失败。它绝不会悄悄给你一个近似结果。
这是通过测量来保证的,而不是靠构造方式。每种候选编码都会被重新渲染为像素,并与源图像进行比较;任何不完全一致的结果都会被丢弃。如果没有候选结果通过,命令就会报错。候选项按结果的实用程度依次尝试:
- 精确几何图形 — 真正可编辑、可无限缩放的路径。根据编码大小,使用矩形或轮廓。
- 嵌入,保留原始字节 — 对源文件进行字节完全一致的往返处理,而不仅仅是像素一致。
- 嵌入,重新编码为 PNG — 渲染结果始终完全一致,但不再保留原始文件。
在测试套件的所有样例中进行测量——平面图稿、像素画、柔和透明度、照片、JPEG 源文件、单个像素——它们的结果均为 PSNR ∞、maxChannelDiff 0。
字节完全一致,且可验证
保留原始文件时,SVG 会记录其 SHA-256。extract 会还原文件并校验摘要:
vecline vectorize logo.png -o keep.svg --mode embed --embed-strategy preserve
vecline extract keep.svg -o recovered.png --against logo.png
✓ recovered.png image/png 2.1 KB
sha256 1c4f95f66b28a40a75672614796de762f6ef8a6afec3191b460dbbd556f10366
digest matches the value recorded when the SVG was written
payload the original file, preserved byte for byte
vs source byte-identical to logo.png
任何不匹配都会导致 extract 以非零状态码退出,因此可以用作 CI 门禁。需要坦诚说明的是:嵌入的 JPEG 可以逐字节保留,但渲染结果不一定位精确,因为 resvg 的解码器对逆 DCT 的舍入方式不同。严格模式 --lossless 会检测到这一点,丢弃该候选项并告知原因。
你会得到哪种结果
| 输入 | 结果 | 大小 | 精确? |
|---|---|---|---|
| 平面图稿 400×300 | 真正的几何图形(轮廓) | 3.9 KB | ✅ |
| 像素画 128×128 | 真正的几何图形(矩形) | 0.6 KB | ✅ |
favicon.ico 64×64 | 真正的几何图形(矩形) | 0.5 KB | ✅ |
| BMP 400×300 | 真正的几何图形(轮廓) | 3.9 KB | ✅ |
| 照片 320×240 | 嵌入 PNG | 230 KB | ✅ |
照片可以转换为精确的几何图形,--prefer geometry 就会这样做——但每个像素都需要一个矩形,因此同一张照片会变成一个 1.99 MB、包含 42,933 个图形的文件。它只有名称上是矢量图,所以 auto 会使用位图,并告知你测得的比例。没有任何信息会被隐瞒。
“100% 准确”的真实情况
大多数矢量化工具都声称准确率完美。以下是实际情况,因为这决定了你应该使用哪种工具:
| 方向 | 能否精确? | 原因 |
|---|---|---|
| SVG → 光栅图 | 可以,在你指定的任意分辨率下 | 渲染是一种定义明确的计算。指定宽度为 4000px,就会得到符合渲染器栅格化规则的精确结果。 |
| 光栅图 → SVG,无损 | 可以 | 有两种不同的方法,结果都位精确。见下文的 pixel 和 embed。 |
| 光栅图 → SVG,描摹为曲线 | 不可以,而且永远不可能 | 照片包含的独立信息多于任何一组紧凑的贝塞尔曲线所能编码的信息。描摹从定义上来说就是近似处理。 |
任何声称能将照片精确矢量化为曲线的人,要么是嵌入了位图却称之为矢量图,要么就是错了。Vecline 能正确完成两种精确转换,出色地完成近似转换,并始终告诉你得到了哪种结果,以及它与原图有多接近。
三种策略,以及各自适用的场景
| 模式 | 输出 | 精确吗? | 适用场景 |
|---|---|---|---|
lossless | 真实几何图形,或嵌入的位图 | 逐位精确,已验证 | 任何场景,只要精确性不容妥协 |
pixel | 真实矢量几何图形(矩形或轮廓) | 逐位精确 | Logo、图标、像素艺术、屏幕截图、图表、纯色图像 |
trace | 真实贝塞尔曲线 | 近似,经过测量 | 照片、复杂艺术作品,以及任何需要缩放或编辑的图像 |
embed | SVG 容器中的位图 | 逐位精确 | 需要将这张精确图像放入 SVG 容器中 |
auto(默认模式)会检查图像,并在 pixel 和 trace 之间进行选择。--lossless 会覆盖所有设置,并提供上述保证。
pixel 模式往往出乎人们的意料。 它会生成真正可编辑、可无限缩放的矢量路径,栅格化后与输入图像的差异像素为零——不是“视觉上相同”,而是字面意义上的完全相同。对于纯色图像,这通常正是你真正想要的。
安装
npm install -g vecline # the CLI
npm install vecline # the library
Node.js 18.17+。原生依赖(sharp、resvg)提供适用于 Linux、macOS 和 Windows 的预构建二进制文件。
适用于任何项目架构
| 需求 | 导入方式 | 原生依赖 |
|---|---|---|
| 完整的 Node 工具包 | import { vectorize } from 'vecline' | 是 |
| ……使用 CommonJS | const { vectorize } = require('vecline') | 是 |
| 仅使用矢量化器 | import { trace } from 'vecline/vectorize' | 无 |
| 仅使用度量功能 | import { compareImages } from 'vecline/metrics' | 无 |
| 仅使用纯 TS 编解码器 | import { encodeBmp } from 'vecline/formats' | 无 |
| 使用完整的可移植功能 | import { vectorizeExact } from 'vecline/core' | 无 |
| 图像编辑(调整大小、旋转……) | import { editImage } from 'vecline/ops' | 仅 sharp |
| 注册自定义编解码器 | import { registerDecoder } from 'vecline/codecs' | 无 |
只安装你用到的依赖。每个标记为 none 的子路径都可以独立导入,并省略原生编解码器(npm install vecline --omit=optional);该包还设置了 "sideEffects": false,因此打包工具会移除所有未导入的内容。vecline/core 是矢量化和测量引擎,零依赖且不使用 Node 内置模块。它接收普通的 { width, height, data }——其布局与浏览器的 ImageData 逐字节相同——因此可以直接传入 canvas 像素:
import { vectorizeExact } from 'vecline/core';
const { data, width, height } = ctx.getImageData(0, 0, canvas.width, canvas.height);
const { svg } = vectorizeExact({ width, height, data }); // bit-exact, no codec needed
零依赖的说法由 CI 断言验证,而非仅仅宣称。 每次提交都会使用 esbuild 将 vecline/core 打包为浏览器版本,不指定 Node 平台,也不使用外部依赖;如果输出中残留任何 node: 模块标识符或 Node 全局对象,构建就会失败。打包后的大小为 96 KB(压缩后),并设有上下限阈值,以免大体积内容悄然进入包中。阅读导入关系图只能证明可见的导入干净;实际打包则能证明用户真正执行的操作可行。
省略可选原生包进行安装(npm install vecline --omit=optional)后,core 仍可正常使用,大小为 2 MB,而非约 100 MB。读取图像文件、将 SVG 渲染回像素,以及已验证的无损保证,都需要真正的编解码器,因此这些功能位于主入口中。
BMP、ICO、PNM 和 TGA 编解码器均在此包中从头编写,因此即使省略原生包也能使用——读取或写入这些格式都不需要 libvips。有一点值得提前说明,以免之后才发现:ICO 条目可能包含完整的 PNG,而非原始像素(大多数 256×256 图标都是如此);解码这类条目仍需要真正的 PNG 编解码器。无论 ICO 容器采用哪种形式,本包都会解析它;但其中的载荷必须交由相应的编解码器处理。存储原始 DIB 的条目(小图标通常采用这种方式)则无需安装任何内容即可读取。
格式
| 读取 | 写入 | |
|---|---|---|
| PNG | ✅ | ✅ |
| JPEG | ✅ | ✅ |
| WebP | ✅ | ✅ 有损 + 无损 |
| AVIF | ✅ | ✅ 有损 + 无损 |
| TIFF | ✅ | ✅ |
| GIF | ✅ 第一帧 | ✅ |
| BMP | ✅ 所有位深、RLE、BITFIELDS | ✅ 24/32 位 |
| ICO / CUR | ✅ 最大条目 | ✅ PNG 或 DIB 负载 |
| PNM(PBM/PGM/PPM) | ✅ P1–P6 | ✅ P4/P5/P6 |
| TGA | ✅ RLE、颜色映射 | ✅ 可选 RLE |
| SVG | ✅ | ✅ |
格式矩阵是方阵:能读取的格式,也都能写入。 这意味着 11 × 11 = 121 种转换,测试套件会在每次提交时运行所有转换——PNG→TGA、ICO→WebP、TGA→SVG、SVG→BMP,随你需要。每种转换都能通过一条命令完成:
vecline convert photo.png thumb.webp # raster → raster
vecline convert logo.png logo.svg # raster → vector
vecline convert logo.svg [email protected] # vector → raster
convert 会根据输入字节 和 输出扩展名来判断格式,而不只是看扩展名——一个名为 .txt、内容却是 <svg> 的文件会被视为 SVG;一个名为 .svg、内容却是 PNG 字节的文件则不会。
BMP、ICO、PNM 和 TGA 都用纯 TypeScript 解码——libvips 并未编译这些格式,而且它们都足够简单、稳定,可以完整实现,无需再添加原生依赖。这四种格式都无损,因此测试套件会断言解码结果逐位完全一致,而不是只要求近似匹配。
未内置: HEIC、JPEG XL、JPEG 2000、PSD、PDF 和相机 RAW。随 sharp 提供的预编译 libvips 无法读写这些格式——HEIC 是因为 HEVC 受专利限制;JXL 和 JP2 则是因为它们根本没有编译进去(已验证:heifsave: Unsupported compression、jxlsave_buffer not found、JP2 output requires OpenJPEG)。为每种格式打包一个 WASM 编解码器,会让 每一次 安装都增加数十 MB,而大多数用户根本不会用到这些格式。
但你可以自行接入。 Vecline 不会附带这些编解码器,而是提供接入接口。自行安装 WASM 解码器并注册后,该格式就能像任何内置格式一样,在所有地方进行读写:
import decodeJxl from '@jsquash/jxl/decode.js';
import { registerDecoder } from 'vecline/codecs';
registerDecoder({
format: 'jxl',
canDecode: (b) => b.length > 2 && b[0] === 0xff && b[1] === 0x0a, // JXL signature
decode: async (bytes) => {
const { data, width, height } = await decodeJxl(bytes);
return { width, height, data: new Uint8ClampedArray(data.buffer) };
},
});
// vecline can now vectorize, convert, and edit .jxl files.
不需要它的用户无需付出任何代价。偶尔使用时,先通过 ImageMagick 或 libheif 转换,仍然是最简单的办法。
快速开始
# Convert, letting vecline choose the strategy
vecline vectorize logo.png
# Prove the result is what it claims
vecline vectorize logo.png --verify
# Guarantee a bit-exact result whatever the input, or fail
vecline vectorize photo.jpg --lossless
# Trace to curves, escalating settings until it hits a quality target
vecline vectorize portrait.jpg --target-ssim 0.95
# SVG back to raster at any size
vecline rasterize logo.svg -o [email protected] --scale 4
vecline rasterize logo.svg -o hero.webp --width 2400 --lossless
# Ask what a file is and what to do with it
vecline info photo.jpg
# Measure any two images against each other — raster or SVG, in any combination
vecline verify original.png result.svg
# Whole directories
vecline batch 'assets/**/*.png' -o dist/ --to svg
透明输出
--transparent 会移除纯色背景,并保留真正的透明效果:
vecline vectorize logo.png --transparent # detect the background colour
vecline vectorize logo.png --transparent '#ffffff' # or name it
vecline rasterize icon.svg -o icon.png --transparent
以下两点让这个功能安全而不会造成破坏:
- 它会从边缘开始进行泛洪填充。 移除“所有白色像素”也会把徽标内部的白色部分挖空——比如眼睛里的高光、字母
o中间的空白。只有从边缘可达的颜色才会被视为背景。使用--bg-everywhere可选择简单粗暴的全局处理方式。 - 容差基于感知进行计算。 JPEG 中的白色永远不会恰好是
#ffffff,而 RGB 阈值若宽松到足以识别它,也会连淡黄色一起抹掉。--bg-tolerance使用 Oklab 色差,因此“略微偏白”和“明显不同的颜色”会分别处于阈值两侧。--feather会柔化抠图边缘。
核心库也提供此功能,因此可在浏览器中使用:removeBackground(image, { tolerance: 0.02 })。
精度,量化呈现
以下所有数字均由 --verify 生成:生成的 SVG 会被重新渲染为像素,并与源图进行比较。运行 npm run bench 即可复现这些数字。
| 输入 | 模式 | 输入大小 | 输出大小 | 像素精确匹配 | PSNR | SSIM | 平均 ΔE₀₀ |
|---|---|---|---|---|---|---|---|
| 平面图稿,400×300 | pixel | 2.1 KB | 7.5 KB | 100.00% | ∞ | 1.0000 | 0.000 |
| 像素艺术精灵图,128×128 | pixel | 0.5 KB | 0.6 KB | 100.00% | ∞ | 1.0000 | 0.000 |
| 平面图稿,400×300 | embed | 2.1 KB | 3.2 KB | 100.00% | ∞ | 1.0000 | 0.000 |
| 照片,320×240 | embed | 14.5 KB | 19.7 KB | 87.94% | 58.04 dB | 0.9982 | 0.060 |
| 照片,320×240 | trace(auto 和 --preset photo 相同) | 14.5 KB | 49.3 KB | 0.01% | 31.31 dB | 0.8476 | 2.931 |
| 平面图稿,400×300 | lossless → pixel | 2.1 KB | 3.9 KB | 100.00% | ∞ | 1.0000 | 0.000 |
| 像素艺术精灵图,128×128 | lossless → pixel | 0.5 KB | 0.6 KB | 100.00% | ∞ | 1.0000 | 0.000 |
| 照片,320×240 | lossless → embed | 14.5 KB | 230.5 KB | 100.00% | ∞ | 1.0000 | 0.000 |
描摹照片这一行又变了——从 49.3 KB 增至 93.8 KB,31.31 降至 30.82 dB, SSIM 从 0.8476 降至 0.8116——值得准确说明这意味着什么。 现在默认启用子像素边缘定位,这使曲线拟合器得以运行;若不启用, 描摹输出中就没有任何曲线命令,只是套着 SVG 外壳的位图。
此列是在源图自身尺寸下,将 SVG 与源图进行评分;在 1 倍缩放下, 像素网格不可能有损:它就是经过重采样的源图。因此,当输出更像矢量、 更不像副本时,这一行的评分就会变差。这不是试图掩盖的隐性代价—— 对描摹器来说,这是个错误的问题;但对上面三个模式来说,这是正确的问题, 它们逐像素完全一致,并始终如此。
使用非循环的测量方式评估——
npm run bench:scale,即与描摹器从未见过的 真实矢量原图进行评分,并在输入中从未包含的放大倍率下测量——精细化处理 在300 个真实徽标上带来了 +3.01 dB 的提升。如果你希望复现输入, 而不是将其还原为形状,pixel、embed和lossless都能做到完全一致;--no-subpixel则会恢复较旧的网格描摹结果,gzip 后的字节数约为其三分之一。
描摹照片这一行从 52.9 KB 降至 49.3 KB,其所有精度数值均精确到每一位、毫无变化。 闭合多边形在
z命令之前重复指定了闭合边,而z本身已经会绘制这条边—— 按定义,z会回到子路径的起点,因此显式的返回边只会留下需要填补的零长度间隙。 删除它后,在整个语料库中,所有路径命令减少了 16.9%;在约 4000 万个子像素通道中, 两种放大倍率下,无论是否带描边,差异通道数均为 0。
有两行值得说明,因为对它们含糊带过正是工具误导用户的原因:
- 照片 +
embed并非 100% 精确,尽管原始 JPEG 字节在 SVG 中得以原样保留。残余误差(每个通道最多 3/255)来自 SVG 渲染器的 JPEG 解码器:其逆 DCT 的舍入方式与参考解码器不同。没有数据丢失;只是两个解码器在最后一位上存在差异。Vecline 报告的是测量结果,而非声称。 - 照片 +
trace确实是近似结果。 像素精确匹配率为 0.01% 并非错误——这正是描摹照片的含义。如果你在意这个数字,就应该使用embed。
与其他矢量化工具对比
关于速度,以及为何此表没有时间列。
scripts/compare.mjs会记录每个工具的ms并打印出来;这些表格省略了这一列,而这一遗漏让我们的表现显得更好。测量 5 次冷启动进程的最短耗时,输入 PNG、在磁盘上生成 SVG,vecline 在每个测试样例上都比 imagetracerjs 慢——慢 1.10× 至 1.55×;在一个小型徽标上,vecline 比 vtracer 慢 4.08×(280 ms 对 69 ms),其中我们约 83 ms 的耗时用于在任何实际工作开始前由 Node 加载模块依赖图。在两张较大的照片上,vecline 的实际运行时间比 vtracer 短(肖像:1001 ms 对 1386 ms;摩托车:1160 ms 对 1970 ms),同时得分也更高。我们有意不在表格中列出
ms一列,以免误导:测试框架测量的是produce(),但不同参测工具内部执行的工作并不相同——vecline 在计时期间要完整编码 并 解码 PNG,imagetracerjs 接收的是原始像素,完全不需要编解码,vtracer 则需要启动进程。修正这一测试方法的工作已列入跟踪;在此之前,上述数字是诚实的总结,而npm run compare会打印原始耗时列,供有需要的人查看。
运行 npm run compare,亲自试试。每个工具生成的 SVG 都使用相同的渲染器和相同的指标进行评分,并置于相同的白色背景上,因此这里的结果并不依赖 Vecline 自己对质量的判断。参测工具包括 potrace、imagetracerjs,以及最强的现代开源竞品 vtracer(VisionCortex,Rust)。将 VECLINE_VTRACER 指向 vtracer 发布的二进制文件,即可对其作者实际发布的工具进行评分;如果找不到该文件,测试框架会退回使用 @neplex/vectorizer 绑定;如果两者都不存在,则直接省略该行,而不是臆测结果。
合成测试样例——无需获得任何人的照片授权即可复现:
| 测试样例 | 工具 | 大小 | PSNR | SSIM | 平均 ΔE₀₀ |
|---|---|---|---|---|---|
| 二值图 | potrace | 1.5 KB | 24.67 dB | 0.9511 | 0.702 |
| imagetracerjs | 1.7 KB | 19.75 dB | 0.8607 | 1.667 | |
| vecline(自动) | 1.4 KB | ∞ | 1.0000 | 0.000 | |
| 彩色艺术图 | potrace 海报化 | 1.9 KB | 14.61 dB | 0.8010 | 21.302 |
| imagetracerjs | 1.6 KB | 26.56 dB | 0.9363 | 0.662 | |
| vtracer | 1.2 KB | 27.88 dB | 0.9490 | 0.582 | |
| vecline(自动) | 1.8 KB | ∞ | 1.0000 | 0.000 | |
| 照片(渐变 + 噪点) | potrace 海报化 | 11.5 KB | 13.19 dB | 0.6024 | 23.911 |
| imagetracerjs | 11.8 KB | 26.85 dB | 0.7143 | 5.623 | |
| vtracer | 42.7 KB | 32.53 dB | 0.7979 | 3.049 | |
| vecline(自动 / 照片) | 19 KB | 31.12 dB | 0.7750 | 2.940 | |
| vecline 无损 | 43.7 KB | ∞ | 1.0000 | 0.000 |
真实照片——480px 的 Kodak 测试集,这才是真正重要的测试。vecline(自动) 是零配置默认模式:运行 vecline convert photo.png out.svg,即可自动根据内容调整调色板。
| 照片 | 工具 | 大小 | PSNR | SSIM | 平均 ΔE₀₀ |
|---|---|---|---|---|---|
| 肖像(肤色柔和) | imagetracerjs | 1621 KB | 25.47 dB | 0.7093 | 5.957 |
| vtracer | 987 KB | 25.88 dB | 0.7652 | 4.743 | |
| vecline(自动) | 1338 KB | 34.80 dB | 0.9140 | 2.663 | |
| 灯塔(天空) | imagetracerjs | 2010 KB | 24.58 dB | 0.7465 | 4.900 |
| vtracer | 1032 KB | 24.25 dB | 0.7624 | 5.665 | |
| vecline(自动) | 1743 KB | 36.26 dB | 0.9453 | 2.575 | |
| 鹦鹉(精细细节) | imagetracerjs | 301 KB | 25.81 dB | 0.7615 | 6.317 |
| vtracer | 344 KB | 23.17 dB | 0.7949 | 7.728 | |
| vecline(自动) | 309 KB | 31.18 dB | 0.8460 | 3.686 |
对于 二值图 和 彩色艺术图,vecline 的结果与原图逐位一致(SSIM 为 1.0000);相比之下,potrace 和 imagetracerjs 都只能近似还原,而彩色艺术图上的 vtracer 得分也只有 0.9490——vecline 生成的文件大小更小或相近。
这两个测试样本都是合成的,而且很长一段时间以来,本节的内容都只基于它们。下文加入了真实的扁平图稿,因为那里的情况确实不同,而且有一个方面对我们更不利。
真实扁平图稿——本项目自己的语料图像,现在已纳入 npm run compare,因此这些数据行由与本文其他表格相同的测试框架、渲染器和指标生成。
| 测试样本 | 工具 | 大小 | PSNR | SSIM | 平均 ΔE₀₀ |
|---|---|---|---|---|---|
| logo-tux(真实徽标) | potrace posterize | 7.7 KB | 14.41 dB | 0.7649 | 6.146 |
| imagetracerjs | 59.8 KB | 24.17 dB | 0.8861 | 1.975 | |
| vtracer (cli) | 64.0 KB | 23.51 dB | 0.8907 | 1.517 | |
vecline --preset logo | 53.4 KB | 26.43 dB | 0.9032 | 1.073 | |
| vecline (auto) | 25.8 KB | ∞ | 1.0000 | 0.000 | |
| vecline lossless | 24.0 KB | ∞ | 1.0000 | 0.000 | |
| alpha-dice(真实图像,带 alpha) | potrace posterize | 11.1 KB | 11.69 dB | 0.6857 | 14.690 |
| imagetracerjs | 72.1 KB | 26.05 dB | 0.8685 | 2.108 | |
| vtracer (cli) | 167.8 KB | 20.90 dB | 0.8577 | 3.213 | |
vecline --preset logo | 139.9 KB | 28.39 dB | 0.8823 | 1.682 | |
| vecline (auto) | 189.1 KB | 35.85 dB | 0.9537 | 0.862 | |
| vecline lossless | 209.6 KB | ∞ | 1.0000 | 0.000 |
有三种诚实的解读,但它们并非都指向同一个结论。
对于真实徽标,逐位精确的结果也是最小的。 lossless 输出 24.0 KB,且 SSIM 1.0000——只有 imagetracerjs 和 vtracer 输出近似结果时所占大小的三分之一。这就是扁平图稿最重要的结论,而且依然成立。但这是因为精确编码这张图比近似编码更省空间,vecline 识别到了这一点并输出了精确副本——并非因为曲线拟合得更好。要求生成曲线,就会得到它上面那一行的结果。
--preset logo 的质量领先,但优势比合成测试样本所显示的要小。 在 logo-tux 上,它比 vtracer 高出 2.9 dB 和 0.013 SSIM,颜色误差减少三分之一,而且文件更小(53.4 KB,相比 64.0 KB)。在 alpha-dice 上,它在三个质量指标上都领先,大小比 vtracer 少 17%。表现不错,但并不像合成测试样本所暗示的那样有 15–19 dB 的差距——logo 会将颜色量化到 16 种,而在真实图稿上,这样做的代价高于在四色测试图案上的代价。
potrace 的文件远小于其他工具,这确实是一种取舍。 它只有 7.7 KB,而我们的结果为 24–53 KB。它的设计采用双色模式,颜色指标也较差(SSIM 0.7649,ΔE 6.1),但如果相比色彩保真度,几 KB 的大小更重要,那么它在文件大小上胜出,而这张表就应该如实说明这一点。
合成照片——纯渐变加噪声——是 vtracer 最擅长的情况;凭借精细的色彩精度描摹,它在 SSIM 上领先。vecline 的 SSIM 达到 0.7750,文件大小不到一半(19 KB,而非 42.7 KB),也明显胜过 imagetracerjs(0.7143)。增加颜色数量可以弥合最后的差距;但在这个合成的最坏情况上,不值得为此多花 2.2 倍的字节数。
这一行在 v1.33.1 中有所调整,而且是向下调整,因此值得说明原因。
photo预设此前在这里的得分是 0.7923——但npm run compare显示,同一预设在每张真实照片上的得分都低于普通 auto,处理时间最长还达到 136 秒。这是因为它强制使用了一个去斑阈值,会抹去照片赖以呈现的精细细节。修复这一问题后,这个合成测试样本的 SSIM 降低了约 0.017,却让下方 Kodak 数据集的 SSIM 提高了 +0.25,速度约快 75 倍。渐变加噪声的图案不是照片;两者结果不一致时,应以真实照片为准。现在photo和auto会采用相同的实测最佳配置,因此合并为一行报告。 在真实照片上,vecline 在所有质量指标上都领先——开箱即用地胜过每个对手的默认配置。 自动模式会根据内容调整调色板,因此零配置默认结果在每张照片上的 SSIM 都高出 0.05–0.18:0.9140 / 0.9453 / 0.8460,而 vtracer 为 0.7652 / 0.7624 / 0.7949,imagetracerjs 为 0.7093 / 0.7465 / 0.7615;PSNR 也高出许多(34.8 / 36.3 / 31.2 dB,而对方为 25.9 / 24.3 / 23.2),颜色误差约减半。合成图像最坏情况的说法,经不起真实照片的检验。
上面的比较是默认配置对默认配置,这对我们有利。 vtracer 支持参数,其中一个有文档说明的预设会改变表格中部分结果——使用相同二进制文件、相同测试工具、相同渲染器和指标:
| 测试样本 | vtracer 设置 | 大小 | PSNR | SSIM | 平均 ΔE₀₀ |
|---|---|---|---|---|---|
| kodak-parrots | --preset poster | 534 KB | 29.86 dB | 0.8643 | 2.560 |
| vecline (auto) | 309 KB | 31.18 dB | 0.8460 | 3.686 | |
| synthetic photo | --preset poster | 58 KB | 36.57 dB | 0.8620 | 1.935 |
| vecline (auto) | 19 KB | 31.12 dB | 0.7750 | 2.940 | |
| all four colour fixtures | -p 8 -g 4 --filter-speckle 0 | 字节数为 4–5 倍 | — | — | 每项都胜过 vecline |
因此,--preset poster 在 kodak-parrots 的 SSIM 上胜过我们(0.8643 对 0.8460),并且在合成照片的三项质量指标上全面领先。而 --color-precision 8 --gradient-step 4 --filter-speckle 0 在全部四个彩色测试样本上的颜色误差都比我们低——代价是文件大小增加 4–5 倍、耗时约为 2.4 倍。我们在肖像和灯塔上的 SSIM 优势依然保持;面对最后一组配置时则仅略微领先(0.9140 对 0.9099;0.9453 对 0.9074)。
这些调优我们也做得到;关键在于,“每项都领先”的结论是在无人调优这些工具的情况下得出的,而认真比较的读者会进行调优。
另一个必须如实说明的情况是:三张照片中有两张,vtracer 的文件更小。 肖像为 987 KB,而 vecline 为 1338 KB;灯塔为 1032 KB,而 vecline 为 1743 KB;只有鹦鹉图上 vecline 更小(309 对 344)。这些字节数换来了上述质量——约 10 dB 的 PSNR 差距绝非舍入误差——但如果你优化的是文件大小,且 0.76 的 SSIM 已经够用,那么这笔权衡中 vtracer 更胜一筹,这张表不该假装并非如此。
在 v1.38.1 中,这些数字对我们不利,数值有所下降。 此前,vtracer 的数据是通过第三方 napi 绑定
@neplex/vectorizer测得的。现在改为使用 vtracer 自己发布的二进制文件(1.0.0-alpha.3,默认配置,未调优)进行测量;该版本在质量相同的情况下生成的文件小得多——绑定生成的文件为 1661 KB,而现在为 987 KB。因此,早先的表格声称 vecline“每次都能生成更小或大小相当的文件”,但对照 vtracer 实际发布的工具,这一说法是错误的。运行VECLINE_VTRACER=/path/to/vtracer npm run compare即可复现。
渐变输出(--gradients;在照片的自动模式下默认启用)会将平滑的色彩过渡——如天空、肤色——重建为 SVG 渐变,而不是平坦色带。每个区域都会分别拟合 <linearGradient>(方向性渐变)和 <radialGradient>(暗角、圆形高光、聚光灯),并保留渲染效果更接近的一种——径向对称的渐变根本没有线性方向,因此会从区域质心开始,按距离向外拟合。在合成暗角图上,它能以约 350 字节实现 0.998 的 SSIM 重建效果;相比之下,平坦色带需要 3–8 KB,SSIM 仅为 0.73–0.88。它只会带来改善:只有当渐变的实际渲染输出(按渲染器的 sRGB 色标插值重现,并在 Oklab 中逐像素评分)优于其所替代的平坦色带时,区域才会变为渐变,因此平面图稿的字节内容保持完全不变,硬边缘也不会受到影响——这只会集中改善色带问题,绝不会导致退化。
几何图元(--primitives)会进行相反类型的清理:当一个区域确实是圆形、椭圆形、矩形、圆角矩形(常用的 UI/图标形状——<rect rx>)或扇形(饼图扇区或圆环片段——所有图表的组成形状)时,vecline 会输出真正的图元,而不是用四段贝塞尔曲线近似。在纯色圆盘上,文件会缩小 68%(一个 42 px 的圆是 <circle cx="60" cy="60" r="42">,而不是路径);四扇区饼图则会从 2323 字节的贝塞尔曲线缩减为 488 字节的真实弧线。与其他所有 JS 描摹工具不同,这种形状在 Illustrator/Inkscape 中仍可作为形状编辑,并且导出为 CAD/DXF 时会成为真正的弧线。替换受残差阈值控制:只有当区域边界与拟合形状的偏差不超过 --primitive-error 像素(默认为 1.0)时才会替换,绝不会把有机形状强行拟合成圆形。detectPrimitive() 是纯函数,位于 vecline/core 中。
<rect> 确实能做到渲染结果完全相同。曲线图元——<circle>、<ellipse> 和扇形——则是一种经过衡量的取舍,并不能保持渲染结果不变:理想弧线无法精确贴合像素阶梯,因此会牺牲约 0.02 的 SSIM,以换取尺寸仅为原来一小部分、且 CAD 工具能识别为真实弧线的几何图形。扇形是按区域而非按颜色识别的,因此饼图切片即使与图例重复使用颜色,描摹结果仍是弧线;当扫角小于约 0.6 弧度或半径小于约 15 px 时,拟合器会放弃拟合并退回到贝塞尔路径,从而确保安全。
数据(运行 npm run bench:features 即可复现——每个数值都是将 SVG 渲染回像素后评分得出,并非断言值):
| 案例 | 基线(SSIM/大小) | vecline 功能(SSIM/大小) |
|---|---|---|
| 径向渐变 · 暗角 · 8 种颜色 | 0.7887 / 4.5 KB | 0.9646 / 759 B |
| 径向渐变 · 暗角 · 12 种颜色 | 0.8745 / 7.9 KB | 0.9977 / 356 B |
| 径向渐变 · 暗角 · 16 种颜色 | 0.9193 / 10.2 KB | 0.9977 / 356 B |
图元 · 圆盘 → <circle> | 1.0000 / 681 B | 0.9897 / 182 B |
渐变行的准确度都高得多,文件大小也比它们替代的平色带小了一个数量级;图元行则以不到 2% 的渲染差异为代价,换来体积缩小 73%、且可作为形状编辑的文件。
照片结果的由来。 早期版本在合成照片上的表现落后于 imagetracerjs,说实话,当时并不知道原因。
scripts/diagnose-photo.mjs拆解了处理流程并找到了问题:主要损失来自曲线拟合器,而非量化(SSIM 损失 0.11–0.24);在准确度和文件大小两方面,1 px 的拟合容差都严格劣于 0.4。重新调整默认值后,大部分差距得以弥合。诊断脚本会保留下来,以便下次遇到类似说法时能用测量验证,而不是靠猜测。
还有两点注意事项。potrace 本质上是二值工具;它的彩色数据行使用了附加功能 posterize,如果不说明这一点就报告结果,会让比较失去公允性。另一个问题是,最强的商业描摹工具(Illustrator Image Trace)和 AI 矢量化工具尚未经过测量——在这里,vecline 在可安装的开源工具中处于领先地位,但尚未证明它胜过所有工具。
关于调优选项,而不只是准确度。 除了数值之外,vecline 现在也提供了这些工具各自知名的控制选项:potrace 的 --threshold/--black-on-white 二值模式及 Otsu 自动阈值处理、用于处理对角线自接触的六种 --turn-policy 模式,以及 --fill-strategy(mean/dominant/median);imagetracerjs 的保边 --blur、--stroke-width 边界重绘(这里经过了测量,不同于上游:对不透明图稿能提升准确度,对透明图稿则会产生光晕),以及 --right-angle 角点吸附(这里将其从精确测试推广为容差,因此也能校正量化后偏离直角一两度的角点);potrace 和 imagetracerjs 都不提供的 --gradients 输出;以及 vecline edit 和 vecline/ops 入口点背后的 sharp 编辑管线。唯一刻意未照搬的是 potrace 用于灰度分色的直方图 rangeDistribution——vecline 的 Wu + Oklab-Lloyd 量化器是明显更好的默认选择,而 --fill-strategy 已经为需要指定代表色的调用方提供了选择。
指标
--verify 会报告标准的全参考度量指标,并按照相关文献的定义方式计算,使其数值可与其他工具的结果比较:
- PSNR — 峰值信噪比,单位为 dB。图像逐位完全相同时才为
∞。 - SSIM — Wang 等人,2004,使用高斯加权的 11×11 窗口、σ=1.5 和
valid卷积(排除边界像素而非填充,与参考实现和 scikit-image 一致)。 - CIEDE2000 — 色差的均值、p95 和最大值。已通过 Sharma、Wu 和 Dalal 测试集的全部 34 对数据验证;该测试集专门用于发现大多数实现都会算错的色相角不连续问题。
- 像素精确匹配率 — 四个通道均匹配的比例。
比较默认采用预乘 alpha,因为完全透明像素下存储的颜色不可见:两张渲染结果相同的图像,测量结果也必须相同。使用 --alpha-mode straight 可改为比较原始存储字节。
测量任意描摹器,包括其他人的工具
verify 和 diff 并非仅适用于 vecline。它们可以接收任意工具生成的任意 SVG,并与源光栅图像进行评分——处理流程中没有任何部分会检查或关心文件由哪个工具写入:
# Score another tool's output. Most tracers emit a transparent ground, so give
# it the colour the artwork was meant to sit on, or every transparent pixel
# counts as a difference.
vecline verify source.png their-output.svg --render-background '#ffffff'
# As a CI gate: exit code 2 if it misses the bar.
vecline verify source.png their-output.svg --render-background '#ffffff' --fail-under 0.98
这个标志并非无关紧要的细节。对于一张 200×200 的圆盘,同一份图稿在白色背景下的评分为 29.92 dB,不带背景时则为 2.12 dB;因此,不使用此标志进行比较,测量的就不是竞争描摹器的表现,而是它们是否恰好输出了背景矩形。Vecline 检测到此不匹配时会发出警告,而不会让这个数值误导人。
--json 会输出一个稳定的对象——psnr、ssim、ssimLuma、rmse、exactRatio、deltaE{mean,p95,max}、maxChannelDiff、lossless,以及尺寸信息——方便脚本根据其中任意指标进行判断。退出代码为 0、1、2,具体说明见 --help。
如需在同一语料库上与 potrace、imagetracerjs 和 vtracer 进行完整的横向比较,可在本仓库的克隆版中运行 npm run compare,执行整套测试;node scripts/fetch-corpus.mjs 会获取图像,并记录其来源和许可信息。
添加你自己的描摹器。 评分方式始终与工具无关,但参测工具名单并非如此。传入命令,即可让它加入所有测试组,并以完全相同的方式进行测量:
npm run compare -- --tool "mytracer=/path/to/bin --input {in} --output {out}"
{in} 接收 PNG,{out} 是预期输出 SVG 的位置;该标志可以重复指定。确切命令会打印在表格上方,因为比较结果是在评价他人的软件,读者应当能够核实具体的调用方式。
工作原理
pixel — 从平面图稿中获取精确几何形状
贪心网格划分会将每个颜色区域分解为最大的矩形:只要颜色保持不变,就向右延伸;只要整个跨度的颜色仍保持不变,就向下延伸。同一种颜色的所有矩形都会放入同一个 <path> 中。
这两个细节都是保证精确性的关键:
- 每个矩形的角都位于像素网格上的整数坐标,因此抗锯齿光栅化器在内部计算出的覆盖率恰好为 1.0,在外部则恰好为 0.0。没有需要舍入的部分覆盖率。
- 覆盖率会在合成前沿整条路径累积,因此,同一路径中的相邻矩形不会产生分属不同元素时可能出现的细线缝隙。
trace — 从任何图像中提取曲线
quantise → segment → trace contours → fit curves
- 颜色量化。 Wu 算法会在 32³ 网格上构建 3D 颜色矩,并拆分能带来最大方差降低的色彩框——过程确定,不使用随机种子。随后在 Oklab 中进行 Lloyd 松弛以优化调色板,因为 Wu 算法在 sRGB 中最小化方差,而相同的数值距离并不代表相同的视觉差异。Alpha 会单独在 1D 中聚类,因此柔和边缘不会占用颜色名额。
- 分割。 使用两遍并查集提取连通分量,采用 4 邻接。
- 轮廓提取采用裂缝跟踪——沿像素之间的边界而非像素中心行走。每个顶点都落在整数格点上,因此未经简化的多边形栅格化后会逐位还原原始掩码,不会产生半像素偏移。
- 曲线拟合。 Douglas–Peucker 算法找出结构上重要的顶点,角点检测将真正的尖锐特征与平缓弯曲区分开来,Schneider 最小二乘算法则对角点之间的原始格点拟合三次 Bézier 曲线——简化后的轮廓只决定在哪里断开,绝不会用作数据。
同色区域共用一个带有 fill-rule="evenodd" 的 <path>。由于每个区域的路径都会包含其所包围对象的边界,因此区域不会覆盖自身的孔洞;无论绘制顺序如何,嵌套关系都能正确处理。
大小预算,附带凭据
“描摹后的 SVG 太大”是矢量化工具最常听到的抱怨,而常见的解决办法是一个不透明的简化滑块,却从不告知你付出了什么代价。Vecline 会渲染并评分自己的输出,因此预算可以附带一份凭据:
vecline vectorize logo.png --max-bytes 40KB
Budget met in 3 steps
size 92.0 KB → 38.4 KB (-58%) target 39.1 KB
accuracy 0.9994 → 0.9971 −0.0023 SSIM paid
搜索会按阶段逐步放宽限制,先采用成本最低的调整方式:先降低曲线拟合精度,再舍入坐标,只有在这些连续调整手段都用尽后,才会舍弃整个特征或调色板条目。这个顺序不仅关乎精度——结构性调整是不连续的(删除一种颜色可能会让文件大小一下减半),因此先采用平滑调整,普通二分搜索才能真正达到目标,而不是越过目标、跌入下一个断崖。你得到的是仍符合预算的最精确结果,而不是碰巧符合预算的第一个结果。--max-nodes 则以锚点数量为目标,这更符合插画师编辑时的体感;两者相互独立,因为它们的变化并不同步(舍入坐标可以减少字节数,却不会删除节点)。如果预算低于这些调整手段所能达到的范围,工具会明确说明,并返回最接近的实际结果,而不会假装已经达到预算。
--extend-under — 一个边界,而非两个
区域按从大到小的顺序绘制,因此绘制小形状时,其下方的大形状已经绘制完毕。这意味着区域的填充无需在共享边缘处停止——它可以继续延伸到下方,渲染结果不会改变,因为后绘制的区域会重新绘制这些像素。
vecline vectorize chart.png --extend-under
由此可得两点。两个区域共享的边界通常会被描绘两次,分别作为各自的边缘;而这里仅描绘一次。此外,两个相邻形状之间可能出现的发丝般细的接缝抗锯齿也不会出现,因为已没有可供其显露的连接处——--stroke-width 正是用来覆盖这种接缝的;而且实测发现,它的作用不止于填补间隙:重新绘制这些只被部分覆盖的边界像素,在测试语料上最多能提升 +0.085 SSIM,字节数增幅不到 3%。除 --preset clean 外,其他情况下默认均关闭,因为它也会绘制到轮廓之外,从而使透明图稿在深色背景上出现光晕。其效果取决于几何图形是否与像素网格对齐:与网格对齐的多边形连接处不会产生抗锯齿,因此描边只会使图形变粗;而 clean 的曲线会留下真正的发丝状细缝——在下方没有内容时渲染,logo-tux 在 2.7x 放大下会漏出 943 个像素,描边可将其减少到 66 个。
默认关闭,原因有实测数据支撑,而非凭空假设:
| 测试图 | 尺寸变化 | 渲染结果 |
|---|---|---|
| 平面图稿 | −28.3% | 相同 |
| 小型徽标 | +100.6% | 相同 |
| 鹦鹉(照片) | +125.8% | 相同 |
| 灯塔(照片) | +128.9% | 相同 |
每次渲染结果都逐位相同——vecline verify a.svg b.svg 会报告 PSNR ∞、SSIM 1.000000——因此这纯粹是在文件大小与接缝之间权衡。对于色块少且较大的平面图稿,它的效果明显;对于后绘制的颜色分布零散的图像,它的效果则很差,因为此时它描绘的并集比被替换的区域更复杂。请在自己的图像上进行测试,不要全局启用。
--target-ssim / --target-psnr
进行描摹、渲染和测量,并逐步提高参数,直到达到目标。每一步都会将调色板加倍,并收紧几何容差,因为失败原因各不相同——颜色太少会表现为色带,容差太宽则会导致细节被圆整。程序会保留最佳尝试结果,因此即使目标无法达到,也会返回最接近的结果,并附上未达标的说明。
CLI
vecline vectorize <input>
| 选项 | 说明 |
|---|---|
-o, --output <file> | 输出路径(默认 <input>.svg) |
-l, --lossless | 保证结果逐位完全一致,否则失败 |
-t, --transparent [color] | 移除背景;省略时自动检测颜色 |
--bg-tolerance <n> | 像素与背景色的距离允许达到多少(Oklab 距离) |
--bg-everywhere | 移除任意位置的匹配像素,而不仅仅是从边缘向内移除 |
--feather | 柔化抠图边缘,而不是保留硬边 |
--prefer <what> | auto(默认)、geometry、size — 无损模式的优化目标 |
--max-geometry-ratio <n> | 在 --prefer auto 下,实际几何图形允许放大的倍数(默认 4) |
-m, --mode <mode> | auto、lossless、pixel、trace、embed |
-p, --preset <preset> | logo、lineart、poster、photo、detailed、pixelart、exact |
-c, --colors <n> | 调色板大小,1–256 |
--alpha-levels <n> | 要保留的不同透明度级别 |
--min-area <px> | 将小于此面积的区域并入相邻区域 |
--tolerance <px> | 轮廓简化容差 |
--fit-error <px> | 曲线拟合的最大误差 |
--corner-angle <deg> | 判定为尖角的转向角度 |
--polygon | 输出多边形而不是曲线 |
--threshold <cutoff|auto> | 跟踪前按亮度缩减为两种颜色(Potrace 风格的二值化;auto = Otsu) |
--black-on-white <bool> | 配合 --threshold 使用:深色像素作为形状(默认 true) |
--speckle-scope <all|isolated> | --min-area 可吸收哪些小区域。all(默认)会吸收所有低于阈值的区域;isolated 只吸收被单一颜色包围的斑点,并保留抗锯齿边缘。在 logo-tux 上测得:小于 8px 的连通区域中,只有 40.2% 是孤立区域,其余都是边缘像素 — 因此 isolated 在保真度效率翻倍的同时缩小文件(0.00041 SSIM/KB 对比 0.00086);在带 JPEG 伪影的照片上,得分为 0.8657 SSIM,而 all 为 0.5488 |
--severity | 配合 --verify 使用:同时报告误差的位置。SSIM、PSNR 和平均 ΔE 都是全局汇总值,无法区分沿每条边缘均匀分布的少量无害抗锯齿噪点与一整块错误区域 — 在百万像素图像上取平均后,两者得分相近。将不同像素扩展以移除单像素细丝,对其聚类,并按面积平方和评分,使连贯的色块比散布的噪点权重更高。增加一项 Composite:对准确度、结构和连贯性取几何平均,因此任何一个表现良好的维度都无法掩盖另一个维度的严重退化 |
--adaptive | 根据每个像素自身的邻域进行阈值处理(Bradley–Roth),而不是对整帧使用同一个阈值。适用于纸张照片,其中一个角落处于阴影中,单一全局值无法兼顾两端:当页面上的墨迹始终比其局部纸张暗 35% 时,Otsu 的 F1 得分为 26.4%(阴影部分被整片判入前景),而此选项得分为 100%。对于最需要此功能的 centerline 和 gcode 也有效。默认关闭 — 对光照均匀的图像,Otsu 是更好的估计方法 |
--adaptive-window <px> | --adaptive 的邻域边长;0(默认)表示较短边的八分之一 |
--adaptive-t <pct> | 比局部均值低多少才算墨迹(默认 15) |
--blur <1-5> | 量化前进行选择性保边模糊 — 去除颗粒而不使边缘变软 |
--blur-delta <n> | --blur 的保边阈值(默认 20) |
--stroke-width <n> | 使用各路径自身的填充色描边,重新绘制边界处覆盖一半的像素。可测得不透明图稿的准确度提升(在 0.5 时,语料库中的 6 个样本全部提升,字节数增加不到 3%)— 但描边会延伸到轮廓之外,因此会使深色背景上的透明图稿出现光晕 |
--underpaint / --no-underpaint | 在所有内容下方用单一路径绘制一次不透明轮廓,避免内部接缝透出背景。两块相接的填充是两个元素,在它们共享的边缘处,这一对元素最多会让像素有四分之一透明;如果没有背景矩形遮挡 — 也就是说,对于带透明度的图稿 — 缺口会沿每条内部边界显现。在 --preset clean 中默认开启:logo-tux 的漏绘像素从 3,621 个(其中 3,171 个与轮廓相距甚远)降至 319 个,且完全没有远离轮廓的漏绘;alpha-dice 则从 1,350/725 降至 243/16,两项指标均胜过付费竞品。logo-tux 的 gzip 大小增加 4%,alpha-dice 增加 12%;不会新增文件中原本没有的坐标;对于没有透明像素的图稿则不产生任何效果 — 输出逐字节完全一致 |
--turn-policy <p> | 处理对角线自接触:left(默认)、right、black、white、minority、majority(Potrace 的 turnPolicy) |
--fill-strategy <how> | 如何从颜色簇中选择每种调色板颜色:mean(默认)、dominant、median(Potrace 的 fillStrategy) |
--right-angle | 将接近水平或垂直的直角拐角吸附到精确的 90° — 让 UI、屏幕截图和像素艺术更加清晰(imagetracerjs 的 rightangleenhance) |
--right-angle-threshold <deg> | --right-angle 的角度容差(默认 12) |
--gradients | 将平滑的颜色渐变(天空、肤色、品牌图稿)重建为 SVG 渐变。仅当渐变能以可测量的精度拟合,且节省的色带数多于其自身成本时才会采用 — 与多色带平面方案进行公平比较,而不是让更简单的模型无法胜出的原始误差竞赛 |
--primitives | 识别圆形、椭圆、矩形、圆角矩形和扇形(饼图/圆环图),并输出真实形状 — 更小、可编辑,可为 CAD 提供真正的圆弧 |
--primitive-error <px> | --primitives 的每顶点残差预算(默认 1.0) |
--layers | 为每种颜色输出一个命名的 Inkscape/Illustrator 图层 — 可编辑,并可用于丝网印刷/乙烯基分色 |
--palette <colors> | 精确使用这些以逗号分隔的颜色进行跟踪(品牌色/专色),例如 "#fff,#e4002b,#000" |
--no-optimize | 不合并可由单条曲线拟合的相邻曲线 |
--opt-tolerance <n> | 曲线合并的误差预算 |
--quadratics | 如果二次曲线(q)能在三次曲线原本需满足的误差预算内描述相同区间,则用二次曲线代替三次曲线(c) — 用四个数字代替六个。每次替换都根据跟踪得到的点进行测量,而不是根据被替换的三次曲线,因此承诺的容差不变。默认关闭;在 --preset clean 下开启后,约 90% 的曲线会转换,原始大小减少 25%/gzip 大小减少 23% |
--refine-iterations <n> | 构建调色板时的 Lloyd 松弛迭代次数 |
--precision <n> | 路径坐标保留的小数位数 |
--no-background | 不将主导颜色合并为一个矩形 |
--target-ssim <v> / --target-psnr <db> | 持续提高质量,直到达到目标 |
--max-bytes <size> | 大小预算 — 放宽条件,直到 SVG 符合大小限制(40KB、1.5MB 或纯字节数),并报告为此付出的准确度代价 |
--max-nodes <n> | 复杂度预算 — 放宽条件,直到几何图形的锚点不超过此数量 |
--verify | 渲染结果并与输入进行比较测量 |
--embed-strategy <s> | auto、preserve、png、webp |
--xlink | 为 SVG 1.1 使用者使用 xlink:href |
--json | 在 stdout 上输出机器可读内容 |
vecline rasterize <input.svg>
| 选项 | 描述 |
|---|---|
-o, --output <file> | 输出路径(默认 <input>.png) |
-w, --width / -h, --height / -s, --scale | 输出尺寸 |
--dpi <n> | 物理单位(mm、pt、in)的分辨率 |
-b, --background <color> | 在图稿下方绘制背景;支持 #rgb、#rrggbbaa、rgb() 或颜色名称 |
-f, --format <fmt> | png、jpeg、webp、avif、tiff、gif |
-q, --quality <n> | 有损编码器质量 |
--lossless | 无损 WebP/AVIF |
--shape-rendering <mode> | geometricPrecision(默认)、crispEdges、optimizeSpeed |
--font-dir <dir...> / --default-font <family> | <text> 的字体解析 |
--verify | 重新解码编码后的文件,并报告编码器造成的损失 |
rasterize 的 --verify 会测量编码器,这是用户实际会遇到的问题——请求 JPEG 却悄悄丢失 alpha 通道,或质量设置造成的损失超出预期。
其他命令
vecline edit photo.jpg -o small.png --resize 800x --grayscale # resize, rotate, crop, tone
vecline crop photo.jpg -a 1:1 -o avatar.jpg # content-aware crop — keeps the subject
vecline component logo.png -o Logo.tsx -f react --current-color # raster → typed component
vecline favicon logo.png -o public/ # full favicon/PWA set + manifest + <head> HTML
vecline responsive hero.jpg -o img/ # AVIF/WebP/fallback ladder + <picture> markup
vecline placeholder hero.jpg -f blurhash # BlurHash string (or -f svg for a tiny LQIP-SVG)
vecline palette art.png --css # perceptual palette as CSS custom properties
vecline extract keep.svg -o out.png --against original.png # byte-identical recovery
vecline convert in.png out.svg # direction inferred from input + extension
vecline convert photo.png thumb.webp # …including plain raster → raster
vecline doc report.pdf -o pages/ --dpi 150 # PDF (or SVG) → one image per page
vecline office report.docx -o report.pdf # Word/Excel/PPT ⇄ PDF (uses your LibreOffice)
vecline pdf page1.png page2.jpg -o album.pdf # combine images into one multi-page PDF
vecline centerline drawing.png -o strokes.svg # single-stroke medial-axis paths
vecline gcode drawing.png --tool laser --feed 800 # ready-to-run laser/plotter G-code
vecline convert logo.png out.dxf --units mm --physical-width 80 # cuts at exactly 80 mm
vecline convert logo.png out.dxf # CAD/CNC/laser vector export (also .eps, .pdf --cmyk)
vecline optimize icon.svg -o icon.min.svg # render-preserving SVG minify
vecline sprite icons/*.svg -o sprite.svg # pack many icons into one <symbol> sheet
vecline animate loading.gif -o loading.svg # animated GIF/APNG/WebP → one CSS-animated SVG
vecline verify a.png b.svg # measure any two images
vecline diff before.png after.png -o diff.png # perceptual visual-regression heatmap
vecline batch 'src/**/*.png' -o out/ --to svg --summary "$GITHUB_STEP_SUMMARY"
vecline mcp # MCP server: expose vecline as tools to AI agents/IDEs
vecline serve # local bridge: let Vecline Studio use your LibreOffice
vecline serve — 在浏览器中转换 Office 文档,无需上传任何内容。 浏览器标签页没有 Office 引擎,而通常只有两种办法能提供一个:打包约 300 MB 的 LibreOffice-in-WASM,或将文档上传到别人的服务。前者会破坏即时加载体验;后者会彻底破坏隐私承诺。所以两者都不选。运行 vecline serve 后,Vecline Studio 会将文档交给你的机器进行转换,再取回转换结果:
npm install -g vecline && vecline serve
整个流程的每个环节都在本地完成——.docx → 你的 LibreOffice → PDF → 标签页内的 mupdf → 像素 → 与这些像素进行评分的描摹 SVG。
它的设计目标就是经受攻击,而且后来确实经受了攻击。 桥接服务只绑定到 127.0.0.1;只接受来自 vecline.xyz 或通过 --allow-origin 指定的来源的请求;仅对这些来源响应 Chrome 的 Private Network Access 预检请求;拒绝被重绑定的 Host;将单个请求正文限制在 64 MB,并且最多同时进行两次转换;只公开两个端点,且只接收字节并返回字节——请求中的任何字段都不会指定文件,因此无从遍历。它只会在你启动时运行。
由四个 Agent 进行的对抗性审查——每项发现随后都通过实时服务独立复测——没有发现任何边界被突破,但发现了四处加固不足的问题,现已全部修复:
- 转换失败时过去会返回 LibreOffice 的原始错误,其中包含临时路径、主目录和账户名称。现在只发送为用户编写的提示信息;详细信息会保留在你的控制台中。
- 过去任何端口上的环回来源都被允许,因此所有从 localhost 提供服务的页面都能完全访问桥接服务。现在只允许你指定的来源。
- 并发数量过去不受限制——六个并发的 64 MB 请求正文会使内存占用从 100 MB 增至 722 MB。
--token=过去会在未启用身份验证的情况下悄悄启动。现在会直接拒绝启动。
建议使用 --token-file <path> 或 VECLINE_TOKEN,而非 --token:任何本地账户都能从进程表中读取命令行参数,而令牌要防范的对象正是这个主体。
在本地运行 Studio?请指定开发来源:vecline serve --allow-origin http://localhost:5173。
构建时。 Vite/Rollup 插件会在构建过程中将资源矢量化——导入带查询后缀的图像,就能直接获得矢量图,无需 CLI 步骤:
// vite.config.ts
import vecline from 'vecline/vite';
export default { plugins: [vecline()] };
import logo from './logo.png?svg'; // the traced SVG string
import Logo from './logo.png?component'; // a React/Vue/Svelte/Solid component
它返回纯 Vite/Rollup 插件对象(不依赖 unplugin),而纯 TS 核心避免了基于 sharp 的插件带来的原生二进制 CI 难题。
CI 原生支持。 GitHub Action 会在每次推送时矢量化/优化图像资源,并将节省空间的统计表直接写入作业摘要:
- uses: shunyagatha/Vecline@v1
with:
files: 'assets/**/*.png'
to: svg
这是一个基于 vecline batch … --summary "$GITHUB_STEP_SUMMARY" 的轻量级组合式 Action,因此继承了纯 TS 核心零原生二进制的安装优势——无需在 CI 中编译 libvips。你也可以在任何地方运行 vecline batch … --summary <file>,生成相同的 Markdown 报告(逐文件列出输入/输出大小和带正负号的节省空间量)。
原生支持 AI。 vecline mcp 会启动一个 Model Context Protocol 服务(stdio),让 AI Agent 或 IDE — Claude、Codex、VS Code、Cursor、Continue — 可以直接调用 vecline:“将这个徽标矢量化”、“把这幅图转换成激光 G-code”、“这个 SVG 和 PNG 有多接近?”。包含 12 个工具(vectorize、convert、centerline、measure、diff、crop、doc_to_images、office_convert、images_to_pdf、palette、placeholder、image_info),以及一个零依赖的 JSON-RPC 实现,不会增加任何安装内容。将客户端的 MCP 配置指向 { "command": "npx", "args": ["vecline", "mcp"] }:
// Claude Desktop: claude_desktop_config.json — Cursor, Windsurf and Continue take the same shape
{
"mcpServers": {
"vecline": { "command": "npx", "args": ["-y", "vecline", "mcp"] }
}
}
// VS Code: .vscode/mcp.json (workspace) — note the key is "servers", not "mcpServers"
{
"servers": {
"vecline": { "type": "stdio", "command": "npx", "args": ["-y", "vecline", "mcp"] }
}
}
# Codex: ~/.codex/config.toml — or .codex/config.toml to scope it to one project
[mcp_servers.vecline]
command = "npx"
args = ["-y", "vecline", "mcp"]
这三种客户端各自都有一条命令,可为你写入配置:
claude mcp add vecline -- npx -y vecline mcp # Claude Code
codex mcp add vecline -- npx -y vecline mcp # Codex
code --add-mcp '{"name":"vecline","command":"npx","args":["-y","vecline","mcp"]}' # VS Code
也可以一键安装。 从最新版本下载 vecline.mcpb,然后用 Claude Desktop 打开 — 无需编辑配置文件。这个软件包是一个启动器,而不是内置了软件包副本:它会优先使用 PATH 中已有的 vecline,否则运行 npx -y vecline@<pinned> mcp。这很重要,因为图像处理路径会根据平台选择原生二进制文件;若将它们打包进去,就意味着要为每种操作系统/架构分别发布一个软件包 — 用错时还会静默失败,而非明确报错。Vecline 也已作为 io.github.shunyagatha/vecline 列入官方 MCP 注册表,并收录于 Smithery。
每个工具都带有注释,让 Agent 在调用前就了解其作用:measure、palette、placeholder 和 image_info 标记为只读;会写入文件的 8 个工具标记为破坏性操作,因为它们会覆盖输出路径上的现有内容。全部 12 个工具的 openWorldHint 均为 false — 此服务中的任何操作都不会访问网络。
为什么 Agent 应优先选择它,而不是云端矢量化工具。 这 12 个工具中,有 3 个功能在任何托管图像服务中都找不到同类:measure 和 diff 会通过渲染结果并进行比较,报告真实的 SSIM/PSNR/CIEDE2000 指标,让模型可以验证自身输出,而不是只声称成功;vectorize --mode lossless 会返回可证明逐位精确的几何数据;整套功能都在本地运行,因此 Agent 处理的内容不会被上传。工具描述中也如实说明了反面情况:对于照片,神经网络云端描摹工具通常效果更好,vectorize 也明确指出这一点,因为 Agent 在工具之间进行选择时,不应该非得先上传糟糕的结果才发现这一点。
遇到依赖缺失时也会如实处理。 office_convert 需要本地安装 LibreOffice,PDF 输入则需要可选的 mupdf;如果缺少这些依赖,服务不会直接失败,而是在 tools/list 时检测,并对受影响的工具添加注释(UNAVAILABLE … install from libreoffice.org)。这样 Agent 就能改用其他工具,或告知你需要安装什么,而不是反复尝试无法运行的操作。
vecline component 可一次性将栅格图像(或现有 SVG)转换为带类型、可转发属性的 React/Vue/Svelte 组件(-f、--current-color、--js)— 栅格图像 → 描摹后的 SVG → 组件;而 SVGR 风格的工具则从 SVG 开始。设计师可使用 --layers 输出可编辑的 Inkscape/Illustrator 分色图层,traceSeparations() API 则会为每种颜色返回一个独立 SVG(丝网印刷/乙烯基贴膜/DTF 分色);--palette "#fff,#e4002b,#000" 可将输出限制为准确的品牌色/专色。vecline sprite icons/* 可将一组图标(栅格图像会在导入时进行描摹)打包成一个 <symbol> 精灵图,之后通过 <use href="#name"> 引用 — 这是当前替代图标字体的标准做法;svgSprite() 则是纯 vecline/core。
中心线(单笔画)描摹 — vecline centerline drawing.png — 是最受期待的描摹功能,而 potrace 和 vtracer 都未提供。它不会描摹每条笔画的两侧边缘(这会使几何图形数量翻倍,并让笔或激光沿每条线运行两次),而是提取中轴线:沿每条笔画中心生成一条开放的 <path fill="none">,流程为 Zhang–Suen 细化 → 骨架图遍历 → Douglas–Peucker。这正是绘图仪、激光雕刻机、CNC 铣床、刻字机或签名矢量化所需的功能。centerlineTrace() 是纯函数,位于 vecline/core。而 vecline gcode drawing.png --tool laser|pen 则完成最后一步——生成可直接运行的 GRBL 风格 G-code 刀具路径(进给/功率/单位/缩放,Y 轴翻转以适配工作台坐标),在其他工具止步于 SVG、让你另找单独的 svg2gcode 时,它提供了少见的端到端 JS 图像→机器流水线。
超越 SVG 的矢量导出是其他 JS 描摹工具都未涉足的创客/CAD 领域:vecline convert in.png out.dxf 会写入 DXF(带真彩色、Y 轴朝上的闭合 LWPOLYLINE),可供 LightBurn、LibreCAD、Fusion 以及所有激光/铣床/刻字机导入;out.eps 会写入 EPS/PostScript;out.pdf 会写入适合印刷的 PDF,并支持通过 --cmyk 进行印前处理(免费 JS 工具都会退回到 sRGB,而 vecline 不会)。这是零件,不是图纸。 --units mm --physical-width 80 会写入声明自身比例的 DXF($INSUNITS,并为读取该值的导入器设置 $MEASUREMENT),同时以真实物理尺寸输出坐标。如果不设置,DXF 就完全没有单位信息——而 LightBurn、LibreCAD 和 Fusion 各自采用不同的默认值,因此同一个文件会因打开它的程序不同而以三种不同尺寸切割。等发现问题时,材料已经切好了。测试套件会重新解析输出的坐标,并断言几何图形的实际跨度符合承诺的距离,因为仅在标题中声明毫米、却仍使用像素坐标,会通过其他所有检查。
而且 DXF 支持圆弧:圆孔或圆形凸台会写成真正的 CIRCLE/ELLIPSE 实体——机器只需一个平滑动作就能切出一段圆弧——而不是写成多段线所需的一圈短弦。(导出器默认执行基本图形识别;传入 primitives: false 可输出原始多段线。)traceGeometry()/toDxf/toEps/toPdf API(均为纯函数,位于 vecline/core)会公开同样的结构化贝塞尔几何——现在每个子路径还带有 primitives 注释——供自定义流水线使用。
资源流水线将流程延伸到 Web 开发者实际部署的内容:vecline favicon 会从一个源文件生成完整的 favicon/PWA 图标集(多尺寸 .ico、Apple 触控图标、192/512 + 可蒙版 PNG)、manifest.webmanifest 和 <head> 标记;vecline responsive 会生成 AVIF/WebP/回退格式的宽度阶梯,以及可直接使用的 <picture>/srcset 标记;vecline placeholder 会输出 BlurHash 字符串或小型 LQIP-SVG(SQIP 的零二进制文件后继方案),用于模糊渐显加载;vecline palette 会以 JSON 或 CSS 自定义属性形式提取感知主色调色板。所有适合纯 TS 实现的功能都会采用纯 TS(blurHash、lqipSvg、extractPalette 位于 vecline/core)。
动画栅格图像 → 动画 SVG — vecline animate loading.gif 会描摹每一帧,并将它们叠加为单个自包含的 CSS 动画 SVG(通过负值 animation-delay 实现翻页动画,无需 JavaScript)。所有帧共用同一个调色板,因此颜色不会逐帧闪烁,放大后也不会出现 GIF 常见的模糊或色带。第 0 帧会作为静态海报,供不支持动画的渲染器或 prefers-reduced-motion 降级显示。framesToAnimatedSvg() 是纯函数(vecline/core);traceAnimation() 负责读取帧(Node)。很少有 JS 工具能将栅格动画转换为动画矢量图。
**动画 GIF、APNG 和动画 WebP 均可使用。**GIF 和 WebP 帧来自 libvips。APNG 不支持,也仍然无法支持:预构建的 sharp 二进制完全不会报告其页数——metadata().pages 是 undefined,而不是 20——因此 APNG 以前会作为单帧图像加载,animate 则会返回只有一帧的 SVG,也不会提示其他帧已被丢弃。
因此,这里会自行读取 APNG,而不委托给其他组件处理。APNG 帧是普通 PNG 图像数据,分散在 fdAT 块中,且移除了头部,所以 src/io/formats/apng.ts 会还原头部,再将完整的 PNG 交给真正的编解码器;src/io/apng-compose.ts 会合成这些帧——在各自偏移处按 SOURCE/OVER 绘制,然后按 NONE/BACKGROUND/PREVIOUS 进行处置。
已在九个符合性测试文件上与 Pillow 逐帧验证,涵盖所有处置和混合运算符、调色板、灰度+Alpha、16 位图像以及一个真实的 20 帧动画。其中七个文件子像素差异为零。另外两个文件仅 Alpha 通道不同,而且问题出在 Pillow:在不透明画布上合成 50% Alpha 的蓝色,结果必须是不透明的,因为只要 a_d 为 1,a = aₛ + a_d(1 − aₛ) 就等于 1。Pillow 返回 191,即 aₛ + aₛ(1 − aₛ)——它用源图像自身的 Alpha,而不是刚生成的画布进行混合。
文档转图像——vecline doc report.pdf -o pages/ --dpi 150 可将 PDF、SVG 或 Office 文档(.docx/.xlsx/.pptx/ODF——通过你的 LibreOffice)渲染为逐页栅格图像,支持任意 DPI 或缩放比例,以及任意输出格式(--format png|jpeg|webp|avif、--pages "1,3-5")。传入 --format svg 可将每一页矢量化——一步将扫描页或栅格页转换为真正可缩放的 SVG。因此,vecline doc slides.pptx -o thumbs/ -f webp 会为每张幻灯片生成一张 WebP 缩略图。SVG 渲染使用内置的 resvg;PDF 渲染遵循 vecline 的自带编解码器原则——它会动态加载可选的纯 WASM mupdf 包(无需编译原生二进制),如果未安装,则会打印一行安装提示,因此基础安装保持轻量。renderPdfPages() / isPdf() 已导出,供程序化使用。
图像 → 单个 PDF——vecline pdf scan1.png scan2.jpg -o album.pdf 会将一组照片或扫描件拼接为一个多页 PDF,每页一张图像,支持任意 --dpi。PDF 由原始字节组装,并带有正确的交叉引用表(每页都以 DCTDecode JPEG 嵌入),因此文件体积小且可在任何地方打开——这是 vecline doc(PDF → 图像)的自然补充。imagesToPdf() / assembleImagePdf() 已导出。
Office 文档 ⇄ PDF——vecline office report.docx -o report.pdf(以及反向转换,如 scan.pdf -o out.docx,还支持 Excel、PowerPoint、ODF、RTF、HTML、CSV——任何 LibreOffice 可转换的格式组合)。要忠实渲染 Word/Excel/PowerPoint,需要完整的办公软件引擎,因此——与 PDF 完全相同——vecline 不捆绑任何内容,而是通过普通子进程调用你的本地 LibreOffice:不增加任何依赖,安装包保持轻量,文档也绝不会离开你的设备(不像大多数转换器,不会上传到云端)。目标格式根据输出扩展名推断;如果找不到 LibreOffice,错误信息会告知如何安装,或提示通过 VECLINE_SOFFICE 指定二进制路径。使用 glob 和 --to 一次转换整个文件夹:vecline office "docs/**/*.docx" --to pdf -o out/ 会为每份文档生成一个 PDF(不同文件夹中同名的文件会以不同名称区分,绝不覆盖)。convertOffice() / convertOfficeBatch() 已导出,供程序化使用。
内容感知裁剪 — vecline crop photo.jpg -a 1:1(或 16:9、4:5、-w 512 --height 512)— 调整画面以匹配目标宽高比,保留有趣的主体,而不是画面中心。它会根据每个候选窗口包含的边缘能量和色彩饱和度为其评分(将 Sobel 重要性图汇总为积分图像,因此每个窗口都能在 O(1) 时间内完成评分),原理与热门的 smartcrop.js 相同,但使用纯 TypeScript 实现,不依赖任何依赖项,因此浏览器中也能在 vecline/core 运行 smartCrop() 和 cropImage()。如果主体偏在一侧,朴素的居中裁剪会把它裁掉;而这种方式会跟随主体调整。
vecline edit 将 sharp 的处理管线封装在一条命令后,并按固定、合理的顺序执行(几何 → 色调 → 色彩 → 形态学 → 合成 → 收尾):--resize/--fit、--rotate、--flip/--flop、--crop、--trim;--blur、--sharpen、--median、--clahe <WxH>、--gamma、--normalize;--grayscale、--negate、--sepia、--tint <color>、--threshold <0-255>、--brightness/--saturation/--hue/--lightness;--dilate/--erode;--unflatten;使用 -b, --background 进行扁平化。任意仿射变形、边缘填充(extend)、逐通道 linear 色阶、自定义 convolve 卷积核、3×3 recomb 以及多图像 composite,可通过 vecline/ops 库入口使用。它仅支持 Node(需要 sharp),并作为独立子路径发布,因此浏览器和边缘端打包不会将其引入。
vecline verify --fail-under 0.98 会在 SSIM 低于阈值时以非零状态退出,因此可用作素材管线的 CI 检查门槛。verify 提供数值,而 vecline diff before.png after.png 则会显示变化位置:它生成 pixelmatch 风格的热力图,以浅色背景显示淡化后的基图,并将所有超出 CIEDE2000 阈值的像素标为红色——采用感知差异,因此一位舍入误差不会让整幅画面亮起,而真实的色相偏移则会被捕捉。--fail-over 0.01 可根据变化像素比例设置 CI 检查门槛;diffImages() 是纯函数(vecline/core),因此同一热力图也能在演练场或浏览器测试运行器中于客户端渲染。
库
import { loadRaster, vectorize, rasterize, compareImages } from 'vecline';
import { readFile, writeFile } from 'node:fs/promises';
const source = await loadRaster(await readFile('logo.png'));
const result = await vectorize(source, {
mode: 'auto',
verify: true,
targetSsim: 0.98,
});
console.log(result.mode); // 'pixel'
console.log(result.lossless); // true — measured, not asserted
console.log(result.quality?.psnr); // Infinity
await writeFile('logo.svg', result.svg);
// SVG → raster
const png = await rasterize(await readFile('logo.svg'), {
width: 2400,
encode: { format: 'png' },
});
await writeFile('[email protected]', png.buffer);
所有转换函数在文件系统层面都是纯函数——只有 loadRaster 会读取文件,没有任何函数会写入文件——因此该库可以组合到你现有的任何管线中。更底层的组件(quantize、connectedComponents、traceComponents、fitLoop、PathBuilder、ssimPlane、deltaE2000)也都已导出。
局限性
坦率地说明这些问题,因为在投入使用之前,你应该了解:
- 描摹照片不会得到看起来像照片的结果。 这是固有的限制,并非调优问题。重视保真度时请使用
embed;更重视可扩展性和可编辑性时请使用trace。 - 对照片输入使用
pixel模式大约需要为每个像素生成一个矩形。Vecline 会拒绝处理,并提示你改用embed或trace。 embed模式中的 WebP 载荷仅适用于浏览器。 不支持 WebP 的 resvg 和 librsvg 构建版本会将 WebP<image>渲染为空白,且不会发出警告。因此,auto永远不会选择 WebP;使用--embed-strategy webp可选择启用,并会打印警告。pixel和trace会将 16 位源图像降为 8 位,因为 SVG 绘制服务器无法表示更高位深。使用--embed-strategy preserve的embed会保留原始位深。- 不支持动画。 多帧输入仅使用第一帧。
trace的内存占用随图像面积增长;处理超大照片时,最好先缩小图像。- 未内置 HEIC、JPEG XL、JPEG 2000、PSD、PDF 和相机 RAW 支持——预编译的 libvips 缺少这些编解码器。你可以通过
vecline/codecs注册自己的编解码器;请参阅格式。 vecline/core无法读取或写入压缩图像文件。 PNG、JPEG、WebP 等格式需要真正的编解码器;core 可处理 BMP、ICO、PNM 和 TGA,因为此软件包自行实现了这些格式。在浏览器中,可使用createImageBitmap/ canvas 解码,并使用canvas.toBlob编码。- 较小的描摹容差会优先保证准确度,而非减少平滑曲线的数量。 重新调校后的默认值(0.4px)会将一段平滑的大弧线处理成精细、像素级准确的多边形,而不是几个 Bézier 曲线。这对于照片和像素保真度来说是正确的选择;但如果你希望徽标由少量可编辑曲线组成,请使用
--preset logo(容差为 0.65),或自行调高--tolerance。这两个目标确实存在取舍;默认设置优先保证准确度。 - 未与商业描摹工具进行比较。 比较涵盖 potrace、imagetracerjs 和 vtracer——这些工具可以安装并通过脚本调用。尚未对 Illustrator、Vector Magic 和 Vectorizer.AI 进行测量,因此不对它们作出任何评价。
更新日志
每个版本都会打上标签,并记录在 CHANGELOG.md 中;该文件索引了 GitHub 上的完整说明,包括更正早期说法的版本。
贡献
欢迎贡献——请参阅 CONTRIBUTING.md。
git clone https://github.com/shunyagatha/Vecline.git
cd Vecline
npm install
npm run build
npm test
测试样例由代码生成,而不是作为二进制文件提交:无法通过差异审查的图像数据块是一种隐患,而图像库中的重采样变更会悄无声息地使基于它构建的所有预期失效。
致谢
本项目借鉴了以下人士的成果:Xiaolin Wu(颜色量化)、Philip J. Schneider(曲线拟合)、Peter Selinger(potrace,其边界描摹方法为本项目提供了启发)、Björn Ottosson(Oklab)、Wang、Bovik、Sheikh 和 Simoncelli(SSIM),以及 Sharma、Wu 和 Dalal(CIEDE2000)。本项目基于 libvips(通过 sharp)和 resvg 构建。
隐私政策
Vecline 不收集任何信息。 没有服务器可供发送数据:所有转换操作——描边、测量、裁剪、PDF 和 Office 渲染——都在你的设备上完成,你的图像和文档绝不会上传。
- 收集: 不收集任何信息。没有文件、账户、分析数据、遥测数据或 Cookie。
- 存储: 文件在内存中处理,并且只写入你指定的位置。
- 第三方: 不会共享任何信息,因为我们不收集任何信息。MCP 桌面扩展可能会在首次运行时从 npm 获取
vecline软件包;Office 转换会调用你设备上已安装的 LibreOffice——这两者都不会传输你的文档。 - 保留: 不保留;没有已存储的数据可供请求或删除。
- 验证方式: 转换时查看浏览器的 Network 标签页,或者断开互联网连接并继续使用。
完整政策:https://vecline.xyz/privacy.html · 联系:提交问题
支持 Vecline
Vecline 免费,并将始终免费。此库、CLI 和 MCP 服务采用 MIT 许可。Studio 永久免费使用、没有使用限制,也无需注册——它不是其他产品的试用版——其源代码也已公开,任何人都可以验证它绝不会上传图像;但它是源代码可用,而非开源:你可以自由阅读、构建和审查,并在重新分发或托管之前查看 其许可协议。这里没有任何内容会对不付费的人隐藏,也没有任何付费层级引导你升级。
如果它帮你省下了一个下午,而你想表达谢意:
tiptopjar.com/vecline — 一次性捐赠,无需账户。二维码指向同一链接,方便用手机访问。
同样有帮助且完全免费的方式还有:提交附有问题图像的错误报告、提供 Vecline 输给其他工具的测量数据,或给项目点个星标。前两者比捐赠更有价值——本项目的核心主张是其输出经过测量验证,而这只有在人们持续检验的情况下才能成立。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
