白话文讲计算机视觉-第九讲-霍夫变换之直线检测

news2024/11/15 3:49:37

        霍夫变换是1962年由霍夫发明的一种检测图像中直线、圆等形状的方法。后来1972年经过Richard O. Duda和Peter E. Hart改进,形成了今天的霍夫变换算法。

        今天我就带大家了解了解霍夫变换之直线检测是怎么特么的一回事。

1.霍夫变换

        说到霍夫变换,首先就得说说坐标系的事情,虽然这都是初、高中的知识了,但经过大学几年的洗刷,不知道各位还能记住多少。为了使大家的远古记忆觉醒,我就领大家回忆回忆!

        首先是直线的直角坐标系方程(以x,y为坐标轴),y=k*x+b,不知道还记不记得?k是斜率,b是直线与y轴的交点,x、y是变量,如图1所示:

图1

        如果我们已知图像上面任意两个点,系数k,b就能够确定,那么方程就可以写成图2的形式。

 图2

        我们确定了k,b之后得到(k1,b1),把它作为一个点的位置,画到另外一个以k,b为坐标轴的直角坐标系中,我们就会得到图3,图3的直角坐标系我们就称为霍夫空间。

 图3

        假定霍夫空间中有一条线,它的方程为b=-x3*k+y3,也就是y3=k*x3+b。那么它对应的x,y直角坐标系就是一个点(x3,y3),如图4。反过来也可以说,直角坐标系中的一个点,就是霍夫空间中的一条直线。

 图4

        霍夫空间有一个定理:假定直角坐标系有N个点,如果这N个点能够在直角坐标系中连成一条直线,那么它们在霍夫空间中就会交于一点。

        假如N=3,有三个点,分别是(1,2),(2,4),(3,6),这三个点在直角坐标系中可以连成一条直线y=2*x,我们看图5,他们在霍夫空间中肯定会交于一点。

 图5

        但是在我们计算机视觉里面,很少有人使用直角坐标系来解决问题,而使用极坐标。其原因是当直角坐标系的直线垂直于x轴时,k为无穷大。这很难在计算机中表示,使用极坐标就能够很好的解决这个问题。极坐标方程如图6所示,ρ代表长度,θ代表角度。这个怎么来的呢,假如我们有一点(x4,y4),它的极坐标求法看图7,一下就明白了。

图6

 图7

        直角坐标系的一点(x4,y4),对应极坐标系下的一条正弦曲线ρ4=x4*cosθ4+y4*sinθ4,我们称这个极坐标系为“极坐标霍夫空间”,盗用别人一张图:

图8

        我们根据上面的原理,直角坐标系有N个点,如果这N个点能够在直角坐标系中连成一条直线,那么它们在霍夫空间中肯定会交于一点。在“极坐标霍夫空间”中也是一样,只是变为曲线交于一点,如图8所示。

        这样,我们就把直角坐标系的点,变换到极坐标霍夫空间中了。

2.直线检测

        我们想象一下,如图8所示,如果有三个点,这三个点在霍夫空间的曲线是交于一点的,那么三个点肯定在一条直线上。我们通过这个原理就可以对直线进行检测了。

        首先我们第一步需要把一张图的边缘给勾勒出来,比如使用canny边缘检测算法。

        接下来,我们来检测边缘中那些是直线。为了简化问题,我们拿一条直线作为边缘来举例。这条直线如图9所示,

  1. 将空间量化成许多小格,绿色的字代表X,Y坐标。黑色为边缘点,是由8个像素点组成。白色为非边缘点(在这里我们不予考虑)

