参考:

  1. opencv HoughLine Transform Tutorial
  2. https://guiqing.blog.csdn.net/article/details/8058336
  3. https://www.cnblogs.com/lancer2015/p/6852488.html

opencv HoughLine结果分析

opencv中,HoughLine的结果,是找到的所有直线的集合,其中每条直线的表示是(ρ, θ)的形式,过图像左上角(0,0)点作找到的直线的垂线,ρ即是(0,0)点到垂足的距离,θ是指垂线与图像水平方向的夹角,如下图所示:

从图中可以看出,用一对这样的(ρ,θ)就能确定一条唯一的直线。ρ的计算值始终是非负值,但opencv进行了处理,当直线与Y轴的交点位于(0,0)点下方时,θ的范围是(0,π),ρ,θ值不变,当直线与Y轴的交点位于(0,0)点上方时,θ的范围为(π,2π),此时,将θ值变为θ-π,变换到(0,π)范围,而ρ值取-ρ。

这样变换的依据是直线的另一种表示方式,即用直线上的一个点坐标加上直线的角度来表示,此处选择的点为上述的垂足,其坐标可表示为

(ρ∗cos(θ),ρ∗sin(θ))=(−ρ∗cos(θ−π),−ρ∗sin(θ−π))(ρ*cos(θ), ρ*sin(θ)) = (-ρ*cos(θ-π), -ρ*sin(θ-π)) (ρ∗cos(θ),ρ∗sin(θ))=(−ρ∗cos(θ−π),−ρ∗sin(θ−π))

故将θ变为θ-π,ρ变为-ρ,计算得到的垂足是一样的,由于θ为法线角度,直线与法线垂直,θ变为θ-π即法线旋转180度,直线的角度不变,故变换前后表示的是同一条直线。

而且用垂线与水平轴的夹角和垂足的坐标来表示直线的话,在绘制该直线时比较方便,直接从垂足向直线两头分别移动一段距离得到两个点,即可用来进行绘制,计算方式如下:

cx=ρ∗cos(θ)cx = ρ*cos(θ) cx=ρ∗cos(θ)
cy=ρ∗sin(θ)cy = ρ*sin(θ) cy=ρ∗sin(θ)
x1=cx+1000∗sin(θ)x1 = cx + 1000 * sin(θ) x1=cx+1000∗sin(θ)
y1=cy−1000∗cos(θ)y1 = cy - 1000 * cos(θ) y1=cy−1000∗cos(θ)
x2=cx−1000∗sin(θ)x2 = cx - 1000 * sin(θ) x2=cx−1000∗sin(θ)
y2=cy+1000∗cos(θ)y2 = cy + 1000 * cos(θ) y2=cy+1000∗cos(θ)

计算原理如下图所示:

opencv HoughLine 原理分析

对于Hough变换,很多文章都会提到图像空间和参数空间的对应关系,参考文章[3]中有如下转换(稍加了修改):

y=mx+b(图像空间)y = mx + b(图像空间) y=mx+b(图像空间)
b=−xm+y(参数空间)b = -xm + y(参数空间) b=−xm+y(参数空间)

在图像空间中,点的坐标是(x,y),而x,y在参数空间中是方程的系数,一对系数就确定参数空间中的一条直线,因此说图像空间中的一个点,对应参数空间中的一条直线,反过来,在参数空间中,点的坐标是(m,b),而m,b在图像空间中是系数,一对系数确定图像空间中的一条直线,因此说参数空间的一个点对应图像空间的一条直线。

但是这对于算法的意义何在?其实HoughLine的本质,是针对图像中所有可能出现的直线的情况,对于每条直线涵盖的图像上的点进行加和,然后看每条线加和的值是否达到阈值,关键就在于对每一对(m,b),确定图像上的哪些点在此对(m,b)确定的直线上,这实现起来比较麻烦,所以转换了另一种思路,对于图像上的每个点(x,y),用参数空间方程计算每个可能的m值对应的b值,由此得到一对(m,b),因为参数空间方程和图像空间方程只是一个简单的形式变换,这对(x,y)和这对(m,b)是由参数空间方程计算得来的,也就必然满足图像空间方程,也就是说,该图像空间的点(x,y)必然在参数空间的点(m,b)确定的图像空间直线上,在同一条直线上的点(x,y),计算得到的(m,b)也必然相同,这样,遍历整个图像,将每个点(x,y)的亮度值分别累加到每条经过该点的直线(m,b)上,就相当于把每条直线涵盖的图像上的点进行了累加,简化了实现,现在关键的问题在于,所有可能的直线是连续的,而算法需要进行离散化,就需要对参数空间中的参数(m,b)进行细分,而这种细分是要做到均匀取值的。

在图像空间方程中,系数b是截距,也就是x为0时,y的值,其取值范围是无穷的,对截距进行离散化无法实现,系数m是斜率,其取值范围也是无穷的,对其进行等离散化也无法实现,故难以直接用该形式的参数空间来进行HoughLine计算,通过上面对opencv HoughLine结果的分析,我们知道可以用一对(ρ,θ)来表示一条直线,且ρ的范围为[0,sqrt(w*w + h*h)],θ的范围为[0,π),我们将参数空间方程看作以下形式:

