OpenCV 常用检测算法

一、直线检测
1、直线检测

cv.HoughLines:使用标准霍夫变换,找到二值图像中的直线

lines = cv.HoughLines(
    image, 			# 8-bit、单通道的二值图像
    rho, 			# 累加器的距离分辨率,以像素为单位
    theta, 			# 累加器的角度分辨率,以弧度为单位
    threshold, 		# 累加器的阈值参数,太大会过滤大部分直线,太小则误检测会很多
    lines, 			# 
    srn, 			# 对于多尺度霍夫变换,它是距离分辨率的除数
    stn, 			# 对于多尺度霍夫变换,它是角度分辨率的除数
    min_theta, 		# 直线检查的最小角度,必须在 0 和 max_theta 之间
    max_theta		# 直线检查的最大角度,必须在 min_theta 和 CV_PI 之间
)

# 实例
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图
lines = cv.HoughLines(edge, 1, np.pi/90, 100)	# 直线检测

result = img.copy()
for rho,theta in lines[:, 0, :]:				# 遍历每一条直线
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho									# 计算坐标原点到直线的垂直点:(x0, y0)
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))					# 垂直点沿直线方向往左延伸1000个像素点:(x1, y1)
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))					# 垂直点沿直线方向往右延伸1000个像素点:(x2, y2)
    y2 = int(y0 - 1000*(a)) 
    cv.line(result,(x1,y1),(x2,y2),(255,0,0),1)
cv.imshow('11', result)
cv.waitKey(0)

注:

  • 粗糙的累加器距离分辨率为 rho,精确的累加器分辨率为 rho/srn;如果 srn=0 和 stn=0,则使用经典的霍夫变换,否则,这两个参数都应该是正的。
  • 检测结果以 ( ρ , θ ) (\rho, \theta) (ρ,θ) 形式返回, ρ \rho ρ 是坐标原点到直线的距离, θ \theta θ 是纵轴与直线的夹角,通过这两个参数就能恢复出一条直线(注意,不是线段)。
  • ( ρ , θ ) (\rho, \theta) (ρ,θ) 中恢复直线的算法见上述 for 循环中的代码,其中参数1000可以修改。

image-20201016111025806

2、线段检测

cv.HoughLinesP:使用概率霍夫变换,找到二值图像中的线段

lines =	cv.HoughLinesP(
    image, 			# 8-bit、单通道的二值化图像
    rho, 			# 累加器的距离分辨率,以像素为单位
    theta, 			# 累加器的角度分辨率,以弧度为单位
    threshold, 		# 累加器的阈值参数
    lines, 
    minLineLength, 	# 线段检查的最小长度,小于该长度的线段被过滤
    maxLineGap		# 连接同一直线上各点之间允许的最大间隙
)

# 实例
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图
lines = cv.HoughLinesP(edge, 1, np.pi/180, 100, minLineLength=200, maxLineGap=500)

result = img.copy()
for p in lines:
    x1,y1,x2,y2 = p[0]
    cv.line(result, (x1, y1), (x2, y2), (255,0,0), 1)	# 画直线
    cv.circle(result, (x1,y1), 5, (0,255,0), -1)		# 画端点1
    cv.circle(result, (x2,y2), 5, (0,255,0), -1)		# 画端点2
cv.imshow('111', result)
cv.waitKey(0)

注:

  • rho/theta 值越小,检测的线段越精细;值越大,被过滤掉的线段越多,只能在某一个区域内检测。
  • 检测结果以 [ x 1 , y 1 , x 2 , y 2 ] [x1,y1,x2,y2] [x1,y1,x2,y2] 形式返回,代表的是线段的两个端点坐标。

image-20201016113208899

二、霍夫圆检测

cv.HoughCircles:使用霍夫变换,找到灰度图中的圆

circles	= cv.HoughCircles(
    image, 		# 8-bit、单通道的灰度图
    method, 	# 检测方法,可以有:霍夫梯度法HOUGH_GRADIENT,优化后的霍夫梯度法HOUGH_GRADIENT
    dp, 		# 累加器分辨率与图像分辨率的反比
    minDist, 	# 探测到的圆中心之间的最小距离
    circles, 
    param1, 	# 参数1,和参数2一起使用
    param2, 	# 参数2,和参数1一起使用,它本身是圆心累加器的阈值
    minRadius, 	# 最小圆半径
    maxRadius	# 最大圆半径
)

# 实例
img2 = cv.imread('./circle.jpg')
gray = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
circles = cv.HoughCircles(
    gray, 
    cv.HOUGH_GRADIENT, 		# cv.HOUGH_GRADIENT_ALT 会报错,可能跟OpenCV版本有关
    1, 						# dp 值设置为 1 效果就比较好
    100, 
    param1=100, 
    param2=30, 
    minRadius=5, 
    maxRadius=300
)

result = img2.copy()
for circle in circles[0]:
    center_x, center_y, radius = circle
    cv.circle(result, (center_x, center_y), radius, (0.,255,0), 2)	# 画圆的轮廓
    cv.circle(result, (center_x, center_y), 3, (255,0,0), -1)		# 画圆心
cv.imshow('22', result)
cv.waitKey(0)

注:

  • dp=1 表示累加器分辨率和图像分辨率相同,dp=2 表示累加器的分辨率是图像分辨率的一半。如果检测方法用的是 HOUGH_GRADIENT_ALT,推荐使用 dp=1.5。
  • minDist 参数过小,会导致很多个相邻的小圆代替正确的大圆被检测到;参数过大,则部分圆会被漏检。
  • param1和param2存在大小比较,较大的那一个将会进入 Canny 边缘检测器;param2 越小,检测到的假圆越多。
  • 在 HOUGH_GRADIENT_ALT 中,param2 越接近 1,检测到的圆形状越好;一般情况下,param2=0.9 就行了,如果想更好的检测小圆,可以将其降低到 0.85~0.8。
  • 检测结果以 [ x , y , r ] [x, y, r] [x,y,r] 形式返回, ( x , y ) (x,y) (x,y) 是圆心坐标, r r r 是圆的半径。

image-20201016154831052

三、轮廓检测
1、轮廓检测

cv.findContours:找到二值图中所有图形的轮廓

contours, hierarchy	= cv.findContours(
    image, 		# 8-bit、单通道的二值图
    mode, 		# 轮廓检索模式,
    method, 	# 轮廓近似方法,
    contours,
    hierarchy, 
    offset		# 可选参数,轮廓偏移量
)

res = cv.drawContours(
    image, 
    contours, 	# 轮廓点
    contourIdx, # 需要绘制的轮廓索引,如果为负数表示绘制所有的轮廓
    color, 		# 颜色
    thickness, 	# 线宽
    lineType, 	# 线型
    hierarchy, 	# 可选参数,层级关系,只在需要绘制其中一些轮廓时才使用
    maxLevel, 	# 绘制轮廓的最大水平
    offset		# 可选参数,轮廓偏移量
)

