在图像处理中,霍夫变换可以用来检测 各种形状,如 直线、圆、椭圆 等
霍夫空间
在笛卡尔坐标系下,一条直线可以表示为 y=kx+b,两点可以确定一条直线;
如果把表达式改为 b=-kx+y,则转换到了 霍夫空间,该空间 横坐标是 k,纵坐标是 b,可以看到 一组 (k,b) 就可以确定一条直线;
即 霍夫空间的一个点 就可以确定 笛卡尔坐标系下 的一条直线,
反过来也成立,霍夫空间的一条直线 对应 笛卡尔坐标系下 的一个点;
如果 霍夫空间的两条直线 相交于 霍夫空间的某个点 X,X 对应了 笛卡尔坐标系下的一条直线 L,霍夫空间的两条直线 又对应了笛卡尔坐标系下的两个点 A B,则 L 经过 A B
有点绕,看图
笛卡尔坐标系 转换到 霍夫空间后,如果 存在如下关系,则说明 笛卡尔坐标系下的点 在 一条直线上
以上用笛卡尔坐标系讲了半天,只是为了方便理解,实际在 霍夫变换 中用的不是 笛卡尔,因为 当直线 垂直于 坐标轴时,k 等于 无穷大;
极坐标
霍夫变换中 采用的是 极坐标系;
极坐标系下的r=xcos(theta)+ysin(theta)。即可用(r,theta)来表示一条直线。其中 r 为该直线到原点的距离,theta 为该直线的垂线与x轴的夹角。如下图所示
很多资料用 ρ 代表 r
极坐标下 与 霍夫空间对应关系类似 笛卡尔坐标
霍夫线检测
在实际应用中,我们初始化一个矩阵,
行代表 ρ ,通常以像素为单位,一般以 1 个像素为单位,最大值为 对角线的 长度;
列代表 θ,通常以弧度为单位,一般以 1弧度为单位,最大为 180;
霍夫检测 需要图像为 二值图(0, 255),然后检测 255 (白色点)所在位置(x,y)对应的 (ρ,θ); 【经测试,不一定是 255,其他也行,155都行,自己试试吧】
在 矩阵对应位置 计数 (+1);
最后 取 矩阵中 大于 num(人工设定)的 (ρ,θ),其对应的 白色点(x,y)构成图像中的一条直线;
OpenCV 用法
两种方法
标准霍夫变换(多尺度霍夫变换)
def HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None)
image:8-bit, single-channel binary source image,单通道 二值图
rho:以像素为单位的 step, 对应上面矩阵中的行
theta:以弧度为单位的 step,对应上面矩阵中的列
threshold:累计经过 (ρ,θ)的阈值,大于 阈值 认为是一条直线,对应上面的 num
return:返回的是一个包含 rho 和 theta 的数组,[rho,theta]
示例
import cv2 as cv import numpy as np # 构建测试图片 image = np.zeros((100, 100), dtype=np.uint8) # 背景图 idx = np.arange(25, 75) # 25-74 序列 image[idx[::-1], idx] = 155 # 对角线 \ 注意这里不是 255,专门测试下 image[idx, idx] = 255 # 对角线 / # hough 线变换 lines = cv.HoughLines(image.astype(np.uint8), 1, np.pi/180, 10) print(lines) 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 + 50 * (-b)) y1 = int(y0 + 50 * (a)) x2 = int(x0 - 50 * (-b)) y2 = int(y0 - 50 * (a)) print(x1, y1, x2, y2) cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv.imshow('hog', image) cv.waitKey(0)
渐进式霍夫变换
def HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None)
参数同上
return:返回的是含有一条直线的起始点和终点坐标[x1,y1,x2,y2]
示例
import cv2 as cv import numpy as np src = cv.imread('imgs/55555.png') gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) # 应用直方图均衡化 dst = cv.equalizeHist(gray) # 高斯滤波降噪 gaussian = cv.GaussianBlur(dst, (11, 11), 0) # 利用Canny进行边缘检测 edges = cv.Canny(gaussian, 80, 180, apertureSize=3) # 自动检测可能的直线,返回的是一条条线段 # 第二个参数为半径的步长,第三个参数为每次偏转的角度 lines = cv.HoughLinesP(edges, 1, np.pi/180, 80, minLineLength=37, maxLineGap=6) print(type(lines)) for line in lines: x1, y1, x2, y2 = line[0] cv.line(src, (x1, y1), (x2, y2), (0, 0, 255), 2) cv.imshow("line", src) cv.waitKey(0) cv.destroyAllWindows()
霍夫圆检测
1. 霍夫线检测输入为 二值图,霍夫圆检测输入可为 灰度图,不必为 二值图
2. 圆公式 (x-xc)^2 + (y-yc)^2 = r^2,故霍夫圆检测需确定 圆心 半径 3个参数,其他类似于霍夫线检测
3. 理论上圆检测比线更耗时,opencv采用了一种霍夫梯度法,优化了效率,感兴趣的可以研究下
def HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None)
image:8位 单通道 灰度图
method:使用的检测方法,opencv 中只有一种可以使用,即霍夫梯度法,cv2.HOUGH_GRADIENT
dp:累加面分辨率(大小) = 原始图像分辨率(大小) × 1/dp。默认 dp = 1 时,两者分辨率相同,应该类似于 霍夫线检测 中的 step
minDist:检测到的圆的圆心间的最小距离,如果小于该值,视为同一个圆
circles:输出参数格式,只有两种 (x, y, radius) or (x, y, radius, votes)
param1:Canny 边缘检测的高阈值,低阈值被自动置为高阈值的一半,默认为 100
param2:累加平面某点是否是圆心的判定阈值。它越大,能通过检测的圆就更接近完美的圆形,默认为 100,类似于 霍夫线检测 中的 num
minRadius:最小半径,默认 0,
maxRadius:最大半径,默认 0,最好规定 最小最大半径,不能盲目检测,否则浪费时间空间
示例代码
img = cv2.imread('imgs/3.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gaussian = cv2.GaussianBlur(gray, (7, 7), 0) edges = cv2.Canny(gaussian, 80, 180, apertureSize=3) # 自动检测圆 circles1 = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 1, param1=10, param2=10, minRadius=200, maxRadius=295) print(np.shape(circles1)) circles = circles1[0, :, :] circles = np.uint16(np.around(circles)) for i in circles[:]: cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 3) #cv2.circle(img, (i[0], i[1]), 2, (255, 0, 255), 10) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows()
参考资料:
https://blog.csdn.net/weixin_44980490/article/details/107507219 霍夫变换 原理讲的比较清楚,结合我的文章和这篇资料,你会懂的
https://www.jb51.net/article/132762.htm 极坐标 讲的比较好
http://www.woshicver.com/FifthSection/4_13_%E9%9C%8D%E5%A4%AB%E7%BA%BF%E5%8F%98%E6%8D%A2/ 还不懂的话 看看这篇
https://blog.csdn.net/qq_45769063/article/details/108556794 有原理,有 参数解释,参数解释比较详细
https://blog.csdn.net/on2way/article/details/47028969
https://blog.csdn.net/wsp_1138886114/article/details/82936218
https://www.jianshu.com/p/70618e629f17
https://www.cnblogs.com/bjxqmy/p/12333022.html 霍夫圆检测 参数解释 c++