图片转手工图纸工具 ArtCraft:线稿提取、十字绣图纸生成与激光切割模板
先说明白ArtCraft 不是一个平台也不是什么天顶星科技它是我自己攒出来的一套“图片转手工图纸”工具集。做它的原因特别朴素我发现手作圈里最难的不是动手而是从一张想法图到一张能照着做的图纸之间隔着一大堆脏活累活。我试过不少现成方案要么只能出线稿要么转十字绣图纸时颜色处理得和垃圾一样要么输出尺寸不对导出来不是糊就是缺线。最后决定自己写一套。ArtCraft 解决的核心问题就三个把图变干净、把边缘变清楚、把像素变成可打印的格子。如果你平时做十字绣、拼豆、丝网印版、激光切割前处理或者只是想把照片变成黑白线稿用来练手那这篇文章里的经验可以直接抄。1. ArtCraft 的第一版动机不是缺工具是工具之间对不上1.1 现成工具为什么老差一步最开始我没打算写程序。我拿手机上的各种滤镜 App 试了一圈问题很清楚线稿类的 App 适合处理线条清晰的漫画一遇到照片就翻车背景纹理全变成一团乱麻。十字绣图纸生成器倒是多但配色逻辑基本是“把图缩成小图然后硬抠颜色”经常出现同一块区域里混进去十几个相近色号绣的时候光换线就换到崩溃。拼豆图纸工具更极端只支持固定几色稍微复杂点的图就直接糊成色块。这不是工具不好而是每个工具都把自己封装成了“一键出图”的黑盒。手工创作流程偏偏不是一条直线同一张照片做线稿、做十字绣图纸、做激光切割模板需要的处理完全不一样。比如线稿希望保留轮廓、弱化阴影十字绣希望颜色离散、色块连通切割模板则要求每条线都是闭合的、能识别的路径。这不是换个滤镜能解决的需要把图片处理拆开每一步都能自己控制。所以我决定写一套小工具名字就叫 ArtCraft。听起来像 “art craft”实际做的事情也就是这两件事把图像当素材把手工当目标中间全部变成可重复的工程流程。1.2 把工作流拆成三段而不是一个“魔法按钮”ArtCraft 的设计逻辑很直白分阶段处理每个阶段留下中间文件。整个流程拆成三段预处理、特征提取、排版输出。预处理管的是“这张图能不能用”。包括降噪、校正亮度、清理背景、确定最终分辨率。特征提取管的是“我们要从图里抽出什么”比如边缘、色块、轮廓。排版输出管的是“怎么把结果变成可打印或可切割的文件”包括 SVG、PDF、带网格的十字绣图纸以及拼豆图纸。拆开的好处是同一张图预处理后我可以分别拿去提取线稿和做十字绣配色不用重新调一套参数。中间文件都是 PNG看哪一步不对就直接改哪一步。如果做成一个黑盒按钮你永远不知道问题出在降噪还是阈值设置上。ArtCraft 的命令行界面也保持了这个思路前后三个阶段完全独立。2. 预处理这关不过后面全是白做2.1 先降噪再转灰度顺序不能乱很多人拿到照片第一件事就是转灰度然后直接跑边缘检测。这个顺序其实是个坑照片里的噪点会在灰度图里被放大边缘检测会把噪点当成细节输出一条条细碎的短线到后面根本没法用。正确顺序是先用高斯模糊或者中值滤波把噪点压下去再转灰度。我当时在 ArtCraft 里的第一版预处理代码大概是这样的import cv2 import numpy as np def preprocess(image_path): img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 这一步先降噪不要先转灰度 denoised cv2.GaussianBlur(img, (3, 3), 0) gray cv2.cvtColor(denoised, cv2.COLOR_RGB2GRAY) return gray高斯模糊的核大小很关键。核越大图越柔但边缘细节损失也越大。对一般手机照片3×3 或者 5×5 都够用扫描出来很旧的手绘稿可以用 5×5噪点特别严重的夜景照片才需要上到 7×7。我见过有人一上来就用 9×9结果边缘全变成了圆弧线稿直接废掉。宁可后面多做几次形态学处理也别一开始就把细节磨没了。中值滤波在某些场景下比高斯更好尤其是处理椒盐噪点比如扫描稿上的黑点和白点。高斯模糊是加权平均中值滤波是取邻域中位数对孤立的噪点更狠。所以 ArtCraft 里我留了两个选项普通照片用高斯扫描稿建议切到中值。2.2 背景处理拍出来的照片总有脏背景手工图纸最怕的其实是背景。一张照片里的桌子纹路、光影渐变、甚至镜头暗角都会在边缘检测阶段变成一堆莫名其妙的线条。最好的解法不是后期擦除而是在预处理阶段就把背景压平。ArtCraft 里我加了一个“背景压平”选项原理很简单先对灰度图做一个大核的高斯模糊得到背景估计图再用原图减去背景估计图得到去掉光照不均的“平坦图”。这一步对白底照片特别有效能解决镜头暗角和偏色问题。代码上其实就是def flatten_background(gray, kernel_size(21, 21)): background cv2.GaussianBlur(gray, kernel_size, 0) flat cv2.subtract(gray, background) return flat这个操作的本质是“高通滤波”把低频背景变化去掉保留高频细节。处理完以后整张图的明暗分布会变得均匀边缘检测就不会因为右下角暗一点就狂出假边缘。要注意的是这个操作会把纯色区域也变成接近中灰所以如果要保留画面原有的光影关系这一步就别开。2.3 先定导出尺寸再处理图否则一定返工这个坑我踩得最深。以前我总是先处理图最后导出时才想打印尺寸结果很多线稿在屏幕上刚刚好缩到 10cm 见方以后细线全部挤成一团。现在 ArtCraft 的预处理阶段就会询问目标尺寸和分辨率后续所有操作都在这个基础上进行。道理很简单一张 2000×1500 像素的图如果要在 A4 纸上打印分辨率定 300 DPI那有效打印区域大概是 16.9cm×12.7cm图不可能完整铺满 A4。不提前算好后面所有的处理都是白费。3. 线稿提取Canny、自适应阈值和我试过的三种组合3.1 Canny 不是跑一下就行阈值要配原图情况边缘检测里最常用的就是 Canny艺术生成里也被用到烂。但很多人以为 Canny 就是两个阈值随便填的数字其实它内部要经过梯度计算、非极大值抑制、双阈值滞后连接好几步。低阈值决定了哪些弱边缘候选会被保留高阈值决定了哪些强边缘肯定存在中间的部分看是否与强边缘相连。如果低阈值太高细线条直接消失高阈值太低噪点和阴影全被画成线。我常用的起始值组合如下实际使用时再按图调整原图情况高斯模糊核Canny 低阈值Canny 高阈值干净的屏幕截图、矢量图(3, 3)40120普通手机拍摄的照片(5, 5)50150扫描的旧画稿(5, 5)60160纹理很重的物体照(7, 7)80180这只是起始参考不是固定参数。艺术处理本来就没有“唯一正确答案”所以 ArtCraft 里我把这些参数全部开放每次处理都输出预览图方便反复调。3.2 彩色图转线稿的坑明度接近的物体会消失线稿提取最容易翻车的场景不是噪点多而是两个物体的颜色完全不同、但明度很接近。比如红苹果放在深灰桌布上肉眼很清楚分界但转成灰度以后两者的灰度值几乎一样边缘检测就是认不出来。ArtCraft 里我试过三种解法。第一种是先用颜色聚类把不同色区分开再分别提边缘第二种是把 RGB 转换到 Lab 色彩空间用 a 通道和 b 通道的变化辅助找边缘第三种更粗暴直接把原图饱和度作为另一个信息通道参与梯度计算。实测下来Lab 方法最稳因为人眼对颜色的感知差异本来就接近 Lab 空间的统计特性。如果你手头的工具只支持灰度输入可以先把图转成 Lab取 a 通道和 b 通道的最大梯度作为边缘输入效果会好不少。3.3 线条细化与闭合直接从边缘图打印会糊边缘检测出来的线是“边缘”不是“画出来的线”。直接拿去打印线条粗细不均匀而且会有大量断点视觉效果非常差。ArtCraft 在后处理里加了两步第一步是形态学闭合把小缺口补上。原理是用一个 3×3 的十字形结构元素做膨胀再腐蚀把距离比较近的断裂边缘连起来。第二步是骨架化把粗边缘缩成 1 像素宽的线条。OpenCV 主库里的形态学操作只负责粗调和细化方向真正的骨架化在扩展模块里如果环境里没装也可以用连续腐蚀加条件恢复来近似实现。kernel cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) closed cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations1)这一步做完线稿的线条才会像人画的线而不是像一块密度不均匀的斑。我的经验是如果最终用途只是参考可以用比较粗的线如果是要做丝网印版或激光切割模板线条必须尽量细同时还要闭合。否则填充、切割的时候会出现识别不到路径的问题。4. 十字绣图纸模式真正的像素换算工程4.1 先定布规格再定最大格数十字绣图纸和线稿完全是两回事。十字绣需要把连续图像离散成一个个格子每个格子就是一个针脚。ArtCraft 做十字绣模式时第一步不是处理颜色而是先问你用的是多少格的布。常见的布规格有 11ct、14ct、18ct意思是每英寸多少格。14ct 布每英寸 14 格换算成公制约 1.8mm 一格。一张 18cm×18cm 的绣绷大约能容纳 100×100 格。这个数字直接影响后面的图片缩放比例如果你硬把一张 800×600 的图塞进 100 格宽度很多细节会直接被压没。ArtCraft 里我一般这么算先设置最大宽度格数比如 100然后按原图宽高比算出高度格数。这样能保证图纸不变形。如果你希望图纸更精细可以减少针脚尺寸对应的成品面积或者直接换大尺寸绣布而不是盲目增加像素采样密度。4.2 颜色量化不是简单缩到 16 色照片缩得很小以后每个像素的颜色其实还是连续的直接取色会出现大量相近色绣的时候根本分不清。必须做颜色量化把整张图的颜色归纳成有限数量。我试过最简单的方法是均匀量化就是把 RGB 每个通道直接砍成几档比如 4×4×464 色。这个方法速度飞快但结果非常差尤其是肤色、天空渐变这些区域会出现明显的色带断层。后来改用 K-Means 聚类效果立刻好了。K-Means 的思路是把颜色在 RGB 空间里分成若干簇每簇的中心色作为代表色每个像素换成离它最近的代表色。代码上 OpenCV 自带实现速度也不慢pixels image.reshape(-1, 3).astype(np.float32) criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 20, 0.001) _, labels, centers cv2.kmeans(pixels, 24, None, criteria, 3, cv2.KMEANS_PP_CENTERS)关于聚类色彩空间直说结论在 RGB 空间里做聚类效果只能说勉强能用转到 Lab 空间再做颜色差异更符合人眼判断尤其是相近色多的时候。代价只是多了两次色彩空间转换计算时间几乎可以忽略。十字绣图纸一般建议控制在 1632 色之间再多就不是绣图了是给自己上刑。4.3 线号映射与符号图纸量化完颜色以后还要把每个代表色映射到实际可买的绣线上。ArtCraft 用的是“预设线号表 最近邻匹配”把常见绣线的 RGB 色值存在一个 CSV 里处理时计算每个代表色与线号表中所有颜色的欧氏距离取最近的那个作为最终线号。实际做的时候有个细节如果直接用 RGB 欧氏距离可能会把偏蓝的灰匹配成偏绿的灰。更好的做法是先转 Lab 再算距离或者至少对 RGB 做加权比如绿色通道权重大一点。我自己的线号表是从几套主流色卡里整理出来的几百行够用。没有现成表的话可以先挑常用的 3040 个线号手工采样色值再慢慢扩。输出图纸时ArtCraft 会生成两种视图彩色格子图和符号图纸。彩色格子图方便对着屏幕看符号图纸则是在每个格子里印一个符号同一线号用同一符号旁边附线号对照表。符号要用简单的 ASCII 字符或者 geometrie 符号不然格子太小印出来全是墨团。设计符号表本身就是个工作量但这一块值得做细。5. 输出 PDF/SVG 时的尺寸、线宽与出血问题5.1 页面尺寸用毫米不要用像素这是很多自制工具最普遍的问题代码里算着方便直接用像素当单位输出结果打印出来完全不是预想大小。ArtCraft 在导出 PDF 时全部用毫米作为基础单位只有在计算像素映射时才考虑 DPI。举个例子14ct 布上一格是 1.8mm。如果图纸要做成 100×80 格成品实际尺寸大约是 180mm×144mm。放到 A4 纸上打印四周还要留边距所以图纸上的格子不能画满整页。ArtCraft 会先计算出每个格子的毫米尺寸再根据页面大小计算缩放比例确保打印出来刚好能对着绣布比划。5.2 线宽设置打印稿和切割稿是两套逻辑输出线稿 SVG 的时候线宽必须区分用途。打印用的线稿我一般设置 0.2mm0.3mm太细的话打印机会糊太粗的话又不像画。激光切割用的模板则完全不同切割线通常要标注成红色线宽可以极细比如 0.05mm只要机器能识别就行真正的切割宽度由激光头功率决定而不是 SVG 描边宽度。如果你输出的是 P)] I need ensure no weird P. Continue.SVG 给激光切割机用内部路径必须闭合。ArtCraft 在导出前会做一次“路径闭合检查”对每个轮廓判断起点和终点是否足够接近如果差距超过阈值就自动连接。这个步骤看似不起眼但漏掉一个缺口切割机上就是一块废料。Need fix line maybe.5.3 闭合轮廓检查填充和切割都怕断口从线稿提取得到的边缘天然是一堆不闭合的线段。做打印图纸还没什么问题但做填充或者切割模板就完全不行。ArtCraft 里我做了一个补救流程先用形态学闭合再用轮廓查找函数把图像转成轮廓列表然后逐个轮廓检查闭合状态。对于距离小于 5 像素的断口直接用直线连接大缺口则标记出来人工确认后再导出。这个方法算不上智能但在实际项目里非常可靠。因为手工图纸的精度要求不一定要到像素级只要轮廓不断后续无论是绣、印还是切都能正常工作。6. 翻车案例与改进方向实测中我最常遇到的四类图6.1 高噪声手机照片一放大全是毛刺手机照片在弱光环境下会自带大量噪点尤其是天空、墙面这些大面积纯色区域。最开始我拿这种图直接跑 ArtCraft 的线稿模式出来的边缘图像一张被猫抓过的毛线团。后来我把中值滤波加进了默认预处理流程同时在边缘检测之后增加了一个“去小连通域”操作把所有面积小于 20 像素的白色区块全部删掉。这样虽然会丢掉一些极细的亮线但整体干净程度提升了非常多。6.2 逆光人像脸部直接成黑块逆光照片的问题不是细节多而是动态范围太大。亮部和暗部对比极强量化以后脸部变成几个大黑块五官全丢了。ArtCraft 的解法是加了一步 gamma 校正把暗部提亮压缩整体对比度再做颜色量化。gamma 值在 1.02.2 之间可调逆光图通常取 1.41.8 效果比较好。这里没有万能公式需要根据直方图判断。6.3 纹理复杂的风景图转化后像马赛克灾难草坪、树叶、水面这类高纹理区域颜色量化后会出现大量孤立色点十字绣图纸基本没法看。我后来加了一个后处理步骤对量化结果做众数滤波也就是每个格子的颜色取周围 3×3 区域里出现次数最多的颜色。这个操作能把孤立点压掉让色块更连续。6.4 线稿追踪很多边缘不连续用 ArtCraft 提取线稿时我发现细线和交叉线很容易在骨架化之后断掉。根源是 Canny 的双阈值滞后连接对弱边缘不友好细线一旦某段稍微模糊就会被判成弱边缘然后丢弃。补丁方案还是形态学闭合加断点连接但更好的办法是先对原图做一次锐化让细线的边缘更强再跑 Canny。锐化核推荐 3×3 的拉普拉斯算子加原图权重不要过度否则会带出白边。最后再分享一个小技巧ArtCraft 发展到后面我越来越觉得手工图纸工具的核心不是算法多高级而是“每一步都能看得到中间结果”。不管你是做线稿、十字绣图还是切割文件中间 PNG 一定要保留参数调坏也不至于从头再来。现在的版本已经附加了拼豆图纸模式和绣绷尺寸计算激光切割模板的路径标注也做得差不多了。说实话早期用现成工具的时候我总是被“一键生成”骗得团团转现在自己做这套工具反而是被那些最朴素的中间文件救了无数次。