问题情形:
需要记录不同的两个Opencv的Mat矩阵,由于数量是动态确定的(且很可能>4个),所以想通过构建cv::Mat的容器来保存结果。同时,每个Mat必须初始化为0矩阵且分配内存。
错误使用:
std::vector<cv::Mat> featureMats(numOfFeatures, cv::Mat::zeros(10, 10, CV_32FC1));
//这里是想给featureMats分配numOfFeatures个10×10的0矩阵
for (int i = 0; i < img_h; ++i)
{
for (int j = 0; j < img_w; ++j)
{
......//一些计算
for (int typeID = 0; typeID < numOfFeatures; ++typeID)
{
....//一些计算
float tmpVal= getFeatureNum(typeID );
featureMats[typeID].at<float>(i, j) = tmpVal;
}
}
}
出现的问题:
上述代码执行完,无论怎么处理,得到的numOfFeatures个矩阵的结果都是一摸一样(实际上应该是不一样的!!)debug过程中,查看了每一次最内部循环中 tmpVal的计算值是不同的,从这里开始发现问题不对劲。
debug过程:
首先断点打在float tmpVal= getFeatureNum(typeID );这一句,然后查看每次计算的tmpVal的值,发现内部循环中每次计算的tmpVal是没问题的,所以问题要么出在赋值,要么出在多线程。
接下来,把多线程关掉,再用上述方法debug,排除了多线程的问题。最后,在所有循环结束后查看featureMats中每一个Mat的值,发现这里就已经全部一模一样了,那就是赋值的问题了。OK,把每次typeID对应的循环中,赋值完的所有Mat的值打印一下,发现第二次给第二个Mat赋值的时候,前一个Mat中的值也被改变了!!!所以初步估计是std::vector<cv::Mat> featureMats(numOfFeatures, cv::Mat::zeros(10, 10, CV_32FC1));这里初始化的时候,内部其实是类似于OpenCV的浅拷贝的形式,所有Mat共用了一块内存!所有修改第二个Mat时第一个也会变。
为进一步验证,提前创建好了不同的几个Mat并初始化:
cv::Mat featureMat0 = cv::Mat::zeros(10, 10, CV_32FC1));
cv::Mat featureMat1 = cv::Mat::zeros(10, 10, CV_32FC1));
cv::Mat featureMat2 = cv::Mat::zeros(10, 10, CV_32FC1));
最内部for循环改成:
featureMat0 .at<float>(i, j) = getFeatureNum(0);
featureMat1 .at<float>(i, j) = getFeatureNum(1);
featureMat2 .at<float>(i, j) = getFeatureNum(2);
最后处理完输出发现featureMat0、featureMat1、featureMat2之间是不同的!确定了问题所在,就是STL的vector初始化时把第一个元素“拷贝”给后续的元素,这里的“拷贝”对于OpenCV的Mat而言,是“浅拷贝”!
解决方案:
只要把featureMats中每个Mat都分配内存实现”深拷贝“即可,方法就很多了!下面是一种方案:
std::vector<cv::Mat> featureMats(numOfFeatures);
for (int i = 0; i < numOfFeatures; ++i)
{
cv::Mat tmp = cv::Mat::zeros(10, 10, CV_32FC1);
tmp.copyTo(featureMats[i]);
}
遇上OpenCV的Mat,一定要注意”浅拷贝“和”深拷贝“的问题,虽然早就”领教“过这个问题了,但是这次又坑在这里了,MD...记录一下,以后再翻着看看...