【OpenCV-Python】——边缘和轮廓&Laplacian/Sobel/Canny边缘检测&查找/绘制轮廓及轮廓特征&霍夫直线/圆变换

目录

前言:

1、边缘检测

1.1 Laplacian边缘检测

 1.2 Sobel边缘检测

 1.3 Canny边缘检测

2、图像轮廓

2.1 查找轮廓

 2.2 绘制轮廓

2.3 轮廓特征

3、霍夫变换

3.1 霍夫直线变换

 3.2 霍夫圆变换

总结:


前言:

图像的边缘是指图像中灰度值急剧变化的位置,边缘检测的目的是为了绘制边缘线条。边缘检测的目的是为了绘制出边缘线条。边缘通常是不连续的,不能表示整体。

图像的轮廓是指将边缘连接起来形成的整体。这次主要学习边缘检测、图像轮廓和霍夫变换。

1、边缘检测

边缘检测结果通常为黑白图像,图像中的白色线条表示边缘。常见的边缘检测算法有Laplacian边缘检测、Sobel边缘检测和Canny边缘检测。

1.1 Laplacian边缘检测

使用图像矩阵与拉普拉斯核进行卷积运算,其本质是计算图像中任意一点与其在水平方向和垂直方向上4个相邻点平均值的差值

dst=cv2.Laplacian(src,depth,ksize,scale,delta,borderType)

depth表示目标图像深度。后四个为可选参数,ksize为用于计算二阶导数滤波器的系数,必须为正数且奇数;scale为可选比例因子;delta为添加到边缘检测结果中的可选增量值;最后为边界类型。

img=cv2.imread('dog2.png')
cv2.imshow('img',img)
img2=cv2.Laplacian(img,cv2.CV_8U)
cv2.imshow('img2',img2)
cv2.waitKey(0)

 1.2 Sobel边缘检测

高斯滤波微分方程结合起来执行图像卷积运算,其结果有一定抗噪性。

dst=cv2.Sobel(src,depth,dx,dy,ksize,scale,delta,borderType)

dx为导数x的阶数,dy为导数y的阶数。后四个为可选参数,ksize是扩展的Sobel内核的大小,必须是1,3,5或7;scale为计算导数的可选比例因子,其他与拉普拉斯一样。

img=cv2.imread('dog2.png')
cv2.imshow('img',img)
img2=cv2.Sobel(img,cv2.CV_8U,0,1)  #表示只计算垂直方向的导数
cv2.imshow('img2',img2)
cv2.waitKey(0)

 1.3 Canny边缘检测

Laplacian和Sobel边缘检测都通过卷积计算边缘,算法比较简单,因此结果噪声较多或者损失过多的边缘信息。Canny的算法则更加复杂:首先使用高斯滤波去除图像噪声;然后使用Sobel核进行滤波,计算梯度;在边缘使用非最大值抑制;对检测出的边缘进行双阈值以去除假阳性;最后分析边缘之间的连接性,保留真正的边缘,消除不明显的边缘。

dst=cv2.Canny(src,threshold1,threshold2,apertureSize,L2gradient)

threshold1是第1阈值,threshold2是第2个阈值。后两个为可选参数,前者为计算梯度时使用的Sobel核大小,后者为标志。

img=cv2.imread('dog2.png')
cv2.imshow('img',img)
img2=cv2.Canny(img,150,200)
cv2.imshow('img2',img2)
cv2.waitKey(0)

2、图像轮廓

图像轮廓是指由位于边缘、连续的、具有相同颜色和强度的点构成的曲线,可用于形状分析、对象检测和识别等。

2.1 查找轮廓

contours,hierarchy=cv2.findContours(image,mode,method,offset)

contours是返回的轮廓,hierarchy是返回的轮廓的层次结构;mode是轮廓检索模式,method是轮廓的近似方法。offset是可选参数,为每个轮廓点移动的可选偏移量。

