C语言中cvpoint后运行出错,OpenCV学习笔记

OpenCV是一个基于C/C++语言的开源图像处理函数库,其特点有:

1.

代码都是经过优化,可以用于实时处理图像;

2.

具有良好的可移植性;

3.

可以进行图像/视频的载入、保存和采集的常规操作;

4.

具有低级和高级的API;

5.

提供了面向Intel

IPP高效多媒体函数的接口,可以针对使用的IntelCPU优化代码,提高程序新跟那个(OpenCV自从2.0版本以后已经不需IPP,所以不再提供相关的接口)

【OpenCV功能】

1.

图像数据操作(内存分配与释放,图像复制,设定和转换);

2.

图像/视频的输入和输出(支持文件或者摄像头的输入,图像/视频文件的输出;

3.

矩阵/向量的数据操作以及线性代数运算(矩阵乘积、矩阵方程求解、特征值、

奇异值分解);

4.

支持多种动态数据结构(链表、队列、数据集、树、图);

5.

基本图像处理(去噪、边缘检测、角点检测、采样与插值、色彩变换、形态学处理、直方图、图像金字塔结构);

6.

结构分析(连通域/分支、轮廓处理、距离转换、图像矩、模板匹配、霍夫变幻、多项式逼近、曲线拟合、椭圆拟合、狄劳尼三角化;

7.

摄像头定标(寻找和跟踪定标模式、参数定标、基本矩阵估计、单位矩阵估计、立体视觉匹配);

8.

运动分析(光流、动作分割、目标跟踪);

9.

目标识别(特征方法、HMM模型);

10.

基本的GUI(显示图像/视频、键盘/鼠标操作、滑动条);

11.

图像标注(直线、曲线、多边形、文本标注)。

【OpenCV基本模块】

cv——核心函数库

cvaux——辅助函数库

cxcore——数据结构与线性代数库

highgui——GUI函数库

ml——机器学习函数库

【OpenCV命名规则】

A.

函数名

cvActionTargetMod(…)

Action=核心功能(core functionality)(eg:set,create)

Target=目标图像区域(target image area)

(eg:contour,polygon)

Mod=(可选的)调整语(optional modifiers)(eg:argument type)

B.

矩阵数据类型

CV_(S|U|F)

S=符号整形

U=无符号整形

F=浮点型

(eg:CV_8UC1是指一个8位无符号整形单通道矩阵,

CV_32FC2是指一个32位浮点型双通道矩阵)

C.

图像数据类型

IPL_DEPTH(S|U|F)

Eg:

IPL_DEPTH_8U图像像素数据是8位无符号整形。

IPL_DEPTH_32P图像像素数据是32位浮点型。

【头文件包含】

#include——核心函数库

#include——辅助函数库

#include——机器学习库

#include——GUI函数库

#include//一般不需要,因为cv.h已经包含该头文件

数据结构与线性代数库

【OpenCV中的基本数据结构】

1.

图像数据结构

A.

IPL图像

IPLImage

Int nChannels

颜色通道的数目(1,2,3,4)

Int depth

像素的位深

IPL_DEPTH_8U

IPL_DEPTH_16S

IPL_DEPTH_32F

IPL_DEPTH_64F

Int width

图像宽度(像素为单位)

Int height

图像高度

Char * imageData

图像数据指针

彩色图像按照BGR的顺序存储数据

Int dataOrder

0——将像素点不同的通道的值交错排在一起,形成单一的像素平面

1——把所有的像素同通道值排在一起,形成若干个通道平面,再把平面排列起来

Int origin

0 –像素原点=左上角

1 –像素原点为左下角(windows bitmaps

Style)

Int widthStep

相邻行的同列点之间的字节数

Int imageSize

图像的大小(字节为单位)=height*widthStep

Struct _IplROI *roi

图像的感兴区域(ROI),ROI非空的会后对图像的处理仅限于ROI区域

Char *imageDataOrigin

图像数据未对齐时候的数据原点指针

(需要正确地重新分配图像内存)

Int align

图像数据的行对齐 :4 or 8 byte

alignment

Char colorModel[4]

颜色模型(OpenCV中没有此项)

2.

矩阵:

A.2D矩阵

CvMat(2D矩阵)

int type

元素类型

int step

整行长度字节数

Int rows,cols

行、列数

int height,width

矩阵高度、宽度、与rows,cols对应

Union data

Uchar *ptr

指向unsigned

char矩阵的指针

Short *s

指向short矩阵的指针

Int * i

指向整形矩阵的指针

Float *fl

指向浮点型矩阵的指针

Double *db

指向双精度浮点型矩阵的指针

B.

N维矩阵

CvMatND(N-维矩阵)

Int type

元素类型(uchar,short,int,float,double)

Int dims

矩阵维数

Union data

Uchar *Ptr

Short *s

Int *

I;

Float *fl;

Double *db

Struct dim[]

各维信息

Size 元素数目

Step 元素间距 字节为单位

C.

CvSparseMat

//N-维稀疏矩阵

D.一般矩阵

CvArr* //仅仅作为函数定义的参数使用

//表明函数可以接受不同类型的矩阵作为参数

//矩阵的类型通过矩阵头部的前4个字节信息来确定

E.标量

CvScalar double val[4];

//4D向量

初始化:

CvScalar s=cvScalar(double val0,double val1=0, double val2=0,

double val3=0);

CvScalar s=cvScalar(20.0);

s.val[0]=20.0

3.

CvPoint p=cvPoint(int x,int

y); //整形二维点

CvPoint 2D32f

p=cvPoint2d32f(float x,float y);//浮点型二维点

Cvpoint3D32f

p=cvPoint3D32f(float x,float y,float z);//浮点型三维点

4.

矩形框大小

(以像素为精度)

cvSize=

cvSize(int width,int height)

CvSize2D32f r=cvSize2D32f(float

width,float height);

5.

矩形框的偏置和大小:

CvRect r=cvRect(int x,

int y, int width , int

height)

【OpenCV的图像读写】

1.

从文件中读入图像:

OpenCV默认将读入的图像强制转换为一副三通道彩色图像:

IplImage

* img=0;

Img=cvLoadImage(fileName);

If(!img)

printf(“could

not load image file :%

s\n”,filename);

OpenCV支持图像格式有:BMP、DIB、JPEG、JPE、PNG、PBM、PGM、PPM、SR、RAS、TIFF、TIF

可以按照如下的方式修改读入的方式:

img=cvLoadImage(filename,flag)

flag: >0 将读入的图像强制转换为一副三通道彩色图像

=0 将读入的图像强制转换为一副单通道灰度图像

<0 将读入的图像通道数与所读入的文件相同

2.

保存图像

If(!cvSaveImage(outFileName,img)) printf(“could not save

:%s\n”,outFileName)

保存的图像的格式由outFileName中的扩展名确定

【OpenCV访问图像像素】

假设现在需要访问第k通道、第i行,第j列的像素,可以有如下的间接和直接访问两种方式:

1.

间接访问(单通道字节型图像)

IplImage *img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);

CvScalar s;

S=cvGet2D(img,i,j);//注意本函数中坐标参数的顺序与其它的openCV函数坐标参数顺序恰好相反,本函数中的i代表height,而j代表width

Printf(“intensity=%f\n”,s.val[0]);

s.val[0]=111;

cvSet2D(img,I,j,s); //设置img(J,i)位置的像素值为s

间接访问(多通道字节型/浮点型图像)

IplImage *

img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3)