9

        2. 假设我们的像素分辨率为1,θ变换角度变换的分辨率为45°,那么我们就对全部8个黑色的点进行极坐标霍夫变换, 变换后我们会得到8条曲线,如第一条曲线是ρ=cosθ+8*sinθ,接下来我们根据曲线求θ=0、45、90、135、180° 的ρ值,用公式ρ=x*cosθ+y*sinθ计算,一共8*8=64个数。计算结果如图10所示(横向代表5个角度,纵向代表从(1,8)到(8,1)的8个像素点,值为ρ

图10

        3. 计算完成后,我们发现θ=45°时候,ρ=6.3640数目最多,为8个,别的θ对应的ρ值数目均小于8个,因此,直线的极坐标方程为 6.3640=x*cos45°+y*sin45°,到此该直线方程就求出来了。我们换算成直角坐标系,在画出线段来即可。

3.一个实例

        举个例子,我们图11(a)是一张原图,我们通过第五节讲过的canny算法,把边缘勾勒出来,如图9(b)所示:

白话文讲计算机视觉-第五讲-canny边缘检测算法_小木希望学园的博客-CSDN博客

图11

        接下来,我们通过一条python语句来进行霍夫变换直线检测:

lines=cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)

        我们需要在找到一个边缘图中值为1的点(X1,Y1)(0为背景,1为边缘),设定像素分辨率为rho,也就是说我们沿着某一个方向(可以是0°,±45°,±90°,180°等等),一次走一个格子,一直走下去,看看能不能和这个(X1,Y1)点连成一条直线。

        那么我们沿着某一个方向怎么确定呢,很简单,再设定一个角度分辨率theta,就是我们可以沿着哪一个方向的角度行走,如果为1的话,走的方向可以是(0,1,2,3,。。。,180度)。

        我们继续设定一个参数threshold——检测一条直线所需最少的交点,假如我们设定该值为20,我们沿着这个角度一直往前走,走20个点之后,我们如果发现20个点在该方向处的极坐标ρ值都与(X1,Y1)相同,那么我们就认为它是一条直线,如果没到20个点值就变了,就不认为这是一条直线了。

        上述情况中,max_line_gap这个值为1,也就是走一个格子检测一下是否有极坐标值交点(ρ值相同),如果要是max_line_gap这个值不为1,为5,那就是说,我沿着这个角度一直往前走,先走5个点,如果这5个点中有2个点和(X1,Y1)的ρ值相同,那么就可以认为是一条直线,然后我们接着往下面走,再走5个点走到第10个点后,里面需要再有一个点和(X1,Y1)的ρ值相同;再走5个点到第15个点,里面需要再有一个点和(X1,Y1)的ρ值相同,依次类推继续前进,直到有20个点和(X1,Y1)的ρ值相同即可认为是一条直线;当小于20个点时,走5个点之后没有点相交就不认为是直线。

        我们把所有的边缘点进行霍夫变换,把所有的角度都给遍历了,我们就会得到一堆线段。

        我们设定变量min_line_length,表示组成一条直线的最少点的数量。比如我们设定和threshold相同或者小于threshold,那么我们就不舍弃任何一个点。如果设定比threshold大,如为50,就舍弃一些直线像素点数小于50个的就不要了。

        最后线段的坐标保存在line中。

        我们的参数设定为:

    rho = 1  # 像素分辨率(1 pix)

    theta = np.pi / 180  # 角度分辨率(1度)

    threshold = 20  # 检测一条直线所需最少的交点

    min_line_length = 50  # 组成一条直线的最少点的数量

    max_line_gap = 20  # 能被认为在一条直线上的亮点的最大距离

最终获得的图像如图12所示,检测的线段效果还行,有些遗漏,有些误检,这需要慢慢调参数来优化。

图12

以下是python的代码:

# 导入类库
import cv2
import numpy as np

if __name__ == '__main__':
    # 读入灰度图片
    img = cv2.imread("linetest.jpg", 0)
    # 显示图片
    cv2.imshow('orgin', img)
    # 进行边缘检测,设定高低阈值分别为50,75。后把canny边缘图片保存到硬盘,名字为canny.jpg
    edges = cv2.Canny(img, 50, 75)
    # 保存图片
    cv2.imwrite("canny.jpg", edges)
    # 显示边缘图片
    cv2.imshow("canny", edges)
    # 输入霍夫变换参数
    rho = 1  # 像素分辨率(1 pix)
    theta = np.pi / 180  # 角度分辨率(1度)
    threshold = 20  # 检测一条直线所需最少的交点
    min_line_length = 50  # 组成一条直线的最少点的数量
    max_line_gap = 20  # 能被认为在一条直线上的亮点的最大距离
    # 创建一张新图
    line_image = cv2.imread("linetest.jpg")
    # 霍夫检测函数
    lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)
    # 把线段画到line_image上面
    for line in lines:
        for x1, y1, x2, y2 in line:
            cv2.line(line_image, (x1, y1), (x2, y2), (0, 0, 255), 5)  # 5代表粗细
    # 显示直线检测图片
    cv2.imshow("Hough", line_image)
    # 保存图片
    cv2.imwrite("Hough.jpg", line_image)
    # 按任意键退出
    cv2.waitKey()
    cv2.destroyAllWindows()