img=cv2.imread('tuxing.png')
cv2.imshow('img',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
ret,thresh=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)  #为了提高准确率,二值化阈值处理
b,c,h=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  #查找轮廓,这里接受参数是三个因为两个报错了
print('轮廓:',c)
print('轮廓类型:',type(c))
print('轮廓个数:',len(c))
print('层次:',h)
print('层次类型:',type(h))
for i in range(3):
    img3=np.zeros(img.shape,np.uint8)+255  #创建一幅与原图等大小的白色图像
    cv2.polylines(img3,[c[i]],True,(255,0,0),2)   #依次绘制轮廓
    cv2.imshow('img%s'%i,img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

cv2.findContours()返回的是一个list对象,保存了轮廓数组。轮廓数组的每个元素都是一个表示轮廓的array对象;返回的轮廓层次是一个numpy.ndarray对象。

轮廓层次:返回的轮廓层次是一个numpy.ndarray对象。根据轮廓的嵌套关系,可将轮廓之间的层次关系分为父级和子级,外部轮廓为父级,内部是子级。numpy.ndarray对象的每个元素关系格式:【下一个轮廓  前一个轮廓  第一个子级轮廓  父级轮廓】,如【-1  0  2  -1】中-1表示不存咋对应轮廓,前一个轮廓在轮廓数组中的序号为0,第一个子级轮廓序号为2.

 2.2 绘制轮廓

绘制轮廓用cv2.drawContours()函数:

image=cv2.drawContours(image,contours,contoursIdx,color,thickness,linetype,hierarchy,maxLevel,offset)

image是在其中绘制轮廓的图像,如先定义的一块白色图像;contours为返回的轮廓数组,contoursIdx是要绘制的轮廓的索引,大于或等于0时绘制对应序号的轮廓,负数时(通常为-1)表示绘制所有的轮廓);color为颜色。后面的都是可选参数,hierarchy是轮廓层次,maxLevel是可绘制的最大轮廓层次深度,offset是绘制轮廓的偏移位置。例如:

img3=cv2.drawContours(img3,c,-1,(0,0,255),2)  #c上面接受了返回的轮廓信息

2.3 轮廓特征

①轮廓的矩

轮廓的矩包含了轮廓的各种几何特征,如面积、位置、角度、形状等。函数是cv2.moments():

ret=cv2.moments(array,binaryImage)

ret是返回的轮廓矩,是一个字典对象。大多数矩的含义比较抽象,但其中的零阶矩(m00)表示轮廓的面积;array为轮廓数组;binaryImage为True时,会将array对象的所有非0值设置为1。

img=cv2.imread('tuxing.png')
cv2.imshow('img',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
ret,thresh=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)  #为了提高准确率,二值化阈值处理
b,c,h=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  #查找轮廓,这里接受参数是三个因为两个报错了
for i in range(3):
    m=cv2.moments(c[i])
    print('轮廓%s的矩'%i,m)
    print('轮廓%s的面积:'%i,m['m00'])   #零阶矩表示面积
cv2.waitKey(0)
cv2.destroyAllWindows()

 ②轮廓的面积

ret=cv2.contourArea(contour,oriented)

ret为返回的面积,直接是一个数值;orient为可选参数,为True时,返回值的正与负表示轮廓时顺时针还是逆时针;为False(默认值)时,函数返回值为绝对值。

③轮廓的长度

ret=cv2.ardLength(contour,closed)

ret是返回值,直接是一个长度值;closed时布尔值,为True时表示轮廓是封闭的。

④轮廓的近似多边形

ret=cv2.approxPolyDP(contours,epsilon,closed)

ret是返回的多边形轮廓数组;是精度,表示多边形接近轮廓的最大距离;close是布尔值,True为封闭。

app=cv2.approxPolyDP(c[0],0.1,True)  #c[0]表示上面得到的轮廓0
img=cv2.drawContours(img,[app],-1,(255,0,0),2)  #绘制多边形轮廓

⑤轮廓的凸包、直边界矩形、旋转矩形、最小外包圆、拟合椭圆、拟合直线、最小外包三角形等,这里不具体介绍,需要时可具体查找。

3、霍夫变换

霍夫变换用于在图像中查找直线和圆等形状。

3.1 霍夫直线变换

