1. OpenCV基本数据类型
OpenCV提供了多种基本数据类型,可在opencv\sources\modules\core\include\opencv2\core中查看详细定义。当然你用VS的话,直接对任何数据类型右键转到定义就能直接跳转了。
常用的有,通常构造函数同结构类型名称,但是首字母不大写
CvPoint, CvPoint2D32f, CvPoint3D32f
CvSize, CvSize2D32f
CvRect
CvScalar(意义:RGBA值,构造函数还有cvRealScalar(), cvScalarAll())
矩阵和图像类型
三种图像的类或结构如下。
CvArr -> CvMat -> IplImage
三者依次为派生关系,IplImage是通常用来对图像进行“编码”的基本结构,这些图像可以是灰度,彩色,4通道且每个通道可包含任意的整数或浮点数,比常见3通道8位RGB图像更通用。
CvMat是OpenCV的矩阵结构,CvArr可视为一个抽象基类,在函数原型中,常看到CvArr (CvArr*),当他出现时,便可以将CvMat*,IplImage*传递到程序。
2. CvMat矩阵结构
创建CvMat结构
CvMat* cvCreateMat(int rows, int cols, inttype)
Type: CV_
CvMat结构如下
typedef struct CvMat {
int type;
int step;//矩阵中行的长度,单位为字节,可用来在矩阵的不同行之间移动指针
int* refcount; // for internal useonly
union {
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data; //数据体,存储的是对应数据类型的指针头
union {
int rows;
int height;
};
union {
int cols;
int width;
};
} CvMat;
CvMat矩阵的创建与释放
//Create a new rows by cols matrix of type ‘type’.
//
CvMat*cvCreateMat( int rows, int cols, int type );
//Create only matrix header without allocating data
//
CvMat*cvCreateMatHeader( int rows, int cols, int type );
//Initialize header on existing CvMat structure
//
CvMat*cvInitMatHeader(
CvMat* mat,
int rows,
int cols,
int type,
void* data = NULL,
int step =CV_AUTOSTEP
);
//Like cvInitMatHeader() but allocates CvMat as well.
//
CvMatcvMat(
int rows,
int cols,
int type,
void* data = NULL
);
//Allocate a new matrix just like the matrix ‘mat’.
//
CvMat*cvCloneMat( const cvMat* mat );
//Free the matrix ‘mat’, both header and data.
//
void cvReleaseMat(CvMat** mat );
例:用固定数据创建矩阵
#include
int main()
{
// Create an OpenCV Matrix containing some fixed data.
//
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025};
CvMat rotmat; //空矩阵结构,下面初始化矩阵头
cvInitMatHeader(
&rotmat,
2,
2,
CV_32FC1,
vals
);
printf("Ex 3_3 matrixinitialized\n");
}
矩阵数据的存取
# include
int main()
{
CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
//-----------------------------------------
//方法一:利用宏CV_MAT_ELEM读取矩阵元素
float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
float element_3_3 = 7.7;
//宏CV_MAT_ELEM_PTR可以同时读取并且设置数据
*( (float*)CV_MAT_ELEM_PTR( *mat, 3, 3 ) ) = element_3_3;
//------------------------------------------
//方法二:利用cvPtr*D, cvGetReal*D, cvGet*D读取
float a32 = *cvPtr2D(mat,3,2);
float a33 = cvGetReal2D(mat,3,3);
printf("a32 = %f; a33 = %f\n",a32,a33);
//利用cvSetReal*D, cvSet*D,cvmSet设置
cvSetReal2D( mat, 3, 4, 0.5 );
cvmSet(mat,4,3,0.2);
//------------------------------------------
//方法三:利用指针的偏移来遍历矩阵,对于整型和浮点型数据(32比特4字节),step/4移向下一行,对于双精度(64比特8字节)step/8
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025};
CvMat rotmat; //空矩阵结构,下面初始化矩阵头
cvInitMatHeader(
&rotmat,
2,
2,
CV_32FC1,
vals
);
//遍历矩阵所有元素并求和
float s = 0.0f;
for( int row=0; row < rotmat.height; row++ )
{
//这里数据类型为float,rotmat.data.fl为数据开头的指针,step/4移向下一行
float* ptr = rotmat.data.fl + row * rotmat.step/4;
for( int col = 0; col < rotmat.width; col++ )
s += *ptr++;
}
printf("s = %f\nsum = %f \n",s,0.866025 + -0.500000 + 0.500000 + 0.866025);
return 0;
}
注意:方法一只能访问1/2维数数组元,且每次调用宏都会重复计算指针,不是存取矩阵的最佳方法,尤其在顺序访问矩阵中所有元素时。对于计算机视觉这种运算密集型的任务,最有效的方法一般是最后一种。
3. IplImage数据结构
IplImage数据头结构
//IplImageheader structure
typedef struct _IplImage {
int nSize;
int ID;
int nChannels;//通道数
int alphaChannel;
int depth;//图像深度,取值有IPL_DEPTH_8U|S, IPL_DEPTH_16S, IPL_DEPTH_32S|F, IPL_DEPTH_64F等
char colorModel[4];
char channelSeq[4];
int dataOrder;//IPL_DATA_ORDER_PIXEL(像素点不同通道的值排在一起)或 IPL_DATA_ORDER_PLANE(同通道值排在一起)
int origin;//IPL_ORIGIN_BL(图像原点左下),IPL_ORIGIN_TL(图像原点左上)
int align;
int width;//宽
int height;//高
struct _IplROI* roi;//感兴趣的区域,一旦被设定则所有操作只限于此区域
struct _IplImage* maskROI;
void* imageId;
struct _IplTileInfo* tileInfo;
int imageSize;
char* imageData;//指向第一行图像数据的指针
int widthStep;//同CvMat中的step,注意不能用width代替,单位仍是字节数
int BorderMode[4];
int BorderConst[4];
char* imageDataOrigin;
} IplImage;
下面的程序设置图像ROI,对于感兴趣区域,增加他的蓝色通道像素值
//main image_name x y width height add#
#include
#include
int main(int argc, char** argv)
{
IplImage* src;
cvNamedWindow("Example3_12_pre", CV_WINDOW_AUTOSIZE);
cvNamedWindow("Example3_12_post", CV_WINDOW_AUTOSIZE);
if( argc == 7 && ((src=cvLoadImage(argv[1],1)) != 0 ))
{
int x = atoi(argv[2]);//atoi(将字符串转为整数)
int y = atoi(argv[3]);
int width = atoi(argv[4]);
int height = atoi(argv[5]);
int add = atoi(argv[6]);
cvShowImage( "Example3_12_pre", src);
//cvSetImageROI设置图像中的感兴趣区域
cvSetImageROI(src,cvRect(x,y,width,height));
//蓝色通道增加像素add
cvAddS(src, cvScalar(add),src);
cvShowImage( "Example3_12_post",src);//只会显示感兴趣区域
cvWaitKey();
cvResetImageROI(src);
cvShowImage( "Example3_12_post",src);
cvWaitKey();
}
cvReleaseImage( &src );
cvDestroyWindow("Example3_12_pre");
cvDestroyWindow("Example3_12_post");
return 0;
}
对于cvSetImageROI()还可以设置掩码或模板,即他的第四个参数const CvArr* mask = NULL,一个8位单通道数组,将操作限制到任意形状的非0像素的掩码区。
当然也可以利用widthstep与指针遍历来构造一个子区域,相对于直接利用ROI的好处是,在需要对同一张图片的多个子区域进行处理时,不需要反复设置并取消ROI
int main(int argc, char** argv)
{
IplImage* interest_img;
CvRect interest_rect;
if( argc == 7 && ((interest_img=cvLoadImage(argv[1],1)) != 0 ))
{
interest_rect.x = atoi(argv[2]);
interest_rect.y = atoi(argv[3]);
interest_rect.width = atoi(argv[4]);
interest_rect.height = atoi(argv[5]);
int add = atoi(argv[6]);
IplImage *sub_img = cvCreateImageHeader(
cvSize(
interest_rect.width,
interest_rect.height
),
interest_img->depth,
interest_img->nChannels
);
sub_img->origin = interest_img->origin;
sub_img->widthStep = interest_img->widthStep;
sub_img->imageData = interest_img->imageData +
interest_rect.y * interest_img->widthStep +
interest_rect.x * interest_img->nChannels;
cvAddS( sub_img, cvScalar(add), sub_img );
//sub_img的数据部分指针指向的是interest_img的数据部分,因此下面只需要释放其header
cvReleaseImageHeader(&sub_img);
cvNamedWindow( "Roi_Add", CV_WINDOW_AUTOSIZE );
cvShowImage( "Roi_Add", interest_img );
cvReleaseImage(&interest_img);
cvDestroyWindow("ROI_ADD");
cvWaitKey();
}
return 0;
}