Learn Opencv ---- 使用Opencv进行霍夫变化

本文介绍了霍夫变换的概念及其在计算机视觉中的应用,特别是在检测图像中的直线和圆形。通过极坐标表示直线方程,利用累加器进行投票,结合OpenCV的HoughLines和HoughLinesP函数实现直线检测,以及HoughCircles函数检测圆形,详细阐述了检测过程和效果。
摘要由CSDN通过智能技术生成

阅读原文


    在这篇博文中,我们将借助一种叫做霍夫变换的技术来检查图像中的直线和圆形。

什么是霍夫变换

    霍夫变换是一种特征提取方法,用于检测图像中的简单图形,如圆形,直线等。简单的图形意味着可以使用几个参数来进行表示,例如,一条直线可以使用两个参数(斜率和截距)来表示,而一个圆可以使用三个参数(圆心和半径)来表示。可以这么说,霍夫变换在寻找图像中的图形方面比较优秀。使用霍夫变换的主要优点是,它对遮挡不敏感。

使用霍夫变换检测直线

在这里插入图片描述
图1: 为一条在极坐标下的直线。

极坐标下的直线方程

    从高中数学课上我们知道,在极坐标下,直线的方程如下所示:

在这里插入图片描述
其中, ρ \rho ρ为直线到原点到距离(以像素为单位), θ \theta θ是直线与 Y Y Y轴的夹角(以弧度为单位)。
    为什么我们不使用传统的斜率+截距的直线方程呢?原因是,传统直线方程的斜率可以从-∞取到+∞,但是对于霍夫变换来说,参数是需要存在边界的。而对于极坐标的直线方程, θ \theta θ是有限的, ρ \rho ρ看起来是无限的,但是由于图像是有限的,因此它也是有限的。

累加器

    想象一个二维数组,其中x轴包含了所有 θ \theta θ的值,y轴包含了所有 ρ \rho ρ的值。这个二维数组的每一个格子都代表了一条直线。
在这里插入图片描述
图2:累加器。
    图2的数组被称为累加器,因为我们将使用这个数组的格子来收集图像中存在的直线。图中的左上角对应 ( − R , 0 ) (-R,0) (R,0),右下角对应 ( R , π ) (R,\pi) (R,π),这个累加器数组会从图像中提取信息,并增加对应当格子的值。

第一步,初始化累加器

    首先,我们需要创建一个累加器数组,而累加器数组的单元格数量是一个设计决策,即需要人为的考虑其大小。假如你的累加器是一个10x10的数组,那就意味着,你最多能检测到100条不同的直线。所以,图像的分辨率也是一个能够决定你设计累加器到底有多大的因素。

第二步,检测边缘

    现在我们已经初始化好了累加器,接下来,就是要手机图像中的直线的信息(证据)。
    我们怎么获取证据呢?具体想法是,如果图像中有一个可见的线,那么可以使用边缘检测器去检测这条线,并获得这条线所占有的像素数组 [ ( x 1 , y 1 ) , ( x 2 , y 2 ) . . . ( x n , y n ) ] [(x_1,y_1),(x_2,y_2)...(x_n,y_n)] [(x1,y1),(x2,y2)...(xn,yn)]

第三步,按边缘像素投票

    对于上诉像素数组中的每个边缘像素 ( x , y ) (x,y) (x,y),我们将 θ \theta θ值从0递增到 π \pi π,并将其带入到直线极坐标方程中,得到 ρ \rho ρ。下图给出了三个像素在不同 θ \theta θ值下的得到的不同 ρ \rho ρ值的曲线图(图中的r标错了,应该是 ρ \rho ρ)。
在这里插入图片描述
    可以看到,这三条曲线相交与一点,即 θ = 1 , ρ = 9.5 \theta=1,\rho=9.5 θ=1,ρ=9.5。通常每条边缘会有很多个像素,累加器用于找到由边缘像素生成的所有曲线的交点。例如,你构建的累加器为20x20,那么就相当于将 0 到 π 0到\pi 0π分为20份,即有20种不同的 θ \theta θ。因此,对于每个边缘的像素 ( x , y ) (x,y) (x,y),我们都能够计算得到20个 ( ρ , θ ) (\rho,\theta) (ρ,θ)。然后再将累加器种对应位置的格子进行递增,较多值的格子即可视为交点。从上述表达可以知道,可以设定一个阈值,用来过滤某些交点较小的数据,即较弱的直线。

