PS. 因为csdn博客文章长度有限制,本文有部分内容被截掉了。
在OpenCV中文站点的wiki上有可读性更好、而且是完整的版本号,欢迎浏览。
OpenCV Wiki :《OpenCV 编程简单介绍(矩阵/图像/视频的基本读写操作)》
Introduction to programming with OpenCV
OpenCV编程简单介绍
Gady Agam
Department of Computer Science
January 27, 2006
Illinois Institute of Technology
-- URL: http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html#SECTION00040000000000000000
译: chenyusiyuan
-- January 26, 2010
-- http://blog.csdn.net/chenyusiyuan
摘要:
本文旨在帮助读者高速入门OpenCV,而无需阅读冗长的參考手冊。掌握了OpenCV的下面基础知识后,有须要的话再查阅相关的參考手冊。
文件夹
一 简单介绍
o OpenCV的特点
o 实用的学习资源
o 命名规则
o 编译建议
o 一个例程
二 GUI命令
o 窗体管理
o 输入处理
三 OpenCV主要的数据结构
o 图像数据结构
o 矩阵与向量
o 其他数据结构
四 图像处理
o 内存分配与释放
o 读取和写入图像
o 訪问图像像素
o 图像转换
o 画图指令
五 矩阵处理
o 内存分配与释放
o 訪问矩阵元素
o 矩阵/向量运算
六 视频处理
o 视频的帧捕捉
o 帧信息的读取与设置
o 保存视频文件
=================================================
一、简单介绍
1、OpenCV的特点
(1) 整体描写叙述
o OpenCV是一个基于C/C++语言的开源图像处理函数库
o 其代码都经过优化,可用于实时处理图像
o 具有良好的可移植性
o 能够进行图像/视频加载、保存和採集的常规操作
o 具有低级和高级的应用程序接口(API)
o 提供了面向Intel IPP高效多媒体函数库的接口,可针对你使用的Intel CPU优化代码,提高程序性能(译注:OpenCV 2.0版的代码已显著优化,无需IPP来提升性能,故2.0版不再提供IPP接口)
(2) 功能
o 图像数据操作(内存分配与释放,图像复制、设定和转换)
Image data manipulation (allocation, release, copying, setting, conversion).
o 图像/视频的输入输出(支持文件或摄像头的输入,图像/视频文件的输出)
Image and video I/O (file and camera based input, image/video file output).
o 矩阵/向量数据操作及线性代数运算(矩阵乘积、矩阵方程求解、特征值、神秘值分解)
Matrix and vector manipulation and linear algebra routines (products, solvers, eigenvalues, SVD).
o 支持多种动态数据结构(链表、队列、数据集、树、图)
Various dynamic data structures (lists, queues, sets, trees, graphs).
o 基本图像处理(去噪、边缘检測、角点检測、採样与插值、色彩变换、形态学处理、直方图、图像金字塔结构)
Basic image processing (filtering, edge detection, corner detection, sampling and interpolation, color conversion, morphological operations, histograms, image pyramids).
o 结构分析(连通域/分支、轮廓处理、距离转换、图像矩、模板匹配、霍夫变换、多项式逼近、曲线拟合、椭圆拟合、狄劳尼三角化)
Structural analysis (connected components, contour processing, distance transform, various moments, template matching, Hough transform, polygonal approximation, line fitting, ellipse fitting, Delaunay triangulation).
o 摄像头定标(寻找和跟踪定标模式、參数定标、基本矩阵预计、单应矩阵预计、立体视觉匹配)
Camera calibration (finding and tracking calibration patterns, calibration, fundamental matrix estimation, homography estimation, stereo correspondence).
o 运动分析(光流、动作切割、目标跟踪)
Motion analysis (optical flow, motion segmentation, tracking).
o 目标识别(特征方法、HMM模型)Object recognition (eigen-methods, HMM).
o 主要的GUI(显示图像/视频、键盘/鼠标操作、滑动条)
Basic GUI (display image/video, keyboard and mouse handling, scroll-bars).
o 图像标注(直线、曲线、多边形、文本标注)
Image labeling (line, conic, polygon, text drawing)
(3) OpenCV模块
o cv – 核心函数库
o cvaux – 辅助函数库
o cxcore – 数据结构与线性代数库
o highgui – GUI函数库
o ml – 机器学习函数库
2、实用的学习资源
(1) 參考手冊:
o /docs/index.htm (译注:在你的OpenCV安装文件夹内)
? 网络资源:
o 官方站点: http://www.intel.com/technology/computing/opencv/
o 软件下载: http://sourceforge.net/projects/opencvlibrary/
(2) 书籍:
o Open Source Computer Vision Library
by Gary R. Bradski, Vadim Pisarevsky, and Jean-Yves Bouguet, Springer, 1st ed. (June, 2006).
chenyusiyuan: 补充下面书籍
o Learning OpenCV - Computer Vision with the OpenCV Library
by Gary Bradski & Adrian Kaehler, O'Reilly Media, 1 st ed. (September, 2008).
o OpenCV教程——基础篇
作者:刘瑞祯 于仕琪,北京航空航天大学出版社,出版日期:200706
(3) 视频处理例程(在 /samples/c/):
o 颜色跟踪: camshiftdemo
o 点跟踪: lkdemo
o 动作切割: motempl
o 边缘检測: laplace
(4) 图像处理例程 (在 /samples/c/):
o 边缘检測: edge
o 图像切割: pyramid_segmentation
o 形态学: morphology
o 直方图: demhist
o 距离变换: distrans
o 椭圆拟合: fitellipse
3、OpenCV 命名规则
(1) 函数名:
cvActionTargetMod(...) Action = 核心功能(core functionality) (e.g. set, create) Target = 目标图像区域(target image area) (e.g. contour, polygon) Mod = (可选的)调整语(optional modifiers) (e.g. argument type)
(2) 矩阵数据类型:
CV_<bit_depth>(S|U|F)C<number_of_channels> S = 符号整型 U = 无符号整型 F = 浮点型 E.g.: CV_8UC1 是指一个8位无符号整型单通道矩阵, CV_32FC2是指一个32位浮点型双通道矩阵.
(3) 图像数据类型:
IPL_DEPTH_<bit_depth>(S|U|F) E.g.: IPL_DEPTH_8U 图像像素数据是8位无符号整型. IPL_DEPTH_32F图像像素数据是32位浮点型.
(4) 头文件:
#include <cv.h> #include <cvaux.h> #include <highgui.h> #include <ml.h> #include <cxcore.h> // 一般不须要,cv.h 内已包括该头文件
4、编译建议
(1) Linux:
g++ hello-world.cpp -o hello-world / -I /usr/local/include/opencv -L /usr/local/lib / -lm -lcv -lhighgui -lcvaux
(2) Windows:
在Visual Studio的‘选项’和‘项目’中设置好OpenCV相关文件的路径。
5、C例程
//////////////////////////////////////////////////////////////////////// // // hello-world.cpp // // 该程序从文件里读入一幅图像,将之反色,然后显示出来. // //////////////////////////////////////////////////////////////////////// #include <stdlib.h> #include <stdio.h> #include <math.h> #include <cv.h> #include <highgui.h> int main(int argc, char *argv[]) { IplImage* img = 0; int height,width,step,channels; uchar *data; int i,j,k; if(argc<2){ printf("Usage: main /n/7"); exit(0); } // load an image img=cvLoadImage(argv[1]); if(!img){ printf("Could not load image file: %s/n",argv[1]); exit(0); } // get the image data height = img->height; width = img->width; step = img->widthStep; channels = img->nChannels; data = (uchar *)img->imageData; printf("Processing a %dx%d image with %d channels/n",height,width,channels); // create a window cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE); cvMoveWindow("mainWin", 100, 100); // invert the image // 相当于 cvNot(img); for(i=0;i<height;i++) for(j=0;j<width;j++) for(k=0;k<channels;k++) data[i*step+j*channels+k]=255-data[i*step+j*channels+k]; // show the image cvShowImage("mainWin", img ); // wait for a key cvWaitKey(0); // release the image cvReleaseImage(&img ); return 0; }
=================================================
二、GUI 指令
1、窗体管理
(1) 创建和定位一个新窗体:
cvNamedWindow("win1", CV_WINDOW_AUTOSIZE); cvMoveWindow("win1", 100, 100); // offset from the UL corner of the screen
(2) 加载图像:
IplImage* img=0; img=cvLoadImage(fileName); if(!img) printf("Could not load image file: %s/n",fileName);
(3) 显示图像:
cvShowImage("win1",img);
该函数能够显示彩色或灰度的字节型/浮点型图像。字节型图像像素值范围为[0-255];浮点型图像像素值范围为[0-1]。彩色图像的三色元素按BGR(蓝-红-绿)顺序存储。
(4) 关闭窗体:
cvDestroyWindow("win1");
(5) 改变窗体大小:
cvResizeWindow("win1",100,100); // new width/heigh in pixels
2、输入处理
(1) 处理鼠标事件:
o 定义一个鼠标处理程序:
void mouseHandler(int event, int x, int y, int flags, void* param) { switch(event){ case CV_EVENT_LBUTTONDOWN: if(flags & CV_EVENT_FLAG_CTRLKEY) printf("Left button down with CTRL pressed/n"); break; case CV_EVENT_LBUTTONUP: printf("Left button up/n"); break; } }
x,y: 相对于左上角的像素坐标
event: CV_EVENT_LBUTTONDOWN, CV_EVENT_RBUTTONDOWN, CV_EVENT_MBUTTONDOWN,
CV_EVENT_LBUTTONUP, CV_EVENT_RBUTTONUP, CV_EVENT_MBUTTONUP,
CV_EVENT_LBUTTONDBLCLK, CV_EVENT_RBUTTONDBLCLK, CV_EVENT_MBUTTONDBLCLK,
CV_EVENT_MOUSEMOVE:
flags: CV_EVENT_FLAG_CTRLKEY, CV_EVENT_FLAG_SHIFTKEY, CV_EVENT_FLAG_ALTKEY,
CV_EVENT_FLAG_LBUTTON, CV_EVENT_FLAG_RBUTTON, CV_EVENT_FLAG_MBUTTON
o 注冊该事件处理程序:
mouseParam=5; cvSetMouseCallback("win1",mouseHandler,&mouseParam);
(2) 处理键盘事件:
o 实际上对于键盘输入并没有专门的事件处理程序.
o 按一定间隔检測键盘输入(适用于循环体中):
int key; key=cvWaitKey(10); // wait 10ms for input
o 中止程序等待键盘输入:
int key; key=cvWaitKey(0); // wait indefinitely for input
o 键盘输入的循环处理程序:
while(1){ key=cvWaitKey(10); if(key==27) break; switch(key){ case 'h': ... break; case 'i': ... break; } }
(3) 处理滑动条事件:
o 定义一个滑动条处理程序:
void trackbarHandler(int pos) { printf("Trackbar position: %d/n",pos); }
o 注冊该事件处理程序:
int trackbarVal=25; int maxVal=100; cvCreateTrackbar("bar1", "win1", &trackbarVal ,maxVal , trackbarHandler);
o 获取当前的滑动条位置:
int pos = cvGetTrackbarPos("bar1","win1");
o 设置滑动条位置:
cvSetTrackbarPos("bar1", "win1", 25);
=================================================
三、OpenCV的基本数据结构
(译注:OpenCV 1.1、1.2或2.0版本号中各数据结构的结构体元素有所调整,下面仅作參考)
1、图像数据结构
(1) IPL 图像:
IplImage |-- int nChannels; // 颜色通道数目 (1,2,3,4) |-- int depth; // 像素的位深: | // IPL_DEPTH_8U, IPL_DEPTH_8S, | // IPL_DEPTH_16U,IPL_DEPTH_16S, | // IPL_DEPTH_32S,IPL_DEPTH_32F, | // IPL_DEPTH_64F |-- int width; // 图像宽度(像素为单位) |-- int height; // 图像高度 |-- char* imageData; // 图像数据指针 | // 注意彩色图像按BGR顺序存储数据 |-- int dataOrder; // 0 - 将像素点不同通道的值交错排在一起,形成单一像素平面 | // 1 - 把全部像素同通道值排在一起,形成若干个通道平面,再把平面排列起来 | // cvCreateImage 仅仅能创建像素交错排列式的图像 |-- int origin; // 0 – 像素原点为左上角, | // 1 – 像素原点为左下角 (Windows bitmaps style) |-- int widthStep; // 相邻行的同列点之间的字节数 |-- int imageSize; // 图像的大小(字节为单位) = height*widthStep |-- struct _IplROI *roi;// 图像的感兴趣区域(ROI). ROI非空时对图像的 | // 处理仅限于ROI区域. |-- char *imageDataOrigin; // 图像数据未对齐时的数据原点指针 | // (须要正确地又一次分配图像内存 ) | // (needed for correct image deallocation) |-- int align; // 图像数据的行对齐: 4 or 8 byte alignment | // OpenCV 中无此项,採用widthStep取代 |-- char colorModel[4]; // 颜色模型 – OpenCV中忽略此项
2、矩阵与向量
(1) 矩阵:
CvMat // 2D 矩阵 |-- int type; // 元素类型 (uchar,short,int,float,double) 与标志 |-- int step; // 整行长度字节数 |-- int rows, cols; // 行、列数 |-- int height, width; // 矩阵高度、宽度,与rows、cols相应 |-- union data; |-- uchar* ptr; // data pointer for an unsigned char matrix |-- short* s; // data pointer for a short matrix |-- int* i; // data pointer for an integer matrix |-- float* fl; // data pointer for a float matrix |-- double* db; // data pointer for a double matrix
CvMatND // N-维矩阵 |-- int type; // 元素类型 (uchar,short,int,float,double) 与标志 |-- int dims; // 矩阵维数 |-- union data; | |-- uchar* ptr; // data pointer for an unsigned char matrix | |-- short* s; // data pointer for a short matrix | |-- int* i; // data pointer for an integer matrix | |-- float* fl; // data pointer for a float matrix | |-- double* db; // data pointer for a double matrix | |-- struct dim[]; // 各维信息 |-- size; // 元素数目 |-- step; // 元素间距(字节为单位)
CvSparseMat // N-维稀疏矩阵
(2) 一般矩阵:
CvArr* // 仅作为函数定义的參数使用, // 表明函数能够接受不同类型的矩阵作为參数, // 比如:IplImage*, CvMat* 甚至是 CvSeq*. // 矩阵的类型通过矩阵头的前4个字节信息来确定
(3) 标量:
CvScalar |-- double val[4]; //4D 向量
初始化函数:
CvScalar s = cvScalar(double val0, double val1=0, double val2=0, double val3=0); // Example: CvScalar s = cvScalar(20.0); s.val[0]=10.0;
注意该初始化函数的函数名与相应的结构体名称差点儿同名,区别仅在于函数名第一个字母是小写的,而结构体名第一个字母是大写的。它并非一个 C++ 构造函数。(译注:相似的还有 cvMat 与 CvMat、cvPoint 与 CvPoint 等等)
3、其他结构类型
(1) 点:
CvPoint p = cvPoint(int x, int y); CvPoint2D32f p = cvPoint2D32f(float x, float y); CvPoint3D32f p = cvPoint3D32f(float x, float y, float z);
E.g.:
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、图像的内存分配与释放
(1) 分配内存给一幅新图像:
IplImage* cvCreateImage(CvSize size, int depth, int channels);
size: cvSize(width,height);
depth: 像素深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F, IPL_DEPTH_64F
channels: 像素通道数. Can be 1, 2, 3 or 4.
各通道是交错排列的. 一幅彩色图像的数据排列格式例如以下:
b0 g0 r0 b1 g1 r1 ...
演示样例:
// Allocate a 1-channel byte image IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); // Allocate a 3-channel float image IplImage* img2=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
(2) 释放图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); cvReleaseImage(&img);
(3) 复制图像:
IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); IplImage* img2; img2=cvCloneImage(img1); // 注意通过cvCloneImage得到的图像 // 也要用 cvReleaseImage 释放,否则easy产生内存泄漏
(4) 设置/获取感兴趣区域ROI:
void cvSetImageROI(IplImage* image, CvRect rect); void cvResetImageROI(IplImage* image); vRect cvGetImageROI(const IplImage* image);
大多数OpenCV函数都支持 ROI.
(5) 设置/获取感兴趣通道COI:
void cvSetImageCOI(IplImage* image, int coi); // 0=all int cvGetImageCOI(const IplImage* image);
大多数OpenCV函数不支持 COI.
2、图像读写
(1) 从文件里读入图像:
IplImage* img=0; img=cvLoadImage(fileName); if(!img) printf("Could not load image file: %s/n",fileName);
支持的图像格式: BMP, DIB, JPEG, JPG, JPE, PNG, PBM, PGM, PPM,
SR, RAS, TIFF, TIF
OpenCV默认将读入的图像强制转换为一幅三通道彩色图像. 只是能够按下面方法改动读入方式:
img=cvLoadImage(fileName,flag);
flag: >0 将读入的图像强制转换为一幅三通道彩色图像
=0 将读入的图像强制转换为一幅单通道灰度图像
<0 读入的图像通道数与所读入的文件同样.
(2) 保存图像:
if(!cvSaveImage(outFileName,img)) printf("Could not save: %s/n", outFileName);
保存的图像格式由 outFileName 中的扩展名确定.
3、訪问图像像素
(1) 如果你要訪问第k通道、第i行、第j列的像素。
(2) 间接訪问: (通用,但效率低,可訪问随意格式的图像)
o 对于单通道字节型图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); CvScalar s; s=cvGet2D(img,i,j); // get the (i,j) pixel value printf("intensity=%f/n",s.val[0]); s.val[0]=111; cvSet2D(img,i,j,s); // set the (i,j) pixel value
o 对于多通道字节型/浮点型图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3); CvScalar s; s=cvGet2D(img,i,j); // get the (i,j) pixel value printf("B=%f, G=%f, R=%f/n",s.val[0],s.val[1],s.val[2]); s.val[0]=111; s.val[1]=111; s.val[2]=111; cvSet2D(img,i,j,s); // set the (i,j) pixel value
(3) 直接訪问: (效率高,但easy出错)
o 对于单通道字节型图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); ((uchar *)(img->imageData + i*img->widthStep))[j]=111;
o 对于多通道字节型图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3); ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G ((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
o 对于多通道浮点型图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3); ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G ((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R
(4) 基于指针的直接訪问: (简单高效)
o 对于单通道字节型图像:
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); int height = img->height; int width = img->width; int step = img->widthStep/sizeof(uchar); uchar* data = (uchar *)img->imageData; data[i*step+j] = 111;
o 对于多通道字节型图像:
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3); int height = img->height; int width = img->width; int step = img->widthStep/sizeof(uchar); int channels = img->nChannels; uchar* data = (uchar *)img->imageData; data[i*step+j*channels+k] = 111;
o 对于多通道浮点型图像(如果图像数据採用4字节(32位)行对齐方式):
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3); int height = img->height; int width = img->width; int step = img->widthStep/sizeof(float); int channels = img->nChannels; float * data = (float *)img->imageData; data[i*step+j*channels+k] = 111;
(5) 基于 c++ wrapper 的直接訪问: (更简单高效)
o 首先定义一个 c++ wrapper ‘Image’,然后基于Image定义不同类型的图像:
template<class T> class Image { private: IplImage* imgp; public: Image(IplImage* img=0) {imgp=img;} ~Image(){imgp=0;} void operator=(IplImage* img) {imgp=img;} inline T* operator[](const int rowIndx) { return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));} }; typedef struct{ unsigned char b,g,r; } RgbPixel; typedef struct{ float b,g,r; } RgbPixelFloat; typedef Image<RgbPixel> RgbImage; typedef Image