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

opencv霍夫变换检测圆cvHoughCircles和直线cvHoughLines2的应用

2015-06-09 19:33 375 查看
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)。

CvSeq* cvHonghLines2(CvArr* image,void* line_storage,int mehtod,

double rho,double theta,int threshold,double param1 =0,double param2 =0);

参数说明:

image

输入 8-比特、单通道 (二值) 图像,当用CV_HOUGH_PROBABILISTIC方法检测的时候其内容会被函数改变。

line_storage

检测到的线段存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回),或者是包含线段参数的特殊类型(见下面)的具有单行/单列的矩阵(CvMat*)。矩阵头为函数所修改,使得它的cols/rows 将包含一组检测到的线段。如果 line_storage 是矩阵,而实际线段的数目超过矩阵尺寸,那么最大可能数目的线段被返回(线段没有按照长度、可信度或其它指标排序).

method

Hough 变换变量,是下面变量的其中之一

CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此,矩阵类型必须是 CV_32FC2 type.

CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割,则效率更高). 它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4.

CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。

rho

与象素相关单位的距离精度

theta

弧度测量的角度精度

threshold

阈值参数。如果相应的累计值大于 threshold, 则函数返回的这个线段.

param1

第一个方法相关的参数:

对传统 Hough 变换,不使用(0).

对概率 Hough 变换,它是最小线段长度.

对多尺度 Hough 变换,它是距离精度 rho 的分母 (大致的距离精度是 rho 而精确的应该是 rho / param1 ).

param2

第二个方法相关参数:

对传统 Hough 变换,不使用 (0).

对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一。

对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2).

2)cvHoughCircles:该函数用Hough变换在二值图像中中寻找圆,成功时返回CvSeq指针

CvSeq *cvHoughCircles(CvArr *image,void *circle_storage,int method,double dp,

double min_dist,double param1,double param2,int min_radius,int max_radius)

image:输入8bit(灰度)图像,其内容可被函数所改变

circle_storage:检测到的圆存储仓,可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回)或者是包含圆参数的特殊类型的具有单行/单列的CV_32FC3型矩阵(CvMat*). 矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的圆。如果 circle_storage 是矩阵,而实际圆的数目超过矩阵尺寸,那么最大可能数目的圆被返回,每个圆由三个浮点数表示:圆心坐标(x,y)和半径.).

method:Hough 变换方式,目前只支持CV_HOUGH_GRADIENT, which is basically 21HT, described in [Yuen03].

dp:寻找圆弧圆心的累计分辨率,这个参数允许创建一个比输入图像分辨率低的累加器。(这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。

min_dist:该参数是让算法能明显区分的两个不同圆之间的最小距离。

param1:用于Canny的边缘阀值上限,下限被置为上限的一半。

param2:累加器的阀值。

The second method-specific parameter. In case of CV_HOUGH_GRADIENT it is accumulator threshold at the center detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned
first.

min_radius:最小圆半径。

max_radius:最大圆半径。

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数和像素深度不是同一个概念。

效果图:

检测圆的原图:


结果:


检测直线的原图:


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