【尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/80252853
本人在使用OpenCV的reshape()函数时,曾经碰到一个大坑,出现一个非常奇葩的问题,先把原始的代码贴上:
#include
#include "continuousCapture.h"
using namespace cv;
using namespace std;
template
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 v(arr, arr + 12);//将一维数组转为vector
cv::Mat dest=convertVector2Mat(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:
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:
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
#include "continuousCapture.h"
using namespace cv;
using namespace std;
template
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 v(arr, arr + 12);//将一维数组转为vector
cv::Mat dest;
dest=convertVector2Mat(v, 1, 4);//将vector转为Mat
cout << "外部dest=\n" << dest << endl;
system("pause");
waitKey();
return 0;
}