我正在寻找一种方法来创建一个类似svg的路径从二进制图像(只有黑色和白色像素)。图像本身将是一个不规则形状的斑点,其中可能有洞。
如果没有孔,我只需要一个边界路径来重建blob的边界。当斑点中有洞时,我可以使用额外的路径(因为单独的路径无法重新创建这个,我猜)。最后,我只需要知道哪条路径是外部路径,哪些是洞。
我已经找到了这些:
- 如何在JavaScript画布中添加描边/轮廓到透明PNG图像
- 从图像边缘创建路径
- 我如何在二维矩阵中找到洞?
另外,我需要检测洞。对于我来说,结果是多边形还是路径并不重要。我只需要有足够高的精度的点,曲线保持弯曲:)
如果有人有一个想法,甚至一些进一步的来源,那就太好了。
PS:我正在使用画布和javascript (fabricJS),如果这有任何区别。
最后,我成功地使用了markE描述的另一个选项(尽管它稍作修改)。我使用Marching Squares Algorithm (MSA)
和Floodfill Algorithm (FFA)
来实现这一点。通过Ramer–Douglas–Peucker Algorithm (RDPA)
简化所得点。
- MAA: https://stackoverflow.com/a/25875512/2577116
- FFA: http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/
- RDPA: https://stackoverflow.com/a/22516982/2577116
- (平滑:https://stackoverflow.com/a/7058606/2577116)
我把所有东西放在这个jsFiddle中。
步骤:
- 在用户完成自由绘图后获取路径对象 从base64路径创建图像
- 转换为二值图像(只有0和255像素,不透明)
- 在0,0位置应用
FFA
,颜色随机,保存颜色 - 转到下一个像素
- 如果像素已知泛洪填充颜色或路径颜色(黑色),移动到下一个
- 否则用新的随机颜色填充,保存颜色
- 移动所有像素,重复5 -7。
- 删除索引1上保存的颜色(它是路径轮廓周围的颜色(填充),所以它既不是路径也不是洞)
- 对所有其他颜色应用
MSA
并简化结果点(使用DPA
) - 从简化点创建多边形或…
- …平滑点并创建路径
- 添加到画布,删除输入路径
- :完成)
为了更简单的代码,我的随机颜色目前只创建灰色阴影。R=G=B和A=255允许更简单的检查。另一方面,这个解限制了轮廓的最大值。254个孔(256个灰度-路径颜色(0)-填充颜色(无孔))。如果需要更多,扩展代码以创建R, G, B甚至a的随机值也没有问题。不要忘记采用相应的颜色检查;)
整个算法可能没有针对性能进行优化,但老实说,我认为目前没有必要这样做。对于我的用例来说已经足够快了。无论如何,如果有人有关于优化的提示,我很高兴听到/读到:)
最佳选择
如果你用你的代码画Blobs,那么最简单的&最好的方法是将每个blob(和子blob)分解成它的组成部分Bezier曲线。FabricJS是开源的,所以你可以看到他们是如何创建曲线的——因此你可以如何分解曲线。结果将是一打左右的贝塞尔曲线,很容易重画或导航。如果你需要导航贝塞尔曲线的帮助,请参阅本教程,介绍沿着路径导航。
其他选择
你需要得到像素信息,所以你需要context.drawImage
你的织物Blob到本地画布上,并使用context.getImagedata
来获取像素信息。
假设:
- 所有像素不是白色就是黑色。
- 斑点为黑色:rgba(0,0,0,255)
- 斑点外部为白色:rgba(255,255,255,255)
- 斑点中的孔为白色:rgba(255,255,255,255)
寻找blob &孔路径:
加载imageData:
context.getImageData(0,0,canvas.width,canvas.height)
在图像的周长上找到一个白色像素
使用泛洪填充算法(FFA)将外层白色替换为透明
使用行进方块算法(MSA)找到最外层的blob周长并保存blob路径
使用泛洪填充算法来填充你在#4中发现的斑点。这使得外部斑点对下一轮MSA"不可见"。在这一点上,你只有白洞——其他一切都是透明的。
使用行进平方算法(MSA)找到下一个白洞的周长并保存该洞的路径
使用泛洪填充算法用透明度填充#6中的白洞。这使得下一轮MSA无法看到这个洞
重复#6 找到每一个剩下的白洞
如果MSA报告没有像素,则完成。
为了提高效率,可以在后续步骤中重复使用步骤#1中的imageData。您可以在完成所有步骤后放弃imageData。
因为blob是曲线,你会发现你的blob路径包含许多点。您可以使用路径点缩减算法将这些点简化为更少的点。