
Hi,我是松柏。
最近我女朋友迷上了拼豆,就是那种把彩色小豆子摆成图案、用熨斗烫一下就粘在一起的手工。 做出来的东西很有像素风的味道,挂件、冰箱贴、杯垫都能搞。

但看她玩了几次之后我发现一个问题:找图纸太麻烦了。
网上能搜到的图纸就那么些,很多时候全靠在抖音上随缘刷到。 想把喜欢的图片变成拼豆图纸得手动把照片像素化,再一个格子一个格子地对色号,光配色就能配到怀疑人生。
作为一个程序员,这种重复劳动当然要交给代码来干!
所以我给自己的小程序加了一个「图片转拼豆图纸」的功能!上传一张图片,自动生成拼豆图纸,还带色号标注和用量统计,拿着图纸直接就能拼。

先简单科普一下,拼豆(也叫融合豆、Perler Beads、Hama 豆)是一种手工玩法。 把彩色的塑料小圆筒豆按照图案摆在带凸点的拼豆板上,摆好之后垫一层烘焙纸,用熨斗加热,豆子受热融化粘连,就变成一块完整的图案了。

豆子直径大概 5mm,板子常见 29×29、50×50 等规格。因为天然就是一个格子一个颜色,所以特别适合还原像素风的游戏角色、动漫人物、表情包。

市面上常见的品牌有 Mard、Artkal、Perler、Hama,每家的色号体系不太一样。我这个工具用的是国内最常用的 Mard 221 色板,9 个色系、221 种颜色可以选。
用法:上传图片 → 调参数 → 生成图纸

1)上传图片
在小程序里选一张图片上传:

2)调参数
有几个可以调的选项:
参数 | 说明 | 默认值 |
|---|---|---|
长边格数 | 成品图纸长边有多少格 | 60 格 |
网格线 | 是否显示格子间的分隔线 | 开 |
色号标注 | 格子里是否标注 Mard 色号 | 开 |
颜色数量 | 限制最多用多少种颜色 | 不限 |
图片增强 | 转换前是否增强对比度和饱和度 | 关 |
抖动 | 是否启用 Floyd-Steinberg 抖动 | 关 |

大部分情况下用默认值就行。如果觉得颜色太杂,可以把颜色数量限制到 24 或 48;如果原图比较暗淡,可以开轻度增强。
3)生成图纸
点一下生成,几秒钟就出来了。输出是一张 PNG 图,包含两部分:
把图纸保存下来,对着买豆子、摆豆子就行了。

这部分聊聊技术实现,怎么把一张几百万像素的照片,变成几十格宽、只有 221 种颜色的像素图,而且看起来还像原图。
拿到照片里每个像素的颜色之后,要从 Mard 的 221 个色号里找一个最接近的。
这里不能直接用 RGB 算距离,因为 RGB 空间里的数值接近和人眼看到的颜色接近不是一回事。
所以我用的是 LAB 色彩空间,这是专门为模拟人眼感知设计的,在 LAB 空间里算欧氏距离,匹配出来的颜色比 RGB 自然得多。

我最初实现的版本是直接把照片缩小到目标格数,每个格子就取一个像素。 但问题是如果照片细节比较多,单像素取样会丢掉很多信息,出来的图纸很乱,也不怎么还原。
但如果先把照片放大采样,再按格子区域求平均色,就会好非常多,相当于做了一次抗锯齿。

即使用了 LAB 匹配,221 种颜色还是不够覆盖所有色彩过渡。比如一片渐变天空,可能会出现明显的色带。
这时候可以开启 Floyd-Steinberg 抖动。原理是把每个像素的颜色匹配误差扩散到周围的像素,让整体看起来更平滑。 但代价是图案会多一些噪点,适合渐变多的照片,线条清晰的图反而不建议开。

量化之后经常会冒出一些孤立点,就是某个格子的颜色和周围完全不一样。这种在图纸上看着不明显,但实际拼的时候要单独挑一颗出来,就很烦。
所以我在最后加了一步孤立点清理:如果一个格子的颜色和上下左右都不同,就把它替换成周围出现最多的颜色。这样就能保证整张图是基本就干净的。

如果上传的是 PNG 透明底的图(比如一个抠好的角色),透明区域会被识别为空白格,不标色号、不计入用量统计。 这样图纸上只显示有内容的部分,买豆子的时候也不会多算。

用一张图总结,大概就是这样:

这个功能目前已经上线了,在我的小程序「松柏工具箱」里就能用。
其实整个开发过程挺有意思的,从最开始的能用但效果拉垮,到后面一步步优化采样、匹配、去噪,效果慢慢好起来,还挺有成就感的~
如果你也在玩拼豆,欢迎试试,有问题可以随时反馈给我!
下期再见,拜拜!👋🏻