霍夫(Hough)直线变换(直线检测)
0 原理
霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。及时要检测的形状存在一点破坏或者扭曲也可以使用。我们下面就看看如何使用霍夫变换检测直线。
首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。
首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

再来看看A、B两个点,对应霍夫空间的情形:

一步步来,再看一下三个点共线的情况:

可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。
如果不止一条直线呢?再看看多个点的情况(有两条直线):

其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。
看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?
但这样做会有一个弊端,那就是当直线趋近于垂直时,斜率a会趋近于无穷大。这一困难的解决方法之一就是使用法线来表示直线。

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的正弦线,这条正弦线上的所有点都过极坐标中的那个点。


简单来说就是每个点在霍夫空间的弦线与其他点的弦线交一次,就会在累加器的ρθ平面中累加一次。下面的动画很好的演示了这个过程

1 OpenCV中的霍夫变换
上面介绍的整个过程在OpenCV中都被封装进了一个函数:cv2.HoughLines()。返回值就是极坐标表示的直线(ρ, θ)。ρ 的单位是像素,θ 的单位是弧度。
cv2.HoughLines(image, rho, theta, threshold, lines, sen, stn, min_theta, max_theta)
- image:输入图像,8-bit灰度图像
- rho:生成极坐标时候的像素扫描步长
- theta:生成极坐标时候的角度步长
- threshold:阈值,只有获得足够交点的极坐标点才被看成是直线
- lines:返回值,极坐标表示的直线(ρ, θ)
- sen:是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
- stn:是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
- min_theta:表示角度扫描范围最小值
- max_theta:表示角度扫描范围最大值
这种方法仅仅是一条直线都需要两个参数,这需要大量的计算。Probabilistic_Hough_Transform 是对霍夫变换的一种优化。它 不会对每一个点都进行计算,而是从一幅图像中随机选取(是不是也可以使用 图像金字塔呢?)一个点集进行计算,对于直线检测来说这已经足够了。但是 使用这种变换我们必须要降低阈值(总的点数都少了,阈值肯定也要小呀!)。
函数如下:
cv2.HoughLinesP(image, rho, theta, threshold, lines, minLineLength, maxLineGap)
- src:输入图像,必须8-bit的灰度图像
- rho:生成极坐标时候的像素扫描步长
- theta:生成极坐标时候的角度步长
- threshold:阈值,只有获得足够交点的极坐标点才被看成是直线
- lines:输出的极坐标来表示直线
- minLineLength:最小直线长度,比这个短的线都会被忽略。
- maxLineGap:最大间隔,如果小于此值,这两条直线 就被看成是一条直线。
举个例子:
import cv2
import numpy as np img = cv2.imread('test19.jpg')
img1 = img.copy()
img2 = img.copy()
img = cv2.GaussianBlur(img, (3, 3), 0)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 110) for line in lines:
rho = line[0][0]
theta = line[0][1]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a)) cv2.line(img1, (x1, y1), (x2, y2), (0, 0, 255), 2) lines = cv2.HoughLinesP(edges, 1, np.pi/180, 30, 300, 5) for line in lines:
x1 = line[0][0]
y1 = line[0][1]
x2 = line[0][2]
y2 = line[0][3]
cv2.line(img2, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow('houghlines3', img1)
cv2.imshow('edges', img2)
cv2.waitKey(0)
print(lines)
结果如下:

带有滑动条,用于调节HoughLines的参数
import cv2
import numpy as np
from matplotlib import pyplot as plt def nothing(x): # 滑动条的回调函数
pass src = cv2.imread('test19.jpg')
srcBlur = cv2.GaussianBlur(src, (3, 3), 0)
gray = cv2.cvtColor(srcBlur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
WindowName = 'Approx' # 窗口名
cv2.namedWindow(WindowName, cv2.WINDOW_AUTOSIZE) # 建立空窗口 cv2.createTrackbar('threshold', WindowName, 0, 60, nothing) # 创建滑动条 while(1):
img = src.copy()
threshold = 100 + 2 * cv2.getTrackbarPos('threshold', WindowName) # 获取滑动条值 lines = cv2.HoughLines(edges, 1, np.pi/180, threshold) for line in lines:
rho = line[0][0]
theta = line[0][1]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a)) cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow(WindowName, img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
结果如下:


带有滑动条,用于调节HoughLinesP的参数
import cv2
import numpy as np
from matplotlib import pyplot as plt def nothing(x): # 滑动条的回调函数
pass src = cv2.imread('test19.jpg')
srcBlur = cv2.GaussianBlur(src, (3, 3), 0)
gray = cv2.cvtColor(srcBlur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
WindowName = 'Approx' # 窗口名
cv2.namedWindow(WindowName, cv2.WINDOW_AUTOSIZE) # 建立空窗口 cv2.createTrackbar('threshold', WindowName, 0, 100, nothing) # 创建滑动条
cv2.createTrackbar('minLineLength', WindowName, 0, 100, nothing) # 创建滑动条
cv2.createTrackbar('maxLineGap', WindowName, 0, 100, nothing) # 创建滑动条 while(1):
img = src.copy()
threshold = cv2.getTrackbarPos('threshold', WindowName) # 获取滑动条值
minLineLength = 2 * cv2.getTrackbarPos('minLineLength', WindowName) # 获取滑动条值
maxLineGap = cv2.getTrackbarPos('maxLineGap', WindowName) # 获取滑动条值 lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold, minLineLength, maxLineGap) for line in lines:
x1 = line[0][0]
y1 = line[0][1]
x2 = line[0][2]
y2 = line[0][3]
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(WindowName, img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
结果如下:


霍夫(Hough)直线变换(直线检测)的更多相关文章
- opencv —— HoughCircles 霍夫圆变换原理及圆检测
霍夫圆变换原理 霍夫圆变换的基本原理与霍夫线变换(https://www.cnblogs.com/bjxqmy/p/12331656.html)大体类似. 对直线来说,一条直线能由极径极角(r,θ)表 ...
- OpenCV 学习笔记03 直线和圆检测
检测边缘和轮廓不仅重要,还经常用到,它们也是构成其他复杂操作的基础. 直线和形状检测与边缘和轮廓检测有密切的关系. 霍夫hough 变换是直线和形状检测背后的理论基础.霍夫变化是基于极坐标和向量开展的 ...
- opencv —— HoughLines、HoughLinesP 霍夫线变换原理(标准霍夫线变换、多尺度霍夫线变换、累积概率霍夫线变换)及直线检测
霍夫线变换的原理 一条直线在图像二维空间可由两个变量表示,有以下两种情况: ① 在笛卡尔坐标系中:可由参数斜率和截距(k,b)表示. ② 在极坐标系中:可由参数极经和极角(r,θ)表示. 对于霍夫线变 ...
- Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())
本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 //2017-04-21更新: 很多网友希望能得到源码,由于在公司做的,所以不太方便传出来 ...
- 霍夫直线检测 opencv
本次实验是检测图像中的直线,用到了HoughLines()和HoughLinesP()函数,其中HoughLinesP()称为累计概率霍夫变换,实验结果显示累计概率霍夫变换要比标准霍夫变换的效果好.具 ...
- opencv:霍夫直线检测
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- opencv-霍夫直线变换与圆变换
转自:https://blog.csdn.net/poem_qianmo/article/details/26977557 一.引言 在图像处理和计算机视觉领域中,如何从当前的图像中提取所需要的特征信 ...
- Matlab 霍夫变换 ( Hough Transform) 直线检测
PS:好久没更新,因为期末到了,拼命复习中.复习久了觉得枯燥,玩玩儿霍夫变换直线检测 霍夫变换的基本原理不难,即便是初中生也很容易理解(至少在直线检测上是这样子的). 霍夫变换直线检测的基本原理:(不 ...
- Win8 Metro(C#)数字图像处理--2.38Hough变换直线检测
原文:Win8 Metro(C#)数字图像处理--2.38Hough变换直线检测 [函数名称] Hough 变换直线检测 HoughLineDetect(WriteableBit ...
- OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27220445 收入囊中 Hough变换 概率Ho ...
随机推荐
- ubuntu22.04 终端显示数字剑雨
数字剑雨是读大学时候常用的屏保,这些年基本也再没有用过,不经意间想到了这个曾经的屏保,发现Ubuntu原版的桌面系统是没有屏保的,又不想换桌面系统,想想还是单独安装一下这个数字剑雨吧. 在Ubuntu ...
- .gitignore文件的使用方法(学习总结版)
平时总是喜欢把日常学习的代码以代码库的形式上传到Gitee或Github上(Github上的代码正逐渐迁移至Gitee上),但是有时候总有一些中间编译的文件不行要一起上传,原有有两个: 1.是不行Gi ...
- 构建无服务器数仓(三 )EMR Serverless 操作要点、优化以及开放集成测试
引言 在数据驱动的世界中,企业正在寻求可靠且高性能的解决方案来管理其不断增长的数据需求.本系列博客从一个重视数据安全和合规性的 B2C 金融科技客户的角度来讨论云上云下混合部署的情况下如何利用亚马逊云 ...
- Ambiguous Mapping 的处理方法
出现原因:两个不同的Controller 中出现相同映射路径 eg: AController:/v1/wdnmd/userList/list BController:/v1/wdnmd/userLis ...
- 嵌入式软件开发人员有必要学习系统移植的知识吗?【ppt获取见文末】
<从零开始学ARM>的配套视频说明 为了让粉丝更好的学习我的新书里面的知识, 一口君特地录制了配套学习视频, <从0学ARM第一期> <从0学ARM第一期> 视频已 ...
- Echarts 5 动态按需引入图表
官网提供的按需引入方法为全量按需引入,在打包分离中,仍旧存在使用不到的图表被打包进去. 例如:组件A使用了折线图.柱状图,组件B只用到了折线图,但是打包组件B的时候,柱状图也就被打包进去. 本文提供一 ...
- WPF 模拟UWP原生窗口样式——亚克力|云母材质、自定义标题栏样式、原生DWM动画 (附我封装好的类)
先看一下最终效果,左图为使用亚克力材质并添加组合颜色的效果:右图为MicaAlt材质的效果.两者都自定义了标题栏并且最大限度地保留了DWM提供的原生窗口效果(最大化最小化.关闭出现的动画.窗口阴影.拖 ...
- java_GUI2
package GUi;import java.awt.*;public class GuI2 { public static void main(String[] args) { MyFrame n ...
- 我恨 gevent
报错了一晚上,最后发现是 python 版本不对.3.11,3.12,3.8,3.10 试了个遍,最后 3.10 终于编译通过了 还有这个 greenlet,每次都是它和 gevent 合着来恶心我 ...
- chmod 使用
数字 权限 4 (100) 读 2 (010) 写 1 (001) 执行 u 表示该文件的拥有者,g 表示该文件的拥有者所属的组,o 表示其他人,a 表示所有人. e.g. # 数字表示法 chmod ...