使用所提供的Python代码制作你自己的马赛克,并理解其背后的基本原理。
文章图片
文章图片
文章图片
可用的算法当我搜索从输入图像生成马赛克的工具时,我没有找到一个让我信服的。因此,我开始尝试神经风格转换,结果是惊人的马赛克,但仔细看,发现很多不现实的瓷砖。后来我发现有几篇关于马赛克计算的科学论文,我注意到一篇更早的论文(2005年的)是由Di Blasi等人写的,它产生了美丽的马赛克。我决定采用已发布的方法,并用Python实现它。事实证明,这比预期的要困难,因为这项任务出奇的复杂。此外,我必须即兴发挥很多,因为算法的最后步骤没有详细解释。现在我想把剧本分享给所有对此感兴趣的人。一些相关的代码行张贴在这里和完整的代码可以在GitHub上( 作者:Johannes Beetz )。
让我们开始首先我们加载一个源图像:
from skimage import data
import matplotlib.pyplot as plt
img0 = data.coffee()
plt.imshow(img0)
图片如下图所示。现在我们可以开始实现算法了。
边缘检测最著名的马赛克类型(opus vermiculatum)的基本设计原则是沿着重要视觉对象的边缘放置瓷砖链。因此,我们的首要任务是从源图像中提取这些边缘特征。本文指出,传统的边缘检测算法不能很好地工作,必须开发一种基于亮度级别的自定义技术。这是在2005年写的。如今,我们可以从深度学习领域的进步中受益。整体嵌套边缘检测(HED)是在Di Blasi十年后发表的,可以使用openCV轻松实现。事实证明,在大多数情况下,HED在检测相关轮廓方面做得更好。你可以在下面的对比中看到,HED产生的背景特征明显较少,但能更好地保留杯子的边缘。
文章图片
文章图片
文章图片
Guidelines现在我们有了边,可以开始在那里放置贴图了。但之后该去哪里呢?让我们准备使用贴图填充整个图像的说明。因此,我们构造了一套平行于检测到的边缘的完整的Guidelines。为此,我们首先必须计算每个像素到最近边缘的距离。幸运的是,SciPy有正确的功能:
from scipy.ndimage import morphology
distances = morphology.distance_transform_edt(img_edges==0,)
在img_edges中,已经检测到的边缘被编码为” 1 “ ,并通过设置img_edges==0将其转换为” False “ 。你可以在下面的图中看到生成的图像。在这里,像素越亮,离边缘越远。
现在我们可以使用关于边缘距离的新知识了:我们移动half_tile(即一个砖块的一半大小; (由用户选择)远离边缘,并一行一行地绘制Guidelines,同时始终保留瓷砖链所需要的空间:
guidelines = np.zeros((w, h), dtype=np.uint8)
guideline_mask = ( (distances.astype(int)+half_tile)%(2*half_tile)==0 )
guidelines[mask] = 1
结果如下所示。最终,瓷砖将沿着这些线放置在中心。然后,我们再次使用距离矩阵来计算每个像素的梯度。稍后我们将需要这些数据以正确的方向放置贴图。
文章图片
文章图片
文章图片
为了使Guidelines对以下步骤有用,我们必须将它们转换为有序的坐标列表。这不是一个容易的任务,但谢天谢地SciPy在这里再次提供了很大的帮助:
from scipy.ndimage import label
guidelines_labeled, chain_count = label(guidelines, structure=[[1,1,1], [1,1,1], [1,1,1]])
瓷砖位置现在我们可以开始沿着Guidelines绘制多边形来实际放置贴图。有了shaely,有一个很棒的python库,用于创建和操作几何形状。
标准瓷砖的放置过程如下:
- 将边1创建为线长2任一端; 以指南居中(下图中的虚线)
- 根据预先计算的梯度矩阵旋转边1
- 前进方向为2手性half_tile像素
- 以与边1相同的方式创建边2
- 通过取四个角点(即两边的两个端点)的凸包创建一个多边形
- 如果新瓦与附近链上的瓦重叠,那么只保留差值
文章图片
如果一个瓷砖是沿着一个弯曲的指南放置的,它的宽度选择较小,以便在最后的马赛克中的圆形对象看起来更好。如上图所示,边2在超过临界角时立即绘制。最后,指导链的最后一个贴图(通常是一条闭合的线)被安装到剩余的空间中,因此可能会有更小的宽度。
所有这些都是在循环中完成的,需要大量的代码行。为了演示shashape背后的简单原理,让我们画一个单一的多边形:
from shapely.geometry import LineString, MultiPoint
from shapely import affinityhalf_tile = 10x0 = 0
y0 = 0
angle0 = 0
line0 = LineString([(x0,y0-half_tile),(x0,y0+half_tile)])
line0 = affinity.rotate(line0, angle0)x1 = 10
y1 = 3
angle1 = 15
line1 = LineString([(x1,y1-half_tile),(x1,y1+half_tile)])
line1 = affinity.rotate(line1, angle1)p = MultiPoint([line0.coords[0], line0.coords[1],
line1.coords[0], line1.coords[1]])
p = p.convex_hull
文章图片
在这个例子中,line0创建在左边,line1创建在右边。两条线都旋转了指定的角度。最后提取两条直线的角点并合并为凸多边形。
现在让我们继续放置循环。在下图(左边)中,你可以看到第一个链完成后的结果。标记的链也用彩色线表示。在右边的图像中,所有链都填充了贴图。但我们还没有完成,因为我们有很多瓷砖之间的间隙。
文章图片
文章图片
瓷砖填补空白【如何使用Python生成罗马风格的马赛克(代码实现)】为了填补空白,我们使用了另一个伟大的Python库scikit-image。为了找到缺口,我们首先绘制所有现有的瓷砖:
from skimage import draw
img_chains = np.zeros((h, w), dtype=np.uint8)
rr,cc = draw.polygon(x, y, shape=img_chains.shape)
img_chains[rr, cc] = 1
在这里,x和y是一个多边形的角坐标。” 绘制” 将它们转换为多边形内所有像素的坐标rr,cc。你可以在下面的左图中看到结果。然后我们再计算到现有贴图的距离。然后,我们使用这些信息在间隙内定义第二组Guidelines(右图)。
文章图片
文章图片
在沿着新的Guidelines放置额外的贴图后,所有必要的多边形都创建好了。另一项任务是切断粘在图像边界外的贴图。然后,让我们看看结果。在下图中,你可以看到整个区域都覆盖着瓷砖:
文章图片
图片作者(基于scikit-image测试图片” coffee” )
处理凹瓷砖如果不仔细观察就很难看到它,但关于贴图形状有一点还不令人满意:它有很多凹多边形!如果两个角点之间的一条线可以延伸到多边形外,则该多边形为凹形。当它与邻近多边形的重叠被移除时,它可以在放置后形成。这些形状不是很自然,因为在现实中,石头通常不是凹的,而是凸的。这就是为什么代码提供了将形状转换为凸形的选项。它实现了两种策略(见下面的草图):
- 移除尖刺: 如果多边形的面积没有明显改变,那么多边形的尖刺部分就会被移除。去除穗后的面积必须比以前小。否则会有与其他贴图重叠的风险。
- 这是右图上的例子。如果标记的角被移除,得到的多边形将比以前大。相反,它被分成两个凸多边形。
文章图片
着色最后,所有的贴图都就位了,并且呈现出了一个凸起的形状。只是颜色少了!我们只是从输入图像中复制它。最简单的方法是选择位于多边形中心的颜色值。因为在某些情况下可以收集到奇怪的像素颜色,所以也可以在一个贴图内的所有像素上平均颜色。你得到的结果如下图所示。
一个缺点是,由此产生的马赛克看起来有点人工,因为不是所有的颜色发生在现代照片可以复制的自然材料碎片。因此,在真正的历史马赛克中可以找到的颜色集合是事先收集的。你可以选择将所有颜色值更改为与该源最接近的颜色值。在下面的例子中(右侧),我们选择了一个来自拉文纳的马赛克源,由于使用了玻璃碎片,它仍然包含了很多颜色。
文章图片
文章图片
完成了!让我们看看一些生成的马赛克。
例如,我们可以尝试不同大小的瓷砖的效果。在左边的图像设置为15px,在右边设置为5px。较粗的图像需要1107个贴图,较细的需要7069个贴图。
文章图片
文章图片
当然,我们可以选择任何一种输入图像:
文章图片
文章图片
文章图片
文章图片
文章图片
文章图片
如果你想自己尝试一下,可以从这里获取代码。只需更改输入图像的路径并运行脚本。
推荐阅读
- 用于WordPress插件开发的7个精彩的工具
- 如何修改Windows 10环境变量(详细图解)
- 每个开发人员都应该知道的9个方便的Git命令
- C中标识符和变量之间有什么区别()
- iCloud和Amazon Drive之间有什么区别()
- HTTP和HTTPS之间有什么区别(有哪些区别?)
- Hive和HBase之间有什么区别()
- 算法(数组元素最高和最低频率之间的差异)
- 微服务架构 | 5. 服务容灾