OpenCV边缘与轮廓

第5章 边缘和轮廓

  • 5.1边缘检测
    • 5.1.1 Laplacian边缘检测
    • 5.1.2 Sobel边缘检测
    • 5.1.3 Canny边缘检测
    • 5.1.4 总结
  • 5.2 图像轮廓
    • 5.2.1查找轮廓
    • 5.2.2 绘制轮廓
  • 5.3 霍夫变换
    • 5.3.1霍夫直线变换
    • 5.3.2霍夫圆变换

5.1边缘检测

图像的边缘是指图像中灰度值发生急剧变化的位置,边缘检测的目的是为了绘制出边缘线条。边缘通常是不连续的,不能表示整体。图像轮廓是指将边缘连接起来形成的整体。

5.1.1 Laplacian边缘检测

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

cv2.Laplacian()函数用于实现Laplacian边缘检测,基本格式如下。
dst=cv2.Laplacian(src,depth[,kszie[,scale[,delta[,borderType]]]])
参数说明如下。
dst表示边缘检测后结果图像
src为原图像
ddepth为目标图像的深度
ksize为用于计算二阶导数滤波器的系数,必须为正数且是奇数
scale为可选比例因子
delta可添加到边缘检测结果中的可选增量值
borderType为边界值类型

import cv2
img=cv2.imread('D:\zhangjing\photo\hua.png',cv2.IMREAD_REDUCED_COLOR_2)
cv2.imshow('original',img)
'''
Laplacian边缘检测
dst=cv2.Laplacian(src,depth[,kszie[,scale[,delta[,borderType]]]])
dst表示边缘检测后结果图像
src为原图像
ddepth为目标图像的深度
ksize为用于计算二阶导数滤波器的系数,必须为正数且是奇数
scale为可选比例因子
delta可添加到边缘检测结果中的可选增量值
borderType为边界值类型
'''
img2=cv2.Laplacian(img,cv2.CV_8U)#拉普拉斯边缘检测
cv2.imshow('Laplacian',img2)
cv2.waitKey(0)
OpenCV边缘与轮廓_第1张图片 图1 原图
OpenCV边缘与轮廓_第2张图片图2 结果图像

5.1.2 Sobel边缘检测

Sobel边缘检测将高斯滤波和微分结合起来进行图像卷积运算,结果具有一定的抗噪性。
dst=cv2.sobel(src,depth,dx,dy[,kszie[,scale[,delta[,borderType]]]])
dx为导数x的阶数
dy为导数y的阶数
ksize为扩展的Sobel内核的大小,必须是1、3、5、7

import cv2
img=cv2.imread('D:\zhangjing\photo\hua.png',cv2.IMREAD_REDUCED_COLOR_2)
cv2.imshow('original',img)
'''
Laplacian边缘检测
dst=cv2.Laplacian(src,depth[,kszie[,scale[,delta[,borderType]]]])
dst表示边缘检测后结果图像
src为原图像
ddepth为目标图像的深度
ksize为用于计算二阶导数滤波器的系数,必须为正数且是奇数
scale为可选比例因子
delta可添加到边缘检测结果中的可选增量值
borderType为边界值类型
'''
img3=cv2.Sobel(img,cv2.CV_8U,0,1)#Sobel边缘检测
cv2.imshow('Sobel',img3)
cv2.waitKey(0)
OpenCV边缘与轮廓_第3张图片 图1 原图
OpenCV边缘与轮廓_第4张图片图2 结果图像

5.1.3 Canny边缘检测

Canny边缘检测的算法包括下列5个步骤。

  1. 使用高斯滤波去除图像噪声。
  2. 使用Sobel核进行滤波,计算梯度。
  3. 在边缘使用非最大值抑制。
  4. 对检测出的边缘使用双阈值以去除假阳性。
  5. 分析边缘之间的连续性,保留真正的边缘,消除不明显的边缘。

cv2.Canny()函数用于实现Canny边缘检测,其基本格式如下。
dst=cv2.Canny(src,threshold1,threshold2[,apertureSize[,L2gradient]])
参数说明如下。
threshold1为第一阈值
threshold2为第二阈值
apertureSize为计算梯度时使用的Sobel核大小
L2gradient为标志

import cv2
img=cv2.imread('D:\zhangjing\photo\hua.png',cv2.IMREAD_REDUCED_COLOR_2)
cv2.imshow('original',img)
'''
Laplacian边缘检测
dst=cv2.Laplacian(src,depth[,kszie[,scale[,delta[,borderType]]]])
dst表示边缘检测后结果图像
src为原图像
ddepth为目标图像的深度
ksize为用于计算二阶导数滤波器的系数,必须为正数且是奇数
scale为可选比例因子
delta可添加到边缘检测结果中的可选增量值
borderType为边界值类型
'''
img4=cv2.Canny(img,200,300)#Canny边缘检测
cv2.imshow('Canny',img4)
cv2.waitKey(0)
OpenCV边缘与轮廓_第5张图片 图1 原图
OpenCV边缘与轮廓_第6张图片图2 结果图像

