形态学转换
侵蚀
侵蚀的原理是,一个卷积核,当它在图片上滑动的时候,只有在卷积核内所有像素都是1时,才被认为是1,如果有一个是0,则全部像素被替换成0。直观感受就是,黑色会向外扩散,从而导致黑色区域的边缘向外扩展。
img=cv.imread("text.jpg")
#创建一个卷积核
kernel=np.ones((5,5),np.uint8)
#侵蚀
erosion=cv.erode(img,kernel,iterations=1)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(erosion),plt.title("erosion")
plt.show()
扩张
它和侵蚀相反,白色会向白色区域边界扩展。因为侵蚀会将噪音去掉,也会导致图像缩小,所以采用扩张,对图像进行放大。
img=cv.imread("text.jpg")
kernel=np.ones((3,3),np.uint8)
#(图片,卷积核,迭代次数)
dilation=cv.dilate(img,kernel,iterations=1)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(dilation),plt.title("erosion")
plt.show()
开运算
开运算就是结合了侵蚀和扩张的滤波器,它先是对图像进行侵蚀,然后再进行扩张,对去噪声很有效。
img=cv.imread("text.jpg")
kernel=np.ones((3,3),np.uint8)
opening=cv.morphologyEx(img,cv.MORPH_OPEN,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(opening),plt.title("erosion")
plt.show()
注意观察红框内变化
闭运算
闭运算和开运算相反,先进行扩张,然后再侵蚀,因此,在图像上表现为黑色区域会减少,甚至被白色取代。
img=cv.imread("text.jpg")
kernel=np.ones((3,3),np.uint8)
closing=cv.morphologyEx(img,cv.MORPH_CLOSE,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(closing),plt.title("erosion")
plt.show()
形态学梯度
它有点类似于电脑上的高对比度模式,也有点开运算与闭运算交集的样子,从图像上看,白色会变成黑色,黑色变成白色,不同颜色仅保留边界的一部分,其余变成黑色。
img=cv.imread("text.jpg")
kernel=np.ones((2,2),np.uint8)
gradient=cv.morphologyEx(img,cv.MORPH_GRADIENT,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(gradient),plt.title("erosion")
plt.show()
顶帽
它是输入图片和开运算的差(隐隐约约有一种水平边缘检测的味道哈哈哈)
img=cv.imread("text.jpg")
kernel=np.ones((2,2),np.uint8)
tophat=cv.morphologyEx(img,cv.MORPH_TOPHAT,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(tophat),plt.title("erosion")
plt.show()
黑帽
它是输入图像与闭运算的差值(这个又像彩色版的垂直边缘检测。。。)
img=cv.imread("text.jpg")
kernel=np.ones((2,2),np.uint8)
blackhat=cv.morphologyEx(img,cv.MORPH_BLACKHAT,kernel)
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.imshow(blackhat),plt.title("erosion")
plt.show()
结构函数
numpy可以建立ones,zeros这类矩形矩阵,但是有时候需要椭圆或者圆型,或者十字矩阵,numpy就会显得比较复杂。openCV提供了一个函数getStrcturingElement来快速创建这些特殊的矩阵,只需要输入矩阵大小就可以创建。
print(cv.getStructuringElement(cv.MORPH_RECT,(5,5)))
print(cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5)))
print(cv.getStructuringElement(cv.MORPH_CROSS,(5,5)))
矩形
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]
椭圆
[[0 0 1 0 0]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[0 0 1 0 0]]
十字
[[0 0 1 0 0]
[0 0 1 0 0]
[1 1 1 1 1]
[0 0 1 0 0]
[0 0 1 0 0]]
图像梯度
Sobel
图像梯度的Sobel操作,是结合高斯函数平滑和Sobel算子的过滤操作,更加抗噪音,效果更好。一般Sobel算子是一个3*3的卷积核,它的形式为(Gy方向):
1,0,-1
2,0,-2
1,0,-1
实际上,Sobel就是将普通的边缘滤波器的中间行或者列,添加了权重。它在深度学习中也同样有用,作为滤波器,它的作用是进行垂直边缘检测。
注意它的图像是从左往右从亮到暗的。
而将它顺时针旋转90°,可以得到水平边缘检测器,同时也是图像梯度的Gx方向。
1,2, 1
0,0 ,0
-1,-2,-1
注意它的图像是从上往下,逐渐变暗.
Sobel_y=np.float32([[1,0,-1],[2,0,-2],[1,0,-1]])
Sobel_x=np.float32([[1,2,1],[0,0,0],[-1,-2,-1]])
plt.subplot(121),plt.imshow(Sobel_y),plt.title("Sobel_y")
plt.gray()
plt.subplot(122),plt.imshow(Sobel_x),plt.title("Sobel_x")
plt.gray()
plt.show()
在openCV中,可以指定卷积核的大小,ksize就是卷积核的大小。
Scharr
Scharr算子是Sobel的进阶版,它给每个元素都添加权重,它的表示形式和Sobel一样,但是数字进行了改变,3 X 3卷积核内元素之间的差值更大,从而更好地进行垂直和水平边缘检测。当ksize=-1时,使用 3 X 3 Scharr算子。它的形式:
3,0, -3
10,0,-10
3,0, -3
同样的,它的图像和Sobel表现一致。
Sobel_y=np.float32([[1,0,-1],[2,0,-2],[1,0,-1]])
Sobel_x=np.float32([[1,2,1],[0,0,0],[-1,-2,-1]])
Scharr_y=np.float32([[3,0,-3],[10,0,-10],[3,0,-3]])
Scharr_x=np.float32([[3,10,3],[0,0,0],[-3,-10,-3]])
plt.subplot(221),plt.imshow(Sobel_y),plt.title("Sobel_y")
plt.gray()
plt.subplot(222),plt.imshow(Sobel_x),plt.title("Sobel_x")
plt.gray()
plt.subplot(223),plt.imshow(Scharr_y),plt.title("Scharr_y")
plt.gray()
plt.subplot(224),plt.imshow(Scharr_x),plt.title("Scharr_x")
plt.gray()
plt.show()
Laplacian
它的每阶导数有Sobel算子计算,然后再用一个特殊的十字滤波器过滤。这个滤波器是:
0,1,0
1,-4,1
0,1,0
Laplacian=np.float32([[0,1,0],[1,-4,1],[0,1,0]])
plt.imshow(Laplacian),plt.title("Laplacian")
plt.gray()
plt.show()
在图片上的效果
彩色图片必须转换成灰色,它们只是一个2D滤波器。
img=cv.imread("text.jpg",0)
laplacian=cv.Laplacian(img,cv.CV_64F)
sobel_x=cv.Sobel(img,cv.CV_64F,1,0,ksize=3)
sobel_y=cv.Sobel(img,cv.CV_64F,0,1,ksize=3)
plt.subplot(221),plt.imshow(img,cmap="gray"),plt.title("Original")
plt.subplot(222),plt.imshow(laplacian,cmap="gray"),plt.title("Laplacian")
plt.subplot(223),plt.imshow(sobel_x,cmap="gray"),plt.title("Sobel_x")
plt.subplot(224),plt.imshow(sobel_y,cmap="gray"),plt.title("Sobel_y")
plt.show()
细节:
注意:
在我们的示例中,输出数据类型为cv.CV_8U 或np.uint8 。但这有一个小问题。黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值)。因此,当您将数据转换np.uint8时,所有负斜率均设为零。简而言之,您会错过这一边缘信息。
如果要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,例如cv.CV_16S ,cv.CV_64F 等,取其绝对值,然后转换回cv.CV_8U 。 下面的代码演示了用于水平Sobel滤波器和
结果差异的此过程。