目录
- 前言:
- 本篇学习内容:
- 注:
- 1.霍夫变换
- 1.1 数学理论
- 1.2 函数使用
- 参考文献:
前言:
笔者目前在校本科大二,有志于进行计算机视觉、计算机图形学方向的研究,准备系统性地、扎实的学习一遍OpenCV的内容,故记录学习笔记,同时,由于笔者同时学习数据结构、机器学习等知识,会尽量根据自己的理解,指出OpenCV的应用,并在加上自己理解的前提下进行叙述。
若有不当之处,希望各位批评、指正。
本篇学习内容:
1.霍夫变换
注:
霍夫变换运用了很巧妙的数学方法。我揣摩很久,难以下笔,觉得很难说清楚这件事。但是学习总是要向前的,本篇我便以自身理解来对其介绍一番。
1.霍夫变换
1.1 数学理论
霍夫变换做的其实是一件统计学上的事情。要理解霍夫变换,首先要理解它是怎么做这个统计的。
接下来的理论说明会同时参考官方文档、《OpenCV3编程入门》、https://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm。
摘自https://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm:
The motivating idea behind the Hough technique for line detection is that each input measurement (e.g. coordinate point) indicates its contribution to a globally consistent solution (e.g. the physical line which gave rise to that image point).
这句话是说,霍夫变换的思想是:每个输入点都代表了其对总体方案的一定贡献。
我们要怎么获得这样的贡献呢?
我们知道,直线是由无穷多个点组成的。而在图像变换中,在计算机领域,我们可以这样处理:当我们发现了超过k个点属于同一条直线(k可以是我们设置的阈值),那我们就认为图像中存在这样一条直线。
那么,怎么发现两个点属于同一条直线?
霍夫变换很巧妙地运用了极坐标方法:
1.一条直线在笛卡尔坐标系可以由斜率,截距表示。(k,b)
2.一条直线在极坐标系可以由极径,极角表示。(r,θ)
3.霍夫变换采用极坐标系的表示方法来表示一条直线,即:
这样做有什么好处呢?
设有点(a,b),那么由上面的表达式,
可以看到,一旦(a,b)给定(它们不再是变量),那么就确定了一个r关于θ的函数(它代表了所有通过(a,b)的直线!换句话说,这是(a,b)对应的所有可能的r-θ值。下图中曲线上的每一个点都代表一条通过(a,b)的直线)。也就是说,笛卡尔坐标系上的每一个点都对应r-θ坐标系上的一条直线(由函数表达式来看,它正好是经过伸缩、平移变换的正弦曲线)!
(图片来源于《OpenCV3编程入门》)
那么,如果对不同点进行上述操作后,它们生成的正弦曲线在r-θ坐标系相交,就代表这两个点经过同一条直线。(这是必然发生的,因为在二维平面两点确定一条直线)
进一步地,如果k个点进行上述操作后,它们生成的正弦曲线在r-θ坐标系相交于同一点,就代表这k个点经过同一条直线。当k足够大时,我们便认为,图像中存在这样一条直线。
我们可以进一步推广这种思想。比如,检测一个圆。
一个圆在笛卡尔系中由三个参数表示:(x,y,r)分别代表圆心和半径。将其变换一下,在给定(x,y,r)的情况下,也可以得到所有可能的(r,θ,m)值(m代表半径大小)。这样,我们在统计时,就需要统计3个参数(r,θ,m)的情况,算量会相应增加。(对应的统计空间变成了三维。之前说的r-θ是一个二维统计空间)
再进一步推广,对于一般的形状,也可以通过这样的变换,来查找对应统计空间下的情况。但是,形状越复杂,算量越大。这里便不再介绍。
1.2 函数使用
这里暂且只介绍标准霍夫变换:HoughLines()和累计概率霍夫变换:HoughLinesP()
HoughLines():
void cv::HoughLines (
InputArray image, //输入图像
OutputArray lines, //输出结果。存储检测到的线条
double rho, //距离精度。以像素为单位
double theta, //角度精度。以角度为单位
int threshold, //阈值。在累加空间中大于阈值的线段才可以被检测通过。
double srn = 0,
double stn = 0,
double min_theta = 0,
double max_theta = CV_PI
)
对部分参数进行进一步解释:
images:必须为8位单通道二进制图像。最好是放边缘检测之后的图像进去。
lines:储存了霍夫线变换检测到的线条的输出矢量。每一条线由(ρ,θ)表示。
srn:对于多尺度的霍夫变换,这是rho的除数。粗略的累加器进步尺寸是rho,精确的累加器进步尺寸是rho/srn。
stn:对于多尺度的霍夫变换,这是theta的除数。粗略的累加器进步尺寸是theta,精确的累加器进步尺寸是theta/stn。如果srn = stn = 0,则使用经典霍夫变换。
min_theta:对于多尺度霍夫变换,所检查线段的最小角度。必须在0到max_theta之间
max_theta:对于多尺度霍夫变换,所检查线段的最大角度。必须在min_theta到CV_PI之间
摘自博文:
标准霍夫变换本质上是把图像映射到它的参数空间上,它需要计算所有的M个边缘点,这样它的运算量和所需内存空间都会很大。如果在输入图像中只是处理m(m<M)个边缘点,则这m个边缘点的选取是具有一定概率性的,因此该方法被称为概率霍夫变换(Probabilistic Hough Transform)。
HoughLinesP():
void cv::HoughLinesP (
InputArray image, //输入图像
OutputArray lines, //输出结果。存储检测到的线条
double rho, //距离精度。以像素为单位
double theta, //角度精度。以角度为单位
int threshold, //阈值。在累加空间中大于阈值的线段才可以被检测通过。
double minLineLength = 0, //检测出来直线的最短长度
double maxLineGap = 0 //检测出来直线点与点之间的最大距离。
)
其实我觉得HoughLinesP()的参数更好懂一些。
这里偷懒照抄了一个官方文档里的示例,用的是HoughLinesP():
Mat src, dst, color_dst;
src = imread("E:/program/image/1.jpg");
Canny(src, dst, 50, 200, 3);
cvtColor(dst, color_dst, COLOR_GRAY2BGR);
vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI / 180, 80, 30, 10);
for (size_t i = 0; i < lines.size(); i++)
{
line(color_dst, Point(lines[i][0], lines[i][1]),
Point(lines[i][2], lines[i][3]), Scalar(0, 0, 255), 3, 8);
}
namedWindow("Source", 1);
imshow("Source", src);
namedWindow("Detected Lines", 1);
imshow("Detected Lines", color_dst);
waitKey(0);
return 0;
左边是原图,右边的白色线条是边缘检测图像。红色线条是霍夫变换检测出来的直线段。
参考文献:
- OpenCV官方文档:https://docs.opencv.org/4.x/
- 《OpenCV3编程入门》毛星云、冷雪飞等编著
- https://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm