形态学转换
形态变换是基于图像形状的简单操作,两种基本形态学算子是侵蚀
和膨胀
,也包括变体形式开运算
和闭运算
等。
侵蚀(Erosion)
侵蚀前景物体的边界,内核在2D卷积时,只有当内核下所有像素都为1
时才为1
,否则被侵蚀变成0
。根据内核大小,边界附近的像素都会被丢弃,因此能减小前景对象(白色区域)的大小,有助于去除小的白色噪声。如下例。
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
plt.imshow(np.hstack((img,erosion)),'gray')
扩张、膨胀(Dilation)
与侵蚀相反,当内核下至少一个像素为1
时,则为1
,因此能增加图像中前景对象(白色区域)的大小。
dilation = cv2.dilate(img,kernel,iterations = 1)
plt.imshow(np.hstack((img,dilation)),'gray')
开运算(Opening)
开运算是先侵蚀再扩张,有助于消除盐噪声,如下例。
opening = cv2.morphologyEx(salt, cv2.MORPH_OPEN, kernel)
plt.imshow(np.hstack((salt,opening)),'gray')
闭运算(Closing)
闭运算是先扩张再侵蚀,有助于去除前景对象内部的小黑点(椒噪声),如下例。
closing = cv2.morphologyEx(pepper, cv2.MORPH_CLOSE, kernel)
plt.imshow(np.hstack((pepper,closing)),'gray')
形态学梯度
图像扩张和侵蚀的差,结果类似于图像的轮廓线。
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # dilation-erosion
plt.imshow(np.hstack((img,gradient)),'gray')
Top Hat
输入图像与开运算的差。
kernel_tophat = np.ones((9,9),np.uint8)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel_tophat) # img-opening
plt.imshow(np.hstack((img,tophat)),'gray')
Black Hat
输入图像与闭运算的差。
kernel_blackhat = np.ones((9,9),np.uint8)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel_blackhat) # img-closing
plt.imshow(np.hstack((img,blackhat)),'gray')
结构元素
可以通过cv.getStructuringElement()
函数来创建椭圆形、圆形的内核,只需传递内核形状和大小即可。
>>> cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) #矩形
array([[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]], dtype=uint8)
>>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) #椭圆
array([[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]], dtype=uint8)
>>> cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) #十字形
array([[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]], dtype=uint8)
图像梯度
OpenCV提供三种类型的梯度滤波器或高通滤波器用于查找图像梯度和边缘,即Sobel,Scharr和Laplacian。
Sobel和Scharr算子
Sobel算子是高斯平滑加微分运算的联合运算,它对噪声更具鲁棒性。可以通过xorder
和yorder
来指定导数方向,通过ksize
指定内核大小,当ksize=-1
时,使用Scharr滤波器
,效果比Sobel滤波器
更好。
Laplacian算子
计算由关系$\Delta src=\frac{\partial src}{\partial^2 x^2}+\frac{\partial^2 src}{\partial y^2}$给出的图像的拉普拉斯图。每一阶导数是由Sobel算子计算。如果ksize=1
,则使用以下内核进行滤波:
$$
kernel=\begin{bmatrix}0&1&0\\1&-4&1\\0&1&0\end{bmatrix}
$$
代码
以下实例在一个图标中展示了所有算子,内核都是5x5
大小,深度设置为1
来得到np.int8
类型的结果图像。
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('sudoku.png',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
plt.figure(figsize=(8,8))
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()