OpenCV常用的图像和矩阵操作总结.docx
《OpenCV常用的图像和矩阵操作总结.docx》由会员分享,可在线阅读,更多相关《OpenCV常用的图像和矩阵操作总结.docx(16页珍藏版)》请在冰豆网上搜索。
OpenCV常用的图像和矩阵操作总结
OpenCv常用图像和矩阵操作
cvmSet(M,i,j,;Mb->Mc
cvDiv(Ma,Mb,Mc); Mb ->Mc
cvAddS(Ma,cvScalar,Mc);Vb->res
学习资料
书籍
LearningOpenCV(影印版)
作者:
GaryBradski,AdrianKaehler
出版社:
东南大学出版社
学习OpenCV(中文版)
译者:
于仕琪刘瑞祯
清华大学出版社
OpenCV中文教程
刘瑞祯于仕琪
网站:
本地安装目录
在安装目录\docs下有各种学习资料
只用在本地安装目录下面就可以查询到大部分需要的信息,当然也可以直接XX,google
图像IplImage
StructureIplImage
OpenCv中图像的结构体为IplImage,位于头文件中,IplImage结构体的定义如下:
32F64F 只有cvCreateImage可以创建交叉存取图像*/
intorigin;/*图像原点位置:
0表示顶-左结构,1表示底-左结构*/
intalign;/*图像行排列方式(4or8),在OpenCV被忽略,使用widthStep代替*/
intwidth;/*图像宽像素数*/
intheight;/*图像高像素数*/
struct_IplROI*roi;/*图像感兴趣区域,当该值非空时,
只对该区域进行处理*/
struct_IplImage*maskROI;/*在OpenCV中必须为NULL*/
void*imageId;/*同上*/
struct_IplTileInfo*tileInfo;/*同上*/
intimageSize;/*图像数据大小(在交叉存取格式下ImageSize=image->height*image->widthStep),单位字节*/
char*imageData;/*指向排列的图像数据*/
intwidthStep;/*排列的图像行大小,以字节为单位*/
intBorderMode[4];/*边际结束模式,在OpenCV被忽略*/
intBorderConst[4];/*同上*/
char*imageDataOrigin;/*指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的*/
}IplImage;}
IplImage;
参数widthStep包括相邻行的同列点之间的字节数。
仅凭变量width是不能计算这个值的,因为为了处理过程更高效每行都会用固定的字节数来对齐;因此在第i行末和第i+1行开始处可能会有些冗于字节。
参数imageData包含一个指向第一行图像数据的指针。
如果图像中有些独立的平面(如当dataOrder=IPL_DATA_ORDER_PLANE)那么把它们作为单独的图像连续摆放,总行数为height和nChannels的乘积。
但通常情况下,它们是交错的,使得行数等于高度,而且每一行都有序地包含交错的通道。
ROI--感兴趣的区域(ROI),实际上它是另一个IPL/IPP结构IplROI的实例。
IplROI包含xOffset,yOffset,height,width和coi成员变量,其中COI代表channelofinterest(感兴趣的通道)。
ROI的思想是:
一旦设定ROI,通常作用于整幅图像的函数便会只对ROI所表示的子图像进行操作。
如果IplImage变量中设置了ROI,则所有的OpenCV函数就会使用该ROI变量。
如果COI被设置成非0值,则对该图像的操作就只作用于被指定的通道上了。
不幸的是,许多OpenCV函数都忽略参数COI。
图像的常用操作
图像载入函数
函数cvLoadImage载入指定图像文件,并返回指向该文件的IplImage指针。
函数支持bmp、jpg、png、tiff等格式的图像。
其函数原型如下:
IplImage*cvLoadImage(constchar*filename,intiscolor);
其中,filename是待载入图像的名称,包括图像的扩展名;iscolor是一个辅助参数项,可选正数、零和负数三种值,正数表示作为三通道图像载入,零表示该图像作为单通道图像,负数表示载入图像的通道数由图像文件自身决定。
窗口定义函数
函数cvNamedWindow定义一个窗口,用于显示图像。
intcvNamedWindow(constchar*name,unsignedlongflags);
其中,name是窗口名,flags是窗口属性指标值,可以选择CV_WINDOW_AUTOSIZE和0两种值。
CV_WINDOW_AUTOSIZE表示窗口尺寸与图像原始尺寸相同,0表示以固定的窗口尺寸显示图像。
图像显示函数
函数cvShowImage是在指定的窗口中显示图像,其函数原型如下:
voidcvShowImage(constchar*name,constCvArr*image);
其中,name是窗口名称,image是图像类型指针,一般是IplImage指针。
图像保存函数
函数cvSaveImage以指定的文件名保存IplImage类型的指针变量,其函数原型如下:
intcvSaveImage(constchar*filename,constCvArr*image);
其中,filename是图像保存路径和名称,image是IplImage指针变量。
Trick:
如果要保存一组图像到result文件夹,图像个数为n,保存名称按照一定的序号递增,假设为,,,,…,,则
操作为:
char*f[30];
for(inti=0;i{sprintf(f,”result/imgTmp%”,i);cvSaveImage(f,img);}借用sprintf函数即可以完成依次命名的功能。图像销毁函数 函数cvReleaseImage销毁已定义的IplImage指针变量,释放占用内存空间。其函数原型如下: voidcvReleaseImage(IplImage**image);其中,image为已定义的IplImage指针。存取图像像素包括获取像素值和对像素值赋值●直接获取假设图像为IplImage*img,图像的depth=IPL_DEPTH_8U(每个像素用8bits表示),获取像素坐标(x,y)的操作为1.灰度图像(单通道img->nChannels=1)对像素赋值:((char*)(img->imageData+y*imge->widthStep))[x]=255;获取像素值:intgrayValue=((char*)(img->imageData+y*imge->widthStep))[x];2.彩色图像(单通道img->nChannels=1)对像素赋值:((char*)(img->imageData+y*imge->widthStep))[3*x]=255;((char*)(img->imageData+y*imge->widthStep))[3*x+1]=255;((char*)(img->imageData+y*imge->widthStep))[3*x+2]=255;获取像素值:ucharb=((char*)(img->imageData+y*imge->widthStep))[x];ucharg=((char*)(img->imageData+y*imge->widthStep))[x];ucharr=((char*)(img->imageData+y*imge->widthStep))[x];注意:注意(char*)这个指针的强制转换是针对img->imageData+y*imge->widthStep的,也就是针对图像的行指针进行的转换,注意括弧的范围。当image->depth为其他值时,则可能每个像素的数据类型需要进行(int*),(float*),(double*)等转换。参数widthStep是相邻行的同列点之间的字节数。仅凭变量width是不能计算这个值的,因为为了处理过程更高效每行都会用固定的字节数来对齐;因此在第i行末和第i+1行开始处可能会有些冗于字节。一次在进行行切换时,一定要widthStep来进行内存的偏移,而不是用depth*width.一般的情况下,假设有N-通道,类型为T的图像:I(x,y)c~((T*)(img->imageData+img->widthStep*y))[x*N+c]y的位置例子:void saturate_sv( IplImage* img ) { for( int y=0; yheight; y++ ) { uchar* ptr = (uchar*) ( Img->imageData + y * img->widthStep ); for( int x=0; xwidth; x++ ) { ptr[3*x] = 255; ptr[3*x+1] = 255; ptr[3*x+2] = 255; } } } 在以上程序中,我们用指针ptr指向第y行的起始位置。接着,我们从指针中析出饱和度和高度在x维的值。因为这是一个三通道图像,所以C通道在x行的位置是3*x+c。该使用方法是受限的ucharb,g,r;b=img->imageData[img->widthStep*row+col*3]g=img->imageData[img->widthStep*row+col*3+1];r=img->imageData[img->widthStep*row+col*3+2];由于imageData指针始终是char*类型的,因此该方法只适用于8bits/pixel的图像表示,其他的图像类型则需要进行指针转换。●宏可以使用宏CV_IMAGE_ELEM(image_header,elemtype,y,x_Nc)I(x,y)c~CV_IMAGE_ELEM(img,T,y,x*N+c),其中c为通道的序号,如彩色图像c=0,1,2不过使用该宏是也要小心数据类型elemtype的问题。也有针对各种图像(包括4通道图像)和矩阵的函数(cvGet2D,cvSet2D),但是它们非常慢。注:该宏在每次调用时,都会重新计算指针的位置。这意味着,先查找数据区中第0个元素的位置,然后,根据参数中的行和列,计算所需要的元素的地址偏移量,然后将地址偏移量与第0个元素的地址相加,获得所需要的元素的地址。所以,以上的方式虽然很容易使用,但是却不是获得元素的最好方式。特别是当你要顺序遍历整个图像中所有元素时,这种每次对地址的重复计算就更加显得不合理。●cvGet2D()和cvSet2D()可以通过cvGet2D()和cvSet2D()两个函数加上一个CvScalar结构体做到获取图像的像素点。OpenCV中,CvScalar结构为:typedefstructCvScalar{ double val[4];}CvScalar; 4个double型变量,算法处理时不至于被强制类型转换而降低精度了。再来看读写函数的定义: cvGet2D 获得某个点的值,idx0=hight行值,idx1=width列值。CVAPI(CvScalar) cvGet2D( const CvArr*arr, int idx0, int idx1); cvSet2D 给某个点赋值。CVAPI(void) cvSet2D( CvArr*arr, int idx0, int idx1, CvScalar value); 有上可见,cvGet2D的返回类型和cvSet2D中value的类型都是CvScalar,这样定义一个CvScalar变量再调用函数就OK了。 OpenCV中像素点读写例子: int main(int argc, char **argv){ IplImage *img= cvLoadImage(argv[1],1); CvScalar pixel; for (int i=0;iheight;++i) { for (int j=0;jwidth;++j) { 格式为CV_(S|U|F)C. 例如:CV_8UC1表示8位无符号单通道矩阵,CV_32SC2表示32位有符号双通道矩阵. 例: CvMat*M=cvCreateMat(4,4,CV_32FC1);●逐点赋值式初始化CvMat*mat=cvCreateMat(2,2,CV_64FC1);cvZero(mat);cvmSet(mat,0,0,1);cvmSet(mat,0,1,2);cvmSet(mat,1,0,3);cvmSet(mat,2,2,4);cvReleaseMat(&mat);●使用现有数组初始化doublea[]={1,2,3,4, 5,6,7,8, 9,10,11,12};CvMatmat=cvMat(3,4,CV_64FC1,a);简单的方法从矩阵中得到一个元素的最简单的方法是利用宏CV_MAT_ELEM()。这个宏(参见例3-4)传入矩阵、待提取的元素的类型、行和列数4个参数,返回提取出的元素的值。例3-4:利用CV_MAT_ELEM()宏存取矩阵1.CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 ); 2.float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 ); 更进一步,还有一个与此宏类似的宏,叫CV_MAT_ELEM_PTR()。CV_MAT_ELEM_PTR()(参见例3-5)传入矩阵、待返回元素的行和列号这3个参数,返回指向这个元素的指针。该宏和CV_MAT_ELEM()宏的最重要的区别是后者在指针解引用之前将其转化成指定的类型。如果需要同时读取数据和设置数据,可以直接调用CV_MAT_ELEM_PTR()。但在这种情况下,必须自己将指针转化成恰当的类型。例3-5:利用宏CV_MAT_ELEM_PTR()为矩阵设置一个数值1.CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 ); 2.float element_3_2 = ; 3.*( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2; 遗撼的是,这些宏在每次调用的时候都重新计算指针。这意味着要查找指向矩阵基本元素数据区的指针、计算目标数据在矩阵中的相对地址,然后将相对位置与基本位置相加。所以,即使这些宏容易使用,但也不是存取矩阵的最佳方法。在计划顺序访问矩阵中的所有元素时,这种方法的缺点尤为突出。麻烦的方法在"简单的方法"中讨论的两个宏仅仅适用于访问1维或2维的数组(回忆一下,1维的数组,或者称为"向量"实际只是一个n×1维矩阵)。OpenCV提供了处理多维数组的机制。事实上,OpenCV可以支持普通的N维的数组,这个N值可以取值为任意大的数。为了访问普通矩阵中的数据,我们可以利用在例3-6和例3-7中列举的cvPtr*D和cvGet*D…等函数族。cvPtr*D家族包括cvPtr1D(),cvPtr2D(),cvPtr3D()和cvPtrND()…。这三个函数都可接收CvArr*类型的矩阵指针参数,紧随其后的参数是表示索引的整数值,最后是一个可选的参数,它表示输出值的类型。函数返回一个指向所需元素的指针。对于cvPtrND()来说,第二个参数是一个指向一个整型数组的指针,这个数组中包含索引的合适数字。后文会再次介绍此函数(在这之后的原型中,也会看到一些可选参数,必要时会有讲解)。例3-6:指针访问矩阵结构1.uchar* cvPtr1D( 2. const CvArr* arr, 3. int idx0, 4. int* type = NULL 5.); 6. 7.uchar* cvPtr2D( 8. const CvArr* arr, 9. int idx0, 10. 11. 12. 13. int idx1, 14. int* type = NULL 15.); 16. 17.uchar* cvPtr3D( 18. const CvArr* arr, 19. int idx0, 20. int idx1, 21. int idx2, 22. int* type = NULL 23.); 24.uchar* cvPtrND( 25. const CvArr* arr, 26. int* idx, 27. int* type = NULL, 28. int create_node = 1, 29. unsigned* precalc_hashval = NULL 30.); 如果仅仅是读取数据,可用另一个函数族cvGet*D。如例3-7所示,该例与例3-6类似,但是返回矩阵元素的实际值。例3-7:CvMat和IPlImage元素函数1.double cvGetReal1D( const CvArr* arr, int idx0 ); 2.double cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); 3.double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); 4.double cvGetRealND( const CvArr* arr, int* idx ); 5. 6.CvScalar cvGet1D( const CvArr* arr, int idx0 ); 7.CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 ); 8.CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); 9.CvScalar cvGetND( const CvArr* arr, int* idx ); cvGet*D中有四个函数返回的是整型的,另外四个的返回值是CvScalar类型的。这意味着在使用这些函数的时候,会有很大的空间浪费。所以,只是在你认为用这些函数比较方便和高效率的时候才用它们,否则,最好用cvPtr*D。用cvPtr*D()函数族还有另外一个原因,即可以用这些指针函数访问矩阵中的特定的点,然后由这个点出发,用指针的算术运算得到指向矩阵中的其他数据的指针。在多通道的矩阵中,务必记住一点:通道是连续的,例如,在一个3通道2维的表示红、绿、蓝(RGB)矩阵中。矩阵数据如下存储rgbrgbrgb...。所以,要将指向该数据类型的指针移动到下一通道,我们只需要将其增加1。如果想访问下一个"像素"或者元素集,我们只需要增加一定的偏移量,使其与通道数相等。另一个需要知道的技巧是矩阵数组的step元素(参见例3-1和例3-3),step是矩阵中行的长度,单位为字节。在那些结构中,仅靠cols或width是无法在矩阵的不同行之间移动指针的,出于效率的考虑,矩阵或图像的内存分配都是4字节的整数倍。所以,三个字节宽度的矩阵将被分配4个字节,最后一个字节被忽略。因此,如果我们得到一个字节指针,该指针指向数据元素,那么我们可以用step和这个指针相加以使指针指向正好在我们的点的下一行元素。如果我们有一个整型或者浮点型的矩阵,对应的有整型和浮点型的指针指向数据区域,我们将让step/4与指针相加来移到下一行,对双精度型的,我们让step/8与指针相加(这里仅仅考虑了C将自动地将差值与我们添加的数据类型的字节数相乘)。 例3-8中的cvSet*D和cvGet*D多少有些相似,它通过一次函数调用为一个矩阵或图像中的元素设置值,函数cvSetReal*D()和函数cvSet*D()可以用来设置矩阵或者图像中元素的数值。例3-8:为CvMat或者IplImage元素设定值的函数1.void cvSetReal1D( CvArr* arr, int idx0, double value ); 2.void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); 3.void cvSetReal3D( 4. CvArr* arr, 5. int idx0, 6. int idx1, 7. int idx2, 8. double value 9.); 10.void cvSetRealND( CvArr* arr, int* idx, double value ); 11. 12.void cvSet1D( CvArr* arr, int idx0, CvScalar value ); 13.void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); 14.void cvSet3D( 15. CvArr* arr, 16. int idx0
{
sprintf(f,”result/imgTmp%”,i);
cvSaveImage(f,img);
}
借用sprintf函数即可以完成依次命名的功能。
图像销毁函数
函数cvReleaseImage销毁已定义的IplImage指针变量,释放占用内存空间。
voidcvReleaseImage(IplImage**image);
其中,image为已定义的IplImage指针。
存取图像像素
包括获取像素值和对像素值赋值
●直接获取
假设图像为IplImage*img,图像的depth=IPL_DEPTH_8U(每个像素用8bits表示),获取像素坐标(x,y)的操作为
1.灰度图像(单通道img->nChannels=1)
对像素赋值:
((char*)(img->imageData+y*imge->widthStep))[x]=255;
获取像素值:
intgrayValue=((char*)(img->imageData+y*imge->widthStep))[x];
2.彩色图像(单通道img->nChannels=1)
((char*)(img->imageData+y*imge->widthStep))[3*x]=255;
((char*)(img->imageData+y*imge->widthStep))[3*x+1]=255;
((char*)(img->imageData+y*imge->widthStep))[3*x+2]=255;
ucharb=((char*)(img->imageData+y*imge->widthStep))[x];
ucharg=((char*)(img->imageData+y*imge->widthStep))[x];
ucharr=((char*)(img->imageData+y*imge->widthStep))[x];
注意:
注意(char*)这个指针的强制转换是针对img->imageData+y*imge->widthStep的,也就是针对图像的行指针进行的转换,注意括弧的范围。
当image->depth为其他值时,则可能每个像素的数据类型需要进行(int*),(float*),(double*)等转换。
参数widthStep是相邻行的同列点之间的字节数。
一次在进行行切换时,一定要widthStep来进行内存的偏移,而不是用depth*width.
一般的情况下,假设有N-通道,类型为T的图像:
I(x,y)c~((T*)(img->imageData+img->widthStep*y))[x*N+c]y的位置
例子:
void saturate_sv( IplImage* img )
for( int y=0; yheight; y++ )
uchar* ptr = (uchar*) ( Img->imageData + y * img->widthStep );
for( int x=0; xwidth; x++ )
ptr[3*x] = 255;
ptr[3*x+1] = 255;
ptr[3*x+2] = 255;
在以上程序中,我们用指针ptr指向第y行的起始位置。
接着,我们从指针中析出饱和度和高度在x维的值。
因为这是一个三通道图像,所以C通道在x行的位置是3*x+c。
该使用方法是受限的
ucharb,g,r;
b=img->imageData[img->widthStep*row+col*3]
g=img->imageData[img->widthStep*row+col*3+1];
r=img->imageData[img->widthStep*row+col*3+2];
由于imageData指针始终是char*类型的,因此该方法只适用于8bits/pixel的图像表示,其他的图像类型则需要进行指针转换。
●宏
可以使用宏CV_IMAGE_ELEM(image_header,elemtype,y,x_Nc)
I(x,y)c~CV_IMAGE_ELEM(img,T,y,x*N+c),其中c为通道的序号,如彩色图像c=0,1,2
不过使用该宏是也要小心数据类型elemtype的问题。
也有针对各种图像(包括4通道图像)和矩阵的函数(cvGet2D,cvSet2D),但是它们非常慢。
注:
该宏在每次调用时,都会重新计算指针的位置。
这意味着,先查找数据区中第0个元素的位置,然后,根据参数中的行和列,计算所需要的元素的地址偏移量,然后将地址偏移量与第0个元素的地址相加,获得所需要的元素的地址。
所以,以上的方式虽然很容易使用,但是却不是获得元素的最好方式。
特别是当你要顺序遍历整个图像中所有元素时,这种每次对地址的重复计算就更加显得不合理。
●cvGet2D()和cvSet2D()
可以通过cvGet2D()和cvSet2D()两个函数加上一个CvScalar结构体做到获取图像的像素点。
OpenCV中,CvScalar结构为:
typedefstructCvScalar
double val[4];
CvScalar;
4个double型变量,算法处理时不至于被强制类型转换而降低精度了。
再来看读写函数的定义:
cvGet2D 获得某个点的值,idx0=hight行值,idx1=width列值。
CVAPI(CvScalar) cvGet2D( const CvArr*arr, int idx0, int idx1);
cvSet2D 给某个点赋值。
CVAPI(void) cvSet2D( CvArr*arr, int idx0, int idx1, CvScalar value);
有上可见,cvGet2D的返回类型和cvSet2D中value的类型都是CvScalar,这样定义一个CvScalar变量再调用函数就OK了。
OpenCV中像素点读写例子:
int main(int argc, char **argv)
IplImage *img= cvLoadImage(argv[1],1);
CvScalar pixel;
for (int i=0;iheight;++i)
for (int j=0;jwidth;++j)
格式为CV_(S|U|F)C.
例如:
CV_8UC1表示8位无符号单通道矩阵,CV_32SC2表示32位有符号双通道矩阵.
例:
CvMat*M=cvCreateMat(4,4,CV_32FC1);
●逐点赋值式初始化
CvMat*mat=cvCreateMat(2,2,CV_64FC1);
cvZero(mat);
cvmSet(mat,0,0,1);
cvmSet(mat,0,1,2);
cvmSet(mat,1,0,3);
cvmSet(mat,2,2,4);
cvReleaseMat(&mat);
●使用现有数组初始化
doublea[]={1,2,3,4,
5,6,7,8,
9,10,11,12};
CvMatmat=cvMat(3,4,CV_64FC1,a);
简单的方法
从矩阵中得到一个元素的最简单的方法是利用宏CV_MAT_ELEM()。
这个宏(参见例3-4)传入矩阵、待提取的元素的类型、行和列数4个参数,返回提取出的元素的值。
例3-4:
利用CV_MAT_ELEM()宏存取矩阵
1.CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
2.float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
更进一步,还有一个与此宏类似的宏,叫CV_MAT_ELEM_PTR()。
CV_MAT_ELEM_PTR()(参见例3-5)传入矩阵、待返回元素的行和列号这3个参数,返回指向这个元素的指针。
该宏和CV_MAT_ELEM()宏的最重要的区别是后者在指针解引用之前将其转化成指定的类型。
如果需要同时读取数据和设置数据,可以直接调用CV_MAT_ELEM_PTR()。
但在这种情况下,必须自己将指针转化成恰当的类型。
例3-5:
利用宏CV_MAT_ELEM_PTR()为矩阵设置一个数值
2.float element_3_2 = ;
3.*( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;
遗撼的是,这些宏在每次调用的时候都重新计算指针。
这意味着要查找指向矩阵基本元素数据区的指针、计算目标数据在矩阵中的相对地址,然后将相对位置与基本位置相加。
所以,即使这些宏容易使用,但也不是存取矩阵的最佳方法。
在计划顺序访问矩阵中的所有元素时,这种方法的缺点尤为突出。
麻烦的方法
在"简单的方法"中讨论的两个宏仅仅适用于访问1维或2维的数组(回忆一下,1维的数组,或者称为"向量"实际只是一个n×1维矩阵)。
OpenCV提供了处理多维数组的机制。
事实上,OpenCV可以支持普通的N维的数组,这个N值可以取值为任意大的数。
为了访问普通矩阵中的数据,我们可以利用在例3-6和例3-7中列举的cvPtr*D和cvGet*D…等函数族。
cvPtr*D家族包括cvPtr1D(),cvPtr2D(),cvPtr3D()和cvPtrND()…。
这三个函数都可接收CvArr*类型的矩阵指针参数,紧随其后的参数是表示索引的整数值,最后是一个可选的参数,它表示输出值的类型。
函数返回一个指向所需元素的指针。
对于cvPtrND()来说,第二个参数是一个指向一个整型数组的指针,这个数组中包含索引的合适数字。
后文会再次介绍此函数(在这之后的原型中,也会看到一些可选参数,必要时会有讲解)。
例3-6:
指针访问矩阵结构
1.uchar* cvPtr1D(
2. const CvArr* arr,
3. int idx0,
4. int* type = NULL
5.);
6.
7.uchar* cvPtr2D(
8. const CvArr* arr,
9. int idx0,
10.
11.
12.
13. int idx1,
14. int* type = NULL
15.);
16.
17.uchar* cvPtr3D(
18. const CvArr* arr,
19. int idx0,
20. int idx1,
21. int idx2,
22. int* type = NULL
23.);
24.uchar* cvPtrND(
25. const CvArr* arr,
26. int* idx,
27. int* type = NULL,
28. int create_node = 1,
29. unsigned* precalc_hashval = NULL
30.);
如果仅仅是读取数据,可用另一个函数族cvGet*D。
如例3-7所示,该例与例3-6类似,但是返回矩阵元素的实际值。
例3-7:
CvMat和IPlImage元素函数
1.double cvGetReal1D( const CvArr* arr, int idx0 );
2.double cvGetReal2D( const CvArr* arr, int idx0, int idx1 );
3.double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 );
4.double cvGetRealND( const CvArr* arr, int* idx );
5.
6.CvScalar cvGet1D( const CvArr* arr, int idx0 );
7.CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );
8.CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );
9.CvScalar cvGetND( const CvArr* arr, int* idx );
cvGet*D中有四个函数返回的是整型的,另外四个的返回值是CvScalar类型的。
这意味着在使用这些函数的时候,会有很大的空间浪费。
所以,只是在你认为用这些函数比较方便和高效率的时候才用它们,否则,最好用cvPtr*D。
用cvPtr*D()函数族还有另外一个原因,即可以用这些指针函数访问矩阵中的特定的点,然后由这个点出发,用指针的算术运算得到指向矩阵中的其他数据的指针。
在多通道的矩阵中,务必记住一点:
通道是连续的,例如,在一个3通道2维的表示红、绿、蓝(RGB)矩阵中。
矩阵数据如下存储rgbrgbrgb...。
所以,要将指向该数据类型的指针移动到下一通道,我们只需要将其增加1。
如果想访问下一个"像素"或者元素集,我们只需要增加一定的偏移量,使其与通道数相等。
另一个需要知道的技巧是矩阵数组的step元素(参见例3-1和例3-3),step是矩阵中行的长度,单位为字节。
在那些结构中,仅靠cols或width是无法在矩阵的不同行之间移动指针的,出于效率的考虑,矩阵或图像的内存分配都是4字节的整数倍。
所以,三个字节宽度的矩阵将被分配4个字节,最后一个字节被忽略。
因此,如果我们得到一个字节指针,该指针指向数据元素,那么我们可以用step和这个指针相加以使指针指向正好在我们的点的下一行元素。
如果我们有一个整型或者浮点型的矩阵,对应的有整型和浮点型的指针指向数据区域,我们将让step/4与指针相加来移到下一行,对双精度型的,我们让step/8与指针相加(这里仅仅考虑了C将自动地将差值与我们添加的数据类型的字节数相乘)。
例3-8中的cvSet*D和cvGet*D多少有些相似,它通过一次函数调用为一个矩阵或图像中的元素设置值,函数cvSetReal*D()和函数cvSet*D()可以用来设置矩阵或者图像中元素的数值。
例3-8:
为CvMat或者IplImage元素设定值的函数
1.void cvSetReal1D( CvArr* arr, int idx0, double value );
2.void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );
3.void cvSetReal3D(
4. CvArr* arr,
5. int idx0,
6. int idx1,
7. int idx2,
8. double value
9.);
10.void cvSetRealND( CvArr* arr, int* idx, double value );
12.void cvSet1D( CvArr* arr, int idx0, CvScalar value );
13.void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
14.void cvSet3D(
15. CvArr* arr,
16. int idx0
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1