# 实例
img2 = cv.imread('./circle.jpg')
gray = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图
contours, hierarchy = cv.findContours(edge, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
res = cv.drawContours(img2, contours, -1, (0, 255, 0), 2)	# 绘制轮廓

for contour in contours:
    M = cv.moments(contour)						# 计算轮廓的矩
    center_x = int(M["m10"] / M["m00"])			# 计算轮廓的中心点坐标
    center_y = int(M["m01"] / M["m00"])
    cv.circle(res, (center_x, center_y), 5, (0.,255,0), -1)

cv.imshow('22', res)
cv.waitKey(0)

注:

  • mode:
    • cv.RETR_EXTERNAL:只检索极端的外部轮廓
    • cv.RETR_LIST:检索所有的轮廓,而不建立任何层次关系
    • cv.RETR_CCOMP:检索所有的轮廓,并将它们组织成一个两层的层次结构。在顶层,存在组件的外部边界;在第二层,有洞的边界。如果被连接部件的孔内有另一个轮廓,它仍然被放在顶层。
    • cv.RETR_TREE:检索所有的轮廓,并重建一个完整的层次嵌套轮廓
  • method:
    • cv.CHAIN_APPROX_NONE:绝对存储所有的轮廓点
    • cv.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角线的片段,只留下他们的端点
    • cv.CHAIN_APPROX_TC89_L1:应用 Teh-Chin 链近似算法中的一种
    • cv.CHAIN_APPROX_TC89_KCOS:应用 Teh-Chin 链近似算法中的一种
  • 检测结果返回轮廓和层级结构(如果 mode 中有的话)
  • drawContours 中 的 maxLevel:如果为0,则只绘制指定的轮廓线;如果为1,函数绘制轮廓线和所有嵌套轮廓线;如果为2,函数绘制轮廓、所有嵌套的轮廓、所有嵌套到嵌套的轮廓,以此类推。只有在有层次结构可用时才考虑此参数。

image-20201016162504195

2、多边形拟合

cv.approxPolyDP:对检测到的轮廓点进行多边形拟合(逼近)

approxCurve	= cv.approxPolyDP(
    curve, 			# 轮廓
    epsilon, 		# 拟合精度,原始曲线和它的近似值之间的最大距离
    closed, 		# 拟合曲线是否封闭
    approxCurve
)

# 实例
img = cv.imread('./line.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图
contours, hierarchy = cv.findContours(edge, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

result = img.copy()
for contour in contours:
    approx = cv.approxPolyDP(contour, 50, True)	# 多边形拟合
    for p in approx:							# 绘制所有顶点
        x,y = p[0]
        cv.circle(result, (x, y), 5, (0.,255,0), -1)
    cv.drawContours(result, [approx], -1, (255,0,100), 1)
cv.imshow('22', result)
cv.waitKey(0)

注:

  • epsilon:可以自己指定值,也可以用 cv.arcLength 计算轮廓周长然后取一定比例
  • 检测以 [ x , y ] [x,y] [x,y] 顶点形式返回,近似后的轮廓有多少条边就有多少个顶点

image-20201016171453336

四、角点检测
1、Harris 角点检测

cv.cornerHarris:Harris 角点检测

cv.cornerHarris(
    src, 			# 8-bit、单通道图像
    blockSize, 		# 邻域大小
    ksize, 			# Sobel算子的孔径参数
    k, 				# 检测器自由参数
    dst, 
    borderType		# 像素外推方法
)

# 实例
img = cv.imread('./line.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图

result = img.copy()
corner_harris = cv.cornerHarris(edge, 15, 19, 0.04)				# 角点检测
result[corner_harris>0.01*corner_harris.max()] = [255, 0, 0]	# 绘制角点
cv.imshow('t', result)
cv.waitKey(0)

注:

  • blockSize:角点的范围大小,值越大得到的角点范围越大
  • ksize:值越大,过滤掉的噪点越多
  • 检测结果以二值图的形式返回,图片大小跟原始图像相同,角点处像素不为0

image-20201016175019467

2、Shi-Tomasi 角点检测

cv.goodFeaturesToTrack:找到图像中或指定图像区域中最突出的角点

corners = cv.goodFeaturesToTrack(
    image, 					# 8-bit、单通道图像
    maxCorners, 			# 返回的最大角点数量
    qualityLevel, 			# 角点最小可接受质量参数
    minDistance, 			# 角点之间可能的最小欧氏距离
    corners, 
    mask, 					# 可选参数,指定检测区域的掩码
    blockSize, 				# 用于计算每个像素邻域上的导数共变矩阵的平均块的大小
    useHarrisDetector, 		# 是否使用 Harris 检测器
    k						# Harris 检测器自由参数
)

# 实例
img = cv.imread('./line.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)		# 灰度图
edge = cv.Canny(gray, 20, 250)					# 二值图

result = img.copy()
corner_shi = cv.goodFeaturesToTrack(edge, 4, 0.1, 20)		# shi-tomasi 角点检测
for p in corner_shi:
    x, y = p[0]
    cv.circle(result, (x, y), 5, (0.,255,0), -1)
cv.imshow('t', result)
cv.waitKey(0)

注:

  • qualityLevel 指定了最小可被接受的角点质量,计算方式是:qualityLevel 乘以质量最高的角点质量,质量低于该数值的角点全被过滤掉。
  • 检测结果以 [ x , y ] [x,y] [x,y] 形式返回,每一个都是检测到的角点

image-20201016195358265

  • 7
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值