ρ=f((x,y),θ)ρ = f((x,y), θ) ρ=f((x,y),θ)

这时,问题转换到对图像上的每个点(x,y),θ取[0,π)范围内的每个离散值,计算对应的ρ的离散值,然后将该像素的亮度累加到(ρ,θ)表示的直线中,最后每条直线的累加值和阈值比较得到符合条件的直线。

现在的问题就是如何由((x,y), θ)计算ρ?考虑上图中的点(x,y),有如下关系:

x=r∗cos(α)x = r * cos(α) x=r∗cos(α)
y=r∗sin(α)y = r * sin(α) y=r∗sin(α)
ρ=r∗cos(θ−α)=r∗cos(θ)∗cos(α)+r∗sin(θ)∗sin(α)=x∗cos(θ)+y∗sin(θ)ρ = r * cos(θ-α) =r*cos(θ)*cos(α)+r*sin(θ)*sin(α) =x*cos(θ)+y*sin(θ) ρ=r∗cos(θ−α)=r∗cos(θ)∗cos(α)+r∗sin(θ)∗sin(α)=x∗cos(θ)+y∗sin(θ)

这就是最终的参数空间方程。

到此为止,所有的谜题都皆晓了,最根本的原理,就是对所有可能的情况进行累加计算,再比较每个情况的累加值与阈值,玄妙的空间变换,其根本,只是为了方便算法的实现。

HoughLine python实现

# -*- coding: utf-8 -*-
import cv2 as cv
import time
import numpy as np
from matplotlib import pyplot as pltdef HoughLines(img, rho, theta, threshold, srn=1, stn=1, min_theta=0, max_theta=np.pi):"""HoughLine 实现# Return找到的直线列表 每条直线表示为[[rho, theta]]# Argumentsimg: 输入的二值化图像rho: 极径分辨率theta: 极角分辨率threshold: 阈值srn: 多尺度计算时,精细极径分辨率=rho/srn   此处直接实现为精细版stn: 多尺度计算时,精细极角分辨率=theta/stn 此处直接实现为精细版min_theta: 最小极角max_theta: 最大极角"""rho_acc = rho / srntheta_acc = theta / stn# 小于180度的 rho为正# 大于180度的 rho取负 角度-180# rho 以 rho_range//2 为0点rho_range = int((img.shape[0] + img.shape[1]) / rho_acc) * 2# 直接计算从[0,pi)范围, 再最后取结果时再过滤pi_range = int(np.pi / theta_acc)hough = np.zeros([pi_range, rho_range], dtype='int32')for y in range(img.shape[0]):for x in range(img.shape[1]):if img[y, x] < 128:continuefor theta in range(pi_range*2):ct = np.cos(theta * theta_acc)st = np.sin(theta * theta_acc)# 参数空间方程rho = int((y * st + x * ct) / rho_acc)if theta < pi_range:the = thetarho = rho_range // 2 + rhohough[the, rho] += 1else:the = theta - pi_rangerho = rho_range // 2 - rhohough[the, rho] += 1# plt.imshow(np.array(hough))# plt.show()res = []for theta in range(pi_range * 2):if theta * theta_acc < min_theta or theta * theta_acc > max_theta:continueif theta >= pi_range:theta -= pi_rangefor rho in range(rho_range):if hough[theta, rho] > threshold:res.append([[(rho - rho_range // 2) * rho_acc, theta * theta_acc]])return np.array(res)def test(img, method=0):gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)if method == 0:lines = HoughLines(gray, 1, np.pi / 90, 250)else:lines = cv.HoughLines(gray, 1, np.pi / 90, 250)print('lines:', np.array(lines).shape)finds = []for line in lines:rho, theta = line[0]ct = np.cos(theta)st = np.sin(theta)x0 = ct * rhoy0 = st * rho# x0, y0 为垂足坐标 下面的两个点是垂足延直线向两头各移动1000个单位x1 = int(x0 - 1000 * st)y1 = int(y0 + 1000 * ct)x2 = int(x0 + 1000 * st)y2 = int(y0 - 1000 * ct)# 过滤重合的线for find in finds:a1 = np.array([rho, theta * 180 / np.pi])a2 = np.array([x1, y1, x2, y2])b1 = np.array(find[:2])b2 = np.array(find[2:])d1 = np.abs(a1 - b1)d2 = np.abs(a2 - b2)if np.all(d1 < 20) or np.all(d2 < 20):# print('line', line[0], [x1,y1,x2,y2], 'find', find, 'overlap')breakelse:finds.append([rho, theta * 180 / np.pi, x1, y1, x2, y2])# print('line', line[0], [x1,y1,x2,y2])cv.line(img, (x1, y1), (x2, y2), (0,0,255), 2)print('finds', len(finds))return imgdef main():img = cv.imread('d:/0.png')time1 = time.time()t1 = test(img, method=0)time2 = time.time()print('self time:', time2 - time1)time1 = time.time()t2 = test(img, method=1)time2 = time.time()print('opencv time:', time2 - time1)t = np.hstack([t1, t2])cv.imshow('self <-----> opencv', t)cv.waitKey()if __name__ == '__main__':main()

