GitHub@ShaneHolmes_OpenCV_image
腐蚀与膨胀( Erosion 与 Dilation) 是OpenCV提供的两种最基本的形态学操作。
简单来讲,形态学操作就是基于形状的一系列图像处理操作。通过将 结构元素 作用于输入图像来产生输出图像。
最基本的形态学操作有二:腐蚀与膨胀(Erosion 与 Dilation)。 他们的运用广泛:
消除噪声
分割(isolate)独立的图像元素,以及连接(join)相邻的元素。
寻找图像中的明显的极大值区域或极小值区域。
腐蚀和膨胀是最基本的形态学算子
结构元素
就相当于我们在滤波中所涉及到的模板,也就是说它是一个给定像素的矩阵,这个矩阵可以是任意形状的, 一般情况下都是正方形,圆形或者菱形的但是在结构元素中有一个中心点(也叫做anchor point)。 和模板中心一样,处理后的结果赋值给和这个中心点对齐的像素点。处理的过程也是基本相同。
结构元素和卷积模板的区别在于,膨胀是以集合运算为基础的,卷积是以算数运算为基础的。 (OpenCV里面的腐蚀膨胀都是针对白色目标区域的)
膨胀:用结构元素的中心点对准当前正在遍历的这个像素, 然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最大值,用这个最大值替换当前像素值,给图像中的对象边界添加像素,使二值图像扩大一圈
腐蚀:用结构元素的中心点对准当前正在遍历的这个像素, 然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最小值,用这个最小值替换当前像素值,删除对象边界的某些像素,使二值图像减小一圈
腐蚀:删除对象边界的某些像素
膨胀:给图像中的对象边界添加像素
腐蚀用于分割(isolate)独立的图像元素, 膨胀用于连接(join)相邻的元素 。腐蚀、膨胀可用于去噪(低尺寸结构元素的腐蚀操作很容易去掉分散的椒盐噪声点),图像轮廓提取、图像分割、寻找图像中的明显的极大值区域或极小值区域等
import cv2
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
img = cv2.imread('C://Users//47463//Desktop//2//car.jpg')
cv2.imshow('jeep', img)
erosion = cv2.erode(img, kernel, iterations=3)
cv2.imshow('erosion', erosion)
dilation = cv2.dilate(img, kernel, iterations=3)
cv2.imshow('dilation', dilation)
cv2.waitKey(0)
cv2.destroyAllWindows()
开运算和闭运算就是将腐蚀和膨胀按照一定的次序进行处理。但这两者并不是可逆的,即先开后闭并不能得到原先的图像。
源代码:
import cv2
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
img = cv2.imread('C://Users//47463//Desktop//2//car.jpg')
cv2.imshow('org', img)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=2)
cv2.imshow('open', opening)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=2)
cv2.imshow('close', closing)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('gradient', gradient)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat', blackhat)
cv2.waitKey(0)
cv2.destroyAllWindows()
闭运算用来连接被误分为许多小块的对象,而开运算用于移除由图像噪音形成的斑点。因此,某些情况下可以连续运用这两种运算。如对一副二值图连续使用闭运算和开运算,将获得图像中的主要对象。同样,如果想消除图像中的噪声(即图像中的“小点”),也可以对图像先用开运算后用闭运算,不过这样也会消除一些破碎的对象。
在OpenCV中,边缘检测的方法有以下几种:
Sobel
Scharr
Laplace
Canny
形态学检测边缘的原理:
在膨胀时,图像中的物体会想周围“扩张”;腐蚀时,图像中的物体会“收缩”。比较这两幅图像,由于其变化的区域只发生在边缘。所以这时将两幅图像相减,得到的就是图像中物体的边缘。实际使用时用Canny或Harris等算法。
源代码:
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('C://Users//47463//Desktop//2//car.jpg',0)
edges = cv2.Canny(img,150,250)
plt.figure(figsize = (20, 10))
plt.subplot(1, 2, 1)
plt.imshow(img,cmap = 'gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image')
plt.axis('off')
plt.show()
pip install matplotlib
python -m pip list
命令查看已经安装好的依赖包:另:
由于canny方法那种高低阈值对结果有影响,可以通过以下代码对该影响进行观察:
源代码:
import cv2
img = cv2.imread("C://Users//47463//Desktop//2//car.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)#灰度图片
gauss = cv2.GaussianBlur(gray, (3, 3), 1)#高斯模糊
def doCanny(x):#回调函数
position = cv2.getTrackbarPos("CannyBar", "Canny")#获取position
canny = cv2.Canny(gauss, position, position*2.5)#使用canny方法进行边缘检测
cv2.imshow("Canny", canny)
cv2.namedWindow("Canny")
cv2.createTrackbar("CannyBar", "Canny", 1, 100, doCanny)#在cv的Canny窗口创建bar
cv2.waitKey(0)
先用十字形的结构元素膨胀像素,这种情况下只会在边缘处“扩张”,角点不发生变化。接着用菱形的结构元素腐蚀原图像,导致只有在拐角处才会“收缩”,而直线边缘都未发生变化。然后是用X形膨胀原图像,角点膨胀的比边要多。这样第二次用方块腐蚀时,角点恢复原状,而边要腐蚀的更多。所以当两幅图像相减时,只保留了拐角处。
源代码:
# coding=utf-8
import cv2
image = cv2.imread("C://Users//47463//Desktop//2//house.jpg", 0)
origin = cv2.imread("C://Users//47463//Desktop//2//house.jpg")
# 构造5×5的结构元素,分别为十字形、菱形、方形和X型
cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
# 菱形结构元素的定义稍麻烦一些
diamond = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
diamond[0, 0] = 0
diamond[0, 1] = 0
diamond[1, 0] = 0
diamond[4, 4] = 0
diamond[4, 3] = 0
diamond[3, 4] = 0
diamond[4, 0] = 0
diamond[4, 1] = 0
diamond[3, 0] = 0
diamond[0, 3] = 0
diamond[0, 4] = 0
diamond[1, 4] = 0
square = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
x = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
# 使用cross膨胀图像
result1 = cv2.dilate(image, cross)
# 使用菱形腐蚀图像
result1 = cv2.erode(result1, diamond)
# 使用X膨胀原图像
result2 = cv2.dilate(image, x)
# 使用方形腐蚀图像
result2 = cv2.erode(result2, square)
# result = result1.copy()
# 将两幅闭运算的图像相减获得角
result = cv2.absdiff(result2, result1)
# 使用阈值获得二值图
retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY)
# 在原图上用半径为5的圆圈将点标出。
for j in range(result.size):
y = int(j / result.shape[0])
x = j % result.shape[0]
if result[x, y]==255:
cv2.circle(image, (y, x), 5, (255, 0, 0))
cv2.imshow("Result", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
更多文章:https://blog.csdn.net/qq_33208851/article/details/95237054