形态学转换

形态变换是基于图像形状的简单操作,两种基本形态学算子是侵蚀膨胀,也包括变体形式开运算闭运算等。

侵蚀(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算子是高斯平滑加微分运算的联合运算,它对噪声更具鲁棒性。可以通过xorderyorder来指定导数方向,通过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()