opencv-python常用函数解析及参数介绍(五)——腐蚀与膨胀

腐蚀与膨胀

  • 前言
  • 膨胀
  • 腐蚀
  • 开运算与闭运算
  • 礼帽与黑帽
  • 运用膨胀和腐蚀获得图像轮廓

前言

有些时候图片上会有一些划痕或者污渍,会影响图片的质量,假设我有一张写有“艾醒”的图片,但是有花花绿绿的划痕和污渍,这时我们就可以运用腐蚀与膨胀消除这些划痕和污渍

膨胀

腐蚀的本质就是白吃黑,即数值较大的(较白的)吃掉数值较小的(较黑的)
我们可以用cv2.dilate进行膨胀,参数为图片,卷积核,膨胀次数
其原理就是在卷积核范围内,如果周围颜色浅的(数值较大的)多,那么当前点就应该是颜色浅的,否则应该是颜色深的
假设我们有一张图片名称为aixing.png

img = cv2.imread('aixing.png')
kernel = np.ones((5,5),np.uint8) 
dilate = cv2.dilate(dige_erosion,kernel,iterations = 3)

cv2.imshow('dilate', dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()

我们用5×5的卷积核来进行3次膨胀

可以看到虽然划痕和污渍几乎没有了,但是这个图片上的字比之前小不少,为了还原字的大小我们还需要进行腐蚀操作,注意腐蚀操作需要跟膨胀操作卷积核和执行次数相同才能还原之前的大小

腐蚀

腐蚀的本质就是黑吃白,即数值较小的(较黑的)吃掉数值较大的(较白的)
我们可以用cv2.erode进行腐蚀,参数为图片,卷积核,腐蚀次数
其原理就是在卷积核中,如果周围颜色深的(数值较小的)多,那么当前点就应该是颜色深的,否则应该是颜色浅的

我们进行腐蚀

kernel = np.ones((5,5),np.uint8) 
erosion = cv2.erode(dilate,kernel,iterations = 3)

cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()


现在我们可以看到划痕和污渍已经去掉了

开运算与闭运算

实际上这两个功能很鸡肋,开运算就是先腐蚀,再膨胀;闭运算就是先膨胀,再腐蚀
opencv中cv2.morphologyEx函数可以实现开闭运算,参数为图片,操作类型,卷积核
操作类型参数

cv2.MORPH_OPENcv2.MORPH_CLOSE
开运算闭运算

很明显,我们刚刚的问题需要进行闭运算

img = cv2.imread('aixing.png')

kernel = np.ones((5,5),np.uint8) 
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()

可以看到效果并不好,这是为什么呢,因为闭运算只进行了一次膨胀一次腐蚀,而我们得到的较好的结果是进行了3次膨胀和3次腐蚀

开运算的代码也放在这里,但要说明当前问题并不适合开运算,如果画面背景是偏深色的,我们需要侧重的部分或者文字是偏浅色的较为适合开运算,如果要尝试我们可以将图片反色,如何反色呢?令255减去图片就好了

img = cv2.imread('aixing.png')

kernel = np.ones((5,5),np.uint8) 
reverse = 255 - img # 反色
cv2.imshow('opening', reverse)
cv2.waitKey(0)
cv2.destroyAllWindows()

opening = cv2.morphologyEx(reverse, cv2.MORPH_OPEN, kernel)

cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()


可以看到效果也不好,所以这个函数了解即可。

礼帽与黑帽

没有什么需要解释的,了解下面的操作就可以了
礼帽 = 原始输入-开运算结果
黑帽 = 闭运算-原始输入

#礼帽
img = cv2.imread('aixing.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

#黑帽
img = cv2.imread('aixing.png')
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()

运用膨胀和腐蚀获得图像轮廓

假设我们有这样一个名为yan.jpg的岩元素图标

为了获取它的轮廓,我们先要将他二值化,即进行前几篇文章提到过的阈值操作,很显然我们应该选用cv2.THRESH_BINARY参数

yan = cv2.imread('yan.jpg', cv2.IMREAD_GRAYSCALE)
ret, img = cv2.threshold(yan, 220, 255, cv2.THRESH_BINARY)
cv_show(img, 'threshold')


然后分别进行一次膨胀和一次腐蚀

kernel = np.ones((5,5),np.uint8) 
dilate = cv2.dilate(img,kernel,iterations = 1)
erosion = cv2.erode(img,kernel,iterations = 1)

res = np.hstack((dilate,erosion))

cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()


左侧是膨胀,右侧是腐蚀的结果
然后膨胀减去腐蚀

cv2.imshow('res', cv2.convertScaleAbs(dilate-erosion))
cv2.waitKey(0)
cv2.destroyAllWindows()

那么为什么是膨胀减腐蚀呢,因为膨胀的白色的区域多,白色的是非0值,所以实际上白色轮廓曾是膨胀后的一部分,但是这样得到的轮廓实际上比真实的轮廓要宽,因为膨胀会让轮廓向里扩展,腐蚀会让轮廓向外扩展
那么如何得到恰好的轮廓呢

真实的轮廓我们可以分为内轮廓和外轮廓
要得到内轮廓,我们只需膨胀减去膨胀前的图就好了

cv2.imshow('res', cv2.convertScaleAbs(dilate-img))
cv2.waitKey(0)
cv2.destroyAllWindows()


同样的,外轮廓可以通过腐蚀前的图像减去腐蚀后的图像得到

cv2.imshow('res', cv2.convertScaleAbs(img-erosion))
cv2.waitKey(0)
cv2.destroyAllWindows()

单独这样看也许并不明显,那么我们将他们画在一起来看对比
首先我们打开原图,我们将在原图上画轮廓

color_yan  = cv2.imread('yan.jpg')

我们要先获取索引来找到哪些点位需要轮廓

idx1, idx2 = np.where((dilate-erosion)!=0)
idx3, idx4 = np.where((img-erosion)!=0)
idx5, idx6 = np.where((dilate-img)!=0)

由于膨胀减去腐蚀的宽度正好可以被原图减腐蚀和膨胀减原图覆盖,即被外轮廓和内轮廓的和覆盖,所以我们分别展示膨胀减腐蚀与内轮廓和外轮廓的对比
与外轮廓的对比

color_yan[idx1, idx2,:] = (255, 0, 0)
color_yan[idx3, idx4,:] = (0, 255, 0)

蓝的是膨胀减去腐蚀的轮廓(下同),绿色的是外轮廓的效果

与内轮廓对比
红色的内轮廓的效果

color_yan  = cv2.imread('yan.jpg')
color_yan[idx1, idx2,:] = (255, 0, 0)
color_yan[idx5, idx6,:] = (0, 0, 255)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年12月1日
下一篇 2023年12月1日

相关推荐