对比手动实现版本和opencv版本,结果基本一致,不过python实现版本的效率相比opencv差了很多,如下图所示,左边是手动实现版本,右边是opencv版本:

以上就是本人对opencv HoughLine的理解,由于水平有限,可能有理解错误的地方,欢迎大家交流和讨论。

opencv HoughLine 理解相关推荐

  1. Python+OpenCV:理解K-Means聚类(K-Means Clustering)

    Python+OpenCV:理解K-Means聚类(K-Means Clustering) 理论 We will deal this with an example which is commonly ...

  2. Python+OpenCV:理解支持向量机(SVM)

    Python+OpenCV:理解支持向量机(SVM) 理论 线性可分数据(Linearly Separable Data) Consider the image below which has two ...

  3. Python+OpenCV:理解k近邻(kNN)算法(k-Nearest Neighbour (kNN) algorithm)

    Python+OpenCV:理解k近邻(kNN)算法(k-Nearest Neighbour (kNN) algorithm) 理论 kNN is one of the simplest classi ...

  4. 双目视觉下空间坐标计算matlab,双目视觉下空间坐标计算 opencv+ 个人理解

    简单的理解思路:(世界坐标系固定到左目) 空间中一点P,在左目像素坐标(u1,v1),转成mm为单位的坐标(x1,y1),在左目坐标系下建立过(x,y)的直线lineL: 同样的思路,空间中同一点P, ...

  5. Opencv copyTo()理解

    image.copyTo(imageROI) 作用是把image的内容复制粘贴到imageROI上: 是将logoImage直接复制黏贴在imgROI区域. image.copyTo(imageROI ...

  6. [机器学习]基于OpenCV实现最简单的数字识别

    http://blog.csdn.net/jinzhuojun/article/details/8579416 本文将基于OpenCV实现简单的数字识别.这里以游戏Angry Birds为例,通过以下 ...

  7. 2、OpenCV图像的读写操作

    OpenCV图像的读写操作 概要 图像由像素组成. 像素可以被认为是非常小的正方形结构,当连接在一起时会生成图像. 它们是任何图像的最小组成部分. 如果您仔细查看前面的图像,您将能够在图像中看到一些正 ...

  8. RIKIBOT使用系列-基于Opencv HSV的色块检测

    目录 一. 简介 二.查找色值 1.摄像头的角度调 2.启动检测与查找 三. 验证HSV色值 1.写入色值到文件 2.启动检测 四.交流方式 一. 简介 这里学习一下如何用摄像头检测HSV色值,Ope ...

  9. Halcon Opencv 数据的不同

    1.基本数据类型 halcon 只具备 两种数据类型 HTuple (tuple) .HObject (object). 对于基本数据的处理应用 HTuple 类型存储与计算.数组.字符串.数字.均可 ...

最新文章

  1. java案例——字符串反转
  2. MYSQL1130错误的解决方案
  3. 用户都跑了,你却还分不清流失用户和流失率
  4. 在家办公效率最高的组合!
  5. javascript事件之:jQuery事件中实例对象和拓展对象之间的通信
  6. 有条件忽略测试的JUnit规则
  7. windows下最好的围棋_学围棋能使学习成绩提高吗?
  8. 全球仅3000人通过的TensorFlow开发人员认证到底有多香!
  9. 作者:兰艳艳,女,中国科学院计算技术研究所副研究员、硕士生导师。
  10. bootstrap怎么在移动端横向布局_移动端筛选中的「不限」到底该怎么用
  11. android retrofit 2.0公共参数,Retrofit2.0 添加公共参数
  12. 语音识别如何识别中英混杂的语句?或者别的不同语言混合的语句?
  13. 终于将win7的basic主题改成黑色了!
  14. 【搬运】不思议的小故事,其实舰娘们也是有灵性的
  15. citp协议服务器,Picturall Octo 媒体服务器
  16. kotlin数组和集合
  17. python excel 空值_Python/Excel/SPSS/SQL数据处理方法比较之4 - 空值处理
  18. 三星 android 调试模式设置,三星 W2016 开启USB调试模式
  19. 网易mumu模拟器的使用
  20. Google 回归中国,你准备好成为 Googler 了吗?

热门文章

  1. ppt嵌入文件对计算机有危害,ppt播放SWF文件提示此文件包括的内嵌内容可能对您的计算机有害!如何关闭此提示?...
  2. 读研攻略(10)—三千字总结,要不要读博?
  3. android ui 框架
  4. 机器视觉汽车配件检测流程介绍
  5. 产品人如何写好产品分析报告?
  6. java基础:运行、注释、标识符、数据类型、运算法则
  7. 一个有意思的分钱模拟问题
  8. 计算机应用基础 小组讨论,【《计算机应用基础》教学探讨】 计算机应用基础 2018...
  9. 【IoT】WiFI、Zigbee和蓝牙通信技术对比解析
  10. HTML入门教程(一)