5.1.4 总结

Laplacian算子法对噪声比较敏感,所以很少用该算子检测边缘,而是用来判断边缘像素视为与图像的明区还是暗区。拉普拉斯高斯算子是一种二阶导数算子,将在边缘处产生一个陡峭的零交叉,Laplacian算子是各向同性的,能对任何走向的界线和线条进行锐化,无方向性。这是拉普拉斯算子区别于其他算法的最大优点。
Sobel算子检测方法对灰度渐变和噪声较多的图像处理效果较好,sobel算子对边缘定位不是很准确,图像的边缘不止一个像素;当对精度要求不是很高时,是一种较为常用的边缘检测方法。
Canny方法不容易受噪声干扰,能够检测到真正的弱边缘。优点在于,使用两种不同的阈值分别检测强边缘和弱边缘,并且当弱边缘和强边缘相连时,才将弱边缘包含在输出图像中。

5.2 图像轮廓

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

5.2.1查找轮廓

cv2.findContours()函数用于从二值图像中查找图像轮廓,其基本格式如下。
contours,hierarchy=cv2.findContours(image,mode,method[,offset])
参数说明如下。
contours为返回的轮廓
hierarchy为返回的轮廓的层次结构
image为原图像
mode为轮廓的检索模式
method为轮廓的近似方法
offset为每个轮廓点的可选偏移量

#查找轮廓
import cv2
import numpy as np
img=cv2.imread('D:\zhangjing\photo\shape.png')#读取图像
cv2.imshow('original',img)#显示原图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将其转换为灰度图像
ret,img2=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)#二值化阈值处理
c,h=cv2.findContours(img2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓
'''
contours,hierarchy=cv2.findContours(image,mode,method[,offset])
contours为返回的轮廓
hierarchy为返回的轮廓的层次结构
image为原图像
mode为轮廓的检索模式
method为轮廓的近似方法
offset为每个轮廓点的可选偏移量
'''
print('轮廓',c)
print('轮廓类型',type(c))
print('伦廓个数',len(c))
print('层次',h)
print('层次类型',type(h))
for n in range((3)):
     img3=np.zeros(img.shape,np.uint8)+255#按原图大小创建一幅白色图像
     cv2.polylines(img3,[c[n]],True,(255,0,0),2)#绘制轮廓
     cv2.imshow('%s'%n,img3)#显示轮廓图像
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV边缘与轮廓_第7张图片

5.2.2 绘制轮廓

1.轮廓的矩
轮廓的矩包含了轮廓的各种几何特征,如面积、位置、角度、形状等。
cv2.moments()函数用于返回轮廓的矩,其基本格式如下。
ret=cv2.moments(array[,binaryImage])
参数说明如下。
ret为返回的轮廓矩,是一个字典对象
array为表示轮廓的数组
binaryimage值为true时,会将array对象中所有非0值设为1

#轮廓的矩
import cv2
import numpy as np
img=cv2.imread('D:\photo\shapes.png')
cv2.imshow('original',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将其转换为灰度图像
ret,img2=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)#二值化阈值处理
c,h=cv2.findContours(img2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓
img3=np.zeros(img.shape,np.uint8)+255#按原图大小创建一副白色图像
img3=cv2.drawContours(img3,c,-1,(0,0,255),2)
cv2.imshow('Contours',img3)
for n in range(len(c)):
    m=cv2.moments(c[n])
    '''
    ret=cv2.moments(array[,binaryImage])
    ret为返回的轮廓矩,是一个字典对象
    array为表示轮廓的数组
    binaryimage值为true时,会将array对象中所有非0值设为1
    '''
    print('轮廓%s的矩:'%n,m)
    print('轮廓%s的面积:'%n,m['m00'])
    cv2.waitKey(0)
    cv2.destroyAllWindows()

运行结果
在这里插入图片描述

2.轮廓的面积
cv2.contourArea()函数用于返回轮廓面积,其基本格式如下。
ret=cv2.contourArea(contour[,oriented]
参数说明如下。
ret为返回的面积
contour为轮廓
oriented为可选参数,其参数值为True时,返回值的正与负表示轮廓是顺指针还是逆时针;参数值为False时,函数返回值为绝对值

#轮廓的面积
import cv2
import numpy as np
img=cv2.imread('D:\photo\shapes.png')
cv2.imshow('original',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将其转换为灰度图像
ret,img2=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)#二值化阈值处理
c,h=cv2.findContours(img2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓
img3=np.zeros(img.shape,np.uint8)+255#按原图大小创建一副白色图像
img3=cv2.drawContours(img3,c,-1,(0,0,255),2)
cv2.imshow('Contours',img3)
for n in range(len(c)):
     m=cv2.contourArea(c[n])
     '''
     ret=cv2.contourArea(contour[,oriented]
     ret为返回的面积
     contour为轮廓
     oriented为可选参数,其参数值为True时,返回值的正与负表示轮廓是顺指针还是逆时针;参数值为False时,函数返回值为绝对值
     '''
     print('轮廓%s的面积'%n,m)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果如下
OpenCV边缘与轮廓_第8张图片

3.轮廓的长度
cv2.arcLength()函数用于返回轮廓的长度,其基本格式如下。
ret=cv2.arcLength(contour,closed)
ret为返回的长度
contour为轮廓
closed为布尔值,为True时表示轮廓是封闭的

#轮廓的长度
import cv2
import numpy as np
img=cv2.imread('D:\photo\shapes.png')
cv2.imshow('original',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将其转换为灰度图像
ret,img2=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)#二值化阈值处理
c,h=cv2.findContours(img2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓
img3=np.zeros(img.shape,np.uint8)+255#按原图大小创建一副白色图像
img3=cv2.drawContours(img3,c,-1,(0,0,255),2)
cv2.imshow('Contours',img3)
for n in range(len(c)):
     m=cv2.arcLength(c[n],True)
     '''
     ret=cv2.arcLength(contour,closed)
     ret为返回的长度
     contour为轮廓
     closed为布尔值,为True时表示轮廓是封闭的
     '''
     print('轮廓%s的长度:'%n,m)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果如下。
OpenCV边缘与轮廓_第9张图片

4.轮廓的近似多边形
cv2.approxPolyDP()函数用于返回轮廓的近似多边形,其基本格式如下。
ret=cv2.approxPolyDP(contour,epsilon,closed)
ret为返回的近似多边形
contour为轮廓
epsilon为精度,表示近似多边形接近轮廓的最大距离
closed为布尔值,为True时表示轮廓是封闭的

轮廓的近似多边形
import cv2
import numpy as np
img=cv2.imread('D:\photo\shape3.png')
cv2.imshow('original',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#将其转换为灰度图像
ret,img2=cv2.threshold(gray,125,255,cv2.THRESH_BINARY)#二值化阈值处理
c,h=cv2.findContours(img2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓
ep=[0.1,0.05,0.01]
arc1=cv2.arcLength(c[0],True)
print(arc1)
img3=np.zeros(img.shape,np.uint8)+255
img3=cv2.drawContours(img3,c,-1,(0,0,255),2)
for n in range(3):
     eps=ep[n]*arc1
     img4=img3.copy()
     app=cv2.approxPolyDP(c[0],eps,True)
     '''
     ret=cv2.approxPolyDP(contour,epsilon,closed)
     ret为返回的近似多边形
     contour为轮廓
     epsilon为精度,表示近似多边形接近轮廓的最大距离
     closed为布尔值,为True时表示轮廓是封闭的
     '''
     img4=cv2.drawContours(img4,[app],-1,(255,0,0),2)
     cv2.imshow('appro%.2f'%ep[n],img4)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV边缘与轮廓_第10张图片

5.轮廓的凸包
cv2.convexHull()函数用于返回轮廓的凸包,其基本格式如下。
cv2.convexHull(contour[,clockwise[,returnPoints]]])
参数说明如下。
hull为返回的凸包,是一个numpy.ndaarray对象,包含了凸包的关键点。
contour为轮廓。
clockwise为方向标记,为True时,凸包为顺时针方向;为False时,凸包为逆时针方向。
returnPoints为True时,返回的hull中包含的是凸包关键点的坐标;为False时,返回的是凸包关键点在轮廓中的索引。

6.轮廓的直边界矩形
cv2.boundingRect()函数用于返回轮廓的直边界矩形,其基本格式如下。
ret=cv2.boundingRect(contour)
ret返回的直边界矩形,是一个四元组,其格式为(矩形左上角x坐标,矩形左上角y坐标,矩形的宽度,矩形的高度)
contour为用于计算直边界矩形的轮廓
OpenCV边缘与轮廓_第11张图片

7.轮廓的旋转矩形
轮廓的旋转矩形是指可容纳轮廓的最小的矩形。cv2.minAreaRect()函数用于返回轮廓的旋转矩形,其基本格式如下。
box=cv2.minAreaRect(contour)
OpenCV边缘与轮廓_第12张图片

8.轮廓的最小外包圆
cv2.minEnclosingCircle()函数用于可返回轮廓的最小外包圆,其基本格式如下。
center,radius=cv2.minEnclosingCircle(contour)
参数说明如下。
center为圆心。
radius为半径。
contour为用于计算最小外包圆的轮廓。
OpenCV边缘与轮廓_第13张图片

OpenCV边缘与轮廓_第14张图片

9.轮廓的拟合椭圆
cv2.fitEllipse()函数用于返回轮廓的拟合椭圆,其基本格式如下。
ellipse=cv2.fitEllipse(contour)
参数说明如下。
ellipse为返回的椭圆。
contour为计算拟合椭圆的轮廓

OpenCV边缘与轮廓_第15张图片

10.轮廓的拟合直线
cv2.fitLine()函数用于返回轮廓的拟合直线,其基本格式如下。
line=cv2.fitLine(contour,distType,param,reps,aeps)
参数说明如下。
line为返回的拟合直线。
contour为用于计算拟合直线的轮廓。
distType为距离类型参数。
param为距离参数,与距离类型参数有关,其设置为0时,函数将自动选择最优值。
reps为计算拟合直线需要的径向精度,通常为0.01。
aeps为计算拟合直线需要的角度精度,通常为0.01。

OpenCV边缘与轮廓_第16张图片

11.轮廓的最小外包三角形
cv2.minEnclosingTriangle()函数用于可返回可容纳轮廓的最小外包三角形,其基本格式如下。
参数说明如下。
retval,triangle=cv2.minEnclosingTriangle(contour)
retval为最小外包三角形的面积。
triangle为最小外包三角形。
contour为用于计算最小外包三角形的轮廓。

OpenCV边缘与轮廓_第17张图片

5.3 霍夫变换

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

5.3.1霍夫直线变换

cv2.HoughLines()函数利用霍夫变换算法检测图像中的直线,其基本格式如下。
lines=cv2.HoughLines(image,rho,theta,threshold)
image为原图像
rho为距离的精度
theta为角度的精度
threshold为阈值,值越小,检测出的直线就越多

#霍夫直线
import cv2
import numpy as np
img=cv2.imread('D:\zhangjing\photo\shape3.jfif')
cv2.imshow('original',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)#霍夫直线变换
'''
lines=cv2.HoughLines(image,rho,theta,threshold)
image为原图像
rho为距离的精度
theta为角度的精度
threshold为阈值,值越小,检测出的直线就越多
'''
img3=img.copy()
for line in lines:#逐条绘制直线
    rho,theta=line[0]
    print()
    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),2)#绘制直线
cv2.imshow('HoughLines',img3)#显示结果图像
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV边缘与轮廓_第18张图片
OpenCV边缘与轮廓_第19张图片
cv2.HoughLinesP()函数利用霍夫变换算法检测图像中的直线,其基本格式如下。
lines=cv2.HoughLinesP(image,rho,theta,threshold[,minLineLength[,maxLineGap]])
minLineLength为可接受的直线最小长度,默认为0
maxLineGap为共线线段之间的最大间隔,默认为0

概率霍夫直线
import cv2
import numpy as np
img=cv2.imread('D:\zhangjing\photo\shape3.jfif')
cv2.imshow('original',img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges=cv2.Canny(gray,50,150,apertureSize=3)
lines=cv2.HoughLinesP(edges,1,np.pi/180,1,minLineLength=100,maxLineGap=10)
'''
lines=cv2.HoughLinesP(image,rho,theta,threshold[,minLineLength[,maxLineGap]])
minLineLength为可接受的直线最小长度,默认为0
maxLineGap为共线线段之间的最大间隔,默认为0
'''
img3=img.copy()
for line in lines:
    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()

OpenCV边缘与轮廓_第20张图片

5.3.2霍夫圆变换

cv2.HoughCircles()函数利用霍夫变换查找图像中的圆,其基本格式如下。
circles=cv2.HoughCircles(image,method,dp,mindist[param1[,param2[,minRadius[,maxRadius]]]])
method为查找方法,可设置为cv2.HOUGH_GRADIENT和cv2.HOUGH_GRADIENT_ALT。
dp为累加器分辨率,它与图像分辨率成反比。
minDist为圆心间的最小距离。
param1为对应Canny边缘检测的高阈值,默认值为100。
param2为圆心位置必须到达达到的投票数,值越大,检测出的圆越少,默认值为100。
minRadius为最小圆半径,半径小于该值的圆不会被检测出来。
maxRadius为最大圆半径,半径大于该值的圆不会被检测出来。

# 霍夫圆
import cv2
import numpy as np
img=cv2.imread('D:\zhangjing\photo\shape3.jfif')
cv2.imshow('original',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))
img2=img.copy()
for i in circles[0,:]:
    cv2.circle(img2,(i[0],i[1]),i[2],(255,0,0),2)
    cv2.circle(img2,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow('circle',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV边缘与轮廓_第21张图片

你可能感兴趣的:(笔记,计算机视觉,opencv,人工智能)