以下所有操作建议使用png格式的原始图片,使用jpg图片虽然不会出错,但一些操作的结果不太好,原因未知,可能是png格式保留信息更多?
(1)图像形态学
--腐蚀操作
先读进来原始图像
import numpy as np import cv2 as cv from matplotlib import pyplot as plt img = cv.imread("circle.jpg")
plt.imshow(img)
plt.show()
图像是一个⚪周围有一些杂乱的线
我们进行腐蚀操作cv.erode(图像,卷积核,操作次数),看看结果
kernel = np.ones((30,30),np.uint8)#设置一个卷积核大小为(30,30),指定的核越大,边缘被腐蚀的可能性越大 erosion1 = cv.erode(img,kernel,iterations = 1)#执行一次腐蚀 erosion2 = cv.erode(img,kernel,iterations = 2)#执行两次腐蚀 erosion3 = cv.erode(img,kernel,iterations = 3) res = np.hstack((erosion1,erosion2,erosion3)) plt.imshow(res) plt.show()
可以看到杂线条没了,且“想内塌陷”
--膨胀操作,我们在腐蚀的结果上膨胀
kernel = np.ones((30,30),np.uint8) dilate1 = cv.dilate(img,kernel,iterations = 1) dilate2 = cv.dilate(img,kernel,iterations = 2) dilate3 = cv.dilate(img,kernel,iterations = 3) dilate4 = cv.dilate(img,kernel,iterations = 4) res = np.hstack((dilate1,dilate2,dilate3,dilate4)) plt.imshow(res) plt.show()
可以发现,第一次膨胀后我们得到了一个完整的⚪但我们的杂线条已经消失了,再继续进行膨胀,图像越来越大
--开运算和闭运算
开运算就是先腐蚀再膨胀
闭运算就是先膨胀再腐蚀
拿原始图像来看一下效果
开运算结果:
opening = cv.morphologyEx(img,cv.MORPH_OPEN,kernel) plt.imshow(opening) plt.show()
闭运算结果:
closing = cv.morphologyEx(img,cv.MORPH_CLOSE,kernel) plt.imshow(closing) plt.show()
--图像梯度运算
#梯度运算等于膨胀-腐蚀 kernel = np.ones((7,7),np.uint8) gradient = cv.morphologyEx(img,cv.MORPH_GRADIENT,kernel) plt.imshow(gradient) plt.show()
这样我们就得到了图像的轮廓了
--礼帽和黑帽
礼帽 = 原始输入 - 开运算结果(开运算为先腐蚀再膨胀结果为原始的不带杂线的,最终结果为那些原始图像的杂线)
黑帽 = 闭运算 - 原始输入 (闭运算为先膨胀再腐蚀结果为原始图像加深杂线,最终为原始整体轮廓)
#礼帽 tophat = cv.morphologyEx(img,cv.MORPH_TOPHAT,kernel) plt.imshow(tophat) plt.show()
#黑帽 blackhat = cv.morphologyEx(img,cv.MORPH_BLACKHAT,kernel) plt.imshow(blackhat) plt.show()
(2)图像梯度处理
超级详细的讲解:https://blog.csdn.net/poem_qianmo/article/details/25560901
--sobel算子
sobel原型:Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) -> dst
src:图片
ddepth:图像深度,当 ddepth为-1时, 输出图像将和输入图像有相同的深度。输入8位图像则会截取顶端的导数
dx =1代表水平方向,dy=1代表竖直方向
ksize:代表算子大小
import cv2 as cv import numpy as np def cvshow(img): cv.imshow("img",img) cv.waitKey(0) cv.destroyAllWindows() img = cv.imread("pie.png") cvshow(img) #进行SObel算子 sobel = cv.Sobel(img,cv.CV_64F,1,0,ksize=3) sobel = cv.convertScaleAbs(sobel)#取绝对值 cvshow(sobel)
当我们不加取绝对值时,结果为一半,加上取绝对值就可以获取到完整的
这里建议分别算X方向的sobel和Y方向的sobel再加一起,这样会比直接dx=1,dy=1一起算的更清晰
--Scharr算子和laplacian算子
import cv2 as cv import numpy as np def cvshow(img): cv.imshow("img",img) cv.waitKey(0) cv.destroyAllWindows() img = cv.imread("car.jpg") scharr_x = cv.Scharr(img,cv.CV_64F,1,0) scharr_y = cv.Scharr(img,cv.CV_64F,0,1) scharr_x = cv.convertScaleAbs(scharr_x) scharr_y = cv.convertScaleAbs(scharr_y) scharr_xy = cv.addWeighted(scharr_x,0.5,scharr_y,0.5,0) laplacian = cv.Laplacian(img,cv.CV_64F) laplacian = cv.convertScaleAbs(laplacian) res = np.hstack((scharr_xy,laplacian)) cvshow(res)
左边为scharr算子,右边为拉普拉多算子,右边差不多只有主体
再对比一下3个的效果,从左至右分别为sobel,scharr,laplacian
import cv2 as cv import numpy as np def cvshow(img): cv.imshow("img",img) cv.waitKey(0) cv.destroyAllWindows() img = cv.imread("lena.jpg",0) sobel_x = cv.Sobel(img,cv.CV_64F,1,0,ksize=3) sobel_y = cv.Sobel(img,cv.CV_64F,0,1,ksize=3) sobel_x = cv.convertScaleAbs(sobel_x) sobel_y = cv.convertScaleAbs(sobel_y) sobel = cv.addWeighted(sobel_x,0.5,sobel_y,0.5,0) scharr_x = cv.Scharr(img,cv.CV_64F,1,0) scharr_y = cv.Scharr(img,cv.CV_64F,0,1) scharr_x = cv.convertScaleAbs(scharr_x) scharr_y = cv.convertScaleAbs(scharr_y) scharr_xy = cv.addWeighted(scharr_x,0.5,scharr_y,0.5,0) laplacian = cv.Laplacian(img,cv.CV_64F) laplacian = cv.convertScaleAbs(laplacian) res = np.hstack((sobel,scharr_xy,laplacian)) cvshow(res)
整体来看,scharr算子保留了最多的细节,但是连通噪点也一起保留了下来,sobel算子保留了大部分最主要的轮廓信息,laplacian只有最主体的信息
以上这一切的处理正是为了我们真正做轮廓处理做准备