图像分割是计算机视觉的一个基本方面,多年来经历了巨大的转变。这将是一系列三篇博客文章,深入研究三种不同的图像分割技术 - 1使用OpenCV的经典分水岭算法,2使用PyTorch实现的基于深度学习的UNet模型,3 SOTA图像分割模型。同时,这部分重点介绍分水岭算法及其使用 OpenCV 的实现。在下一部分中,我们还将在人类分割数据集上训练UNet模型,展示基于深度学习的技术的强大功能和适用性。
图像分割涉及将图像分区为多个段或区域,每个段或区域包含一组像素。最终目标是将图像的表示简化或修改为更有意义的内容,从而使其更易于分析。这些技术已广泛应用于从图像中的物体识别到医学成像诊断的众多应用中。
在传统的图像分割方法领域,分水岭算法占有重要地位。该算法将图像可视化为地形景观,在图像内生成“集水盆地”和“分水岭线”以隔离不同的对象。以简化的方式,任何灰度影像都可以被视为地形表面,其中高强度表示山峰和丘陵,而低强度表示山谷。
尽管在概念上易于理解和有效,但分水岭算法有时会导致过度分割,即对象被分成许多段。但是,微调算法并添加预处理步骤可以提高算法的性能。
# Load image
img = cv2.imread('water_coins.jpg')
imshow("Original image", img)
# Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold using OTSU
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
imshow("Thresholded", thresh)
2.开运算(侵蚀后扩张):在此步骤中,执行打开操作,即侵蚀操作,然后进行扩张操作。此步骤的目的主要是消除噪音。侵蚀操作消除了图像中的小白噪声,但它也会缩小我们的对象。在此之后,通过膨胀操作,我们可以保留物体的大小,同时将噪声拒之门外。
先让我们了解侵蚀和扩张
# noise removal
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN,kernel, iterations = 2)
让我们分解一下:
# sure background area
sure_bg = cv2.dilate(opening, kernel, iterations=3)
4. 距离变换:流域算法涉及应用距离变换来识别可能成为前景的区域。下面是此步骤的代码:
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)
在此步骤中,我们将执行两项操作:
5.识别未知区域:我们识别未知区域,即既不是确定前景也不是确定背景的区域。我们首先将确定的前景(sure_fg)转换为无符号的8位整数。然后我们从确定背景(sure_bg)中减去确定前景以获得未知区域。未知区域是分水岭算法的关键,因为它表示不同对象之间或对象与背景之间的过渡区域。
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
imshow("SureFG", sure_fg)
imshow("SureBG", sure_bg)
imshow("unknown", unknown)
6. 标记sure_bg、sure_fg和未知区域:这涉及创建标记并标记其中的区域。我们标记的区域是确定背景 ()、确定前景 () 和未知区域。下面是此步骤的代码片段:sure_bg
sure_fg
# Marker labelling
# Connected Components determines the connectivity of blob-like regions in a binary image.
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
此外,我们希望确定背景的标记与确定前景不同,我们为标记图像中的所有标签添加 1。执行此操作后,确定背景像素标记为 1,确定前景像素从 2 开始标记。
7. 应用分水岭算法
接下来,步骤是将分水岭算法应用于标记(在前面的步骤中找到的标记区域)
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
imshow("img", img)
cv2.watershed() 函数修改标记图像(标记)本身。对象的边界在标记图像中用 -1 标记。图像中的不同对象用不同的正整数标记。我们不确定是背景还是前景的区域是由分水岭算法确定的——它们要么被分配给背景,要么被分配给某个对象,从而在对象和背景之间产生清晰的边界划分。
流域算法中的“洪水”和“大坝建设”概念本质上是一种描述算法如何工作的隐喻方式
应用分水岭算法后,我们的标记图像(最初具有确定前景、确定背景和未知区域的标签)现在包含图像中每个不同对象的标签。我们有效地将图像分割成不同的对象(硬币)和背景。
分水岭算法提供了一种直观高效的图像分割方法,允许从复杂图像中有意义地提取特征。使用 OpenCV 库在 Python 中的实际实现进一步简化了该过程,并提供了一种执行图像分割的快速方法。虽然它的基本形式可能会受到过度分割的影响,但适当的图像预处理和参数调整可以有效地解决这个问题,使其成为图像分析领域的强大工具。永远记住,分割技术的选择取决于项目的具体要求和约束。
参考资料:
What is OpenCV? The Complete Guide (2023) - viso.ai
Jaskaran Bhatia – Medium