霍夫变换一种特征提取技术,是从图像中识别几何形状的基本方法之一
霍夫变换的三种形态:
1.标准霍夫变换(SHT),由HoughLines函数调用
2.多尺度霍夫变换(MSHT),由HoughLines函数调用
3.累计概率霍夫变换(PPHT),由HoughLinesP函数调用
在霍夫变换中,采用极坐标系表示直线
总结一下:霍夫变换的实质是,寻找通过图像中某一点的所有直线,再寻找通过图像另一点的所有直线,并绘制出曲线图,两个曲线图的交点就是通过这两点的那条直线。
程序:
在程序中会用到的函数:
函数 cvRound, cvFloor, cvCeil 用一种舍入方法将输入浮点数转换成整数。 cvRound 返回和参数最接近的整数值。 cvFloor 返回不大于参数的最大整数值。cvCeil 返回不小于参数的最小整数值。
line()函数
void MyLine( Mat img, Point start, Point end )
{
int thickness = 2;
int lineType = 8;
line( img,
start,
end,
Scalar( 0, 0, 0 ),
thickness,
lineType );
}
标准霍夫变换:
int main()
{
Mat src=imread("lou.jpg",0);
blur(src,src,Size(3,3)); //滤波去噪
Mat dst;
Canny(src,dst,50,150,3); //取边缘,输出边缘二值图像
vector<Vec2f> lines; //存储线段
HoughLines(dst,lines,1,CV_PI/180,150); //第三个是p的步进,第四个参数是theta的步进,最后一个是阈值
for(int i=0;i<lines.size();i++)
{
double p=lines[i][0]; //lines的第一维存储线段,第二维存储线段的属性
double theta=lines[i][1];
Point pot1,pot2;
double a,b;
a=sin(theta);
b=cos(theta);
pot1.x=cvRound((p+2)*b); //这里加2减2是为了让一个点扩散成一个线
pot1.y=cvRound((p+2)*a);
pot2.x=cvRound((p-2)*b);
pot2.y=cvRound((p-2)*a);
line(dst,pot1,pot2,Scalar(100,200,10));
}
imshow("a",dst);
waitKey(0);
}
累计概率霍夫变换:
int main()
{
Mat src=imread("lou.jpg",0);
blur(src,src,Size(3,3));
Mat dst;
Canny(src,dst,50,150,3);//取边缘,输出边缘二值图像
vector<Vec4i> lines; //vector内可以存结构体的
HoughLinesP(dst,lines,1,CV_PI/180,150,20,10); //150是阈值,20是最小线段长度,10是最大间隔
for(int i=0;i<lines.size();i++)
{
Vec4i ln=lines[i];
line(dst,Point(ln[0],ln[1]),Point(ln[2],ln[3]),Scalar(100,200,20),1);
}
imshow("a",dst);
waitKey(0);
}
比标准霍夫变换的优势在于可以自动控制线的长度,不至于将物体边缘所在的直线全部绘制出来。
霍夫圆变换
跟上述两个函数比,不需要用二值图像进行霍夫圆变换
可以检测灰度图像中的圆形
int main()
{
Mat src=imread("yuan2.jpg",0);
GaussianBlur(src,src,Size(3,3),1,1);
// imshow("a",src);
// waitKey(0);
Mat dst;
vector<Vec3f> circles;
HoughCircles(src,circles,CV_HOUGH_GRADIENT,1,10,100,50,0,0); //第四个参数=1,累加器与输入图像有相同分辨率
//=2,累加器有图像一半的宽度和高度
//第五个参数10是圆心的最小距离,参数太小多个相邻圆会变成一个。
//第六七个参数200,100是两个阈值参数,高阈值是低阈值的2倍,阈值越小表明检测的圆越多,越大检测的圆越准,也少。
cout<<circles.size()<<endl;
for(int i=0;i<circles.size();i++)
{
Vec3f c=circles[i];
Point center(cvRound(c[0]),cvRound(c[1]));
int r=cvRound(c[2]);
circle(src,center,3,Scalar(255,0,0),-1,8,0); //线粗定义为 thickness = -1, 因此次圆将被填充
circle(src,center,r,Scalar(0,255,0),3,8,0);
cout<<i<<endl;
}
imshow("b",src);
waitKey(0);
}