图像的形态学操作主要是腐蚀和膨胀,在原理上就是结构元和原图进行卷积操作,只不过原始卷积是进行计算,而腐蚀和膨胀是看结构元覆盖范围内1的情况。
腐蚀定义:若结构元覆盖的二值图像素都为1,则该结构元覆盖的像素置为1
膨胀定义:若结构元覆盖的二值图像素有1,则该结构元附带的像素置为1
由于图像经过腐蚀之后边缘向内缩了一圈,那么原图减去腐蚀后结果就是图像的内边界
def inner_edge(img):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
img_erode = cv2.erode(img, kernel)
img_edge = img - img_erode
return img_edge
结果图:
同样地,图像经过膨胀后,边缘向外扩展了一圈,那么再减去原图就是外边界
def outer_edge(img):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
img_dilate = cv2.dilate(img, kernel)
img_edge = img_dilate - img
return img_edge
效果图:
形态学梯度,就是膨胀后的结果减去腐蚀后结果,也就是内外边界相加
def grade_edge(img):
edge_inner = inner_edge(img)
edge_outer = outer_edge(img)
grade_edge = edge_inner + edge_outer
return grade_edge
效果图:
高通滤波器在空域的卷积,起到的效果就是提取轮廓信息,常用的算子有Sobel,Scharr和Laplacian算子。
由于Sobel算子只对x或者y方向进行轮廓提取,所以为了得到完整轮廓,需要将两个方向的轮廓进行整合。同时由于Sobel算子存在负元素,所以使用16s进行及计算,然后再取绝对值。
def sobel_edge(img):
edge_x = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=1)
edge_y = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=1)
Scale_x = cv2.convertScaleAbs(edge_x)
Scale_y = cv2.convertScaleAbs(edge_y)
img_edge = cv2.addWeighted(Scale_x, 0.5, Scale_y, 0.5, 0)
return img_edge
效果图:
当Sobel算子的大小设置为-1是即为特殊情况,被称作Scharr算子。同样地,Scharr算子也存在只对一个方向提取轮廓,所以需要将两个方向的轮廓相加。
def scharr_edge(img):
edge_x = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=-1)
edge_y = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=-1)
Scale_x = cv2.convertScaleAbs(edge_x)
Scale_y = cv2.convertScaleAbs(edge_y)
img_edge = cv2.addWeighted(Scale_x, 0.5, Scale_y, 0.5, 0)
return img_edge
结果图:
与Sobel和Scharr算子不同之处在于,Laplacian没有对于某个方向的限制,所以代码上是需要一次处理即可。
def laplacian_edge(img):
img_edge = cv2.Laplacian(img, cv2.CV_16S, ksize=1)
img_edge = cv2.convertScaleAbs(img_edge)
return img_edge
效果图: