zl程序教程

您现在的位置是:首页 >  后端

当前栏目

OpenCV实现Mat与vector,Mat与数组互转

Opencv数组 实现 vector 互转 MAT
2023-09-11 14:17:47 时间

OpenCV实现Mat与vector互转

尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/80253066

1、Mat与vector互转

   下面是鄙人实现的Mat与vector互转的方法,需要注意的是vector转Mat时,使用reshape()后,必须clone()一份,否则返回的结果出错,关于这方面的原因,可以参考鄙人的博客:https://blog.csdn.net/guyuealian/article/details/80252853

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

/***************** Mat转vector **********************/
template<typename _Tp>
vector<_Tp> convertMat2Vector(const Mat &mat)
{
	return (vector<_Tp>)(mat.reshape(1, 1));//通道数不变,按行转为一行
}

/****************** vector转Mat *********************/
template<typename _Tp>
cv::Mat convertVector2Mat(vector<_Tp> v, int channels, int rows)
{
	cv::Mat mat = cv::Mat(v);//将vector变成单列的mat
	cv::Mat dest = mat.reshape(channels, rows).clone();//PS:必须clone()一份,否则返回出错
	return dest;
}


int main()
{
	/* char ->CV_8SC
	* unsigned char,uchar ->CV_8UC
	* unsigned short int,ushort->CV_16UC
	* short int->CV_16SC
	* int   ->CV_32SC
	* float ->CV_32FC
	* double->CV_64FC
	*/
	uchar arr[4][3] = { { 1, 1,1 },{ 2, 2,2 },{ 3, 3,3 },{ 4,4, 4 } };
	cv::Mat srcData(4, 3, CV_8UC1, arr);
	cout << "srcData=\n" << srcData << endl;


	vector<uchar> v = convertMat2Vector<uchar>(srcData);
	cv::Mat dest = convertVector2Mat<uchar>(v, 1, 4);//把数据转为1通道,4行的Mat数据
	cout << "dest=\n" << dest << endl;

	system("pause");
	waitKey();
	return 0;
}

运行结果:

srcData=
[  1,   1,   1;
   2,   2,   2;
   3,   3,   3;
   4,   4,   4]
dest=
[  1,   1,   1;
   2,   2,   2;
   3,   3,   3;
   4,   4,   4]

2、Mat与数组互转

      利用Mat来存储数据,避免使用数组等操作

cv::Mat mean = (cv::Mat_<float>(2, 1) << 0.4404, 0.3111);
cout << "mean=" << mean << endl;
float a=mean.at<float>(0, 0);
float b = mean.at<float>(0, 0);

       将数组内容传递给Mat,示例代码:

unsigned char cbuf[height][width];
cv::Mat img(height, width, CV_8UC1, (unsigned char*)cbuf);

       将Mat中的内容传递给数组,如果Mat中的数据是连续的,那么对于传递到一维vector我们可以这样:

std::vector<uchar> array(mat.rows*mat.cols);
if (mat.isContinuous())
    array = mat.data;

       同样的,传递到一维数组我们可以这样

unsigned char *array=new unsigned char[mat.rows*mat.cols];

if (mat.isContinuous())
    array = mat.data;

        对于二维vector的传值,我们可以这样处理​​​​​​​

uchar **array = new uchar*[mat.rows];
for (int i=0; i<mat.rows; ++i)
    array[i] = new uchar[mat.cols];
 
for (int i=0; i<mat.rows; ++i)
    array[i] = mat.ptr<uchar>(i);

 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)是不同的

4字节对齐的情况

        http://lib.csdn.net/article/opencv/24030

        但如果图像大小不是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的内存都是连续的,只有极个别时候需要担心这个问题,这里有说明,和这里