对于图像的处理,基本的步骤是这样的:
取得图像数据 —— 将图像进行平滑处理 —— 进行边缘检测,阈值分析 —— 进行形态学的操作 —— 获取某些特征点 —— 分析数据
那么在这里,我就讲解下边缘检测的这部分,然后后续再进行其他的讲解。
在OpenCV中,边缘检测的方法有以下几种:
Sobel
Scharr
Laplace
Canny
其中前三种的边缘检测是带方向的
那么我们就先从Sobel算子开始讲起吧!
先来看看Sobel方法的定义
def Sobel
(src,ddepth,dx,dy,dst=None,ksize=None,scale=None,delta=None,borderType=None)
ddepth:图像的颜色深度,针对不同的输入图像,输出目标图像有不同的深度,具体组合如下:
- 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
- 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
- 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
dx:int类型的,表示x方向的差分阶数,1或0
dy:int类型的,表示y方向的差分阶数,1或0
kSize:模板大小,前面虽然提到过,不过对于Sobel算子这里要补充下,这里的取值为1,3,5,7,当不输入的时候,默认为3。特殊的,当kSize = 1的时候,采用的模板为1*3或者3*1 而非平时的那些格式。
接下来看Scharr的定义
def Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)
会看到参数和Sobel算子一致,不过,该函数与Sobel的区别在于,Scharr仅作用于大小为3的内核。具有和sobel算子一样的速度,但结果更为精确
第一列显示的Sobel算子的在默认3*3模板下的图像,第二列显示的是Scahrr算子在默认3*3模板下的图像,会发现有明显的不同,Scharr给出的更为精确。第三列放的原图像和Sobel算子x=1 y=1的情况。 代码如下:
import cv2
img = cv2.imread("C:/Users/DELL/Desktop/img.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
sobel_x = cv2.Sobel(gray, cv2.CV_8U, 1, 0)
sobel_y = cv2.Sobel(gray, cv2.CV_8U, 0, 1)
sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 1)
scharr_x = cv2.Scharr(gray, cv2.CV_8U, 1, 0)
scharr_y = cv2.Scharr(gray, cv2.CV_8U, 0, 1)
cv2.imshow("src", img)
cv2.imshow("Sobel_x", sobel_x)
cv2.imshow("Sobel_y", sobel_y)
cv2.imshow("Sobel", sobel)
cv2.imshow("Scharr_x", scharr_x)
cv2.imshow("Scharr_y", scharr_y)
cv2.waitKey(0)
接下来讲下Laplace
def Laplacian(src,ddepth,dst=None,ksize=None,scale=None,delta=None,borderType=None)
scale:Double类型的,计算拉普拉斯可选比例因子,有默认值1
delta:加到输出像素的值,默认为0
borderType:边界模式。默认值BORDER_DEFAULT
Laplace其实利用Sobel算子的运算,它通过Sobel算子运算出图像在x方向和y方向的导数,来得到我们载入图像的拉普拉斯变换结果。
这里简单看下Laplace的说明,就在方法里面的
The function calculates the Laplacian of the source image by adding up the second x and y
. derivatives calculated using the Sobel operator:
由于这个,我这里特意选取了同样kSize的两种方法做下比较:
发现Laplace的轮廓更为清晰。
我觉得Laplace算是Sobel的升级版。
代码如下:
import cv2
img = cv2.imread("C:/Users/DELL/Desktop/img.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
laplace = cv2.Laplacian(gray, cv2.CV_8U, ksize=3)
sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 1)
cv2.imshow("Laplace", laplace)
cv2.imshow("Sobel", sobel)
cv2.waitKey(0)
最后讲下Canny函数:
def Canny(image,threshold1,threshold2,edges=None,apertureSize=None,L2gradient=None)
threshold1:int类型的,低阈值
threshold2:int类型的,高阈值
edeges:单通道存储边缘的输出图像
apertureSize:Sobel算子内核(kSize)大小
L2gradiend:Bool类型的,为真表示使用更精确的L2范数进行计算(两个方向的倒数的平方再开放),为假表示用L1范数(直接将两个方向导数的绝对值相加)
由于和阈值相关,那么我们这里就不指定阈值了,使用进度条的方式来调整阈值获得我们想要图像,直接上图,进度值直接看图吧:
会发现,当我们改变阈值后,图像后渐渐过滤掉一些背景,当然,重在阈值的选取上,当阈值选大的时候,也是会将图像的特征完全损坏的。
代码如下:
import cv2
img = cv2.imread("C:/Users/DELL/Desktop/img.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gauss = cv2.GaussianBlur(gray, (3, 3), 1)
def doCanny(x):
position = cv2.getTrackbarPos("CannyBar", "Canny")
canny = cv2.Canny(gauss, position, position*2.5)
cv2.imshow("Canny", canny)
cv2.namedWindow("Canny")
cv2.createTrackbar("CannyBar", "Canny", 1, 100, doCanny)
cv2.waitKey(0)