阅读(714) (1)

OpenCV Hough Line变换

2017-09-12 09:46:03 更新

目标

在本教程中,您将学习如何:

理论

注意
下面的解释属于Bradski和Kaehler 的“ 学习OpenCV ”一书。

Hough Line变换

  1. Hough Line变换是用于检测直线的变换。
  2. 为了应用变换,首先需要边缘检测预处理。

它是如何工作的?

  1. 如你所知,图像空间中的一行可以用两个变量表示。例如:在笛卡尔坐标系中:参数:(m,b);在极坐标系中:参数:(r,θ)

Hough Line变换

对于Hough 变换,我们将在极地系统中表达线条。因此,线性方程可以写为:Hough Line变换

Hough Line变换

排列术语:r=xcosθ+ysinθ

  • 通常,对于每个点(x0,y0),我们可以定义通过该点的行给出如下公式:

Hough Line变换

意思是每一对的 (rθ,θ)表示通过每一排的 (x0,y0)


  • 如果对于给定的 (x0,y0)我们绘制通过它的线族,我们得到一个正弦曲线,例如,对于x0=8y0=6 ,我们得到以下图(在平面 θ - r)

Hough Line变换

我们只需考虑这样的情况: r>0 和 0<θ<2π.

  • 我们可以对图像中的所有点执行上述相同的操作。如果两个不同点的曲线在平面θ - r相交,那意味着两个点都属于同一行。例如,按照上面的例子,并绘制两个点的x1=4, y1=9 和 x2=12, y2=3,,我们得到:

Hough Line变换

三个曲线在一个点(0.925,9.6)相交,这些坐标是参数 ( θ,r) 或其中(x0,y0), (x1,y1) 和 (x2,y2) lay.

  • 上面所有的东西是什么意思?这意味着一般来说,通过找到曲线之间的交点数可以检测到一条线。更多的相交曲线意味着由该交点表示的线具有更多的点。通常,我们可以定义检测线路所需的最小交点数的阈值。
  • 这就是Hough Line变换所做的。它跟踪图像中每个点的曲线之间的交点。如果交叉点的数量高于某个阈值,则它将其声明为具有交点的参数(θ,rθ)的行。

标准和概率Hough Line变换

OpenCV实现了两种Hough线变换:

一个。标准Hough变换

  • 它几乎包含在上一节中我们刚刚解释的内容。它给你一个向量的结果(θ,rθ)
  • 在OpenCV中,它使用函数cv :: HoughLines实现

湾 概率霍夫线变换

  • 更有效地实现Hough Line变换。它给出检测出的行的极值(x0,y0,x1,y1)
  • 在OpenCV中,它使用函数cv :: HoughLinesP来实现

Code

  1. 这个程序是做什么的?加载图像应用标准Hough Line变换或概率线变换。在两个窗口中显示原始图像和检测到的线。
  2. 我们将从这里下载我们将要解释的示例代码。这里可以找到一个稍微优雅的版本(其中显示了Hough 标准和带有轨迹的概率更改阈值)。
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void help()
{
    cout << "\nThis program demonstrates line finding with the Hough transform.\n"
            "Usage:\n"
            "./houghlines <image_name>, Default is ../data/pic1.png\n" << endl;
}
int main(int argc, char** argv)
{
    cv::CommandLineParser parser(argc, argv,
        "{help h||}{@image|../data/pic1.png|}"
    );
    if (parser.has("help"))
    {
        help();
        return 0;
    }
    string filename = parser.get<string>("@image");
    if (filename.empty())
    {
        help();
        cout << "no image_name provided" << endl;
        return -1;
    }
    Mat src = imread(filename, 0);
    if(src.empty())
    {
        help();
        cout << "can not open " << filename << endl;
        return -1;
    }
    Mat dst, cdst;
    Canny(src, dst, 50, 200, 3);
    cvtColor(dst, cdst, COLOR_GRAY2BGR);
#if 0
    vector<Vec2f> lines;
    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];
        Point 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> lines;
    HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );
    for( size_t i = 0; i < lines.size(); i++ )
    {
        Vec4i l = lines[i];
        line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
    }
#endif
    imshow("source", src);
    imshow("detected lines", cdst);
    waitKey();
    return 0;
}

说明

  • 加载图像
Mat src = imread(filename, 0);
if(src.empty())
{
  help();
  cout << "can not open " << filename << endl;
  return -1;
}
  • 使用Canny检测器检测图像的边缘
Canny(src,dst,50,200,3);

现在我们将应用Hough Line变换。我们将解释如何使用可用于此目的的两个OpenCV功能:

  • 标准霍夫线变换

首先,你应用变换:

vector<Vec2f> lines;
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );

具有以下参数:

  1. dst:边缘检测器的输出。它应该是一个灰度图像(尽管事实上它是二进制的)
  2. 行:一个向量,将存储检测到的行的参数(r,θ)
  3. rho:参数的分辨率(以像素为单位)。我们使用1像素。r
  4. theta:以弧度表示的参数的分辨率。我们使用1度(CV_PI / 180)θ
  5. threshold:将“* detect *”一行的最小交点数
  6. srn和stn:默认参数为零。查看OpenCV参考了解更多信息

然后通过绘制线条显示结果。

for( size_t i = 0; i < lines.size(); i++ )
{
  float rho = lines[i][0], theta = lines[i][1];
  Point 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, LINE_AA);
}
  • Probabilistic Hough Line变换

首先你应用变换:

vector<Vec4i> lines;
HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );

有论据:

  1. dst:边缘检测器的输出。它应该是一个灰度图像(尽管事实上它是二进制的)
  2. 行:将存储检测到的行的参数的(xstart,ystart,xend,yend)
  3. rho:参数的分辨率(以像素为单位)。我们使用1像素。r
  4. theta:以弧度表示的参数的分辨率。我们使用1度(CV_PI / 180)θ
  5. threshold:将“* detect *”一行的最小交点数
  6. minLinLength:可以形成一行的最小点数。小于这个点数的行被忽略。
  7. maxLineGap:在同一行中考虑的两点之间的最大差距。

然后通过绘制线条显示结果。

for( size_t i = 0; i < lines.size(); i++ )
{
  Vec4i l = lines[i];
  line( cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 3, LINE_AA);
}
  • 显示原始图像和检测到的线条:
imshow("source", src);
imshow("detected lines", cdst);
  • 等到用户退出程序
waitKey();

结果

注意
以下结果是使用我们在“ 代码”部分提到的稍微优雅的版本获得的。它仍然实现与上述相同的内容,只添加Threshold的Trackbar。

使用输入图像,如:

Hough Line变换

我们通过使用Probabilistic Hough Line变换得到以下结果:

Hough Line变换

您可能会发现在更改阈值时检测到的行数会有所不同。解释很明显:如果建立更高的Threshold,将检测到更少的行(因为您需要更多的点来声明检测到的行)。