【学习OpenCV】Mat::data指针

指针遍历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-------------------------------------


  • 54
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值