您的位置:首页 > 运维架构

学习OpenCV范例(十五)——霍夫变换

2014-03-22 16:33 344 查看
本次范例通过霍夫变换检测直线和圆,讲解霍夫线变换和霍夫圆变换的原理,代码实现,和演示结果,使用霍夫线变换之前, 首先要对图像进行边缘检测的处理,也即霍夫线变换的直接输入只能是边缘二值图像。而霍夫圆变换则只要输入灰度图像即可,因为在霍夫圆变换的过程中已经用到了canny边缘检测。

1、原理

霍夫线变换:

众所周知, 一条直线在图像二维空间可由两个变量表示. 例如:在 笛卡尔坐标系: 可由参数:斜率和截距表示.在 极坐标系: 可由参数:极径和极角表示对于霍夫变换, 我们将用 极坐标系 来表示直线. 因此, 直线的表达式可为:化简得:一般来说对于点,我们可以将通过这个点的一族直线统一定义为:这就意味着每一对代表一条通过点的直线.如果对于一个给定点我们在极坐标对极径极角平面绘出所有通过它的直线,将得到一条正弦曲线. 例如, 对于给定点and我们可以绘出下图(在平面-):只绘出满足下列条件的点and.我们可以对图像中所有的点进行上述操作. 如果两个不同点进行上述操作后得到的曲线在平面-相交,这就意味着它们通过同一条直线. 例如, 接上面的例子我们继续对点:,和点,绘图,得到下图:这三条曲线在-平面相交于点,坐标表示的是参数对 () 或者是说点,点和点组成的平面内的的直线.那么以上的材料要说明什么呢? 这意味着一般来说, 一条直线能够通过在平面-寻找交于一点的曲线数量来 检测.越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成. 一般来说我们可以通过设置直线上点的 阈值 来定义多少条曲线交于一点我们才认为 检测 到了一条直线.这就是霍夫线变换要做的. 它追踪图像中每个点对应曲线间的交点. 如果交于一点的曲线的数量超过了 阈值, 那么可以认为这个交点所代表的参数对在原图像中为一条直线.标准霍夫线变换和统计概率霍夫线变换OpenCV实现了以下两种霍夫线变换:标准霍夫线变换原理在上面的部分已经说明了. 它能给我们提供一组参数对的集合来表示检测到的直线在OpenCV 中通过函数 HoughLines 来实现统计概率霍夫线变换这是执行起来效率更高的霍夫线变换. 它输出检测到的直线的端点在OpenCV 中它通过函数 HoughLinesP 来实现

霍夫圆变换:

霍夫圆变换可以根据霍夫线变换来实现通过极坐标来表示圆(a,b)表示圆心,R表示半径,则圆表示为:x = a + Rcosθy = b + Rsinθθ的值为0-360一开始我们假设R是已知的,那么我们就可以把x,y空间的公式变换为关于a、b空间的公式:a = x1 – Rcosθb = y1 – Rsinθx1、y1为x、y空间中的每一个非零像素点,如此一来,后面的变换就可以跟霍夫线变换一样操作了。具体操作步骤为:①、读取一副图像②、检测边缘,产生一副二值图像③、对于每个非零像素转换到ab空间中
④、对于每个ab空间中的点,进行累加
⑤、提取数量最多的点作为圆点
在上面提到R是假设已知的,那么当R是未知的情况怎么办?其实很简单,就是将R设置为1、2、3……这样子已知下去,慢慢试,再对R和圆心数量进行阈值就可以了。

2、代码实现

#include "stdafx.h"

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

Mat src, dst, cdst;
const char* filename = "hough.png";
const char *winname="hough";
const char *trackbarname="1.houghlines\n2.houghlinep\n3.houghcircle";
int savevalue=50,houghtype;
const int maxthreshold=150;

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

vector<Vec2f> lines;
Canny(src, dst,50, 200, 3);
cvtColor(dst, cdst, CV_GRAY2BGR);
HoughLines(dst, lines, 1, CV_PI/180, savevalue+10, 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);
}
imshow(winname, cdst);
}
void choisehoughlinep()
{
vector<Vec4i> lines;
Canny(src, dst,50, 200, 3);
cvtColor(dst, cdst, CV_GRAY2BGR);
HoughLinesP(dst, lines, 1, CV_PI/180, savevalue+10, savevalue+10, 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);
}
imshow(winname, cdst);

}
void choisehoughcircle()
{
vector<Vec3f> circles;
cvtColor(src, cdst, CV_GRAY2BGR);
/// Apply the Hough Transform to find the circles
HoughCircles( src, circles, CV_HOUGH_GRADIENT, 1, dst.rows/10, 200, savevalue+10, 0, 0 );
/// Draw the circles detected
printf("%d",circles.size());
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle( cdst, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( cdst, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
imshow(winname, cdst);
}
void choice(int,void *)
{
switch (houghtype)
{
case 0:choisehoughlines();break;
case 1:choisehoughlinep();break;
case 2:choisehoughcircle();break;
}
}
int main(int argc, char** argv)
{

src = imread(filename, 0);
if(src.empty())
{
help();
cout << "can not open " << filename << endl;
return -1;
}
namedWindow(winname);
createTrackbar(trackbarname,winname,&houghtype,3,choice);
createTrackbar("thresholdvalue",winname,&savevalue,maxthreshold,choice);
imshow("source", src);
choice(0,0);
waitKey();

return 0;
}
3、运行结果


图1、原图
图2、houghlines
图3、houghlinep
图4、houghcircle

4、总结

霍夫线变换需要对图片线进行边缘提取,图像二值化处理,而霍夫圆变换则不用,只需输入灰度图像即可;霍夫圆变换不能检测出同心圆的多个圆,只能标记出其中的一个,而且通过阈值来选择哪个圆,大家可以通过上面的滑动条来动态的检验结果。检测到的结果一般都用vector容器来存储。

5、用到的类和函数

HoughLines:

功能:利用 Hough 变换在二值图像中找到直线
结构:
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
image :输入 8-比特、单通道 (二值) 图像lines :输出的角度和r长度rho :与象素相关单位的距离精度theta :弧度测量的角度精度threshold :阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段.srn 、stn :多尺度变换,距离精度 rho 的分母,角度精度 theta 的分母。

HoughLinesP:

功能:通过统计概率的霍夫线变换找到线段
结构:
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
image :输入 8-比特、单通道 (二值) 图像lines :输出线段的两个端点,保存为(x_1, y_1, x_2, y_2)
rho :与象素相关单位的距离精度theta :弧度测量的角度精度
threshold :阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段.
minLineLength :最小的线段长度maxLineGap :这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于maxLineGap时,将其合二为一。

HoughCircles:

功能:利用 Hough 变换在灰度图像中找圆
结构:
void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0 )
image :输入 8-比特、单通道 (二值) 图像circles :输出圆心坐标(x、y),和半径rmethod : Hough 变换方式,目前只支持CV_HOUGH_GRADIENTdp :累加器图像的分辨率。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。minDist :让算法能明显区分的两个不同圆之间的最小距离。param1 :用于Canny的边缘阀值上限,下限被置为上限的一半。param2 :累加器的阀值。minRadius :最小圆半径maxRadius :最大圆半径,默认为最大值 max(image_width, image_height)

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: