Hough 变换,对图像中直线的残缺部分、噪声、以及其它的共存结构不敏感,因此,具有很强的鲁棒性。

它常用来检测 直线和曲线 (圆形),识别图像中的几何形状,甚至可用来分割重叠或有部分遮挡的物体。

1  平面坐标和极坐标

1)  平面坐标的 <=> 极坐标(平面化)的曲线

所谓极坐标平面化是指, 将 ρ-θ 的关系像 x-y 那样在平面内展开。

公式推导: x-y坐标中的点 (x0, y0), 代入极坐标 ρ-θ 中得

$\quad \begin{align*}\rho = x_{0}\: cos\theta + y_{0}\: sin\theta = \sqrt{x_{0}^{2}+y_{0}^{2}}\:\left (\frac{x_{0}}{\sqrt{x_{0}^{2}+y_{0}^{2}}}\: cos\theta  +  \frac{y_{0}}{\sqrt{x_{0}^{2}+y_{0}^{2}}}\:sin\theta\right )
= \sqrt{x_{0}^{2}+y_{0}^{2}} \; sin(\varphi_{0} +\theta ) \end{align*} $

其中, $sin\varphi_{0} = \frac{x_{0}}{\sqrt{x_{0}^{2}+y_{0}^{2}}}$

由上述公式可以明显的看出ρ与θ之间的函数关系。

2)  极坐标的 <=> 平面坐标的直线

    公式推导: ρ-θ 极坐标中的点 (ρ0, θ0), 代入平面坐标 x-y 中得

$\quad y = ( -\tfrac{cos\theta_{0} }{sin\theta_{0}} )\:x + \tfrac{\rho_{0} }{sin\theta_{0}}$

由此可知, 极坐标中的一个定点(ρ0, θ0), 对应于平面坐标内的一条直线。若极坐标中有N条曲线相交于一点(如下左图), 则说明在平面坐标中, 该点对应的直线包含N条曲线对应的N个点, 也即这N个点组成了一条直线。

              

上面左图中, 是平面坐标中的三个点 (8,6), (4, 9), (12,3) 分别对应的三条曲线。

因此, 霍夫变换的目的是将平面坐标内的直线检测, 转换为极坐标内的交点检测, 显然寻找一个交点要比寻找直线(实际上是寻找许多点)容易得多了。

2  OpenCV中的函数

1)  HoughLines

  1. void cv::HoughLines(
      InputArray image, // 输入图像
      OutputArray lines, // 输出位直线数组
      double rho, // 用像素个数表示的长度分辨率
      double theta, // 用像素个数表示的角度分辨率
      int threshold, // 累加器阈值 (只有大于该阈值的才有足够的投票)
      double srn = , // 若 srn = stn = 0,则使用的是经典霍夫变换
      double stn =,
      double min_theta = , // 取值为 0 ~ max_theta
      double max_theta = CV_PI // 取值为 min_theta ~ CV_PI
    );

