有些时候图片上会有一些划痕或者污渍,会影响图片的质量,假设我有一张写有“艾醒”的图片,但是有花花绿绿的划痕和污渍,这时我们就可以运用腐蚀与膨胀消除这些划痕和污渍
腐蚀的本质就是白吃黑,即数值较大的(较白的)吃掉数值较小的(较黑的)
我们可以用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()
我们用5x5的卷积核来进行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_OPEN | cv2.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)