使用opencv和python实现图像的智能处理,opencv面向python

  使用opencv和python实现图像的智能处理,opencv面向python

  这次我们来看看图像分割,这也是OpenCV的重要组成部分。图像分割是按照一定的原则将一幅图像分割成若干互不相交的局部区域的过程,是图像处理中最基本的研究领域之一。目前,图像分割方法很多,其中分水岭算法是一种基于区域的图像分割算法。分水岭算法由于其方便性,已经广泛应用于医学图像、模式识别等领域。

  传统分水岭算法的基本原理

  经典的流域计算方法是由L. Vincent于1991年在PAMI提出的[1]。传统的分水岭分割方法是一种基于拓扑理论的数学形态学分割方法。它的基本思想是把一幅图像看成一个测地线拓扑地形。图像中每个像素的灰度值代表该点的海拔高度,每个局部极小值及其影响区域称为汇水盆地,而汇水盆地的边界形成分水岭。分水岭的概念和形成可以通过模拟浸泡过程来解释。在每一个局部最小值的表面,扎一个小洞,然后将整个模型慢慢浸入水中。随着浸没的加深,各局部极小值的影响区域逐渐向外扩展。在两个集水池的汇合处筑坝如下图所示,即形成一个分水岭。让我们看看传统分水岭算法的示意图:

  然而,基于梯度图像的直接分水岭算法容易导致图像的过分割。造成这种现象的主要原因是输入图像中微小区域太多,产生了很多小的汇水盆地,导致分割后的图像无法代表图像中有意义的区域。因此,必须合并分割结果的相似区域。

  基于改进分水岭算法的基本原理

  因为传统的分水岭分割算法会由于图像中的噪声或其他不规则性而导致过分割。所以OpenCV实现了一个基于标记的分水岭算法,可以指定哪些谷点要合并,哪些不合并。这是一个交互式图像分割。我们所做的是给已知的物体贴上不同的标签。用一种颜色(或强度)标记我们确定为前景或物体的区域,用另一种颜色标记我们确定为背景或非物体的区域,最后用0标记我们不确定的区域。这是我们的标记。然后应用分水岭算法。然后我们的标签会用我们给的标签更新,对象的边界值会是-1。传统的基于梯度的分水岭算法和改进的基于标记的分水岭算法的示意图如下图所示:

  从上图可以看出,传统的基于梯度的分水岭算法由于局部极小值太多,分割后的分水岭很多。而基于标记的分水岭算法,洪泛过程从预定义的标记图像(像素)开始,较好地克服了过分割的不足。本质上,基于标记点的改进算法是一种利用先验知识帮助分割的方法。因此,改进算法的关键在于如何获得准确的标记图像,即如何准确标记前景物体和背景。

  OpenCV中的图像分割

  OpenCV提供了分水岭分割的相关函数API。让我们看看函数原型:

  愤怒的头发ers=cv.watershed(图片,愤怒的头发ers)

  图像:输入8位3通道图像。

  怒发ers:标记I/O 32位单通道映像(图)。它应该和图片一样大。

  我们使用示例图像:

  首先,我们使用Otsu的二值化来找到硬币的近似估计值:def washed (img):

  gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)

  ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)

  cv2.imshow(show ,thresh)

  cv2.waitKey(0)

  现在我们需要去除图像中的任何小的白噪声,所以我们需要使用形态学开运算。为了去除物体中的小孔,我们需要使用形态学闭运算。所以,现在我们可以确定靠近物体中心的区域是前景,远离物体的区域是背景,只有硬币的边界区域才是不确定区域。

  我们需要提取出确定它们是硬币的区域,腐蚀掉边界像素,剩下的不管是什么,都可以确定是硬币。如果他们不碰对方,我们可以继续。如果是这样,另一个好的选择是找到距离变换并应用适当的阈值。

  为此,我们扩展了结果,将对象边界添加到背景中。通过这种方法,我们可以确保背景中的任何区域都是真实的背景,因为边界区域被去除了。

  剩下的区域是我们不知道的区域,无论是硬币还是背景。分水岭算法应该能找到。这些区域通常围绕着前景和背景相遇(甚至是两个不同的硬币相遇)的硬币边界,可以通过sure_bg面积减去sure_fg面积得到。

  看看代码:#noiseremoval

  kernel=np.ones((3,3),np.uint8)

  opening=cv2 . morphologyex(thresh,cv2。MORPH_OPEN,内核,迭代次数=2)

  #surebackgroundarea

  sure_bg=cv2.dilate(opening,kernel,iterations=3)

  #Findingsureforegroundarea

  dist _ transform=cv2 . distance transform(打开,cv。DIST_L2,5)

  ret,sure _ fg=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)

  #Findingunknownregion

  sure_fg=np.uint8(sure_fg)

  unknown=cv2.subtract(sure_bg,sure_fg)

  我们来看看处理过的图像,sure_bg:

  sure_fg:

  看一下处理后的图像:

  现在我们可以确定哪些区域是硬币,哪些是背景,哪些是背景。因此,我们创建标记(它是一个与原始图像大小相同的数组,但使用int32数据类型)并标记其中的区域。

  cv2.connectedComponents()

  将图像的背景标记为0,然后将其他对象标记为从1开始的整数。

  我们知道如果背景是0,那么分水岭就会被认为是未知区域,所以我们用不同的整数来标记,0代表未知定义的未知区域。#标记标签

  Ret,怒发ers=cv2 .连通分量(sure _ fg)

  # addonetoalllabelsssothatsurebackgrounds不是0,而是1

  愤怒的头发ers=愤怒的头发ers 1

  #现在,愤怒的头发

  愤怒的头发ers[unknown==255]=0

  标记准备好了,现在是最后一步,应用分水岭,最后的代码:defwatershed(img):

  gray=cv2.cvtColor(img,cv2。COLOR_BGR2GRAY)

  ret,thresh=cv2.threshold(gray,0,255,cv2。THRESH_BINARY_INV cv2。OTSU)

  kernel=np.ones((3,3),np.uint8)

  opening=cv2 . morphology ex(thresh,cv2.morph _ open,kernel,iterations=2) #形态学开运算

  sure_bg=cv2.dilate(opening,kernel,iterations=3)

  dist _ transform=cv2 . distance transform(opening,cv2。DIST_L2,5)

  ret,sure _ fg=cv2 . threshold(dist _ transform,0.7*dist_transform.max(),255,0)

  sure_fg=np.uint8(sure_fg)

  unknown=cv2.subtract(sure_bg,sure_fg)

  Ret,怒发ers=cv2 .连通分量(sure _ fg)

  愤怒的头发ers=愤怒的头发ers 1

  愤怒的头发ers[unknown==255]=0

  愤怒的头发ers=cv2.watershed(img,愤怒的头发ers)

  Img[愤怒的头发ers==-1]=[255,0,0]

  cv2.imshow(img ,img)

  cv2.waitKey(0)

  如你所见,最终结果显示了完美的分割。

  更多openvino技术信息可以群里交流~

  OpenVINO技术交流群QQ223824636

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: