opencv霍夫变换检测圆cvHoughCircles和直线cvHoughLines2的应用
1)cvHonghLines2:直线
2)cvHoughCircles:该函数用Hough变换在二值图像中中寻找圆
3)cvCircle:是指绘制圆形的一个程序函数
4)cvLine:简单的绘制直线函数
5)cvLoadImage:载入图像的函数
霍夫变换是一种在图像中寻找直线、圆及其他简单形状的方法。原始的霍夫变换是一种直线变换,即在二值图像中寻找直线的一种相对快速的方法。
变换可以推广到其他普通的情况,而不仅仅是简单的直线,对于图像中共线的点集{(x0,y0), (x1,y1), ...}都经过直线y=kx+b,先在我们换一个说法,“斜率为k,截距为b的直线y=kx+b包含了所有在该直线上的点”。
一种强调的是图像中的点集,另一种强调的是直线的参数k和b,通过直线的点集去描述这条直线明显没有直接通过k,b两个参数去描述那样直接方便。而Hough变换就是将我们“点共线”的思维转化到参数空间{k,b}进行描述,图像空间中所有经过y=kx+b的点经过Hough变换后在参数空间都会相交于点(k,b),这样,通过Hough变换,就可以将图像空间中直线的检测转化为参数空间中对点的检测。我们不妨将y=kx+b进行一下变形:
这就是Hough变换将图像空间坐标(x,y)转化为参数空间(k,b)的Hough变换式。
Hough变换的步骤(执行过程):
在参数空间中建立一个二维(分别对应k,b)计数器,实际就是二维数组kbcnt,k维度为图像中直线斜率可能范围,b维度为图像中截距可能范围;数组中所有值都初始化为0;
扫描图像空间中的所有点(xi,yi),Hough变换式进行图像空间到参数空间的变换(ki,bi),计数kbcnt(ki,bi)++
设定阈值thr(图像中有多少个点共线才认为存在直线),kbcnt(ki,bi)>thr的ki,bi组成图像中的直线y=ki*x+bi
然而,上面的检测直线的方案貌似还有些问题:如果图像中存在竖直的直线呢,那kbcnt的k维度岂不是要无穷大!因此,才有了另一种参数空间的方案:利用极坐标参数而非“斜率-截距式”描述直线。
极坐标中的直线方程为
将其改写成Hough变换式,即自变量(x,y)到参数变量(r,theta)的映射:
使用极坐标参数空间,Hough变换的步骤不变,只不过将kbcnt替换成rthcnt,r范围是图像对角线的长度,th范围是0~2*pi。因为图像是离散的,所以r和th都有一个步进值dr和dth。
Hough变换除了检测直线,还可用来检测任何能用数学表达式表示的形状,如最常见的圆、椭圆,基本原理都是将图像空间的像素转变到参数空间,然后在参数空间中对共线/圆/椭圆的点进行统计,最后通过阈值判决是否是符合要求的形状。
1)cvHonghLines2:此函数是opencv图像变换函数中的一个,主要用来访问霍夫变换的两个算法———标准霍夫变换(SHT)和累计概率霍夫变换(PPHT)。
对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2).
2)cvHoughCircles:该函数用Hough变换在二值图像中中寻找圆,成功时返回CvSeq指针
CvSeq *cvHoughCircles(CvArr *image,void *circle_storage,int method,double dp,3)cvCircle:是指绘制圆形的一个程序函数。 中文意思 Circle,即圆形。cvCircle是指绘制圆形的一个程序函数。
cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color,
int thickness=1, int lineType=8, int shift=0)
img为图像指针,单通道多通道都行,不需要特殊要求
center为画圆的圆心坐标
radius为圆的半径
color为设定圆的颜色,比如用CV_RGB(255, 0,0)设置为红色
thickness为设置圆线条的粗细,值越大则线条越粗,为负数则是填充效果
4)cvLine:简单的绘制直线函数
void cvPolyLine( CvArr* img, CvPoint* pt1, CvPoint* pt2, CvScalar color,
int thickness=1,int line_type=8,int shift=0);
img 图像。
pt1 线段的第一个端点。
pt2 线段的第二个端点。
color 线段的颜色。
thickness 线段的粗细程度。
line_type 线段的类型。 8 (or 0) - 8-connected line(8邻接)连接 线。 4 - 4-connected line(4邻接)连接线。
CV_AA - antialiased 线条。
代码
#include <cv.h>
#include <highgui.h>
#include <iostream>
#include <opencv2/legacy/legacy.hpp>
//#pragma comment(lib, "opencv_legacy2411.lib")
using namespace cv;
using namespace std;
int main()
{
char * soutceFile = "D:\\VC98\\C++项目\\opencv\\hough\\hough\\a4.jpg";
IplImage * image_Resource = cvLoadImage(soutceFile, CV_LOAD_IMAGE_GRAYSCALE);
assert(image_Resource);
cvNamedWindow("原始图像", CV_WINDOW_AUTOSIZE);
cvNamedWindow("题目_a", CV_WINDOW_AUTOSIZE);
cvShowImage("原始图像", image_Resource);
//---------------------------a:开始--------------------------------//
IplImage *img1 = cvCreateImage (cvGetSize(image_Resource), IPL_DEPTH_8U, 1);//处理的图像必须是八位单通道的
cvZero(img1);
if (image_Resource->nChannels == 1)
{
img1 = cvCloneImage (image_Resource);
}
else
{
cvCvtColor (image_Resource, img1, CV_RGB2GRAY); //转为单通道
}
CvMemStorage* storage = cvCreateMemStorage(0);
cvSmooth(img1,img1,CV_GAUSSIAN,5,5);//高斯平滑滤波,降噪处理
cvThreshold(img1,img1,100,100,CV_THRESH_TRUNC);//二值化
CvSeq* results = cvHoughCircles(//cvHoughCircles函数需要估计每一个像素梯度的方向,
//因此会在内部自动调用cvSobel,而二值边缘图像的处理是比较难的
img1,//输入可以是灰度图
storage,
CV_HOUGH_GRADIENT,
1,//累加器图像分辨率,增大则分辨率变小
image_Resource->width / 10,//两个圆之间最小距离
100,//边缘阀值,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定)
25,//决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃
10,//最小圆半径,这个可以通过图片确定你需要的圆的区间范围
150//最大圆半径
);
IplImage *image_Result_a = cvCreateImage (cvGetSize(img1), IPL_DEPTH_8U, 3); //用一个三通道的图片来显示红色的圆圈
cvZero(image_Result_a);
cvCvtColor (img1, image_Result_a, CV_GRAY2RGB); //转为3通道
for (int i = 0; i < results->total; i++)
{
float* p = (float*)cvGetSeqElem(results, i);
CvPoint pt = cvPoint(cvRound(p[0]), cvRound(p[1]));//圆心坐标(p(0),p(1))
cvCircle(
image_Result_a,
pt,//确定圆心
cvRound(p[2]),//确定半径
CV_RGB(255,0,0),
3
);
}
cvShowImage("题目_a",image_Result_a);
cvSaveImage("result.jpg",image_Result_a);
//---------------------------a:结束--------------------------------//
//---------------------------b:开始--------------------------------//
soutceFile = "D:\\VC98\\C++项目\\opencv\\hough\\hough\\house1.png";
image_Resource = cvLoadImage(soutceFile, CV_LOAD_IMAGE_UNCHANGED);
assert(image_Resource);
int i;
IplImage* dst = cvCreateImage(cvGetSize(image_Resource), 8, 1);
IplImage* color_dst = cvCreateImage(cvGetSize(image_Resource), 8, 3);
CvMemStorage* storage2 = cvCreateMemStorage(0);
CvSeq* lines = 0;
IplImage* src1 = cvCreateImage(cvSize(image_Resource->width, image_Resource->height), IPL_DEPTH_8U, 1);
cvCvtColor(image_Resource, src1, CV_BGR2GRAY);
cvCanny(src1, dst, 50, 200, 3);
cvCvtColor(dst, color_dst, CV_GRAY2BGR);
lines = cvHoughLines2(dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 80, 30, 10);
for (i = 0; i < lines->total; i++)
{
CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i);
cvLine(color_dst, line[0], line[1], CV_RGB(255, 0, 0), 2, 8);
}
cvNamedWindow("Source", CV_WINDOW_NORMAL);
cvShowImage("Source", image_Resource);
cvNamedWindow("Hough", CV_WINDOW_NORMAL);
cvShowImage("Hough", color_dst);
cvSaveImage("result1.jpg",color_dst);
//---------------------------b:结束--------------------------------//
cvWaitKey(0);
cvReleaseImage(&image_Resource);
cvReleaseImage(&image_Result_a);
cvReleaseImage(&color_dst);
cvReleaseImage(&img1);
cvDestroyAllWindows();
return 0;
}
程序说明:
(5)cvLoadImage():载入图像的函数
原型:CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR));
第一个参数是:指向文件名称的指针(不可修改);
第二个参数是:读入图像的颜色和深度;
enum
{
/* 8bit, color or not */
CV_LOAD_IMAGE_UNCHANGED =-1,
/* 8bit, gray */
CV_LOAD_IMAGE_GRAYSCALE =0,
/* ?, color */
CV_LOAD_IMAGE_COLOR =1,
/* any depth, ? */
CV_LOAD_IMAGE_ANYDEPTH =2,
/* ?, any color */
CV_LOAD_IMAGE_ANYCOLOR =4
};
指定的颜色可以将输入的图片转为3信道(CV_LOAD_IMAGE_COLOR)也即彩色(>0), 单信道 (CV_LOAD_IMAGE_GRAYSCALE)也即灰色(=0),或者保持不变(CV_LOAD_IMAGE_ANYCOLOR)(=4)。
深度指定输入的图像是否转为每个颜色信道每象素8位,(OpenCV的早期版本一样),或者同输入的图像一样保持不变。
选中CV_LOAD_IMAGE_ANYDEPTH,则输入图像格式可以为8位无符号,16位无符号,32位有符号或者32位浮点型。
如果输入有冲突的标志,将采用较小的数字值。比如CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYCOLOR 将载入3信道图。CV_LOAD_IMAGE_ANYCOLOR和CV_LOAD_IMAGE_UNCHANGED是等值的。但是,CV_LOAD_IMAGE_ANYCOLOR有着可以和CV_LOAD_IMAGE_ANYDEPTH同时使用的优点,所以CV_LOAD_IMAGE_UNCHANGED不再使用了。
如果想要载入最真实的图像,选择CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR。
另:通常用cvLoadimage()函数进行读图像,参数选择上建议大家选择CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR,这样的参数组合读出的图像信息保持了原是图像的信息(包括通道信息和位深信息)。其中像素深度指每个通道用多少位来表示,通道就是指每个像素的颜色数了。而我们一般在图像处理书上看到的图像的像素的bit数,在这里应该是:通道*像素深度。可以看出像素的bit数和像素深度不是同一个概念。
效果图: