简介:本文围绕霍夫变换相关内容展开,先是讲解霍夫变换基本原理,包含从 xy 坐标系到 kb 坐标系及极坐标系的映射等。接着介绍了 cv2.HoughLines、cv2.HoughLinesP 概率霍夫变换、cv2.HoughCircles 霍夫圆变换的函数用法、参数含义、与常规霍夫变换区别以及实现步骤,并附代码示例,助力读者理解和运用这些图像检测技术。
如果我的文章对你有帮助,请点赞收藏关注,我将会持续为您带来更多与OpenCV有关的知识。
霍夫变换:原理剖析与 OpenCV 应用实例
霍夫变换基本原理
画一张图 y = kx+b:,用xy坐标系如图所示:
如果用k,b坐标系该这样表示:
(xy空间的一条线确定了kb空间的一个点)
我现在在xy轴里取一个点 (x0,y0):
此时x0,y0确定,他可能在任意一条直线上,根据公式 y0 = kx0+b,那么b = y0-kx0,所以在kb空间上:
说明xy的一个点对应 kb的一条线
再来观察,两个xy坐标的点 x0,y0 x1,y1,同时两点确定了一条直线,看图:
对应在kb空间应该是如下图:
我们会进行三步推理:
第一步:xy坐标系的一条直线上的两个点,对应着kb空间经过同一个点的两条直线。
第二步:推理可以知道,kb坐标系中间经过一个点的直线越多,说明在xy坐标系中是由更多的点所构成。
第三步:已知两个点确定一条直线,对于图像中的线怎么确定不是谬误,噪声呢?那多个点确定的直线不出错的概率相对来说肯定是更大的
第四步:霍夫变换选择直线的基本思路是,选择又尽可能多的直线交汇的点
但是,有一种特殊情况不能完成从xy坐标系映射到kb坐标系:垂线
所以可以映射到极坐标系:r = xcosa+ ysina
cv2.HoughLines函数
lines 返回的ndarray数组,数组的每一对数据都是(r,a) = cv2.HoughLines(image输入图像必须是八位单通道二值图像 ,rho为r的精度一般为1,theta为角度的精度一般情况使用pi/180,threshold是阈值,他是霍夫空间的直线条数大于阈值才能被认为是xy空间的直线,所以阈值越小找到的直线越多)
我们选择验证的例子基于这张图片,我给他命名为calculator.JPG放在跟代码同一个文件夹下,大家可以复制我的图片重命名自己去运行代码:
实现一次霍夫变换
(第一步)读取图片
(第二步)转化为灰度图
(第三步)使用cv2.Canny边缘检测获得边缘
(第四步)使用霍夫变换
(第五步)为了看到霍夫变化的差异,将原图从bgr转化成 rgb,并复制一张原图片
(第六步)对霍夫变换的结果从极坐标转换成xy坐标并在原图画线
(第七步)展示原图与画线后的图片差别
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("calculator.JPG")
# 转化为灰度图
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#使用cv2.Canny边缘检测获得边缘
edges = cv2.Canny(gray,50,150,apertureSize = 3)
#使用霍夫变换
lines = cv2.HoughLines(edges,1,np.pi/180,133)
#为了看到霍夫变化的差异,将原图从bgr转化成 rgb,并复制一张原图片
orgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
oShow = orgb.copy()
# 对霍夫变换的结果从极坐标转换成xy坐标并在原图画线
for line in lines:
rho,theta =line[0]
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(orgb,(x1,y1),(x2,y2),(0,0,255),2)
plt.subplot(121)
plt.imshow(oShow)
plt.axis("off")
plt.title("original")
plt.subplot(122)
plt.imshow(orgb)
plt.axis("off")
plt.title('huofu')
plt.show()
cv2.HoughLinesP概率霍夫变换
与霍夫变换的区别:
- 霍夫变换考虑了所有的点,而概率只考虑了一个足以进行线检测的随机点的子集
- 概率设置了所接受直线的最小长度
- 接受直线时所允许的最大像素点间距
语法上多了两个参数 一个是最小长度minLineLength和maxLineGap
步骤上并没有太多区别,仅仅是在调用函数时后多两个参数,阈值调低(后两个参数保证了即使低阈值也不会产生很多无效的直线)。
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("calculator.JPG")
# 转化为灰度图
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#使用cv2.Canny边缘检测获得边缘
edges = cv2.Canny(gray,50,150,apertureSize = 3)
#使用概率霍夫变换 设置minLineLength和maxLineGap
lines = cv2.HoughLinesP(edges,1,np.pi/180,1,minLineLength = 120,maxLineGap = 10)
#为了看到霍夫变化的差异,将原图从bgr转化成 rgb,并复制一张原图片
orgb = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
oShow = orgb.copy()
# 对霍夫变换的结果从极坐标转换成xy坐标并在原图画线
for line in lines:
x1,y1,x2,y2 = line[0]
cv2.line(orgb,(x1,y1),(x2,y2),(0,0,255),2)
plt.subplot(121)
plt.imshow(oShow)
plt.axis("off")
plt.title("original")
plt.subplot(122)
plt.imshow(orgb)
plt.axis("off")
plt.title('huofu')
plt.show()
霍夫圆变换cv2.HoughCircles
需要考虑圆半径和圆心x,y。两轮筛选,第一轮找出可能存在的圆心,第二轮判断半径。
circles 由圆心坐标和半径组成的ndarray= cv2.HoughCircles(image八位单通道灰度图,method检测方法 cv2.HOUGH_GRADIENT,dp累计其分辨率,若dp=1输入图像和结果具有相同的分辨率,minDist圆心之间的最小间距,太小会有多个邻近的圆,太大会漏掉一些圆,param1默认为100他是canny边缘检测的阈值,param2默认是100他越大检测到的圆越少,minRadius和maxRadius圆半径的最大值最小值 )
注意:在调用函数之前要进行平滑处理,减少噪声避免误判
使用这张图我命名为bicycly.JPG,存在和代码同一个文件夹下,大家可以复制我的图片然后重命名:
import numpy as np
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("bicycle.JPG",0)
o = cv2.cvtColor(img,cv2.COLOR_BAYER_BG2RGB)
oshow = o.copy()
# 使用中值滤波
img = cv2.medianBlur(img,5)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,200,param1 = 5,
param2 = 5,minRadius =3, maxRadius = 200)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
cv2.circle(o,(i[0],i[1]),i[2],(255,0,0),12)
cv2.circle(o,(i[0],i[1]),2,(255,0,0),12)
plt.subplot(121)
plt.imshow(oshow)
plt.axis("off")
plt.title("original")
plt.subplot(122)
plt.imshow(o)
plt.axis("off")
plt.title("circle")
plt.show()
致谢
本文参考了一些博主的文章,博取了他们的长处,也结合了我的一些经验,对他们表达诚挚的感谢,使我对 OpenCV霍夫变换 有更深入的了解,也推荐大家去阅读一下他们的文章。纸上学来终觉浅,明知此事要躬行:
【OpenCV学习笔记】之霍夫变换(Hough Transform)