原文:
towardsdatascience.com/image-contouring-with-opencv-5b18f17a8386
快速成功数据科学
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c89b26265d2a3078a2c853083a649579.png
“轮廓化图像”意味着生成包围具有相似颜色或强度的连续区域的边界。在计算机科学中,轮廓通常由定义这些边界的点列表表示。
轮廓在计算机视觉应用中得到了广泛的应用,包括目标检测;图像分割;以及计算形状描述符,如面积和质心。
在这个快速成功数据科学项目中,我们将使用世界上最受欢迎的计算机视觉库,OpenCV,在月亮图片上生成轮廓。我们的目标将是创建一些有趣的艺术作品,但同样的基本过程也适用于更复杂的应用。
安装库
对于这个项目,我们将使用 NumPy,Matplotlib 和 OpenCV 库。OpenCV 严重依赖于 NumPy 数组,我们可以使用 Matplotlib 作为快速简单的可视化轮廓图像的方法(尽管 OpenCV 也包括直接可视化图像的方法)。
你可以在之前的链接中找到 NumPy 和 Matplotlib 的安装说明,以及 OpenCV 的这里。
作为 Anaconda 用户,我一直很难使用 conda 安装 OpenCV。它似乎安装正确,但每次我尝试将库导入脚本时,总会遇到错误。为了避免这种情况,我创建了一个新的 conda 环境,使用 conda 安装我需要的所有东西,除了 OpenCV,然后作为最后一步,使用 pip 安装 OpenCV:
pip install opencv-python
NOTE: 如果你对将 conda 与 pip 混合感兴趣,请参阅我文章末尾的附录,介绍 Conda 环境。
月亮图片
这里是我们将要使用的月亮图片。这是一张来自 Unsplash 的公共图片,我已经稍微旋转了一下。我还使用微软的 Paint 3D 图片编辑器将黑色背景替换成了白色。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1a0869a365810b271011168d9b3ba8dc.png
月亮图片由 Mike Petrucci 提供 (Unsplash)。
要使用这张图片,右键点击它并选择“另存为”。将其命名为 “moon.jpg”,并保存在包含你的笔记本或 Python 脚本的文件夹中,这样我们就不必处理路径名了。
代码
以下代码是在 JupyterLab 的单个单元格中编写的。它加载一张图片,将其转换为灰度图,平滑处理,生成轮廓,然后在原始图片的透明版本上绘制轮廓。
导入库和处理图片
在检测轮廓之前,通常一个好的做法是进行一些预处理来增强特征和减少噪声。最常见的步骤是将彩色图像转换为灰度,并使用如高斯模糊这样的过滤器对结果图像进行平滑处理。幸运的是,OpenCV 提供了执行这些任务的方法。
下面是预处理代码。详细的解释将在后面进行。
importnumpyasnpimportmatplotlib.pyplotaspltimportcv2ascv# Use these constants to tune your results:SMOOTHING_KERNEL=(11,11)# Must be odd numbers.NUM_CONTOURS=8ALPHA=0.6# Load the moon image:IMG_GRAY=cv.imread('moon.jpg',cv.IMREAD_GRAYSCALE)# Smooth the image using Gaussian blur:smoothed_img=cv.GaussianBlur(IMG_GRAY,SMOOTHING_KERNEL,sigmaX=0)在导入库之后,我们为“调整”轮廓和最终图像分配了三个常量。第一个,SMOOTHING_KERNEL,代表 OpenCV 的GaussianBlur()类的输入。
模糊减少了图像的锐度。这个过程使用一个称为核的正方形矩阵构成的过滤器。这个核在图像上逐渐滑动,并对核内的所有像素计算其强度值的加权平均值。然后,它将矩阵中心像素的值替换为这个平均值。这移除了极端值,并创建了一个“更平滑”的图像。
核参数是一个包含宽度和高度的元组,以像素为单位。由于平均值放置在核的中心像素,因此核的尺寸始终应该是正数和奇数。所以(5, 5)是可行的,但(2, 2)则不行。(要了解更多关于图像平滑的信息,请访问这个 OpenCV 网站)。
下一个常量NUM_CONTOURS代表 OpenCVcontour()方法的级别参数,我们将在稍后讨论。
levels参数指定定义轮廓的强度级别。如果您输入一个单一值,例如 8,OpenCV 将自动将图像的强度范围分成 8 个区间。
您也可以手动指定区间。而不是使用我们这里使用的常量值,您可以将值作为参数传递。例如,levels=[50, 100, 150]将导致在强度级别 50、100 和 150 处绘制轮廓。
最后一个常量ALPHA决定了在初始图像上绘制轮廓的透明度。值越低,图像越透明。
如前所述,如果结果看起来太嘈杂或太稀疏,可以使用这三个常量来“微调”结果。
接下来,我们使用 OpenCV 的imread()方法和IMREAD_GRAYSCALE参数将图像以灰度形式加载。然后我们使用GuassianBlur()类对其进行平滑处理,传递给该类的参数包括图像、核大小和标准差值(sigmaX)。将最后一个参数设为0告诉 OpenCV 自动估计该值。
绘制图像
现在我们已经准备好生成带有轮廓的图像,并使用 Matplotlib 进行绘制。
# Create a 2D Matplotlib figure:fig,ax=plt.subplots()# Set the contour levels:contour_levels=np.linspace(0,255,NUM_CONTOURS)# Create the contour plot:contour_plot=ax.contour(smoothed_img,levels=contour_levels,colors='black')# Add the smoothed image:ax.imshow(smoothed_img,cmap='gray',alpha=ALPHA)# Customize the plot settings:ax.set_title('Smoothed Moon Image with Contours')ax.set_axis_off()# fig.savefig('moon_contour_medium.png', dpi=500)plt.show()首先,我们创建 Matplotlib 的figure和axes对象来容纳图表。接下来,我们确定每个轮廓要使用的阈值值。为此,我们调用 NumPy 的linspace()方法,该方法返回一个 NumPy 数组,包含最大可能的强度范围(0–255),并按 NUM_CONTOURS 常量指定的 8 个值细分。
然后,我们在ax对象上调用 OpenCV 的contour()方法,传入平滑后的图像、contour_levels数组和一个线条颜色。此时,我们只看到一个白色背景上的黑色轮廓。为了给它增添一些活力,我们在ax上调用 Matplotlib 的imshow()方法,传入平滑后的图像、Matplotlib 颜色图(cmap='gray')和一个alpha参数以防止它压倒轮廓。
最后,我们添加一个标题并关闭 x 和 y 轴。以下是结果:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/f741e03a69075b816f0db0572f05a762.png
灰色月球轮廓图像(作者绘制)
增加核大小可以使图像更加平滑,并产生更简单的轮廓图案。以下是使用 23 x 23 像素核(SMOOTHING_KERNEL = (23, 23))和ALPHA=0.7的结果:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bd09afdb041bb22fe70374224402d73e.png
使用 23×23 核平滑的月球轮廓图像(作者绘制)
与颜色图一起玩乐
我们的“月球草图”让我想起了书中的插图,《星球大战:星球与卫星必备指南》。确实,通过更改底层的颜色图,你可以轻松地将月球变成一个有趣的科幻或奇幻世界。以下是一个示例,我们将imshow()方法中的cmap参数从gray更改为terrain。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/298dbfd4ca05ccccd8f6c8c603195600.png
使用地形颜色图和黑色背景绘制的月球轮廓图(作者绘制)
月球上的深灰色区域被称为月海(拉丁语意为“海洋”),因为古人认为它们像被水填满的盆地。通过在代码中更改单个参数,我们可以想象如果月海真的是海洋,月球是一个类似地球的活生生世界会是什么样子。
通过使用反转的Reds颜色图,我们可以创建一个像 Arrakis 或 Tatooine 一样的沙漠世界:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5269fc77de57f1a18efa47ad93b4ebdf.png
使用 Reds_r 颜色图和黑色背景绘制的月球轮廓图(作者绘制)
Matplotlib 自带了数十种有趣的颜色图。你可以在这个网站找到它们。
说到沙漠行星,你可以在火星上使用轮廓绘制。下面是基于公共领域内 NASA 图像的著名水手谷的视图(JPL 图像使用政策(nasa.gov))。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a0e7248cb1ecf8f1f5e910b22db5baa4.png
使用 RdGy 颜色图绘制的火星地形图像(作者绘制)
当然,任何图像都可以进行轮廓绘制(尽管不一定总是很好)。你能认出这位同伴吗:
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/ede08df3650c2133604acf9236298366.png
轮廓化的乔治·华盛顿(作者绘制)
限制
轮廓算法在具有清晰定义物体边缘的高质量图像上效果最佳。当背景繁忙,前景和背景物体——或者重叠物体——具有相似值时,它们往往会失败或返回不满意的结果。
预处理技术,如灰度转换和模糊,可以改善结果,但它们的效果有限。幸运的是,OpenCV 和其他 Python 库提供了处理低对比度图像等问题的额外工作流程。虽然这些超出了本文的范围,但你可以在这里查看一个示例工作流程。
OpenCV 还包含用于后处理的轮廓图像功能。这包括提取和平滑轮廓的方法,以及移除包围小区域的轮廓的方法。
摘要
在本文中,我们探讨了使用 Python 最受欢迎的计算机视觉库 OpenCV 简单快速地进行图像轮廓绘制的方法。这些轮廓代表了不同强度区域之间的边界。
为了放置这些轮廓,我们选择了阈值,称为“轮廓级别”,通过指定我们希望在最终图像中包含的轮廓数量,并让算法选择强度值。或者,我们也可以提供一个我们希望在特定位置放置轮廓的特定强度值的列表。
图像轮廓绘制在计算机视觉中是一个极其重要的过程,而我们仅仅触及了皮毛。我们在这里使用轮廓来“勾勒”月球和火星,以及创造想象世界的景象。但应用范围远不止于此,从识别肿瘤到识别面部,再到实现自动驾驶汽车。
感谢!
感谢阅读,请关注我,未来将有更多快速成功数据科学项目。