cv2.HoughLines()函数利用霍夫变换算法检测图像中的直线:

lines=cv2.HoughLines(image,rho,theta,threshold)

lines是返回的直线,rho是距离的精度(以像素为单位)通常是1;theta是角度的精度,通常使用Π/180°,表示搜索所有可能的角度;threhold未阈值,值越小检测出的直线越多。

一般是在边缘检测如Canny后再检测直线,注意返回的是直线的数组信息,还要通过line函数绘制:

img=cv2.imread('xiangqi.png')
cv2.imshow('img',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
edges=cv2.Canny(gray,50,150,apertureSize=3)  #边缘检测
lines=cv2.HoughLines(edges,1,np.pi/180,150)  #霍夫直线变换
img3=img.copy()
for line in lines:   #一种固定用法。lines是一系列数组,line是一个数组(一个直线的信息)。逐条绘制
    rho,theta=line[0]
    a=np.cos(theta)
    b=np.sin(theta)
    x0,y0=a*rho,b*rho
    pt1=(int(x0 + 1000 * (-b)), int(y0 + 1000 * (a))) #计算直线端点
    pt2=(int(x0 - 1000 * (-b)), int(y0 - 1000 * (a))) #计算直线端点
    cv2.line(img3,pt1,pt2,(0,0,255),1)  #绘制
cv2.imshow('HoughLines',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

   

 另一种cv2.HoughLinesP()函数利用概率霍夫变换算法来检测图像中的直线:

lines=cv2.HoughLinesP(image,rho,theta,threshold,minLineLength,maxLineGap)

后两个是可选参数,前者是可接受的直线的最小长度,默认值0;后者是共线线段之间的最大间隔,默认为0。

img=cv2.imread('xiangqi.png')
cv2.imshow('img',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
lines=cv2.HoughLinesP(edges,1,np.pi/180,1,100,10)  #霍夫直线变换
img3=img.copy()
for line in lines:   #一种固定用法。lines是一系列数组,line是一个数组(一个直线的信息)。逐条绘制
    x1,y1,x2,y2=line[0]
    cv2.line(img3,(x1,y1),(x2,y2),(0,0,255),2)  #绘制
cv2.imshow('HoughLines',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

 3.2 霍夫圆变换

cv2.HoughCircles()函数利用霍夫变换查找图像中的圆:

circles=cv2.HoughCircles(image,method,dp,minDist,param1,param2,minRadius,maxRadius)

method是查找方法,可设置为cv2.HOUGH_GRADIET和cv2.HOUGH_GRADIET_ALT;dp是累加器分辨率,与图像分辨率成反比,如dp=1时累加器与输入图像的分辨率相同,dp=2时累加器的宽度和高度是输入图像的一半;minDist是圆心间的最小距离。

后四个是可选参数,param1是对应Canny边缘检测的高阈值(低阈值时高阈值的一半),默认100;param2时圆心位置必须达到的投票数,值越大,检测出的圆越少,默认100。minRadius是最小圆半径,默认为0;最后是最大圆半径,默认为0:

img=cv2.imread('xiangqi.png')
cv2.imshow('img',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #转换为灰度图
edges=cv2.Canny(gray,50,150,apertureSize=3)  #边缘检测
circles=cv2.HoughCircles(edges,cv2.HOUGH_GRADIENT,1,50,param2=30,minRadius=10,maxRadius=40)  #霍夫直线变换
circles=np.uint16(np.around(circles))
img3=img.copy()
for i in circles[0,:]:   #一种固定用法。lines是一系列数组,line是一个数组(一个直线的信息)。逐条绘制
    cv2.circle(img3,(i[0],i[1]),i[2],(0,255,0),2)  #绘制圆
    cv2.circle(img3, (i[0],i[1]),2,(255,0,0),3)  # 绘制圆心
cv2.imshow('HoughCircles',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结:

由于是初学者可能很多地方没有总结完全或者有误,后续深入学习后会不断回来该删,也欢迎各位朋友指正!下次学习直方图

猜你喜欢

转载自blog.csdn.net/weixin_51658186/article/details/130370996