-
如您所知,图像空间中的一条线可以用两个变量表示。例如:
- 在笛卡尔坐标系中: 参数:。
- 在极坐标系中:参数:
对于霍夫变换,我们将在极地系统中表达线条。因此,线方程可以写成:
安排条款:
-
通常对于每个点,我们可以定义经过该点的行族:
意味着每对代表经过的每一条线。
-
如果对于给定的我们绘制经过它的线族,我们得到一个正弦曲线。例如,对于和我们得到如下图(在一个平面上- ):
我们只考虑这样的点和。
-
我们可以对图像中的所有点执行相同的操作。如果两个不同点的曲线在平面中相交- 这意味着两个点都属于同一条线。举例来说,上面的例子以下和绘图积两分以上:,和,我们得到:
所述三条曲线相交在一个单点,这些坐标是参数()或线,其中,与铺设。
-
上面的所有东西意味着什么?这意味着通常可以通过查找曲线之间的交叉点数来检测线。交叉的曲线越多意味着该交点所代表的线具有更多的点。通常,我们可以定义检测线所需的最小交叉点数量的阈值。
-
这就是霍夫线变换所做的。它跟踪图像中每个点的曲线之间的交点。如果交叉点的数量高于某个阈值,则它将其声明为具有交叉点参数的线。
标准和概率Hough线变换
OpenCV实现了两种Hough Line变换:
- 标准霍夫变换
- 它包含了我们刚才在前一节中解释过的内容。它为您提供了一对夫妇的矢量
- 在OpenCV中,它使用函数HoughLines实现
- 概率Hough线变换
- Hough Line变换的更有效实现。它将检测到的线的极值作为输出
- 在OpenCV中,它使用函数HoughLinesP实现
代码
- 这个程序做什么用的?
- 加载图像
- 应用标准Hough线变换或概率线变换。
- 在两个窗口中显示原始图像和检测到的线条。
- 我们将解释的示例代码可以从这里下载 。可以在此处找到稍微更漂亮的版本(显示Hough标准和带轨迹栏的概率以更改阈值) 。
#include “opencv2 / highgui / highgui.hpp”
#include “opencv2 / imgproc / imgproc.hpp”
#include <iostream>
使用 命名空间 cv ;
using namespace std ;
void help ()
{
cout << “ \ n 此程序演示使用Hough变换进行寻线。\ n ”
“用法:\ n ”
“./houghlines <image_name>,默认为pic1.jpg \ n ” << endl ;
}
int main (int argc , char ** argv )
{
const char * filename = argc > = 2 ? argv [ 1 ] : “pic1.jpg” ;
Mat src = imread (filename , 0 );
如果(SRC 。空())
{
帮助();
cout << “无法打开” << filename << endl ;
返回 - 1 ;
}
Mat dst , cdst ;
Canny算子(SRC , DST , 50 , 200 , 3 );
cvtColor (dst , cdst , CV_GRAY2BGR );
#if 0
vector <Vec2f>行;
HoughLines(dst,lines,1,CV_PI / 180,100,0,0);
for(size_t i = 0; i <lines.size(); i ++)
{
float rho = lines [i] [0],theta = lines [i] [1];
点pt1,pt2;
double a = cos(theta),b = sin(theta);
double x0 = a * rho,y0 = b * rho;
pt1.x = cvRound(x0 + 1000 *( - b));
pt1.y = cvRound(y0 + 1000 *(a));
pt2.x = cvRound(x0 - 1000 *( - b));
pt2.y = cvRound(y0 - 1000 *(a));
line(cdst,pt1,pt2,Scalar(0,0,255),3,CV_AA);
}
#else
vector < Vec4i > 行;
HoughLinesP (dst , lines , 1 , CV_PI / 180 , 50 , 50 , 10 );
为( 为size_t 我 = 0 ; 我 < 线。大小(); 我++ )
{
Vec4i 升 = 线[ 我];
line ( cdst , Point (l [ 0 ], l [ 1 ]), Point (l [ 2 ], l [ 3 ]), Scalar (0,0 ,255 ), 3 , CV_AA );
}
#endif
imshow (“source” , src );
imshow (“检测到的行” , cdst );
waitKey ();
返回 0 ;
}
说明
-
加载图像
Mat src = imread (filename , 0 ); 如果(SRC 。空()) { 帮助(); cout << “无法打开” << filename << endl ; 返回 - 1 ; }
-
使用Canny检测器检测图像的边缘
Canny算子(SRC , DST , 50 , 200 , 3 );
现在我们将应用霍夫线变换。我们将解释如何使用可用于此目的的两个OpenCV函数:
-
标准霍夫线变换
-
首先,您应用变换:
vector < Vec2f > 行; HoughLines (DST , 线, 1 , CV_PI / 180 , 100 , 0 , 0 );
使用以下参数:
- dst:边缘检测器的输出。它应该是灰度图像(虽然实际上它是二进制图像)
- lines:一个向量,用于存储检测到的行的参数
- rho:参数的分辨率,以像素为单位。我们使用1个像素。
- theta:以弧度表示的参数分辨率。我们使用1度(CV_PI / 180)
- 阈值:“ 检测 ”线的最小交叉点数
- srn和stn:默认参数为零。查看OpenCV参考以获取更多信息。
-
然后通过绘制线条显示结果。
为( 为size_t 我 = 0 ; 我 < 线。大小(); 我++ ) { 浮子 RHO = 线[ 我] [ 0 ], THETA = 线[ 我] [ 1 ]; 点 pt1 , pt2 ; double a = cos (theta ), b = sin (theta ); double x0 = a* rho , y0 = b * rho ; pt1 。x = cvRound (x0 + 1000 * (- b )); pt1 。y = cvRound (y0 + 1000 * (a )); pt2 。x = cvRound (x0 - 1000 * (- b )); pt2 。y = cvRound (y0 - 1000 * (a )); 线( cdst , PT1 , PT2 , 标量(0 ,0 ,255 ), 3 , CV_AA ); }
-
-
概率Hough线变换
-
首先应用转换:
vector < Vec4i > 行; HoughLinesP (DST , 线, 1 , CV_PI / 180 , 50 , 50 , 10 );
与参数:
- dst:边缘检测器的输出。它应该是灰度图像(虽然实际上它是二进制图像)
- lines:一个向量,用于存储检测到的行的参数
- rho:参数的分辨率,以像素为单位。我们使用1个像素。
- theta:以弧度表示的参数分辨率。我们使用1度(CV_PI / 180)
- 阈值:“ 检测 ”线的最小交叉点数
- minLinLength:可以形成一条线的最小点数。忽略少于此点数的行。
- maxLineGap:在同一行中考虑的两点之间的最大间隙。
-
然后通过绘制线条显示结果。
为( 为size_t 我 = 0 ; 我 < 线。大小(); 我++ ) { Vec4i 升 = 线[ 我]; 线( cdst , 点(升[ 0 ], 升[ 1 ]), 点(升[ 2 ], 升[ 3 ]), 标量(0 ,0 ,255 ), 3, CV_AA ); }
-
-
显示原始图像和检测到的线条:
imshow (“source” , src ); imshow (“检测到的行” , cdst );
-
等到用户退出程序
waitKey ();
结果
注意
下面的结果是使用我们在“ 规范”部分中提到的略微更加漂亮的版本获得的。它仍然实现与上面相同的内容,只为阈值添加了Trackbar。
使用输入图像,例如:
我们通过使用概率Hough线变换得到以下结果: