调整OpenCV Mat向量向量的大小时出乎意料的结果



我在Ubuntu 16.04,GCC 5.4,最新OpenCV。假设我有一个double

的向量
  std::vector<std::vector<double>> vecvecdouble;
  vecvecdouble.resize(3, std::vector<double>(3, 0));
  for (int j = 0; j < 3; j++){
    for (int i = 0; i < 3; i++){
      if (i == 0){
        vecvecdouble[i][j] = 1;
        vecvecdouble[i][j] = 1;
      }
      if (i == 1){
        vecvecdouble[i][j] = 2;
        vecvecdouble[i][j] = 2;
      }
      if (i == 1 && j == 0){
        std::cout << vecvecdouble[i - 1][j] << std::endl;
        std::cout << vecvecdouble[i][j] << std::endl;
        std::cout << vecvecdouble[i + 1][j] << std::endl;
      }
    }
  }

它打印

1
2
0

正如预期的。但是,如果我对OpenCV cv::mat

做同样的事情
  std::vector<std::vector<cv::Mat>> vecvecmat;
  vecvecmat.resize(
      3, std::vector<cv::Mat>(3, cv::Mat(4, 4, CV_64FC1, cv::Scalar(0.0))));
  for (int j = 0; j < 3; j++){
    for (int i = 0; i < 3; i++){
      if (i == 0){
        vecvecmat[i][j].at<double>(0, 0) = 1;
        vecvecmat[i][j].at<double>(0, 1) = 1;
      }
      if (i == 1){
        vecvecmat[i][j].at<double>(0, 0) = 2;
        vecvecmat[i][j].at<double>(0, 1) = 2;
      }
      if (i == 1 && j == 0){
        std::cout << vecvecmat[i - 1][j] << std::endl;
        std::cout << vecvecmat[i][j] << std::endl;
        std::cout << vecvecmat[i + 1][j] << std::endl;
      }
    }
  }

它打印

[2, 2, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]
[2, 2, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]
[2, 2, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]

这是完全出乎意料的,因为我希望它能打印

[1, 1, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]
[2, 2, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]
[0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0;
 0, 0, 0, 0]

但是,如果我不尝试将矢量大小调整为一条线,然后浏览两个循环,则实际上将返回预期的结果

  std::vector<std::vector<cv::Mat>> vecvecmat;
  vecvecmat.resize(3);
  for (int i = 0; i < 3; i++){
    for (int j = 0; j < 3; j++){
      cv::Mat mymat = cv::Mat(4, 4, CV_64FC1, cv::Scalar(0.0));
      vecvecmat[i].push_back(mymat);
    }
  }
  for (int j = 0; j < 3; j++){
    for (int i = 0; i < 3; i++){
      if (i == 0){
        vecvecmat[i][j].at<double>(0, 0) = 1;
        vecvecmat[i][j].at<double>(0, 1) = 1;
      }
      if (i == 1){
        vecvecmat[i][j].at<double>(0, 0) = 2;
        vecvecmat[i][j].at<double>(0, 1) = 2;
      }
      if (i == 1 && j == 0){
        std::cout << vecvecmat[i - 1][j] << std::endl;
        std::cout << vecvecmat[i][j] << std::endl;
        std::cout << vecvecmat[i + 1][j] << std::endl;
      }
    }
  }

这条线怎么了?

vecvecmat.resize(3, std::vector<cv::Mat>(3, cv::Mat(4, 4, CV_64FC1, cv::Scalar(0.0))));

cv::Mat say的复制构造函数的文档(强调矿山):

参数

m数组(整体或部分)被分配给构造的矩阵。没有这些构造函数复制数据。,指向M数据或其子阵列的标题是构造并与之关联的。参考计数器(如果有)会增加。因此,使用该构造函数修改矩阵时,还可以修改m 的相应元素。如果您想拥有子阵列的独立副本,请使用Mat::clone()

您在调用std::vector::resize中构建的初始矩阵正在使用此复制构造函数*复制到向量的每个元素中,因此它们都指向相同的数据。修改一个矩阵时,您会修改所有矩阵。

*或使用operator=,它做同样的事情(我不确定哪个,但不会影响结果)

我建议这样初始化您的向量:

std::vector<std::vector<cv::Mat>> vecvecmat;
vecvecmat.resize(3, std::vector<cv::Mat>());
for(auto& v : vecvecmat)
{
    for(std::size_t i = 0; i < 3; ++i)
    {
        v.emplace_back(4, 4, CV_64FC1, cv::Scalar(0.0));
    }
}

最新更新