未完待续-源代码留待以后剖析:

  1. /*
  2. Here image is an input raster;
  3. step is it's step; size characterizes it's ROI;
  4. rho and theta are discretization steps (in pixels and radians correspondingly).
  5. threshold is the minimum number of pixels in the feature for it
  6. to be a candidate for line. lines is the output
  7. array of (rho, theta) pairs. linesMax is the buffer size (number of pairs).
  8. Functions return the actual number of found lines.
  9. */
  10. static void
  11. HoughLinesStandard( const Mat& img, float rho, float theta,
  12. int threshold, std::vector<Vec2f>& lines, int linesMax,
  13. double min_theta, double max_theta )
  14. {
  15. int i, j;
  16. float irho = / rho;
  17.  
  18. CV_Assert( img.type() == CV_8UC1 );
  19.  
  20. const uchar* image = img.ptr();
  21. int step = (int)img.step;
  22. int width = img.cols;
  23. int height = img.rows;
  24.  
  25. if (max_theta < min_theta ) {
  26. CV_Error( CV_StsBadArg, "max_theta must be greater than min_theta" );
  27. }
  28. int numangle = cvRound((max_theta - min_theta) / theta);
  29. int numrho = cvRound(((width + height) * + ) / rho);
  30.  
  31. #if defined HAVE_IPP && !defined(HAVE_IPP_ICV_ONLY) && IPP_VERSION_X100 >= 810 && IPP_DISABLE_BLOCK
  32. CV_IPP_CHECK()
  33. {
  34. IppiSize srcSize = { width, height };
  35. IppPointPolar delta = { rho, theta };
  36. IppPointPolar dstRoi[] = {{(Ipp32f) -(width + height), (Ipp32f) min_theta},{(Ipp32f) (width + height), (Ipp32f) max_theta}};
  37. int bufferSize;
  38. int nz = countNonZero(img);
  39. int ipp_linesMax = std::min(linesMax, nz*numangle/threshold);
  40. int linesCount = ;
  41. lines.resize(ipp_linesMax);
  42. IppStatus ok = ippiHoughLineGetSize_8u_C1R(srcSize, delta, ipp_linesMax, &bufferSize);
  43. Ipp8u* buffer = ippsMalloc_8u(bufferSize);
  44. if (ok >= ) ok = ippiHoughLine_Region_8u32f_C1R(image, step, srcSize, (IppPointPolar*) &lines[], dstRoi, ipp_linesMax, &linesCount, delta, threshold, buffer);
  45. ippsFree(buffer);
  46. if (ok >= )
  47. {
  48. lines.resize(linesCount);
  49. CV_IMPL_ADD(CV_IMPL_IPP);
  50. return;
  51. }
  52. lines.clear();
  53. setIppErrorStatus();
  54. }
  55. #endif
  56.  
  57. AutoBuffer<int> _accum((numangle+) * (numrho+));
  58. std::vector<int> _sort_buf;
  59. AutoBuffer<float> _tabSin(numangle);
  60. AutoBuffer<float> _tabCos(numangle);
  61. int *accum = _accum;
  62. float *tabSin = _tabSin, *tabCos = _tabCos;
  63.  
  64. memset( accum, , sizeof(accum[]) * (numangle+) * (numrho+) );
  65.  
  66. float ang = static_cast<float>(min_theta);
  67. for(int n = ; n < numangle; ang += theta, n++ )
  68. {
  69. tabSin[n] = (float)(sin((double)ang) * irho);
  70. tabCos[n] = (float)(cos((double)ang) * irho);
  71. }
  72.  
  73. // stage 1. fill accumulator
  74. for( i = ; i < height; i++ )
  75. for( j = ; j < width; j++ )
  76. {
  77. if( image[i * step + j] != )
  78. for(int n = ; n < numangle; n++ )
  79. {
  80. int r = cvRound( j * tabCos[n] + i * tabSin[n] );
  81. r += (numrho - ) / ;
  82. accum[(n+) * (numrho+) + r+]++;
  83. }
  84. }
  85.  
  86. // stage 2. find local maximums
  87. for(int r = ; r < numrho; r++ )
  88. for(int n = ; n < numangle; n++ )
  89. {
  90. int base = (n+) * (numrho+) + r+;
  91. if( accum[base] > threshold &&
  92. accum[base] > accum[base - ] && accum[base] >= accum[base + ] &&
  93. accum[base] > accum[base - numrho - ] && accum[base] >= accum[base + numrho + ] )
  94. _sort_buf.push_back(base);
  95. }
  96.  
  97. // stage 3. sort the detected lines by accumulator value
  98. std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));
  99.  
  100. // stage 4. store the first min(total,linesMax) lines to the output buffer
  101. linesMax = std::min(linesMax, (int)_sort_buf.size());
  102. double scale = ./(numrho+);
  103. for( i = ; i < linesMax; i++ )
  104. {
  105. LinePolar line;
  106. int idx = _sort_buf[i];
  107. int n = cvFloor(idx*scale) - ;
  108. int r = idx - (n+)*(numrho+) - ;
  109. line.rho = (r - (numrho - )*0.5f) * rho;
  110. line.angle = static_cast<float>(min_theta) + n * theta;
  111. lines.push_back(Vec2f(line.rho, line.angle));
  112. }
  113. }

