OpenCV简介
OpenCV的特征 OpenCV具有以下的特征: (1)开源计算机视觉采用C/C++编写。 (2)使用目的是开发实时应用程序。 (3)独立与操作系统、硬件和图形管理器。 (4)具有通用的图象/视频载入、保存和获取模块。 (5)具有底层和高层的应用开发包。
OpenCV的功能 应用OpenCV能够实现以下功能: (1)对图象数据的操作,包括分配、释放、复制和转换数据。 (2)对图象和视频的输入输出,指文件和摄像头作为输入,图象和视频文件作为输出。 (3)具有对距陈和向量的操作以及线性代数的算法程序,包括距阵、解方程、特征值以及奇异值。 (4)可对各种动态数据结构,如列表、队列、集合、树和图等进行操作。 (5)具有基本的数字图象处理能力,如可进行滤波、边缘检测、角点检测、采样与差值、色彩转换、形态操作、直方图和图象金字塔等操作。
(6)可对各种结构进行分析,包括连接部件分析、轮廓处理、距离变换、各种距的计算、模板匹配、Hongh变换、多边形逼近、直线拟合、椭圆拟合和Delaunay三角划分等。 (7)对摄像头的定标,包括发现与跟踪定标模式、定标、基本矩阵估计、齐次矩阵估计和立体对应。 (8)对运动的分析,如对光流、运动分割和跟踪的分析。 (9)对目标的识别,可采用特征法和隐马尔科夫模型(HMM)法。 (10)具有基本的GUI功能,包括图像与视频显示、键盘和鼠标事件处理及滚动条等。 (11)可对图像进行标注,如对线、二次曲线和多边形进行标注,还可以书写文字(目前之支持中文)。
OpenCV模块 OpenCV包括以下几个模块,其具体功能是: (1)CV主要的OpenCV函数。 (2)CVAUX辅助的(实验性)OpenCV函数。 (3)CXCORE数据结构与线性代数支持。 (4)HIGHGUI图像界面函数。 (5)ML机器学习,包括模式分类和回归分析等。 (6)CVCAM负责读取摄像头数据的模块(在以后版本中,当HIGHGUI模块中加入Direct Show支持后,此模块将被废除)。
学习资源 目前,OpenCV方面的资源已经很多,当然最简单的方法还是在网上搜索,比如在Google搜索引擎(http://www.google.com) 中输入”OpenCV”即可进行相关查找,这里给出一些信息作为参考。 (1)参考手册 英文 请打开文件<opencv-root>/docs/index.htm进行查询。 中文 请打开网页http://www.opencv.org.cn/进行查阅。 (2)网络资源 官方网站 网址是http://www.intel.com/technology/computing/opencv/。 中文官方网站 网址是http://www.opencv.org.cn/。 软件下载 网址是http://sourceforge.net/projects/opencvlibrary/。 (3)书籍 请阅读北京航空航天大学出版社出版的《OpenCV教程》。
OpenCV基础 1.OpenCV命名规则 2.基本数据结构 3.矩阵的使用与操作 4.图象的使用与操作 5.数据结构与数据操作
OpenCV命名规则 1.函数命名规则 2.矩阵数据类型 通用函数名为 cvActionTargetMod(…) 其中,Action表示核心函数(比如:Set,Create); Target表示目标图像区域(比如:轮廓,多边形); Mod表示可选变种(比如:变量类型)。 2.矩阵数据类型 通用矩阵数据类型为 CV_<bit_depth><S|U|P)C<number_of_channels> 其中,S表示带符号整数; U表示无符号整数; F表示浮点数。 例如:CV_8UC1表示8位无符号单通道矩阵; CV_32FC2表示32位浮点数双通道矩阵。
3.通用图像数据类型为 IPL_DEPTH_<bit_depth>(S|U|F) 其中,S,U,F的意义同矩阵数据类型。 例如:IPL_DEPTH_8U表示8位无符号整数图像 IPL_DEPTH_32F表示32位浮点数图像 4.头文件 OpenCV的头文件有如下几个: #include<cv.h> #include<cvaux.h> #include<highgui.h> #include<cxcore.h>//不是必须的,它包含在cv.h中
基本的数据结构 1.图像结构 2.矩阵的与向量结构 3.其他的数据结构 (1)点的表示 (2)长方形维数的表示 (3)有偏移量的长方形表示
图像结构 Opencv中的所有图像都采用同一个结构IplImage,该图像结构说明参考后面的PPT。实际上, IplImage是借鉴于Intel公司最早发布的IPP图像处理开发包中的定义;但由于IPP并非开源项目,因此对于OpenCV基本采用这个较复杂的图像结构,其版权方面的问题还有待研究。
矩阵与向量结构 矩阵与向量结构的 和说明也可以参见后面的PPT。OpenCV采用一种独特的结构CvArr*。 该结构是一个通用数组的表现形式,用做函数的参数,说明函数在调用该参数时接受多种类型的输入数据形式,比如IplImage*,CvMat*甚至CvSeq*。具体的数组形式在运行时应根据实际情况来分析。
其他数据结构 (1)点的表示: CvPoint p=cvPoint(int x,int y); CvPoint2D32f p=cvPoint2D32f (float x,float y); CvPoint3D32f p=cvPoint3D32f (float x,float y,float z); Eg: p.x=5.0 p.y=5.0
(2)长方形的维数表示: CvSize r=cvSize(int width,int height); CvSize2D32f r=cvSize2D32f(float width,float height); (3)有便移量的长方形表示 Cvrect r=cvrect (int x,int y,int width,int height);
矩阵的使用和操作 1.分配和释放矩阵 OpenCV有一个矩阵操作的C语言的接口,另外也有一些C++语言的矩阵操作接口,通常C++语言接口更方便,且同样有效。 在OpenCV中,向量是被当成是行或列为1的矩阵,并且矩阵在内存中的存储方式是按行存储,且每行按4字节对齐。
(1)分配矩阵 分配矩阵的原型为 CvMat * cvCreateMat(int rows,int cols,int type); 其中,type 表示矩阵元素的类型,可以用如下形式表达 CV_<bit_depth>(S|U|F)C<number_of_channels > Eg: CvMat* M = cvCreateMar(4,4CV_32FC1); (2)释放矩阵 释放矩阵的函数原型为 Void cvReleaseMat(CvMat**); Eg: CvMat* M = cvCreateMat(4,4,CV_32FC1); cvReleaseMat(&M);
(3)复制矩阵 复制矩阵的函数原型为 CvMat* cvCloneMat(cvMat); Eg: CvMat* M1 = cvCreateMat(4,4,CV_32FC1); CvMat* M2; M2 = cvCloneMat(M1); (4)初始化矩阵 可按如下方法初始化一个3行4列矩阵。 Double a[] = {1,2,3,4, 5,6,7,8, 9,10,11,12}; CvMat Ma = cvMat(3,4,CV_64FC1,a); 或者: CvMat Ma; CvInitMatHeader(&Ma,3,4,CV_64FC1,a);
(5)初始化单位矩阵 可按如下方法初始化一个单位矩阵。 CvMat* M = cvCreateMat(4,4,CV_32FC1); cvSetIdentity(M);
访问矩阵元素 直接访问 下例是对元素M(i,j)进行数据赋值和读取。 cvmSet(M,I,j,2,0); //set M(i,j) t = cvmGet(M,i,j); //Get M(i,j) (2)已知对齐方式的直接访问 下例是对32位对齐方式存储的元素进行赋值。 CvMat* M = cvCreateMat(4,4CV_32FC1); int n = M->cots; float * data = M->data.fl; Data[i*n+j] = 3.0;
(3) 未知对齐方式的直接访问 下例是对某种对齐方式存储的元素进行赋值。 CvMat* M = cvCreateMat(4,4CV_32FC1); int step = M->step/sizeof(float); float * data = M->data.fl; (data + i*step) [i]= 3.0; (4)直接访问一个已初始化的矩阵 下例对已出吃化的矩阵a进行数据赋值。 Double a[16]; CvMat Ma = cvM<at(3,4,CV_64FC1,a); A[i*4+j] = 2.0; //Ma(i,j) = 2.0;
矩阵的向量操作 对矩阵和向量的操作分以下几种类型。 (1)矩阵与矩阵之间的操作 此类型包括: CvMat* Ma,*Mb,*Mc; cvAdd(Ma,Mb,Mc); //Ma+Mb->Mc cvSub(Ma,Mb,Mc); //Ma-Mb->Mc cvMatMul(Ma,Mb,Mc); //Ma*Mb->Mc
(2)矩阵元素之间的操作 此类型包括: CvMat* Ma,*Mb,*Mc; cvMul(Ma,Mb,Mc); //Ma.*Mb->Mc cvDiv(Ma,Mb,Mc); //Ma./Mb->Mc cvAddS(Ma,cvScalar(-10.0),Mc); //Ma.-10->Mc (3)向量乘法操作 double va[]={1,2,3}; double vb[]={0,0,1}; double vc[3]; CvMat Va = cvMat(3,1,CV_64FC1,va); CvMat Vb = cvMat(3,1,CV_64FC1,vb); CvMat Vc = cvMat(3,1,CV_64FC1,vc); double res = cvDotProduct(&Va,&Vb); // dot product: Va.Vb->res cvCrossProduct(&Va,&Vb,&Vc); //cross product:Va*Vb->Vc 注意VA,VbVc在叉积中必须是3个元素的向量。
(4)单个矩阵的操作 此类型包括: CvMat *Ma,*Mb; cvTranspose(Ma,Mb); CvScalar t = cvTrace(Ma0; Double d = cvDet(Ma); cvInvert(Ma,Mb); (5)非齐次线性系统求解操作 CvMat* A = cvCreateMat(3,3,CV_32FC1); CvMat* x = cvCreateMat(3,3,CV_32FC1); CvMat* b = cvCreateMat(3,3,CV_32FC1); cvSolve(&A,&b,&x);
(6)特征值分析(对称矩阵)操作 此类型包括: CvMat* A= cvCreateMat(3,3,CV_32FC1); CvMat* E= cvCreateMat(3,3,CV_32FC1); CvMat* I= cvCreateMat(3,3,CV_32FC1); cvEigenVV(&A,&E,&I); (7)奇异值分解操作 CvMat* U= cvCreateMat(3,3,CV_32FC1); CvMat* D= cvCreateMat(3,3,CV_32FC1); CvMat* V= cvCreateMat(3,3,CV_32FC1); cvSVD(A,DU,V,CV_SVD_U_T|CV_SVD_V_T);
图象的使用与操作 1.分配和释放图象 (1)分配一幅图象 分配图象的函数原型为 IplImage* cvCreateImage(CvSize size,int depth,int channels); Eg1: IplImage* img1=cvCreateImage(cvSize(640,480), IPL_DEPTH_8U,1); Eg2: IplImage* img2=cvCreateImage(cvSize(640,480), IPL_DEPTH_32F,3);
(2)释放一幅图象 释放图象的函数原型为 Void cvReleaseImage(IplImage**); Eg: IplImage* img=cvCreateImage(cvSize(640,480), IPL_DEPTH_8U,1); cvReleaseImage(&img); (3)复制一幅图象 复制图象的函数原型为 IplImage* cvCloneImage(IplImage*); IplImage* img1=cvCreateImage(cvSize(640,480), IplImage *img2; Img2 = cvCloneImage(img1);
(4)设置或得到感兴趣区域ROI 函数原型分别为 Void cvSetImageROI(IplImage* image,CvRec rect); Void cvResetImageROI(IplImage* image); vRect cvGetImageROI(IplImage* image); 大多数OpenCv函数都支持ROI。 (5)设置或得到感兴趣区域COI Void cvSetImageCOI(IplImage* image,int coi);//0=all Int cvGetImageCOI(IplImage* image); 大多数OpenCv函数都支持COI。
图象的读写 (1)从文件读区图象 IplImage* img = 0; img = cvLoadImage(fileName); If(!img) printf(“Couldnot load image file:%s\n”,fileName); 默认情况下: img = cvLoadImage(fileName,flag); flag取不同的值表示的意义不同 (2)向文件中写图象 If(!cvSaveImage(outFileName,img)) printf(“Could not save:%s\n”,outFileName); 输出的图象文件格式依赖于文件的扩展名。
访问图像像素 (坐标是从0开始的,并且是相对图像原点的位置。图像原点或者是左上角 (img->origin=IPL_ORIGIN_TL) 或者是左下角 (img->origin=IPL_ORIGIN_BL) ) 假设有 8-bit 1-通道的图像 I (IplImage* img): I(x,y) ~ ((uchar*)(img->imageData + img->widthStep*y))[x]
假设有 8-bit 3-通道的图像 I (IplImage* img): I(x,y)blue ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3] I(x,y)green ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+1] I(x,y)red ~ ((uchar*)(img->imageData + img->widthStep*y))[x*3+2] 例如,给点 (100,100) 的亮度增加 30 ,那么可以这样做: CvPoint pt = {100,100}; ((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3] += 30; ((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+1] += 30; ((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3+2] += 30; 或者更高效地: CvPoint pt = {100,100}; uchar* temp_ptr = &((uchar*)(img->imageData + img->widthStep*pt.y))[pt.x*3]; temp_ptr[0] += 30; temp_ptr[1] += 30; temp_ptr[2] += 30;
假设有 32-bit 浮点数, 1-通道 图像 I (IplImage* img): I(x,y) ~ ((float*)(img->imageData + img->widthStep*y))[x]
现在,一般的情况下,假设有 N-通道,类型为 T 的图像: I(x,y)c ~ ((T*)(img->imageData + img->widthStep*y))[x*N + c] 你可以使用宏 CV_IMAGE_ELEM( image_header, elemtype, y, x_Nc ) I(x,y)c ~ CV_IMAGE_ELEM( img, T, y, x*N + c ) 也有针对各种图像(包括 4 通道图像)和矩阵的函数(cvGet2D, cvSet2D), 但是它们非常慢。
数据结构与数据操作 1 CvPoint 2 CvPoint2D32f 3 CvPoint3D32f 4 CvSize 5 CvSize2D32f 6 CvRect 7 CvScalar 8 CvTermCriteria 9 CvMat 10 CvMatND 11 CvSparseMat 12 IplImage 13 CvArr
CvPoint 二维坐标系下的点,类型为整型 typedef struct CvPoint { int x; /* X坐标, 通常以0为基点 */ int y; /* y坐标, 通常以0为基点 */ } CvPoint; /* 构造函数 */ inline CvPoint cvPoint( int x, int y ); /* 从 CvPoint2D32f类型转换得来 */ inline CvPoint cvPointFrom32f( CvPoint2D32f point )
CvPoint2D32f 二维坐标下的点,类型为浮点 typedef struct CvPoint2D32f { float x;/* X坐标, 通常以0为基点*/ float y; /* Y坐标, 通常以0为基点*/ } CvPoint2D32f; /* 构造函数 */ inline CvPoint2D32f cvPoint2D32f( double x, double y ); /* 从CvPoint转换来 */ inline CvPoint2D32f cvPointTo32f( CvPoint point );
CvPoint3D32f 三维坐标下的点,类型为浮点 typedef struct CvPoint3D32f { float x; /* x-坐标, 通常基于0 */ float y; /* y-坐标, 通常基于0 */ float z; /* z-坐标, 通常基于0 */ } CvPoint3D32f; /* 构造函数 */ inline CvPoint3D32f cvPoint3D32f( double x, double y, double z );
CvSize 矩形框大小,以像素为精度 typedef struct CvSize { int width; /* 矩形宽 */ int height; /* 矩形高 */ } CvSize; /* 构造函数 */ inline CvSize cvSize( int width, int height );
CvSize2D32f 以亚像素精度标量矩形框大小 typedef struct CvSize2D32f { float width; /* 矩形宽 */ float height; /* 矩形高 */ } CvSize2D32f; /* 构造函数*/ inline CvSize2D32f cvSize2D32f( double width, double height ); { CvSize2D32f s; s.width = (float)width; s.height = (float)height; return s; }
CvRect 矩形框的偏移和大小 typedef struct CvRect { int x; /* 方形的最左角的x-坐标 */ int y; /* 方形的最上或者最下角的y-坐标 */ int width; /* 宽 */ int height; /* 高 */ } CvRect; /* 构造函数*/ inline CvRect cvRect( int x, int y, int width, int height );
CvScalar 可存放在1-,2-,3-,4-TUPLE类型的捆绑数据的容器 typedef struct CvScalar { double val[4] } CvScalar; /* 构造函数:用val0初始化val[0]用val1初始化val[1], 以此类推*/ inline CvScalar cvScalar( double val0, double val1=0, double val2=0, double val3=0 ); /* 构造函数:用val0123初始化所有val[0]...val[3] */ inline CvScalar cvScalarAll( double val0123 ); /* 构造函数:用val0初始化val[0],用0初始val[1],val[2],val[3] */ inline CvScalar cvRealScalar( double val0 ); http://doc.blueruby.mydns.jp/opencv/classes/OpenCV/CvScalar.html
CvTermCriteria 迭代算法的终止准则 #define CV_TERMCRIT_ITER 1 #define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER #define CV_TERMCRIT_EPS 2 typedef struct CvTermCriteria { int type; /* CV_TERMCRIT_ITER 和CV_TERMCRIT_EPS二值之一,或者二者的组合 */ int max_iter; /* 最大迭代次数 */ double epsilon; /* 结果的精确性 */ } CvTermCriteria; /* 构造函数 */ inline CvTermCriteria cvTermCriteria( int type, int max_iter, double epsilon ); 在满足max_iter和epsilon的条件下检查终止准则并将其转换使得type=CV_TERMCRIT_ITER+CV_TERMCRIT_EPS */ CvTermCriteria cvCheckTermCriteria( CvTermCriteria criteria, double default_eps, int default_max_iters );
CvMat多通道矩阵
CvMatND 多维、多通道密集数组
CvSparseMat 多维、多通道稀疏数组
IplImage IPL 图像头
IplImage结构来自于 Intel Image Processing Library(是其本身所具有的)。OpenCV 只支持其中的一个子集: alphaChannel 在OpenCV中被忽略。 colorModel 和channelSeq 被OpenCV忽略。OpenCV颜色转换的唯一函数 cvCvtColor把原图像的颜色空间的目标图像的颜色空间作为一个参数。 dataOrder 必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取),然而平面图像的被选择通道可以被处理,就像COI(感兴趣的通道)被设置过一样。 align 是被OpenCV忽略的,而用 widthStep 去访问后继的图像行。 不支持maskROI 。处理MASK的函数把他当作一个分离的参数。MASK在 OpenCV 里是 8-bit,然而在 IPL他是 1-bit。 tileInfo 不支持。 BorderMode和BorderConst是不支持的。每个 OpenCV 函数处理像素的邻近的像素,通常使用单一的固定代码边际模式。 除了上述限制,OpenCV处理ROI有不同的要求。要求原图像和目标图像的尺寸或 ROI的尺寸必须(根据不同的操作,例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除以2 ±1)精确匹配,而IPL处理交叉区域,如图像的大小或ROI大小可能是完全独立的。 [编辑]
CvArr 不确定数组 typedef void CvArr; CvArr* 仅仅是被用于作函数的参数,用于指示函数接收的数组类型可以不止一个,如 IplImage*, CvMat* 甚至 CvSeq*. 最终的数组类型是在运行时通过分析数组头的前4 个字节判断。 取自http://www.opencv.org.cn/index.php/Cxcore%E5%9F%BA%E7%A1%80%E7%BB%93%E6%9E%84 相关函数的运用见实例(PPT)