文档结构  
可译网翻译有奖活动正在进行中,查看详情 现在前往 注册?
原作者:未知    来源:docs.opencv.org [英文]
coyee    计算机    2016-06-13    0评/556阅
翻译进度:83%   参与翻译: 卓小混混 (2), 赵君沛然 (1), CY2 (1), toypipi (1)

目标

在这篇教程中您将会学习如何:

理论

注意

以下解释属于Bradski and Kaehler的书《Learning OpenCV

霍夫线变换:

  1. 霍夫线变化是用于检测直线的一种变化。
  2. 为了运用这种变化,您需要首先进行一个边缘检测预处理

它是如何工作的?

  1. 众所周知, 一条直线在图像二维空间可由两个变量表示. 例如:

             1.在 笛卡尔坐标系: 可由参数:  斜率和截距表示.

             2.在 极坐标系: 可由参数:  极径和极角表示

Line variables

对于霍夫变换, 我们将用 极坐标系 来表示直线. 因此, 直线的表达式可为:

y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )

第 1 段(可获 2 积分)

Arranging the terms:

  1. In general for each point (x_{0}, y_{0}), we can define the family of lines that goes through that point as:

    r_{\theta} = x_{0} \cdot \cos \theta + y_{0} \cdot \sin \theta

    Meaning that each pair (r_{\theta},\theta) represents each line that passes by (x_{0}, y_{0}).

  2. If for a given (x_{0}, y_{0}) we plot the family of lines that goes through it, we get a sinusoid. For instance, for x_{0} = 8 and y_{0} = 6 we get the following plot (in a plane \theta - r):

    Polar plot of a the family of lines of a point

    We consider only points such that r> 0 and 0< \theta < 2 \pi.

  3. We can do the same operation above for all the points in an image. If the curves of two different points intersect in the plane \theta - r, that means that both points belong to a same line. For instance, following with the example above and drawing the plot for two more points: x_{1} = 4, y_{1} = 9 and x_{2} = 12, y_{2} = 3, we get:

    Polar plot of the family of lines for three points

    The three plots intersect in one single point (0.925, 9.6), these coordinates are the parameters (\theta, r) or the line in which (x_{0}, y_{0}), (x_{1}, y_{1}) and (x_{2}, y_{2}) lay.

  4. What does all the stuff above mean? It means that in general, a line can be detected by finding the number of intersections between curves.The more curves intersecting means that the line represented by that intersection have more points. In general, we can define a threshold of the minimum number of intersections needed to detect a line.

  5. This is what the Hough Line Transform does. It keeps track of the intersection between curves of every point in the image. If the number of intersections is above some threshold, then it declares it as a line with the parameters (\theta, r_{\theta}) of the intersection point.

第 2 段(可获 2 积分)

标准霍夫线变换和统计概率霍夫线变换

OpenCV实现了以下两种霍夫线变换:

  1. 标准霍夫线变换
  • 原理在上面的部分已经说明了. 它能给我们提供一组参数对 (\theta, r_{\theta}) 的集合来表示检测到的直线
  • 在OpenCV 中通过函数 HoughLines 来实现
  1. 统计概率霍夫线变换
  • 这是执行起来效率更高的霍夫线变换. 它输出检测到的直线的端点 (x_{0}, y_{0}, x_{1}, y_{1})
  • 在OpenCV 中它通过函数 HoughLinesP 来实现

代码 

  1. 这个程序是用来做什么的?
    • 加载一幅图片
    • 对图片进行 标准霍夫线变换 或是 统计概率霍夫线变换.
    • 分别在两个窗口显示原图像和绘出检测到直线的图像.
  2. 我们将要说明的例程能从 这里 下载。 一个更高级的版本 (能同时演示标准霍夫线变换和统计概率霍夫线变换并带有活动条来改变变换的阈值) 能从 这里 下载。
第 3 段(可获 2 积分)
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

void help()
{
 cout << "\nThis program demonstrates line finding with the Hough transform.\n"
         "Usage:\n"
         "./houghlines <image_name>, Default is pic1.jpg\n" << endl;
}

int main(int argc, char** argv)
{
 const char* filename = argc >= 2 ? argv[1] : "pic1.jpg";

 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, CV_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, CV_AA);
  }
 #endif
 imshow("source", src);
 imshow("detected lines", cdst);

 waitKey();

 return 0;
}
第 4 段(可获 2 积分)

说明

  1. 加载图片

    Mat src = imread(filename, 0);
    if(src.empty())
    {
      help();
      cout << "can not open " << filename << endl;
      return -1;
    }
    
  2. 用Canny算子对图像进行边缘检测

    Canny(src, dst, 50, 200, 3);
    

    现在我们将要执行霍夫线变换. 我们将会说明怎样使用OpenCV的函数做到这一点:

  3. 标准霍夫线变换

    1. 首先, 你要执行变换:

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

      有以下自变量:

      • dst: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
      • lines: 储存着检测到的直线的参数对 (r,\theta) 的容器
      • rho : 参数r 的分辨率(以像素为单位)。 我们使用1像素。
      • theta: 参数 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
      • threshold: 要”检测” 一条直线所需最少的的曲线交点
      • srn and stn: 参数默认为0. 查阅OpenCV参考文献来获取更多信息.
    2. 然后通过画出检测到的直线来显示结果.

      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);
      }
      
  4. 统计概率霍夫线变换

    1. 首先, 你要执行变换:

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

      有以下自变量:

      • dst: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
      • lines: 储存着检测到的直线的参数对 (x_{start}, y_{start}, x_{end}, y_{end}) 的容器
      • rho : 参数r 的分辨率(以像素为单位)。 我们使用1像素。
      • theta: 参数 \theta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
      • threshold: 要”检测” 一条直线所需最少的的曲线交点
      • minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.
      • maxLineGap: 能被认为在一条直线上的亮点的最大距离.
    2. 通过画出检测到的直线来显示结果.

      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, CV_AA);
      }
      
  5. 显示原始图像和检测到的直线:

    imshow("source", src);
    imshow("detected lines", cdst);
    
  6. 等待用户按键推出程序

    waitKey();
    
第 5 段(可获 2 积分)

结果

注意

得到的结果使用的是在上面 代码 部分提到的更高级版代码. 霍夫线变换的代码没有改变, 唯一不同的是在GUI的部分加入了活动条可动态改变阈值.输入图像为:

Result of detecting lines with Hough Transform

通过执行统计概率霍夫线变换我们能得到下面的结果:

Result of detecting lines with Hough Transform

当你使用滑动条来改变 阈值 的时候会观察到检测到线的数目的改变. 这是因为: 如果你设置了一个更大的阈值, 能检测到的线的数目将更少 (你需要更多的点来表示一条能检测到的直线).

第 6 段(可获 2 积分)

文章评论