指针遍历Mat
这是一个很简单的问题,但是如果粗心大意写错了i和j,将会造成数据出错。
为什么要用指针访问Mat?在Release模式下的at方法其实效率跟指针是一样的,编码时没要为了效率牺牲可读性而使用指针。但有一种场合必须使用指针,就是编写opencv无关的API,例如写dll函数时,调用方不想涉及任何关于opencv的东西,包括其数据结构,此时就不能采用Mat传递参数了,只能采用指针。因为Mat是C++的数据结构,如果在子函数内部定义了Mat,在该函数返回时会自动释放掉Mat的数据,所以不要想着通过取Mat的数据指针来传参。只能通过内部new一段内存,把Mat的数据逐个元素地扔进内存里。
RGB图(3维矩阵)
BYTE* iPtr = new BYTE [height*width*3]; for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { for(int k=0;k<3;k++) { iPtr[i*width*3+j*3+k] = img.at<Vec3b>(i,j)[k]; } } }其中,img是一个3维uchar的Mat,Vec3b代表3个uchar
对于灰度图、4维矩阵等,只要把通道数和at的数据类型改一下就可以套用以上格式
还有一点千万注意,Mat的(i,j)是按(行,列)的规则,而图像中则是(高,宽),跟Size(x,y),Rect(x,y)的(x,y)是不同的
--------------------------------------------------------------------
Mat::data的使用
在opencv中,Mat很方便;但当用到不是以opencv为主体的代码中,就不能直接使用Mat,而要转换为其它形式的数据结构。最简单的解决方案是指针,即将Mat拷贝到自定义的指针中。
Mat::data是数据段的首地址;使用memcpy()将Mat的数据拷贝至某个指针中,当然要先new一段内存。
memcpy的说明:http://blog.csdn.net/sszgg2006/article/details/7989404
常用到vector<Mat>,要用push_back方法对其进行赋值,vector的使用说明:http://blog.csdn.net/hancunai0017/article/details/7032383
4字节对齐的情况
但如果图像大小不是4的整数倍,某些场合下不能直接使用Mat::data。因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元(一般是补够4的倍数或8的倍数,有些地方也称作“位对齐”。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。此时调用Mat::ptr<>()方法就等价于Mat::data
int nr=image.rows; int nc=image.cols; if(image.isContinuous()) { nr=1; nc=nc*image.rows*image.channels(); } for(int i=0;i<nr;i++) { const uchar* inData=image.ptr<uchar>(i); uchar* outData=outImage.ptr<uchar>(i); for(int j=0;j<nc;j++) { *outData++=*inData++; } }
例如保存BMP格式的图像时,BMP要求图像数据按四字节对齐,此时就需要对Mat中的数据进行补零
对齐方法就是在每一行尾部补零,零的个数可能是1~3个
Mat::data的默认类型
Mat::data的默认类型为uchar*,但很多时候需要处理其它类型,如float、int,此时需要将data强制类型转换,如:
Mat src(1000,1000,CV_32F); float* myptr = (float*)src.data;
无论Mat的type是何种类型,Mat::data均为uchar*
--------------------------------------------------------------------
指针数据拷贝至Mat
这个千万注意,使用Mat接收指针指定的一段内存数据,通过指针初始化一个Mat:
Mat(row,col,CV_8U,ptr)此时,Mat::data就等于ptr,例子
uchar ptr[25]={0,1}; Mat mat(5,5,CV_8U,ptr); mat = mat*255;
修改mat就修改了ptr指向内存的值
注意:Mat的类型要与指针的类型一致,如,uchar指针对应CV_8U,double指针对应CV_64F,如果把double指针赋给一个CV_32F的Mat,那Mat的每个元素只占32位,即把double数一分为二,是错误的。
这里又涉及一个问题,类型转换。opencv提供了Mat::convertTo接口进行类型转换,当然我们可以逐个元素进行强制类型转换,但有时候两者是有区别的。例如,把double转unsigned int,这是有符号数转无符号数,如果使用convertTo方法,会把负数置零;如果使用强制类型转换(unsigned int),结果是错误的,因为负数应该变成其补数,无符号的第一个比特位是有意义的
--------------------------------------------------------------------
Mat数据拷贝至指针
有些时候,我们不希望函数的调用者看到opencv的数据结构Mat,可以通过把Mat数据拷贝至一段动态申请的内存,此时千万要注意数据类型,指针和Mat要统一。
typedef ushort mtype; Mat src; ... mtype* psrc = (mtype*)src.data; mtype *pdst = new mtype [src.total()]; for(int i=0;i<h;i++) //遍历行 { const mtype* p0 = psrc + i*w; mtype* p1 = pdst + i*w; for(int j=0;j<w;j++) //遍历列 { *p1++= p0[j]; } } //耗时:0.7ms以上是最浅显的做法,如果使用memcpy可以更高效:
memcpy(pdst,psrc,src.total()*sizeof(mtype)); //耗时:0.5mssrc是一个1000*1000的ushort矩阵
--------------------------------END-------------------------------------