使用Opencv完成检测

    没错,不用我们瞎操心啦,Opencv提供来两个函数来用于霍夫变换。分辨是HoughLinesHoughLinesP,他们的参数如下:

  • edges:边缘检测器的输出。
  • lines:一个向量,用来表示一条直线的开始点和结束点。
  • rho:分辨率参数 ρ \rho ρ,单位为像素。
  • theta:分辨率参数 θ \theta θ,单位是弧度。
  • threshold:检测一条线的最小交点数。
import cv2
import numpy as np
import sys

def onTrackbarChange(max_slider):
	global img
	global dst
	global gray

	dst = np.copy(img)

	th1 = max_slider 
	th2 = th1 * 0.4
	# 边缘检测
	edges = cv2.Canny(img, th1, th2)
	# 使用概率霍夫变换
	# 边缘图,线段以像素为单位的距离精度,线段以弧度为单位的角度精度
	# 累加器的阈值,线段以像素为单位的最小长度,一方向上两条线段判定为一条线段的最大允许间隔
	lines = cv2.HoughLinesP(edges, 2, np.pi/180.0, 50, minLineLength=10, maxLineGap=100)

	# 把直线绘制到图中
	for line in lines:
		x1, y1, x2, y2 = line[0]
		cv2.line(dst, (x1, y1), (x2, y2), (0,0,255), 1)

	cv2.imshow("Result Image", dst)	
	cv2.imshow("Edges",edges)

if __name__ == "__main__":
	# 读取图像
	img = cv2.imread('lanes.jpg')
	# 创建图像的深拷贝(防止数据污染)
	dst = np.copy(img)
	gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

	# 创建显示窗口
	cv2.namedWindow("Edges")
	cv2.namedWindow("Result Image")
	  
	# 初始化的阈值
	initThresh = 500
	# 可选的最大阈值
	maxThresh = 1000
	# 滑杆取值控件
	# onTrackbarChange为滑杆数据变化时的响应函数
	cv2.createTrackbar("threshold", "Result Image", initThresh, maxThresh, onTrackbarChange)
	# 主动调用
	onTrackbarChange(initThresh)

	while True:
		key = cv2.waitKey(1)
		if key == 27:
			break

	cv2.destroyAllWindows()

检测效果

在这里插入图片描述

使用Opencv中的霍夫变换检测圆形

    由于一个圆形需要使用三个参数(圆心和半径)来表示,因此,我们需要构建一个三维的累加器。圆形公式定义如下:

在这里插入图片描述
    检测圆形的步骤如下:

  1. 使用边缘检测器获取到图像中的边缘信息。
  2. 为了检测图像中的圆,为半径设置了最大值和最小值。
  3. 在三维累加器中,对不同中心和半径的圆进行累加计算。

    Opencv中给出HoughCircles函数来检测图像中的圆,它的参数如下:

  • image: 输入的图像。
  • method: 检测的方法
  • dp: 累加器分辨率与图像分辨率的反比。
  • mindst: 所检测的圆的中心之间的最小距离。
  • param_1和param_2: 这两个是特定方法的参数。
  • min_Radius: 要检测的圆的最小半径。
  • max_Radius: 要检测的圆的最大半径。
import cv2
import numpy as np
import sys

def onTrackbarChange(max_slider):
    cimg = np.copy(img)

    p1 = max_slider
    p2 = max_slider * 0.4

    
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, cimg.shape[0]/64, param1=p1, param2=p2, minRadius=25, maxRadius=50)

    # 判断是否有圆
    if circles is not None:
        # 获取圆的个数
        cir_len = circles.shape[1]
        # 取整
        circles = np.uint16(np.around(circles))
        for i in circles[0, :]:
            # 绘制圆的外轮廓
            cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2)
            # 绘制圆心小点
            cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)
    else:
        cir_len = 0 
    
    cv2.imshow('Image', cimg)    

    edges = cv2.Canny(gray, p1, p2)
    cv2.imshow('Edges', edges)

    

    
if __name__ == "__main__":
    # 读取图像
    img = cv2.imread('brown-eyes.jpg', 1)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 创建显示窗口
    cv2.namedWindow("Edges")
    cv2.namedWindow("Image")
    

    # 初始化的阈值和最大阈值
    initThresh = 105 
    maxThresh = 200 

    # 滑杆取值控件
    # onTrackbarChange为滑杆数据变化时的响应函数
    cv2.createTrackbar("Threshold", "Image", initThresh, maxThresh, onTrackbarChange)
    onTrackbarChange(initThresh)
    
    while True:
        key = cv2.waitKey(1)
        if key == 27:
            break

    cv2.destroyAllWindows()

检测效果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值