PS:直线检测是所有的点在极坐标空间交于一点,我们利用同样的方法可以检测圆形,方法是“所有的点到某一定点的距离都是同一个值大家可以自己继续琢磨琢磨看。

霍夫变换直线检测到这里就结束啦,谢谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/447249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++——类和对象

作者:几冬雪来 时间:2023年4月21日 内容:C类和对象 目录 前言: 1.inline函数: 2.auto(补充): 3.类: 4.类的访问限定符及封装: 5.封装:…

从零开始下载torch+cu(无痛版)

从零开始下载torchcu(无痛版) 文章目录 从零开始下载torchcu(无痛版)一,前言二,配置torch的GPU版具体步骤1,查看电脑安装的Cuda版本2,在pytoch官网检索待下载whl包名以及版本3,下载指…

playwright实战篇(tx、ali225)

人人都笑金角,人人都是金角推荐文章: 1、https://playwright.dev/python/docs/api/class-playwright //官方文档 2、https://cuiqingcai.com/36045.html //崔庆才教程 3、https://github.com/qqq732004709/ //实战参考 4、https://www.cnblogs.com/ca…

asp.net mvc网上书店购物系统(带项目文档)

c#_asp.net mvc网上书店购物系统(带项目文档) mvc网上书店购物系统 主要技术: 基于asp.net mvc架构和sql server数据库,并采用EF实体模型开发。 功能模块: 系统包括前台和后台两个部分,用户可在前台浏览…

KL散度和交叉熵的对比介绍

KL散度(Kullback-Leibler Divergence)和交叉熵(Cross Entropy)是在机器学习中广泛使用的概念。这两者都用于比较两个概率分布之间的相似性,但在一些方面,它们也有所不同。本文将对KL散度和交叉熵的详细解释…

学系统集成项目管理工程师(中项)系列03_职业道德规范

1. 职业道德 1.1. 涵盖了从业人员与服务对象、职业与职工、职业与职业之间的关系 1.2. 是所有从业人员在职业活动中应该遵循的行为准则 1.3. 包括爱岗敬业、诚实守信、办事公道、服务群众和奉献社会 2. 道德 2.1. 通常与法律相对应,具有非强制性 2.2. 指人们依…

《花雕学AI》26:多维度了解ChatGPT思维链提示的原理、方法、使用和发展的22个问题

早上5点起床,没有去打羽毛球,打开电脑,漫无边际的浏览,偶然发现了一个提法:ChatGPT思维链提示。于是,我使用与ChatGPT同源技术的新Bing引擎(GPT-4),来进行搜索与了解相关…