cvScalar s;

s=cvGet2D(img,I,j) //得到(j,i)位置的像素值

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)//设置img的(j,i)位置的像素的值为s

2.

直接访问(效率很高,但是很容易出错)

单通道字节型图像:

IplImage

*img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);

((uchar*

)(img->imageData+i*img->widthstep))[j]=111;

多通道字节型图像:

IplImage *

img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);

((uchar*)

(img->imageData+i*img->widthStep))[j*img->nChannels+0]=111;

((ucahr

*)(img->imageData+i*img->widthStep))[j*img->nChannels+1]=112;

((uchar

*)(img->imageData+i*img->widthStep))[j*img->nChannels+2]=113;

多通道浮点型图像:

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

3.

基于指针的直接访问

单通道字节型图像:

IplImage

*img=cvCreateImage(cvSIze(640,480),IPL_DEPTH_8U,1)

Int

height =img->height;

Int

width =img->width;

Int

Step =img->widthStep;

Uchar

*data =(uchar

*)img->imageData

Data[i*setp+j]=111;

多通道浮点型图像(假设图像数据采用4字节(32位)行对齐方式)

IplImage

*img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3)

int height =img->height;

int width =img->width;

int step =img->widthStep;

int channels =img->nChanns

float

*data =(float *)img->imageData

data[i*step+j*Channels+k]=111

4.

基于C++

wrapper的直接访问(更加简单高效)

首先定义一个C++ wrapper

‘Image’,然后基于Image定义不同类型的图像:

Template 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 RgbImage;

typedef Image RgbImageFloat;

typedef Image BwImage;

typedef image BwImageFloat;

对于单通道字节型图像:

IplImage *img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1)

BwImage imgA(img);

imgA[i][j]=111;

对于多通道字节型图像:

IplImage *

img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);

RgbImage imgA(img);

imgA[i][j].b=111;

imgA[i][j].g=111;

imgA[i][j].r=111;

对于多通道浮点型图像

IplImage

*img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3)

RgbImageFloat

imgA(img);

imgA[i][j].b=111;

imgA[i][j].g=111;

imgA[i][j].r=111;

【图像转换】

1)

字节型图像的灰度——彩色转换

cvConcertImage(src,dst,flags=0)

src=float/byte grayscale/color

image

dst=byte grayscale/color

imae

flags=CV_CVTIMG_FLIP //垂直翻转图像

CV_CVTIMG_SWAP_RB //置换R和B通道

2)

彩色图像->灰度图像

cvCvtColor(cimg,gimg,CV_BGR2GRAY)//cimg->gimg

//使用直接转换

for(i=0;iheight;i++)

for(j=0;jwidth;j++)

gimgA[i][j]=(uchar)(cimgA[i][j].b*0.114+ //直接利用转换关系将图像用灰阶表示

cimgA[i][j].g*0.587+

cimgA[i][j].r*0.299);

3)

不同彩色空间之间的转换

cvCvColor(src,dst,code)

code=CV_2

/=RGB,BGR,GRAY,HSV,YCrCb,XYZ,Lab,Luv,HLS

【矩阵处理】

1.

内存的分配与释放

因为OpenCV使用C语言来进行矩阵操作,但是用C++的替代方案可以更加高效地完成操作。

在OpenCV中向量被当做是有一个维数为1的N维矩阵。

矩阵按照行—行方式存储,每行4byte(32bit)对齐。

2.

为新的矩阵分配内存

CvMat

*cvCreateMat(int rows, int cols , int type);

其中Type是矩阵元素的类型:

CV(S|U|F)C方式指定

Eg:

CvMat *

M=cvCreateMat(4,4,CV_32FC1);

3.

释放矩阵内存

CvMat *M=cvCreateMat(4,4,CV_32FC1);

cvReleaseMat(&M);

4.

复制矩阵

CvMat

*M1=cvCreateMat(4,4,CV_32FC1);

cvMat *M2;

M2=cvCloneMat(M1);

5.

初始化矩阵

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);

6.

初始化矩阵为单位矩阵

CvMat

*M=cvCreateMat(4,4,CV_32FC1);

cvSetIdentity(M);

7.

访问矩阵元素

假设我们现在需要访问一个2D浮点型矩阵的第(i,j)个单元

1.

间接访问

cvmSet(M,I,j,2.0);

//设置M的(I,j)位置的值为2.0

t=cvmGet(M,I,j)

2.

直接访问(假设矩阵按照4字节对齐)

CvMat

*M=cvCreateMat(4,4,CV_32FC1);

Int

n =M->cols;

Float *data=

M->data.f1;

Data[i*n+j]=3.0;

3.

直接访问(当数据的对齐可能存在间隙的时候)

CvMat

*M=cvCreateMat(4,4,CV_32FC1);

Int

step=M->Setp/sizeof(float);

Float

*data=M->data.f1;

(data+i*step)[j]=3.0;

4.

对于初始化后的矩阵进行直接访问

double a[16];

CvMat

Ma=cvMat(3,4,CV_64FC1,a);

a[i*4+j]=2.0

【基本运算】

1.

矩阵之间的运算

CvMat *Ma,*Mb,*Mc;

cvAdd(Ma,Mb,Mc);

cvSub(Ma,Mb,Mc);

cvMatMul(Ma,Mb,Mc);

2.

矩阵之间的元素级运算

CvMat *Ma,*Mb,*Mc;

cvMul(Ma,Mb, Mc);

cvDiv(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);//向量点乘

cvCrossProduct(&Va,&Vb,&vc);//向量叉乘

4.

单一矩阵的运算

CvMat * Ma,* Mb;

cvTranspos(Ma,Mb);//转置transpose(Ma)->Mb(转置不能返回给Ma本身

CvScalar

t=cvTrace(Ma);

cvInvert(Ma,Mb);//逆矩阵

inv(Ma)->Mb

5.

非齐次线性方程求解

cvMat

*A=cvCreateMat(3,3,CV_32FC1);