2)  HoughLinesP

  1. void cv::HoughLinesP ( InputArray image, OutputArray lines,
  2. double rho, double theta, int threshold,
  3. double minLineLength=, double maxLineGap= )
  4. /*
  5. 1) image
  6. 8-bit, single-channel binary source image. The image may be modified by the function.
  7. 2) lines
  8. Output vector of lines. Each line is represented by a 4-element vector x1, y1, x2,y2), where (x1, y1) and (x2, y2) are the ending points of each detected line segment.
  9. 3) rho
  10. Distance resolution of the accumulator in pixels.
  11. 4) theta
  12. Angle resolution of the accumulator in radians.
  13. 5) threshold
  14. Accumulator threshold parameter. Only those lines are returned that get enough votes(>threshold).
  15. 6) minLineLength
  16. Minimum line length. Line segments shorter than that are rejected.
  17. 7) MaxLineGap
  18. Maximum allowed gap between points on the same line to link them. */

Progressive Probabilistic Hough Transform Algorithm Outline:

  1. /* 摘自文献 J. Matas, Robust Detection of Lines Using the Progressive Probabilistic Hough Transform
  2. 1. Check the input image; if it is empty then finish.
  3. 2. Update the accumulator with a single pixel randomly selected from the input image.
  4. 3. Remove the selected pixel from input image.
  5. 4. Check if the highest peak in the accumulator that was modified by the new pixel is higher than threshold thr(N). If not then goto 1.
  6. 5. Look along a corridor specified by the peak in the accumulator, and find the longest segment that either is continuous
  7. or exhibits a gap not exceeding a given threshold.
  8. 6. Remove the pixels in the segment from input image.
  9. 7. “Unvote” from the accumulator all the pixels from the line that have previously voted.
  10. 8. If the line segment is longer than the minimum length add it into the output list.
  11. 9. Goto 1. */

3)  HoughCircles

  1. void cv::HoughCircles ( InputArray image, OutputArray circles,
  2. int method, double dp, double minDist,
  3. double param1 = , double param2 = ,
  4. int minRadius = , int maxRadius = )
  5. /*
  6. 1) image
  7. 8-bit, single-channel, grayscale input image.
  8. 2) circles
  9. Output vector of found circles. Each vector is encoded as a 3-element floating-point vector (x, y, radius).
  10. 3) method
  11. Detection method, see cv::HoughModes. Currently, the only implemented method is HOUGH_GRADIENT
  12. 4) dp
  13. Inverse ratio of the accumulator resolution to the image resolution.
  14. For example, if dp=1 , the accumulator has the same resolution as the input image.
  15. If dp=2 , the accumulator has half as big width and height.
  16. 5) minDist
  17. Minimum distance between the centers of the detected circles.
  18. If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one.
  19. If it is too large, some circles may be missed.
  20. 6) param1
  21. First method-specific parameter.
  22. In case of CV_HOUGH_GRADIENT , it is the higher threshold of the two passed to the Canny edge detector (the lower one is twice smaller).
  23. 7) param2
  24. Second method-specific parameter.
  25. In case of CV_HOUGH_GRADIENT , it is the accumulator threshold for the circle centers at the detection stage.
  26. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned first.
  27. 8) minRadius
  28. Minimum circle radius.
  29. 9) maxRadius
  30. Maximum circle radius. */

3  代码示例

在进行霍夫变换之前,首先要对图像进行"边缘检测"的预处理。

