参考:

  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. linux安装grub
  2. 知识图谱最新论文清单,高阶炼丹师为你逐一解读
  3. 用计算机语言画曲线,用C语言控制台画简单的曲线
  4. SRS性能、内存优化工具用法
  5. 判断两个数组中是否存在相同的数字
  6. 命中率极高的 Go 面试题,赶紧收藏!
  7. 医疗人工智能市场有多大?
  8. 说一下朗数可视化快速开发平台
  9. Java Swing/AWT和GTK混合GUI编程
  10. oracle 启动报错03113,oracle数据库无法启动,总报ora-03113错误
  11. VS2013使用技巧汇总
  12. SCSI硬盘接口是什么
  13. ft232h引脚_K9K8G08U0B-PIB0--斗门--镁光MICRON内存收购
  14. C语言旅途之输出N的M次方的后三位数
  15. 苹果服务器维护不能刷机,iphone刷机失败不开机报错维修方法分享
  16. 为什么要配置环境变量以及配置环境变量的步骤
  17. stm32实现毫秒ms微秒us级延时
  18. 空间分析工具:GIS
  19. springboot整合redis后整合es,报错Failed to instantiate [org.elasticsearch.client.transport.TransportClient]
  20. 终于搞懂了,用大白话给你解释Zookeeper的选举机制,包教会

热门文章

  1. android ifw 启动广告,应用控制器清爽无广告版-应用控制器官方最新版v1.9.5 免费版-腾牛安卓网...
  2. Python Pandas 导入dta文件的方法
  3. android 漂亮的开源ui框架
  4. 前端学习笔记(四)网站SEO优化之TDK三大标签
  5. 三平面映射TriPlanar
  6. 【例8-13】用字符指针操作字符串
  7. 中间件_Redis_02_Redis的数据类型
  8. 最优的宽带出租解决方案——wayos+easyradius
  9. 计算用1分钱、2分钱、5分钱组成1元钱的方式
  10. linux删除磁带设备,Linux下磁带管理命令