霍夫变换:从黑白图像中检测直线和曲线。
优点:对数据的不完全或噪声不是非常敏感。
首先对图像进行边缘检测的处理,即霍夫线变换的直接输入只能是边缘二值图像。
多尺度霍夫变换(MSHT)是经典霍夫变换(标准霍夫变换SHT)在多尺度下的一个变种。
累计概率霍夫变换(PPHT)是SHT的改进。他在一定的范围内进行霍夫变换,计算单独线段的方向机范围,从而减少计算量,缩短计算时间。
标准霍夫变换SHT原理:
霍夫变换运用两个坐标空间之间的变换,将在一个(图像)空间中具有相同形状(数学模型+参数)的曲线或直线,映射到另一个(参数)坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题。
我们知道,一条直线在直角(图像)坐标系下可以用y=kx+b表示, 霍夫变换的主要思想是将该方程的参数和变量交换,即用x,y作为(参数、系数),k,b作为(变量),所以直角坐标系下的一个点(x1,y1)在直角坐标系下表示为一条直线:y1=x1·k+b, 其中(k,b)是该直线上的任意点;直角坐标系下的一条直线y=kx+b在参数空间表示为一个点:(k,b),为了计算方便,并解决k(直线的斜率)接近无穷大的问题,使用了一种标准的直线标识法,将参数空间的坐标表示为极坐标下的γ和θ。
因为同一条直线上的点对应的参数(k,b)/(γ,θ)是相同的,因此可以先将图片进行边缘检测,然后对图像上每一个非零像素点,在参数坐标下变换为一条直线,那么在直角坐标下属于同一条直线的点便在参数空间形成多条直线并内交于一点,这一点即为对应直线的参数,所有直线在(γ,θ)参数空间中得到一系列对应曲线。因此可用该原理进行直线检测。
Hough变换在计算上的吸引力在于将参数空间进一步分割为所谓的累加器单元,如图9所示。其中(amax,amin)和(bmax,bmin)为参数值的期望范围,一般来说,值的最大范围是-90°<θ<90°和-D<ρ<D,其中D是图像中角点间的距离。 最初,这些单元被设置为0。然后,对于图像平面上的每一个非背景点(xk, yk),令θ等于θ轴上允许的细分值,并通过公式ρ=xkcosθ+yksinθ求出相应的ρ值。然后,将得到的ρ值四 舍五入为最接近的、ρ轴上允许的单元值,相应的累加器单元相加。在这个过程的最后,A(i,j)意味着x-y平面上的Q个点位于线xcosθj+ysinθj=ρi上。ρ-θ平面上的细分数决定了这些点共线的精度。
(1)读取原始图像,并转成灰度图像。
(2)采用小波边缘检测算法对其进行边缘检测,得到二值化的边缘图像。
(3)对此边缘图像做Hough变换。
(4)使用函数houghpeaks 做峰值检测。函数houghpeaks的算法如下:
①找到包含有最大值的Hough变换单元,并记下它的位置;
②把上步找到的最大值点的领域中的Hough 变换单元设为0;
③重复该步骤,直到找到需要的峰值数为止,或者达到一个指定的阈值时为止。
(5)一旦在Hough 变换中识别出了一组候选的峰波,则还要留待确定是否存在与这些峰值相关的线段及它们的起始和终止为止。对每一个峰值来说,第一步是找到图像中影响到峰值的每一个非0 值点的位置。为此,编写函数houghpixls 来实现这一功能。
(6)使用函数houghlines 实现直线边缘连接,函数houghlines的算法如下:
①将像素位置旋转90° - θ ,以便它们大概位于一条垂直线上;
②按旋转的x值来对这些像素位置排序;
③使用函数diff 找到裂口。忽略掉小裂口,这将合并被小空白分离的相邻线段;
④返回比最小阈值长的线段的信息。
-
众所周知, 一条直线在图像二维空间可由两个变量表示. 例如:
- 在 笛卡尔坐标系: 可由参数:
斜率和截距表示.
- 在 极坐标系: 可由参数:
极径和极角表示
对于霍夫变换, 我们将用 极坐标系 来表示直线. 因此, 直线的表达式可为:
化简得:
- 在 笛卡尔坐标系: 可由参数:
-
一般来说对于点
, 我们可以将通过这个点的一族直线统一定义为:
这就意味着每一对
代表一条通过点
的直线.
-
如果对于一个给定点
我们在极坐标对极径极角平面绘出所有通过它的直线, 将得到一条正弦曲线. 例如, 对于给定点
and
我们可以绘出下图 (在平面
-
):
只绘出满足下列条件的点
and
.
-
我们可以对图像中所有的点进行上述操作. 如果两个不同点进行上述操作后得到的曲线在平面
-
相交, 这就意味着它们通过同一条直线. 例如, 接上面的例子我们继续对点:
,
和点
,
绘图, 得到下图:
这三条曲线在
-
平面相交于点
, 坐标表示的是参数对 (
) 或者是说点
, 点
和点
组成的平面内的的直线.
说明:给定定点(x,y)在极坐标对极径极角平面会出所有通过它的直线,得到一条正弦曲线。(该曲线上任一点均表示通过定点的直线)。几条曲线(由定点确定的)相交于一点意味着这些定点是共线的。设置直线上点的阈值(个数)来定义多少条曲线交于一点,即可检测到一条直线。
函数:
void 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 );
第一个参数:输入灰度图像
第二个参数:输出图像
第三个参数:极径
第四个参数:极角
第五个参数:累加平面阈值
第六个参数:对于多尺寸霍夫变换,只是第三个参数进步尺寸的除数距离。rho/srn.
第七个参数:对于多尺寸霍夫变换,只是第四个参数进步尺寸的除数距离。
示例:
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
int Dot_amount = 150;
Mat img, img1, dst;
void on_traker(int, void *)
{
img = imread("d://temp/18.png");
imshow("原始图", img);
Canny(img, img1, 150, 200, 3);
imshow("边缘检测", img1);
cvtColor(img1, dst, CV_GRAY2BGR);
vector<Vec2f>lines;
HoughLines(img1, lines, 1, CV_PI / 180, Dot_amount, 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(x0 - 1000 * (a));
line(dst, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
imshow("H效果图", dst);
}
int main()
{
namedWindow("H效果图1", WINDOW_AUTOSIZE);
createTrackbar("点数", "H效果图1", &Dot_amount, 250, on_traker);
on_traker(Dot_amount, 0);
while (1)
{
int key = waitKey(1);
if ((char)key == 27)break;
}
return 0;
}
累计概率霍夫变换:效果很好
void HoughLinesP( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double minLineLength = 0, double maxLineGap = 0 );
第一个参数:输入灰度图像
第二个参数:输出检测的线条
第三个参数:极径
第四个参数:极角
第五个参数:累加平面阈值
第六个参数:最低线段长度
第七个参数:点点之间的最大距离
示例:
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
int Dot_amount = 150;
Mat img, img1, dst;
void on_traker(int, void *)
{
img = imread("d://temp/16.jpg");
imshow("原始图", img);
Canny(img, img1, 150, 200, 3);
imshow("边缘检测", img1);
cvtColor(img1, dst, COLOR_GRAY2BGR);
vector<Vec4i>lines;
HoughLinesP(img1, lines, 1, CV_PI / 180, Dot_amount,50, 10);
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
line(dst,Point(l[0],l[1]),Point(l[2],l[3]), Scalar(0, 0, 255), 1, LINE_AA);
}
imshow("H效果图", dst);
}
int main()
{
namedWindow("H效果图1", WINDOW_AUTOSIZE);
createTrackbar("点数", "H效果图1", &Dot_amount, 250, on_traker);
on_traker(Dot_amount, 0);
while (1)
{
int key = waitKey(1);
if ((char)key == 27)break;
}
return 0;
}
霍夫圆变换-----------霍夫梯度法原理
霍夫圆变换的基本原理和上面讲的霍夫线变化大体上是很类似的,只是点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代。
void HoughCircles( InputArray image, OutputArray circles,
int method, double dp, double minDist,
double param1 = 100, double param2 = 100,
int minRadius = 0, int maxRadius = 0 );
- 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的灰度单通道图像。
- 第二个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量, 每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
- 第三个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符 为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。
- 第四个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创 建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1 时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高 度。
- 第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区 分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个 重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
- 第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。 对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈 值,而低阈值为高阈值的一半。
- 第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
- 第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
- 第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。
过点(x1,y1)的所有圆可以表示为(a1(i),b1(i),r1(i)),过点(x2,y2)的所有圆可以表示为(a2(i),b2(i),r2(i)),过点(x3,y3)的所有圆可以表示为(a3(i),b3(i),r3(i)),如果这三个点在同一个圆上,那么存在一个值(a0,b0,r0),使得 a0 = a1(k)=a2(k)=a3(k) 且b0 = b1(k)=b2(k)=b3(k) 且r0 = r1(k)=r2(k)=r3(k),即这三个点同时在圆(a0,b0,r0)上。
从下图可以形象的看出:

三个圆锥面的交点A 既是同时过这三个点的圆。