3.1  检测直线 (带滑动条)

  1. #include "opencv2/imgcodecs.hpp"
  2. #include "opencv2/highgui.hpp"
  3. #include "opencv2/imgproc.hpp"
  4.  
  5. using namespace cv;
  6. using namespace std;
  7.  
  8. // 全局变量
  9. Mat src, src_gray, edges;
  10. Mat standard_hough, probabilistic_hough;
  11. int min_threshold = ;
  12. int max_trackbar = ;
  13.  
  14. const char* probabilistic_name = "Probabilistic Hough Lines";
  15.  
  16. int p_trackbar = max_trackbar;
  17.  
  18. void Probabilistic_Hough( int, void* ); // 函数声明

  19. int main( int, char** argv )
  20. {
  21. // 读图
  22. src = imread("line_demo.png");
  23. if(src.empty())
  24. return -;
  25.  
  26. // 灰度图
  27. cvtColor(src, src_gray, COLOR_RGB2GRAY);
  28.  
  29. // cany 边缘检测
  30. Canny(src_gray, edges, , , );
  31.  
  32. // 阈值滑动条
  33. char thresh_label[];
  34. sprintf( thresh_label, "Thres: %d + input", min_threshold );
  35.  
  36. namedWindow(probabilistic_name, WINDOW_AUTOSIZE );
  37. createTrackbar(thresh_label, probabilistic_name, &p_trackbar, max_trackbar, Probabilistic_Hough);
  38.  
  39. // 回调函数
  40. Probabilistic_Hough(, );
  41.  
  42. waitKey();
  43. }
  44.  
  45. void Probabilistic_Hough(int, void*)
  46. {
  47. vector<Vec4i> p_lines;
  48. // gray -> rgb
  49. cvtColor(edges, probabilistic_hough, COLOR_GRAY2BGR);
  50. // Probabilistic Hough Transform
  51. HoughLinesP(edges, p_lines, , CV_PI/, min_threshold + p_trackbar, , );
  52. // 显示
  53. for( size_t i = ; i < p_lines.size(); i++ ) {
  54. Vec4i l = p_lines[i];
  55. line(probabilistic_hough, Point(l[], l[]), Point(l[], l[]), Scalar(,,), , LINE_AA);
  56. }
  57. imshow( probabilistic_name, probabilistic_hough );
  58. }

3.2  检测圆形 (不带滑动条)

  1. #include <opencv2/imgproc.hpp>
  2. #include <opencv2/highgui.hpp>
  3.  
  4. using namespace cv;
  5. using namespace std;
  6.  
  7. int main(int argc, char** argv)
  8. {
  9. // 读图
  10. Mat src, src_gray;
  11. src = imread("circle_demo.png");
  12. if(src.empty())
  13. return -;
  14.  
  15. // 灰度图 + 高斯滤波
  16. cvtColor(src, src_gray, COLOR_BGR2GRAY);
  17. GaussianBlur(src_gray, src_gray, Size(, ), , );
  18.  
  19. // 画圆
  20. vector<Vec3f> circles;
  21. HoughCircles(src_gray, circles, HOUGH_GRADIENT, , src_gray.rows/, , );
  22. for(size_t i=; i<circles.size(); ++i)
  23. {
  24. Point center(cvRound(circles[i][]), cvRound(circles[i][]));
  25. int radius = cvRound(circles[i][]);
  26. circle(src, center, , Scalar(,,), -, , ); // 圆心
  27. circle(src, center, radius, Scalar(,,), , , ); // 圆形
  28. }
  29.  
  30. // 窗体
  31. namedWindow("circles", WINDOW_AUTOSIZE);
  32. imshow("circles", src);
  33.  
  34. waitKey();
  35. }

参考资料:

<图像处理、分析与机器视觉> 第4版, 6.2.6 Hough 变换

OpenCV Tutorials / Image Processing (imgproc module) / Hough Line Transform

OpenCV Tutorials / Image Processing (imgproc module) / Hough Circle Transform

