OpenCV reshape函数需要注意的细节

OpenCV reshape函数需要注意的细节

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

    本人在使用OpenCV的reshape()函数时,曾经碰到一个大坑,出现一个非常奇葩的问题,先把原始的代码贴上:

#include <opencv2/opencv.hpp>
#include "continuousCapture.h"

using namespace cv;
using namespace std;

template<typename _Tp>
cv::Mat convertVector2Mat(vector<_Tp> v, int cn, int rows)
{
	cv::Mat mat = cv::Mat(v);//将vector变成一列的mat
	cv::Mat dest = mat.reshape(cn, rows);
	cout << "内部dest=\n" << dest << endl;
	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
	 */
	//int arr[4][3] = { { 1, 1,1 },{ 2, 2,2 },{ 3, 3,3 },{ 4,4, 4 } };	
	uchar arr[12] = { 1, 1,1 ,2, 2,2, 3, 3,3 ,4,4, 4 };
	vector<uchar> v(arr, arr + 12);//将一维数组转为vector
	cv::Mat dest=convertVector2Mat<uchar>(v,1, 4);//将vector转为Mat
	cout << "外部dest=\n" << dest << endl;

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

     上面的程序,声明一个函数convertVector2Mat()实现将vector转OpenCV的Mat类型,因此用到了reshape()函数,刚看convertVector2Mat()函数,你是很难发现问题,但运行结果,且出乎意外:

内部dest=
[  1,   1,   1;
   2,   2,   2;
   3,   3,   3;
   4,   4,   4]
外部dest=
[221, 221, 221;
 221, 221, 221;
 221, 221, 221;
 221, 221, 221]

    Why?什么情况,convertVector2Mat()函数里的dest和返回dest的结果为什么不一样的??为什么在函数计算正确的值,返回结果后却出现问题了。 这个坑,鄙人弄了好久,一开始以为是类型出现问题,于是统一所有数据的类型为uchar或者int类型;后来,我改为引用的方式获得dest的值,然而....问题依旧存在!!!

   再后来,一条代码一条代码的审核,终于定位到reshape()函数,百度搜索的reshape()函数的用法都是坑,没有一个说清楚,后来还是到OpenCV官网查看最权威的解释:https://docs.opencv.org/3.2.0/d3/d63/classcv_1_1Mat.html#a4eb96e3251417fa88b78e2abd6cfd7d8

Changes the shape and/or the number of channels of a 2D matrix without copying the data.

The method makes a new matrix header for *this elements. The new matrix may have a different size and/or different number of channels. Any combination is possible if:

  • No extra elements are included into the new matrix and no elements are excluded. Consequently, the product rows*cols*channels() must stay the same after the transformation.
  • No data is copied. That is, this is an O(1) operation. Consequently, if you change the number of rows, or the operation changes the indices of elements row in some other way, the matrix must be continuous. See Mat::isContinuous .

For example, if there is a set of 3D points stored as an STL vector, and you want to represent the points as a 3xN matrix, do the following:

std::vector<Point3f> vec;
...
Mat pointMat = Mat(vec). // convert vector to Mat, O(1) operation
reshape(1). // make Nx3 1-channel matrix out of Nx1 3-channel.
// Also, an O(1) operation
t(); // finally, transpose the Nx3 matrix.
// This involves copying all the elements
Parameters
cn New number of channels. If the parameter is 0, the number of channels remains the same.
rows New number of rows. If the parameter is 0, the number of rows remains the same.

     看到了吧,(1)由于reshape只是在逻辑上改变矩阵的行列数或者通道数, 但不会进行任何的数据的复制操作,新矩阵中不包含额外元素,也不包含任何元素。 因此,转换后rows*cols*channels()必须保持不变。(2)没有数据被复制。 也就是说,这是一个O(1)操作。 因此,如果更改行数,或者操作以其他方式更改元素行的索引,则矩阵必须是连续的。关于OpenCV连续性的使用方法可以参考本人博客:https://blog.csdn.net/guyuealian/article/details/78614662

     知道这个reshape()的实现机制,鄙人猜测出现这种问题的原因了,应该是convertVector2Mat()函数内部创建的mat和dest变量本质上是同一块内存空间的,而返回后就已经被释放掉。dest引用的数据是来自于mat,并没有拷贝数据,因此在mat没有释放的情况下是正常的,但引用的内存就被释放后就变成野数据。要想解决这个问题也简单,返回时,clone一份即可。如:

#include <opencv2/opencv.hpp>
#include "continuousCapture.h"

using namespace cv;
using namespace std;


template<typename _Tp>
cv::Mat convertVector2Mat(vector<_Tp> v, int cn, int rows)
{
	cv::Mat mat = cv::Mat(v);//将vector变成一列的mat
	cv::Mat dest = mat.reshape(cn, rows).clone();//clone一份再返回
	cout << "内部dest=\n" << dest << endl;
	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
	 */
	//int arr[4][3] = { { 1, 1,1 },{ 2, 2,2 },{ 3, 3,3 },{ 4,4, 4 } };	
	uchar arr[12] = { 1, 1,1 ,2, 2,2, 3, 3,3 ,4,4, 4 };
	vector<uchar> v(arr, arr + 12);//将一维数组转为vector
	cv::Mat dest;
	dest=convertVector2Mat<uchar>(v, 1, 4);//将vector转为Mat
	cout << "外部dest=\n" << dest << endl;

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

猜你喜欢

转载自blog.csdn.net/guyuealian/article/details/80252853