基于在线优化的快速模型预测控制仿真Fast_MPC(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 ​模型预测控制(Model Predictive Control,MPC)是一种基于在线计算的控制优化算法,能够统一处理带约束的多参数优化控制问题。…

ESP8266 最简单的OTA升级模式

最近在做一个项目时有OTA的需求,之前是通过arduino自带的ota模式,虽说也可以用,但操作比较麻烦,昨晚在网上搜索相关教程,发现了使用巴法云来实现ota的功能,现分享如下 1、首先要让你的设备联网 2、注册巴法…

jsp基础语法

前提 安装jdk参考 下载tomcat解压&#xff0c;运行tomcat不报错即可 第一个jsp程序 切换到webapps下ROOT文件夹下&#xff0c;将除了WEB-INF文件外的全部内容删除&#xff0c;新建index.jsp index.jsp内容如下 <!DOCTYPE html> <html lang"zh-CN"&g…

React Proxy 详细流程与配置方式(webpack、setupProxy.js、package.json)

一、 package.json 配置方式 全部以 GET 请求为例&#xff0c;每次修改配置后&#xff0c;重启项目&#xff0c;否则不生效。 访问 http://127.0.0.1:6000/api/user/list 接口为例检查自己脚手架版本 $ create-react-app -V若脚手架版本在 2.0 以下&#xff0c;可以使用对象类型…

【C++】deque的实现原理简单介绍

前言 deque被称为双端队列&#xff0c;它的出现主要是为了结合vector和list的优点并减小它们的缺点&#xff0c;实际上deque确实结合了vector和list的优点减小了它们的缺点&#xff0c;但是它的结合也让它自己的优点没有原始的vector和list那么极致&#xff0c;导致deque变得很…

C++好难(1):C++的入门

目录 前言&#xff1a; C的历史&#xff1a; c的领域 1.C的关键字&#xff1a; 2.命名空间 2.1命名空间的定义&#xff1a; 1&#xff09;命名空间的普通定义&#xff1a; 2&#xff09;命名空间的嵌套定义&#xff1a; 3&#xff09;命名空间相同的处理&#xff1a; …

没有公网IP,自建网站如何让外网访问?

受创业潮影响&#xff0c;身边很多朋友都自己开公司创业了&#xff0c;作为一个IT行业从业者&#xff0c;我就莫名的开始忙起来了&#xff0c;因为掌握编程技术&#xff0c;朋友们经常找到我&#xff0c;让我帮他们做公司网站。但是存在一个无法回避的问题&#xff0c;就是我的…

0/1背包问题——从LeetCode题海中总结常见套路

目录 问题讨论 01背包问题公式 为什么状态压缩到一维时候需要逆序&#xff1f; 怎样求次数&#xff1f; 转化成最大和sum/2的01背包&#xff1a;LeetCode.416.分割等和子集 转化成最大和sum/2的01背包&#xff1a;LeetCode1049.最后一块石头的重量II LeetCode.494.目标和…

微软文字转语音不能试用了,分享三个方法给大家!

最近很多小伙伴告诉我&#xff0c;微软文字转语音不能在线试用了&#xff0c;这是因为微软关闭了官方的使用页面&#xff0c;所以现在不能直接使用微软的网页版进行文字转语音了。 那么我们还有没有更好的方法去“白嫖”微软的文字转语音呢&#xff1f; 答案是肯定的&#xf…

初识NoSQL(一文读懂)

最近参加了Oracle的数据库培训&#xff0c;对NoSQL非常好奇&#xff0c;总结一下关于NoSQL的认识。 NoSQL是Not Only SQL&#xff0c;并不是去除掉SQL&#xff0c;泛指非关系型的数据库。关系&#xff0c;指关系模型&#xff0c;具体指同一个对象在不同属性上的值 以及 不同对…

一个AK/SK泄露检测的实现思路

01、简介 在企业上云的过程中&#xff0c;AK/SK泄露导致的数据泄露事件屡见不鲜。在企业混合云架构下&#xff0c;公有云和私有云都存在大量的AccessKey&#xff0c;如何有效地检测可能的AK/SK泄露事件&#xff0c;一直困扰着企业的安全人员。 本文提供了一种比较容易实现的思路…

UART协议学习——异步全双工串行通信方式

文章目录 前言一、简介1、优点2、缺点 二、数据格式三、波特率1、定义2、波特率和采样频率 四、常见接口电平1、TTL电平2、RS232&#xff08;负逻辑&#xff09;3、RS485 前言 2023.4.22 世界地球日 一、简介 UART&#xff1a;Universal Asynchronous Receiver/Transmitter&a…

Albert-Z-Guo/Deep-Reinforcement-Stock-Trading

深加固股票交易 该项目打算在投资组合管理中利用深度强化学习。框架结构的灵感来自Q-Trader。代理人的奖励是在每个行动步骤评估的未实现净利润&#xff08;意味着股票仍在投资组合中且尚未兑现&#xff09;。对于每一步的不作为&#xff0c;投资组合中都会增加负惩罚&#xf…