cvMat

*x=cvCreateMat(3,1,CV_32FC1);

CvMat

*b=cvCreateMat(3,1,CV_32FC1);

cvSolve(&A,&b,&x); //solve(Ax=b)for  x

6.

特征值与特征向量(矩阵为方阵)

CvMat

*A=cvCreateMat(3,3,CV_32FC1);

CvMat

*E=cvCreateMat(3,3,CV_32FC1);

CvMat

*I=cvCreateMat(3,1,CV_32FC1);

cvEigenVV(A,E,l);//l=A的特征值(递减顺序)E=对应的特征向量(行向量)

7.

奇异值分解(SVD)

CVMat

*A=cvCreateMat(3,3,CV_32FC1);

CvMat

*U=cvCreateMat(3,3,CV_32FC1);

CvMat *D=cvCreateMat(3,3,CV_32FC1);

CvMat *V=cvCreateMat(3,3,CV_32FC1);

cvSVD(A,D,U,V,CV_SVD_U_T|CV_SVD_V_T);//A=UDV^T

标志位使得矩阵U或者V按照转置的形式返回(若不转置可能运算出错)

【OpenCV视频处理】

1.

从视频流中捕捉一帧画面

(1)

OpenCV支持从摄像头或者视频文件(AVI格式)中捕捉帧画面;

(2)

初始化一个摄像头捕捉器:

CvCapture

*capture=cvCaptureFromCAM(0);

(3)

初始化一个视频文件捕捉器:

CvCapture

*capture=cvCaptureFromAVI(“infile.avi”);

(4)

捕捉一帧画面:

IplImage *img=0;

If(!cvGrabFrame(capture)){

捕捉一帧

Printf(“could not grav a frame\n\7”);

Exit(0);

}

Img=cvRetrieveFrame(capture); //检索捕捉到的这一帧给图像img

若要从多个摄像头中同步捕捉画面,则必须先从每个摄像头中抓取一帧,紧接着要将被捕捉的帧画面恢复到一个IplImage *型图像中。(这个过程可以使用cvQueryFrame()函数一步完成;

(5)

释放视频流捕捉器:

cvReleaseCapture(&capture);

2.

获取/设置视频流信息

(1)

获取视频流设备信息:

CvQueryFrame(capture);//在读取视频流信息之前,要先执行此操作

int frameH=(int)cvGetXaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT);

int frameW=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH);

int fps=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FPS);

int numFrames=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_COUNT);

fps仅仅对于视频文件有效,但是不太准确

(2)

获取帧图信息:

float

posMsec=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_MSEC):

int posFrames=(int)

cvGetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES):

float

posRatio=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_AVI_RATIO);

(3)

设置从视频文件抓取第一帧画面的位置:

CvSetCaptureProperty(capture,CV_CAP_PROP_POS_AVI_RATIO,(double)0.9);

3.

保存视频文件

(1)

初始化视频编写器:

CvVideoWriter

*writer=0;

int isColor=1;

int fps=25;

int frameW =640;

int frameH=480;

write=cvCreateVideoWriter(“out.avi”,CV_FOURCC(‘P’,’I’,’M’,’1’),

fps,cvSize(frameW,frameH),isColor)

其他的编码器代号包括:

其它的编码器代号包括:

CV_FOURCC('P','I','M','1') = MPEG-1 codec

CV_FOURCC('M','J','P','G') = motion-jpeg codec (does not work well)

CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec CV_FOURCC('D', 'I',

'V', '3') = MPEG-4.3 codec CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4

codec CV_FOURCC('U', '2', '6', '3') = H263 codec CV_FOURCC('I',

'2', '6', '3') = H263I codec CV_FOURCC('F', 'L', 'V', '1') = FLV1

codec 若编码器代号为 -1,则运行时会弹出一个编码器选择框.

(2)

保持视频文件

IplImage *img=0;

Int nFrames=50;

for(i=0;i

{

cvGrabFrame(capture); //捕捉一帧

img=cvRetrieveFrame(capture);//重新保存这帧图像在img中

//img=cvQueryFrame(capture);

cvWriteFrame(write,img);

}

要查看所抓取到的帧画面,在循环中加入下列语句即可:

cvShowImage(“mainWin”,img);

key=cvWaitKey(20);

注意显示的时候cvWaitkey不能小于20ms,否则画面的显示可能出错。

(3)

释放视频编写器

cvReleaseVideoWriter(&writer);

你可能感兴趣的:(C语言中cvpoint后运行出错,OpenCV学习笔记)