OpenCV-Python系列之开运算与闭运算
图像的腐蚀与膨胀是本次教程的核⼼——开运算与闭运算的基础,如果结构元素为圆形,则膨胀操作可填充图像中⽐结构元素⼩的孔洞以及图像边缘处⼩的凹陷部分。⽽腐蚀可以消除图像中的⽑刺及细⼩连接成分,并将图像缩⼩,从⽽使其补集扩⼤。但是,膨胀和腐蚀并⾮互为逆运算,所以它们可以结合使⽤。在腐蚀和膨胀两个基本运算的基础上,可以构造出形态学运算簇,它由膨胀和腐蚀两个运算的复合与集合操作(并、交、补等)组合成的所有运算构成。例如,可使⽤同⼀结构元素,先对图像进⾏腐蚀然后膨胀其结果,该运算称为开运算;或先对图像进⾏膨胀然后腐蚀其结果,称其为闭运算。开运算和闭运算是形态学运算族中两种最为重要的运算。
对于图像X及结构元素S,⽤符号表⽰S对图像X作开运算,⽤符号表⽰S对图像X作闭运算,它们的定义为:
⾸先需要来了解⼀个函数:
参数说明:
src传⼊的图⽚
op进⾏变化的⽅式
kernel表⽰定义的卷积核的⼤⼩以及形状
op =  cv2.MORPH_OPEN 进⾏开运算,指的是先进⾏腐蚀操作,再进⾏膨胀操作
op = cv2.MORPH_CLOSE 进⾏闭运算,指的是先进⾏膨胀操作,再进⾏腐蚀操作
开运算
开运算指的就是对图像先进⾏腐蚀操作,然后再进⾏膨胀操作,⽽通常情况下,它是对图像的明亮的区域进⾏操作,可以消除图像中的⽩噪声,现在我们来看例⼦,先看⼀幅图像:
现在我们想要消除图像中的⿊⾊的⽑刺,但是如果直接对图像进⾏开运算是不⾏的,因为开运算是对图像的明亮区域进⾏操作,看⼀下直接进⾏开运算会有什么效果:
import cv2
import numpy as np
img = cv2.imread('open.jpg',0)
kernel = StructuringElement(cv2.MORPH_RECT,(3,3))
open =phologyEx(img,cv2.MORPH_OPEN,kernel)
cv2.imshow("img",img)
cv2.imshow("result", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到,图像的⽑刺没有被去除,现在我们需要将原图进⾏阈值化翻转,也就是⿊⽩颠倒,这样才⽅便进⾏形态学的处理,我们在前⾯阈值部分讲过,这⾥就不再讲述了,直接看代码:
import cv2
import numpy as np
img = cv2.imread('open.jpg',0)
threshold = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV|
cv2.THRESH_OTSU)[1]
cv2.imshow("img",img)
cv2.imshow("thres",threshold)
cv2.waitKey(0)
cv2.destroyAllWindows()
现在图像已经被⿊⽩颠倒了过来,现在我们可以开始进⾏开运算了,当然⾸先也是需要定义⼀个卷积
核的,这在上个教程中已经谈到,在这⾥我们定义⼀个3*3的矩形卷积核:
import cv2
import numpy as np
img = cv2.imread('open.jpg',0)
threshold = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV|
rectangle函数opencvcv2.THRESH_OTSU)[1]
kernel = StructuringElement(cv2.MORPH_RECT,(3,3))
open =phologyEx(threshold,cv2.MORPH_OPEN,kernel)
cv2.imshow("img",img)
cv2.imshow("thres",threshold)
cv2.imshow("result", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
这样效果就显⽽易见了,如果我们将卷积核改成5*5的呢:
import cv2
import numpy as np
img = cv2.imread('open.jpg',0)
threshold = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV|
cv2.THRESH_OTSU)[1]
kernel = StructuringElement(cv2.MORPH_RECT,(5,5))
open =phologyEx(threshold,cv2.MORPH_OPEN,kernel)
cv2.imshow("thres",threshold)
cv2.imshow("result", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
这就说明操作过度了,所以对于形态学处理卷积核的适当选取是⾮常重要的,现在我们对处理之后的图像进⾏还原:
import cv2
import numpy as np
img = cv2.imread('open.jpg',0)
threshold = cv2.threshold(img,0,255,cv2.THRESH_BINARY_INV|
cv2.THRESH_OTSU)[1]
kernel = StructuringElement(cv2.MORPH_RECT,(3,3))
open =phologyEx(threshold,cv2.MORPH_OPEN,kernel)
result = cv2.threshold(open,0,255,cv2.THRESH_BINARY_INV|
cv2.THRESH_OTSU)[1]
cv2.imshow("img",img)
cv2.imshow("thres",threshold)
cv2.imshow("open", open)
cv2.imshow("result",result)
cv2.waitKey(0)
cv2.destroyAllWindows()
看⼀下最终还原的结果:
事实上,卷积核的灵活运⽤将会极⼤的⽅便图像的形态学处理,我们来进⾏⼀个实战,⽐如现在给出⼀幅图像:
我们将⽤开运算分别提炼出横线和竖线,我们使⽤13*1的卷积核进⾏实验:
import cv2
import numpy as np
img = cv2.imread('hengshu.jpg',0)
kernel = StructuringElement(cv2.MORPH_RECT,(13,1))
open =phologyEx(img,cv2.MORPH_OPEN,kernel)
cv2.imshow("img",img)
cv2.imshow("open", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
是不是很神奇,现在我们使⽤1*13的卷积核进⾏实验:
import cv2
import numpy as np
img = cv2.imread('hengshu.jpg',0)
kernel = StructuringElement(cv2.MORPH_RECT,(1,13))
open =phologyEx(img,cv2.MORPH_OPEN,kernel)
cv2.imshow("img",img)
cv2.imshow("open", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
竖线也被完美的提取出来了,在以后的项⽬实战中,我们将会⽤到这些知识,合理的过滤掉图像中多余的信息,事实上,我们还发现,处理之后的图像偏暗,没有原图那么明亮,这在下次教程中的顶帽——⿊帽操作中可以进⾏处理。
闭运算
我们折腾了半天的开运算,现在我们来玩玩闭运算,闭运算跟开运算相反,是先膨胀再腐蚀,它通常被⽤来去除图像明亮区域内部的噪声。我们来看⼀幅图像:
现在我们将要⽤闭运算去除图像明亮区域内部的⿊点,定义⼀个7*7的卷积核,我们看代码:
import cv2
import numpy as np
img = cv2.imread('close.jpg',0)
kernel = StructuringElement(cv2.MORPH_RECT,(7,7))
open =phologyEx(img,cv2.MORPH_CLOSE,kernel)
cv2.imshow("img",img)
cv2.imshow("open", open)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到,效果很好,现在我们来进⾏另⼀个具有实战意义的实验,先看图⽚:
我们需要将这些字轮廓提炼出来,并且⽤⽅框标定出来,每⼀⾏字⽤⼀个⽅框标定出来,当然,这个涉及到以后将要讲解的轮廓提取以及轮廓近似,但是在这⾥我们先进⾏⼀个实验,如果我们想将每⼀⾏字⽤⼀个⽅框标定出来,那么⾸先需要满⾜的条件就是每⼀⾏的字必须连在⼀块,形成⼀个整体,
这样的话才可以⽤OpenCV提取他们整体的轮廓,进⽽标定出来,但现在我们看到这些字都是独⽴的,它们并没有连在⼀起,这个时候我们就可以采⽤闭运算了。我们来看代码:
import cv2
import numpy as np
img = cv2.imread('text1.jpg',0)
kernel = StructuringElement(cv2.MORPH_RECT,(21,5))
open =phologyEx(img,cv2.MORPH_CLOSE,kernel)
cv2.imshow("img",img)
cv2.imshow("open", open)
cv2.waitKey(0)
cv2.destroyAllWindows()