OpenCV 之 霍夫变换的更多相关文章

  1. 【OpenCV新手教程第14】OpenCVHough变换:霍夫变换线,霍夫变换圆汇编

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...

  2. 【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑

    http://blog.csdn.net/poem_qianmo/article/details/26977557 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  3. 学习 opencv---(13)opencv霍夫变换:霍夫线变换,霍夫圆变换

    在本篇文章中,我们将一起学习opencv中霍夫变换相关的知识点,以及了解opencv中实现霍夫变换的HoughLines,HoughLinesP函数的使用方法,实现霍夫圆变换的HoughCircles ...

  4. 【OpenCV新手教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...

  5. OpenCV-Python教程(9、使用霍夫变换检测直线)

    相比C++而言,Python适合做原型.本系列的文章介绍如何在Python中用OpenCV图形库,以及与C++调用相应OpenCV函数的不同之处.这篇文章介绍在Python中使用OpenCV的霍夫变换 ...

  6. opencv-霍夫直线变换与圆变换

    转自:https://blog.csdn.net/poem_qianmo/article/details/26977557 一.引言 在图像处理和计算机视觉领域中,如何从当前的图像中提取所需要的特征信 ...

  7. OpenCV探索之路(七):霍夫变换

    我们如何在图像中快速识别出其中的圆和直线?一个非常有效的方法就是霍夫变换,它是图像中识别各种几何形状的基本算法之一. 霍夫线变换 霍夫线变换是一种在图像中寻找直线的方法.OpenCV中支持三种霍夫线变 ...

  8. C++ Opencv HoughLines()用霍夫变换在二元图像中寻线

    一.霍夫变换简介 参考http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm 二.HoughLines()函数详解 该函数接受的输入矩阵只能是8位单通道的二 ...

  9. opencv 霍夫变换 实现图片旋转角度计算

    在OCR实际开发中,证件照采集角度有很大的偏差,需要将图片进行旋转校正, 效果图: 在应用中发现应该加入高斯模糊,可以极大减少误差线条. 知道线条后 通过求斜率 得旋转角度 .(x1-x2)/(y1- ...

随机推荐

  1. Bootstrap 实现CRUD示例及代码

    https://github.com/wenzhixin/bootstrap-table-examples/blob/master/crud/index.html <!DOCTYPE html& ...

  2. 234. Palindrome Linked List【Easy】【判断链表是否回文】

    Given a singly linked list, determine if it is a palindrome. Example 1: Input: 1->2 Output: false ...

  3. 从InitialContext获取数据源

    概述 本文介绍如何从javax.naming.InitialContext中获取web容器配置的数据源. 在web开发中,常见的获取数据源的方式是把数据源定义为spring的bean,其他类通过spr ...

  4. [BZOJ4861][BJOI2017]魔法咒语(AC自动机+矩阵优化DP)

    4861: [Beijing2017]魔法咒语 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 217  Solved: 105[Submit][Sta ...

  5. JZYZOJ1536 [haoi2014]走出金字塔

    http://172.20.6.3/Problem_Show.asp?id=1536 确实不难,找规律的题,开始想复杂了,分了好多情况.开始为省几个变量加了一大堆max,min,abs代码一下子复杂太 ...

  6. 20162325金立清 实验四 Android程序设计 实验报告

    实验四 Android程序设计 实验报告 代码托管地址 码云链接 实验内容 安装使用Android Stuidio Activity测试 UI测试 布局测试 事件处理测试 Android程序设计-1 ...

  7. 邮件发送javamail

    写在前面: 最近要将dms系统原始发邮件的功能(调用的webservice)改变成使用smtp服务来发送邮件(使用javamail来发送),这里简单记录下,方便日后有用到,直接拿来用即可. 首先导入需 ...

  8. JavaScript的深拷贝与浅拷贝

    深拷贝和浅拷贝是在面试中经常遇到的问题.今天在这里总结一下. 深拷贝与浅拷贝的问题,涉及到JavaScript的变量类型,先来说说变量的类型,变量类型包括基本类型和引用类型. 基本类型:Undefin ...

  9. #iOS问题记录# UITextview富文本链接,禁止长按事件

    UITextView的富文本组装,添加图片点击事件,启动 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *) ...

  10. rocketmq持久化方式

    推荐看下RocketMQ,使用文件做持久化, 并支持分布式事务(虽然可能造成较多的写脏), 异步刷盘,内存预分配, 高可用采用了同步双写及异步复制的方式, 通信是用netty做的,基本上所有耗时的操作 ...