【学习OpenCV】Mat::data指针

来源:互联网 发布:知字书法 编辑:程序博客网 时间:2024/05/21 06:44

指针遍历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的倍数,称为padding。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如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的内存都是连续的,只有极个别时候需要担心这个问题,这里有说明,和这里


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.5ms
src是一个1000*1000的ushort矩阵
需要注意的是,src.data需要先进行强制类型转换


通过指针构造Mat


方法:

Mat img(image_size,TYPE,img_ptr);

这样,可以避免重复申请内存,对Mat的修改就是对指针内容的修改


--------------------------------END-------------------------------------


0 0