霍夫变化可以从图像中快速的提取出直线或者圆等形状的图像。
霍夫变化是图像处理中的一种特征提取技术,霍夫变化分为霍夫线变化和霍夫圆变化。
一、霍夫线变化
霍夫线变化用来寻找直线,在进行霍夫线变化之前,需要对图像进行边缘检测处理,也就是说,霍夫线变化的输入只能是二值图像。
在opencv中,支持三种霍夫线变化:
(1)、标准霍夫变化,HoughLines函数
(2)、多尺度霍夫变化,HoughLines函数
(3)、累计概率霍夫变化,HoughLinesP函数
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,是离坐标原点((0,0)(也就是图像的左上角)的距离。 是弧度线条旋转角度(0~垂直线,π/2~水平线)。
第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
第二个参数,InputArray类型的lines,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。
第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
第六个参数,double类型的minLineLength,有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
第七个参数,double类型的maxLineGap,有默认值0,允许将同一行点与点之间连接起来的最大的距离。
二、霍夫圆变化
霍夫梯度法的原理:
(1)、首先对图像进行边缘检测
(2)、对边缘检测图像中的每一个非0点,考虑其局部梯度,可以用sobel算子计算x和y方向上的sobel一阶导数来得到梯度
(3)、利用得到的梯度,由斜率指定的每一个点都在累加器中被累加
(4)、标记边缘图像中每一个非0像素的位置
(5)、从累加器中选择候选的中心
(6)、对每一个候选的中心,考虑所有的非0像素
(7)、这些非0像素按照其与中心的距离排序,选择一条最好的半径
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,表示圆半径的最大值。
//#define _HOUGH_
#ifdef _HOUGH_
void usage_HoughLines()
{
Mat src, dst,tmp,gray;
src = imread("./images/10.jpg");
//canny
Canny(src,tmp, 50, 200, 3);
//graytobgr
cvtColor(tmp,dst,CV_GRAY2BGR);
//lines
vector<Vec2f> lines;
HoughLines(tmp, lines, 1, CV_PI / 180, 150, 0, 0);
//paint the liens in the dst image
for (int i = 0; i < lines.size(); i++){
float rho = lines[i][0];
float theta = lines[i][1];
Point p1, p2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
p1.x = cvRound(x0 + 1000 * (-b));
p1.y = cvRound(y0 + 1000 * (a));
p2.x = cvRound(x0 - 1000 * (-b));
p2.y = cvRound(y0 - 1000 * (a));
line(dst, p1, p2, Scalar(0,0, 200));
}
//show the images
imshow("src", src);
imshow("canny", tmp);
if (!dst.empty())
imshow("dst", dst);
waitKey(0);
}
void usage_HoughLinesP()
{
Mat src, tmp, dst;
src = imread("./hjimg/1.jpg");
//canny
Canny(src, tmp, 50, 230, 3);
//cvt
cvtColor(tmp, dst, CV_GRAY2BGR);
//lines
vector<Vec4i> lines;
//houghlinesp
HoughLinesP(tmp, lines, 1, CV_PI / 180, 70, 50, 10);
//paint lines
for (int i = 0; i < lines.size(); i++){
Vec4i iline = lines[i];
line(dst, Point(iline[0], iline[1]), Point(iline[2], iline[3]),Scalar(0,0,240));
}
//show
imshow("src", src);
imshow("canny", tmp);
imshow("dst", dst);
waitKey(0);
}
void usage_HoughCircles()
{
Mat src, tmp;
src = imread("./hjimg/3.jpg");
//gray
cvtColor(src, tmp, CV_BGR2GRAY);
//gaussian
GaussianBlur(tmp, tmp, Size(7, 7), 2, 2);
//houghcircles
vector<Vec3f> circles;
HoughCircles(tmp, circles, CV_HOUGH_GRADIENT, 1.5,10);
//paint the circles
for (int i = 0; i < circles.size(); i++){
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
//the center
circle(src, center, 3, Scalar(0, 250, 0));
//the circle
circle(src, center, radius, Scalar(0, 0, 250));
}
//show image
imshow("src", src);
waitKey(0);
}
Mat g_src_image, g_dst_image, g_tmp_image;
vector<Vec4i> g_lines;
int g_nthreshold = 200;
static void onHoughLines(int, void*)
{
Mat dst_image = g_dst_image.clone();
Mat tmp_image = g_tmp_image.clone();
vector<Vec4i> lines;
//houghlines
HoughLinesP(tmp_image, lines,1,CV_PI / 180, g_nthreshold+1, 50, 10);
//paint the lines
for (int i = 0; i < lines.size(); i++){
Vec4i iline = lines[i];
line(dst_image, Point(iline[0], iline[1]), Point(iline[2], iline[3]), Scalar(0, 0, 200));
}
//show this image
imshow("dst", dst_image);
}
void usage_hough()
{
//read image
g_src_image = imread("./hjimg/1.jpg");
namedWindow("dst",CV_WINDOW_AUTOSIZE);
//trackbar
createTrackbar("value", "dst", &g_nthreshold, 400, onHoughLines);
//canny
Canny(g_src_image, g_tmp_image, 50, 220, 3);
//gray to bgr
cvtColor(g_tmp_image, g_dst_image, CV_GRAY2BGR);
//show and run.
onHoughLines(0, 0);
if (waitKey(0)>=0){
return;
}
}
#endif