介绍
形态学操作其实就是改变物体的形状,比如腐蚀就是”变瘦”,膨胀就是”变胖”,看下图就明白了
形态学操作一般作用于二值化图,来连接相邻的元素或分离成独立的元素。腐蚀和膨胀是针对图片中的白色部分!
主要是应用在二值图像分析中,灰度图像亦可。
可以看做膨胀是将白色区域扩大,腐蚀是将黑色区域扩大
一、膨胀与腐蚀
相关函数
def getStructuringElement(shape, ksize, anchor=None): # real signature unknown; restored from __doc__
第一个参数shape:表示内核的形状,有三种形状可以选择
矩形:MORPH_RECT; 交叉形:MORPH_CORSS; 椭圆形:MORPH_ELLIPSE;
第二个参数ksize:是内核的尺寸(n,n)
第三个参数anchor:锚点的位置
getStructuringElement函数会返回指定形状和尺寸的结构元素。
1、腐蚀erode
腐蚀的效果是把图片”变瘦”,其原理是在原图的小区域内取局部最小值。因为是二值化图,只有0和255,所以小区域内有一个是0该像素点就为0,这样原图中边缘地方就会变成0,达到了瘦身目的。
OpenCV中用cv2.erode()
函数进行腐蚀,只需要指定核的大小就行:
img = cv2.imread('j.bmp', 0) kernel = np.ones((5, 5), np.uint8) erosion = cv2.erode(img, kernel) # 腐蚀
这个核也叫结构元素,因为形态学操作其实也是应用卷积来实现的。结构元素可以是矩形/椭圆/十字形,可以用cv2.getStructuringElement()
来生成不同形状的结构元素,比如:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 矩形结构 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 椭圆结构 kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 十字形结构
代码实现
1 import cv2 as cv 2 import numpy as np 3 4 def erode_image(image): 5 print(image.shape) 6 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) 7 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) #大律法获取二值图像 8 cv.imshow('binary',binary) 9 kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3)) #可以修改卷积核大小来增加腐蚀效果,越大腐蚀越强 10 dst = cv.erode(binary,kernel) 11 cv.imshow('erode_demo',dst) 12 13 img = cv.imread('1.jpg') 14 cv.imshow('input image',img) 15 erode_image(img) 16 cv.waitKeyEx(0) 17 cv.destroyAllWindows()
2、膨胀dilate
膨胀与腐蚀相反,取的是局部最大值,效果是把图片”变胖”:
dilation = cv2.dilate(img, kernel) # 膨胀
代码实现
1 import cv2 as cv 2 import numpy as np 3 4 #膨胀就是白色部分变多 5 def dilate_image(image): 6 print(image.shape) 7 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) 8 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) #大律法获取二值图像 9 cv.imshow('binary',binary) 10 kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3)) #可以修改卷积核大小来增加腐蚀效果,越大膨胀越强 11 dst = cv.dilate(binary,kernel) 12 cv.imshow('dilate_demo',dst) 13 14 img = cv.imread('1.jpg') 15 cv.imshow('input image',img) 16 dilate_image(img) 17 cv.waitKeyEx(0) 18 cv.destroyAllWindows()
3、可以不进行灰度处理,对彩色图片进行处理
(1)膨胀
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) dst = cv.dilate(src,kernel) cv.imshow("result",dst)
(2)腐蚀
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) dst = cv.erode(src,kernel) cv.imshow("result",dst)
二、开闭运算
1、开
先腐蚀后膨胀叫开运算(因为先腐蚀会分开物体,这样容易记住),其作用是:分离物体,消除小区域。
特点:消除噪点,去除小的干扰块,而不影响原来的图像
这类形态学操作用cv2.morphologyEx()
函数实现:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 定义结构元素 img = cv2.imread('j_noise_out.bmp', 0) opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开运算
代码实现
1 import cv2 as cv 2 import numpy as np 3 4 def camp(val1,val2): 5 pv = val1 + val2 6 if pv > 255: 7 return 255 8 elif pv < 0: 9 return 0 10 else: 11 return pv 12 13 def open_demo(image): 14 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) #(h,w) = (576,1024) 15 print(gray.shape) 16 for i in range(1000): 17 h = np.random.random_integers(0,gray.shape[0]-1) #因为返回的是闭区间内的随机整数,h是从0开始的,所以要减1 18 w = np.random.random_integers(0,gray.shape[1]-1) 19 value = np.random.random_integers(0,255) #随机添加的像素值 20 gray[h,w] = camp(gray[h,w],value) 21 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) 22 cv.imshow('binary',binary) 23 kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3)) 24 open = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel) #开操作,先腐蚀后膨胀,会消除一些为1的白色噪点 25 cv.imshow('open_demo',binary) 26 27 img = cv.imread('1.jpg') 28 cv.imshow('input image',img) 29 open_demo(img) 30 cv.waitKey(0) 31 cv.destroyAllWindows()
2、闭
闭运算则相反:先膨胀后腐蚀(先膨胀会使白色的部分扩张,以至于消除/“闭合”物体里面的小黑洞,所以叫闭运算)
特点:可以填充闭合区域
img = cv2.imread('j_noise_in.bmp', 0) closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算
代码实现
1 import cv2 as cv 2 import numpy as np 3 4 def contours_demo(image): 5 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) 6 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) 7 cv.imshow('binary',binary) 8 kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3)) 9 close_image = cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel) 10 cv.imshow('close image',close_image) 11 img = cv.imread('flower.png') 12 cv.imshow('flower',img) 13 contours_demo(img) 14 cv.waitKey(0) 15 cv.destroyAllWindows()
3、补充
从图中可以看出闭操作将白色区域被连接起来了,或者被扩大了。任何黑色区域如果小于结构元素的大小都会被消除
相反的,开操作消除了白点,这些白点的大小都小于结构元素。
这两种操作通常用在目标检测中。闭操作将错误分开成小片的物体从新连接成一个整体。而开操作则是去除一小块的噪点。因此,他们两个结合起来用效果会更好。先闭操作然后再开操作,最后的结果就是得到了图像中我们想要的目标。
也可以先进行开操作,然后再闭操作。但是这样做可能会消除更多的碎块。
值得注意的是对于同一幅图像连续使用开操作或者闭操作是没有任何意义的,操作结果都和第一次的相同。
如果我们的目标物体外面有很多无关的小区域,就用开运算去除掉;如果物体内部有很多小黑洞,就用闭运算填充掉
三、其他形态学操作
1、顶帽与黑帽
顶帽 tophat(原图像与开操作之间的差值图像)
黑帽 blackhat(原图像与闭操作图像的差值)
1 import cv2 as cv 2 import numpy as np 3 4 def top_hat_demo(image): 5 gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) 6 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) 7 cv.imshow('binary',binary) #这里的二值化图像就可以看作是原图像(注意:基于腐蚀膨胀是可以直接对彩色图像操作的)) 8 kernel = cv.getStructuringElement(cv.MORPH_OPEN,(5,5)) 9 dst = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel) 10 cv.imshow('open',dst) #查看开操作图像 11 12 #tophat 13 dst = cv.morphologyEx(binary,cv.MORPH_TOPHAT,kernel) 14 cv.imshow('tophat',dst) #tophat差值图像 15 16 #blackhat 17 dst = cv.morphologyEx(binary,cv.MORPH_BLACKHAT,kernel) 18 cv.imshow('blackhat',dst) 19 img = cv.imread('flower.png') 20 cv.imshow('input image',img) 21 top_hat_demo(img) 22 cv.waitKey(0) 23 cv.destroyAllWindows()
2、形态学梯度Gradient
1 import cv2 as cv 2 import numpy as np 3 4 def gradient_demo(image): 5 gray = cv.cvtColor(imgae,cv.COLOR_BGR2GRAY) 6 ret , binary = cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) 7 cv.imshow('binary',binary) 8 kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,5)) 9 dilate_binary = cv.dilate(binary,kernel) 10 cv.imshow('dilate_binary',dilate_binary) 11 erode_binary = cv.erode(binary,kernel) 12 cv.imshow('erode_binary',erode_binary) 13 14 #基本梯度 cv.MORPH_GRADIENT 15 dst = cv.morphologyEx(binary,cv.MORPH_GRADIENT,kernel) 16 cv.imshow('gradient_binary',dst) 17 18 #内部梯度 19 dst2 = cv.subtract(binary,erode_binary) 20 cv.imshow('inner gradient',dst2) 21 22 #外部梯度 23 dst3 = cv.subtract(binary,dilate_binary) 24 cv.imshow('outer gradient',dst3) 25 26 img = cv.imread('flower.png') 27 cv.imshow('input image',img) 28 gradient_demo(img) 29 cv.waitKey(0) 30 cv.destroyAllWindows()