资讯动态

当图像处理遇上手工艺:用Python和聚类算法实现参数化图纸生成

发布时间:2026/10/14 8:36:49 来源:尧图企业网站定制
artcraft是我在业余时间搭起来的一个参数化设计小项目名字就是把art和craft拼在一起。做它的初衷很简单我一直觉得数字设计和传统手工艺之间隔了条沟。一边是精确到像素的算法和屏幕色另一边是讲究触感、染色和痕迹的手作两边好像天然不搭。但我把第一张照片跑成十字绣图纸再真的拿针线绣出来之后这个想法彻底变了——数字工具不是来取代手工艺的它只是帮你在下针之前想清楚很多事。这个项目能做什么简单说它用Python把任意一张图片拆成网格、做颜色量化再映射成十字绣、拼豆、马赛克或者编织配色能直接用的图案稿。你不需要很会画画也不需要精通软件只要给出一张参考图设定网格大小和颜色数量它就能自动出一套带图例的图纸。如果你经历过把图片贴到像素软件里一格一格点颜色的日子应该能立刻get到这是个什么样的解放。文章适合谁看我猜至少三类人会觉得有用一是想尝试算法工具但不知道从哪入手的手工艺爱好者二是做过图片处理、想把能力延伸到实物创作的开发者三是沉迷于“做一个自己的小工具”的独立创作者。下面我会把整个项目的定位、核心算法、实操流程和踩坑记录都摊开讲希望能给你一点参考。1. artcraft到底做了什么定位和设计逻辑1.1 为什么把编程和手工艺放一起十字绣这个手艺本身是很“数字化”的。成品是一格一格的像素网格每一格是一个颜色这不就是数字图像的低分辨率降采样吗我第一次意识到这件事的时候手腕上还挂着绣绷子。既然绣图本身就是像素画那我完全可以用程序来生成它——把图片读进来缩到目标针数然后统计颜色配上色号再输出一张符号图。这就是artcraft最早的雏形。当时我手上有一张自己拍的风景照想绣成一幅宽度约80格的装饰画。以前我用过一些在线转图工具结果是颜色数量没控制打出来像马赛克又不够像马赛克符号密密麻麻绣到一半根本分不清哪个是哪个最崩溃的是它并没有告诉我每种颜色要用多少线量。很多免费工具只给你一张“能看”的图不给你“能直接用”的图纸。artcraft从设计上就要解决三件事颜色可控、符号可读、用料可算。这里面其实藏着一个选型逻辑。我没有选择直接使用现成的刺绣软件因为它们大多数偏向商业工作流学习成本高也没法按我的规则来定制。而用Python自己做等于把整个流程的每个旋钮都暴露在自己手里。想控制输出尺寸改一个参数就行。想限定色板换一个线色库文件就行。这种自由度对于个人项目很重要因为你总会在第三天发现工具里那个“不重要的选项”恰恰是你最需要的功能。1.2 技术选型Python Pillow OpenCV scikit-learn我选Python不是因为它是“最强的图像处理语言”而是因为它的库生态让这个项目的复杂度断崖式下降。Pillow负责常规图片读写和缩图OpenCV用来做更好的预处理比如降噪和边缘感知的缩放scikit-learn里现成的KMeans直接接管颜色聚类matplotlib用来排版和输出PDF。这四个库几乎把一个“完整图像处理可视化”流程的所有脏活累活都包圆了。这里也说一下为什么选KMeans而不是别的颜色压缩方法。图像有上百万种颜色但绣线色号最多也就几百种。如果直接取图片里出现频率最高的颜色画面里会有很多近似色每个色块都很碎如果单纯减少位深又会丢失整体层次感。KMeans聚类处理的是“把颜色空间切成K个代表色”每个像素认领离自己最近的代表色这样既控制了颜色总数又能在“像照片”和“像绣图”之间调平衡。实测中K在8到24之间是最常用的范围K值越大图案越接近原照片但工艺成本和颜色的区分难度也会上升。有些朋友会问为什么不用最新潮的深度学习方法。我的回答很简单深度模型确实能在某些场景下生成看起来更“聪明”的风格化结果比如自动补全细节、模拟笔触但它的问题是不可控——你要它保持绣图颜色严格限定在预置线色里它往往做不到而且跑一轮推理的时间是KMeans的几十倍。手工艺图纸的本质是“严谨的生产文档”不是“艺术滤镜”稳定可控比什么都重要。1.3 它能覆盖哪些手工艺场景artcraft虽然是从十字绣长出来的但核心逻辑并不依赖十字绣。只要把最后的“符号映射”换掉同一套颜色量化和网格化流程可以输出很多种手工设计稿。像素拼豆网格当作拼豆矩阵颜色映射到对应色号的胶珠成品是挂件或杯垫。串珠图输出单元格的颜色和对应米珠色号适合做斜纹或方格串珠包。马赛克拼贴要看马赛克颗粒的规格把图像映射为固定尺寸的作品。毛线编织的像素图案比如提花图案、刺绣贴核心同样是网格和配色。也就是说artcraft本质上是一个“统一的前端图片到网格加颜色标记”后端接什么样的手工艺完全取决于你想让它接什么。这个抽象在我后续迭代中帮了大忙我甚至只换了一个配置文件就让它能输出串珠配色清单。所以你现在看到的技术路线里有大量参数化设计的思想代码不绑定某个具体成品而是生成一份可被不同工艺流程消费的规范文档。2. 核心算法拆解从照片到手工图纸的关键步骤2.1 读图与预处理别让噪点毁了下针读图这步看起来最简单实际上最影响后续结果。如果你把一张原本是1200x800像素的照片直接缩小到80x100网格很多细节会以一种很糟糕的方式混成一团。为什么因为普通缩图算法会做平均采样边缘会发灰细小的高光会被“稀稀拉拉”地涂成噪声。更好的做法是先在原始分辨率下进行少量降噪比如5x5的高斯模糊把颗粒感磨平再缩图这样网格上的每个格子得到的是更稳定的颜色。当然降噪力度也要看用途。如果原图是光滑渐变多的照片模糊多一点没关系如果原图本身就是像素画或插画基本不需要模糊不然会把锐利的边界弄糊。我现在的管线里默认是低强度模糊同时开放了模糊半径参数让使用者自己判断。除了模糊还有一个常见问题是图片比例。十字绣网格通常不是正方形比如我的常用规格是80x100格。如果直接resize到80x100图片会被强行拉变形。所以我在resize之前会先做中心裁剪按目标宽高比裁好再resize。这一步别省不然绣了半天发现脸是歪的。预处理的另一个隐藏收益是“减少无效计算”。一张大图的边缘区域如果只是渐变天空对绣图而言信息量很低。裁掉多余比例后网格里的每一格都承载有效内容同样的网格数能表达更多有效主体。2.2 颜色量化怎么把上百万颜色压缩到二十种线色这是整个项目最核心的部分。我把颜色量化拆成两段第一段用KMeans把图片压缩成K个代表颜色。输入是一个[高x宽, 3]的二维数组每个元素是RGB值。KMeans会找到K个聚类中心然后把每个像素归到最近的中心。我们最后要的不是像素本身而是每个聚类的中心颜色和它所占的像素数量。像素数量可以换算成线量如果某个颜色占了四分之一个图案那它需要的绣线量大约是总针数的四分之一乘上单针用线长度就能估算耗材。第二段把聚类中心映射到实际绣线色号。照片里的颜色很自由线色却是一堆离散的色块所以我们要在预设颜色库里为每个聚类中心找“最近”的颜色。这里有个容易踩的坑如果直接用RGB欧氏距离来比结果经常偏。比如RGB空间里暗红色和暗棕色之间的距离可能比暗红色和红色之间还近那映射出来就会出现绣图整体偏棕。更可靠的方案是在Lab色彩空间算色差。我用了scikit-image的deltaE_ciede2000函数它是CIE的推荐公式考虑了人眼的非线性感知。虽然慢一点但颜色判定的准确度好很多尤其对肤色、天空这类颜色非常敏感的场景。这里我特别建议保留“聚类中心颜色”和“映射后线色”的对比步骤。拿两个小色条并排打印出来肉眼扫一眼就知道是量化损失还是映射错误。很多人直接跳到出图结果颜色不对还要反过来猜是哪个环节的问题。2.3 网格映射与符号映射让图纸“可读”颜色处理完之后每一个网格单元都对应一个颜色标签。接下来要做的是给每个颜色分配一个独立符号然后把网格画成“符号图”。符号图的意义在于十字绣图纸讲究的是“一眼能分清”而不是“好看”。同一块色区里如果全是碎碎点点的符号绣起来眼睛很累。我采用的做法是先给颜色按像素数量从多到少排序用简单的符号去匹配大面积色块例如大块底色用“空白带点”或“横线”小面积细节用“X”或“三角”这类明显符号。这个设计规律看起来不起眼但实际使用中能省很多眼力。生成符号图我用matplotlib绘制。每格画一个符号同时保留网格线图纸边上附一个图例列出“符号、颜色名、线号、所需线束数”。导出PDF的DPI设为300一张A4纸恰好容纳80x100格。这里有一个细节直接导出PDF时会发现符号很小打印出来容易糊。我的解决办法是加大fontsize并适当减少网格线宽度让符号成为画面的绝对主角。如果你想让图纸更像专业刺绣杂志的风格还可以引入“半格”和“特殊针法”概念。但这会显著增加生成逻辑的复杂度。我个人认为对业余手工爱好者来说保持纯一格一色、一格一符反而更容易上手。2.4 风格化控制让参数成为艺术表达的一部分很多人觉得“生成式工具做出来的东西千篇一律”这其实是参数没调好。artcraft里有两个最能影响风格的旋钮一个是grid_size也就是网格数。网格越多图案越写实但手工程量也会成倍上升网格越少成品越像抽象的“像素画”反而能突出大色块的冲击力。第二个旋钮是quantize_k也就是最终的颜色类别数。K低时画面会变成高对比度的海报风格K高时照片的细腻层次会出现但线色采购成本上升。我在一套作品里曾经用同一个原图生成过三个不同K的版本K8像一个复古游戏截图K24几乎能看清照片的光影过渡。两版放在一起气质完全不同。还有一个容易被忽略的控制项色彩平衡的预处理。比如给原图先做一点色相偏移或饱和度拉伸出来的绣图气质会变化很大。换句话说artcraft不能替你决定什么是“好看的图案”但它可以让你在不同方案之间快速做A/B测试——这比真绣完一个版本才发现不对要省钱得多。3. 实操记录完整跑通一套artcraft流程3.1 环境准备与依赖安装先说明一下我的运行环境Python 3.10虚拟环境。安装依赖很简单python -m venv .venv source .venv/bin/activate pip install numpy pillow opencv-python scikit-learn scikit-image matplotlib如果面向全新使用者这套依赖里最重的是scikit-learn和opencv-python但现代机器装起来基本也就一两分钟。我还是推荐用虚拟环境因为matplotlib和opencv对版本比较敏感直接装在全局环境里容易出依赖地狱。3.2 生成第一个十字绣样稿下面这段是我最初版本的核心代码做了精简但流程完整。我拿一张风景插画做测试目标网格80x100目标颜色12种。需要注意我这里为了展示逻辑省略了符号排序和线量统计但那两步在生产代码里不能省。import numpy as np from PIL import Image, ImageFilter from sklearn.cluster import KMeans import matplotlib.pyplot as plt from skimage import color from skimage.color import deltaE_ciede2000 # 1. 读图并预处理 img Image.open(input.jpg).convert(RGB) img img.filter(ImageFilter.GaussianBlur(2)) scale_w, scale_h 80, 100 img img.resize((scale_w, scale_h), Image.LANCZOS) pixels np.array(img).reshape(-1, 3).astype(np.float32) # 2. KMeans颜色聚类 K 12 kmeans KMeans(n_clustersK, n_init10, random_state42, n_jobs-1) labels kmeans.fit_predict(pixels) centers kmeans.cluster_centers_.astype(np.uint8) # 3. 映射到绣线色板 thread_colors np.array([ [238, 233, 233], [220, 224, 225], [216, 178, 163], # 简化的示例色板 [190, 193, 180], [137, 153, 173], [148, 177, 200], [117, 146, 170], [166, 198, 190], [211, 203, 179], [222, 120, 129], [112, 71, 72], [57, 48, 49], ], dtypenp.float32) lab_centers color.rgb2lab(centers[None]) lab_threads color.rgb2lab(thread_colors) mapped [] for center in lab_centers[0]: dists deltaE_ciede2000(center, lab_threads) mapped.append(np.argmin(dists)) # 4. 重建网格图 grid labels.reshape(scale_h, scale_w) # 5. 用matplotlib绘制符号图 symbols [x, o, *, ., , s, ^, v, D, P, X, h] fig, ax plt.subplots(figsize(8, 10)) for y in range(scale_h): for x in range(scale_w): sym symbols[grid[y, x]] ax.text(x 0.5, y 0.5, sym, hacenter, vacenter, fontsize6) ax.set_xlim(0, scale_w) ax.set_ylim(scale_h, 0) ax.set_aspect(1) # 6. 保存PDF plt.savefig(pattern_output.pdf, bbox_inchestight, dpi300)这段代码跑完之后你会得到一份PDF打开以后是一页带网格的符号图。不过我强调一句这只是“最简可用版”我第一次跑出来的图谱面非常拥挤因为我没有按颜色像素数量重新排序符号导致同一个符号频繁来回跳。后面我在绘制前增加了一步统计每个簇的像素数量并排序面积大的簇分配到阅读性最好的符号。3.3 导出打印清样与排版打印这一步有很多细节决定成败。首先图纸不是直接按屏幕尺寸打印的它必须带物理尺寸标注。对十字绣来说绣布的规格通常是14ct意思是每英寸14个格子。80x100格的图案绣布宽度大概是80/14约等于5.7英寸不到15厘米所以其实是个很迷你的装饰画。在打印排版时我用matplotlib一次性生成三个元素网格符号主图、图例表、耗线量统计表。图例不是简单列颜色而是包含“线号、色名、每个符号、估算线束”。估算线束的公式是针数乘单针线长再除以每束线长。经验上14ct绣布每一针大约需要2.5厘米的线线束长度按8米计算。所以某一颜色的针数是1200针估算线束就是1200乘0.025除以8约等于3.75束。有一个坑我特别提醒这一步的图例表很容易超出页边距。你要么把整张图的字体调小要么把图例放到第二页。我后来干脆单独做了一页“材料清单”主图和图例分开这个改动极大减少了打印时的大量废纸。3.4 从数字图案回到实物一次真实的刺绣验证软件流程跑通之后我建议一定要做一次实物验证因为只有实物会把程序问题反馈得明明白白。我拿一张80x100格的风景样稿买好相应颜色的绣线选了14ct的白色绣布按程序给出的数量配线。实际绣的过程中出现了三件事第一大面积底色区域的符号确实好认但小面积高光区域因为符号太小几乎看得眼花。我当时把符号字体调大了两档问题才缓解。所以不要迷信默认参数打印出来对着光看如果一张A4纸上符号难分辨就直接加大字体或者减少网格数。第二电脑上的颜色匹配和真实绣线颜色相比总会有冷暖偏差。我的做法是把真实线拿到屏幕前比较给线色库重新校正了一遍RGB参考值。很多软件自带的色库由于色板印刷批次不同根本不是你所购买的实线颜色。手动的“屏幕对线”校准一次之后所有生成结果都会准很多。第三我意识到“自动生成的图案”不等于“好看的图案”。照片转出来的绣图局部细节很多但缺少工艺美术上讲的那种“色块规划”。后来我在流程里加了一个后处理步骤把小于某个阈值的孤立色块合并到邻近的大色块里。这一步能在很大程度上提升成品整洁度有点像图像处理里的“形态学闭运算”。效果很明显绣出来的画面不会到处都是零星的跳线。4. 高频问题与我的排查思路4.1 颜色怎么调都不对问题出在哪一个最常被问的问题“为什么转出来的颜色和我看到的照片差这么多”排查路径一般是三条原图有色偏比如从网上保存的照片可能带sRGB和Display P3的差异Python读出来就是偏的。可以先做一个白平衡或者手动调整通道增益。聚类数量不合适。如果颜色总数量小于目标增加K值但也要注意增加K值后线色映射环节经常把两个很接近的聚类映射成同一个线号等于白加。这时要检查“有效映射色数”而不是单纯的簇数。色板本身和真实绣线不一致。这是常见原因色板RGB值来自厂商官方文档实际绣线在不同灯光下看完全不一样。校准方法是按实线暗调。我还建议在代码里把“聚类中心颜色”和“映射后的线色”并排列出来拉一张对比图。立刻就能看出是量化损失还是映射错误。4.2 图片一大就卡内存和速度怎么平衡KMeans对数据量很敏感。如果你的原图有2000x2000像素直接输入KMeans就是四百万个样本点。在我的旧笔记本上跑一次K值16的聚类差不多要半分钟而且内存占用明显。解决思路很简单别拿原图做颜色量化先缩到目标网格尺寸。网格最大通常也就120x120也就是14400个像素这个数量级对KMeans毫无压力。我后来在代码里明确分了两条路径在缩到网格尺寸后聚类然后再用同一个模型把原图缩略图做颜色替换用于生成预览。整个过程最多两三秒。如果你确实需要在缩图之前做更精细的颜色统计可以先随机采样10万个像素做聚类。用numpy.random.choice取索引就行没必要全量计算。聚类模型本质上只关心颜色分布不关心像素空间位置采样不会带来明显偏差。4.3 图纸打印出来和屏幕有色差怎么办打印机色域比显示器窄颜色偏是不可避免的。我这里说的是“图案符号”层面不是打印色彩精度本身。如果你是打印出来给刺绣当参考实际上并不要求颜色完全一致只要求图例里的线号能对上。所以更实用的做法是把图例设计成黑白灰版本只靠符号和线号区分不依赖色块填充颜色。这样即使打印机颜色失调只要符号清楚线号列表清楚制作过程就不会出错。如果坚持要打印彩色预览图建议在导出时关闭色彩管理或选择“相对色度”渲染意图可以减少颜色压缩带来的偏差。另外用铜版纸和喷墨纸打出来的效果差很多普通家用喷墨纸吸墨太厉害颜色会变灰。4.4 符号图太大眼睛看花了怎么办我自己遇到过的最现实问题就是一张A4纸打印的80x100符号图符号又多又小绣到第20行就开始眼花。解决方向有几个增大打印尺寸A3甚至A2。网格线宽调细符号字体调大但总尺寸变大后绣的时候要频繁抬头看图其实也累。分块打印把图纸切成四个象限每一块单独打印成A4旁边标注拼接对齐点。这个方案对手工艺人最友好。降低符号复杂度把相近的颜色合并让图纸里的符号类别变少辨识度提升。电子图纸配合放大在屏幕或平板上看图能任意放大配合图例使用非常顺。我现在实际制作时就很少用整张纸而是把PNG传给平板放大到能看清每个格子。从我的个人经验而言“好看”和“好用”的图纸差别就在细节。很多在线生成的图案只考虑美观根本不管你在绣绷上连续操作时的感受。artcraft最大的价值是让我重新理解了“工艺图纸”不是装饰画而是一份严谨的生产文档。最后再分享一个我现在还在用的习惯每次生成一套图纸我都会把原图、网格大小、颜色数量、线色映射结果存成一个带参数记录的JSON。这样几个月后想复刻或者调整方案时不用重新捣鼓参数直接改一个种子值就能得到新的变体。这种“参数化留存”的思维方式或许才是这类项目给我带来的最大帮助。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