使用`std :: unique_ptr`时`std :: vector`中的数据不同

时间:2019-08-31 21:40:58

标签: c++ opencv c++11 smart-pointers

我编写了一个自定义类来存储图像,并最终基于这些图像计算校准,但是在存储图像的方式上遇到了问题。我有两个重载函数可以执行此操作,一个函数使用cv::imread从文件中读取图像,另一个函数使用中间的Snapshot数据结构来保存数据。使用cv::imread的函数可以正常工作,但是使用自定义数据结构的函数则不能。我现在正在尝试存储三个图像,问题是当我将图像推入矢量时,第二个图像的数据被复制到第一个图像中。

这是工作功能:

bool CalibClass::AddImage(const std::string& snapshotPath) {
    cv::Mat img = cv::imread(snapshotPath);

    // _snapshots is a private member declared as a std::vector<cv::Mat>
    _snapshots.push_back(img);

    return true;
}

此功能不起作用:

bool CalibClass::AddImage(const ImageSet& snapshot) {

    RGBImage *rgb_image_ptr = snapshot.GetRGBImage();

    std::vector<unsigned char> img_data(rgb_image_ptr->GetData());
    cv::Mat img(rgb_image_ptr->GetHeight(), rgb_image_ptr->GetWidth(), CV_8UC3, img_data.data());

    _snapshots.push_back(img);

    return true;
}

ImageSet类将图像存储为std::unique_ptr<RGBImage>RGBImage类将图像数据存储为std::vector<unsigned char>

这是将图像从main加载到类中的方式:

cv::Mat img1 = cv::imread("img1.png");
cv::Mat img2 = cv::imread("img2.png");      
cv::Mat img3 = cv::imread("img3.png");

int length = img1.total() * img1.elemSize();

std::vector<unsigned char> data1;
std::vector<unsigned char> data2;
std::vector<unsigned char> data3;
for (int i = 0; i < length; i++) {
    data1.push_back(img1.data[i]);
}

for (int i = 0; i < length; i++) {
    data2.push_back(img2.data[i]);
}

for (int i = 0; i < length; i++) {
    data3.push_back(img3.data[i]);
}


CalibClass calib_test;

std::unique_ptr<RGBImage> rgb_image_ptr1(new RGBImage(img1.rows, img1.cols, data1));
ImageSet new_snap1(rgb_image_ptr1, nullptr, 0);
calib_test.AddImage(new_snap1);


std::unique_ptr<RGBImage> rgb_image_ptr2(new RGBImage(img2.rows, img2.cols, data2));
ImageSet new_snap2(rgb_image_ptr2, nullptr, 0);
calib_test.AddImage(new_snap2);

std::unique_ptr<RGBImage> rgb_image_ptr3(new RGBImage(img3.rows, img3.cols, data3));
ImageSet new_snap3(rgb_image_ptr3, nullptr, 0);
calib_test.AddImage(new_snap3);

当我在函数中放置一个断点并检查_snapshots的内容时,第一个元素是第二个图像,第二个和第三个元素是第三个图像。当我在所有AddImage()调用之后都设置断点时,_snapshots的内容将第二个图像作为第一个元素,将第三个图像作为第二个元素,而第三个元素将一个{{1 }}中包含无效数据。

这两种方法存储图像的原因为何?解决此问题的方法是什么?

1 个答案:

答案 0 :(得分:5)

这些症状听起来很像是在进行浅表复制,这意味着第二种方法的行为不确定,因为向量中的cv::Matimg_data寿命更长。让我看看是否可以找到您使用的构造函数的文档。

找到了here。是的,它会进行浅表复制(添加了强调):

  

采用数据和步骤参数的矩阵构造函数不分配矩阵数据。相反,他们只是初始化指向指定数据的矩阵标题,这意味着不复制任何数据

因此,当第二种方法将图像推送到_snapshots时,该图像的数据将存储在局部变量img_data中。然后函数结束,使该数据无效。因此,当您查看数据时,会得到不确定的行为。

要解决此问题,您需要确保已复制数据。您还希望确保在某些时候释放数据以避免内存泄漏。一种方法是定义一个类,该类由cv::Mat和一些用于存储数据的类(可能是std::vector<unsigned char>)组成。 (使用后一个成员代替局部变量img_data。)起点可能是:

class MatWrapper {
    public:
        explicit MatWrapper(const RGBImage & rgb_image) :
            data(rgb_image.GetData()),
            image(rgb_image.GetHeight(), rgb_image.GetWidth(), CV_8UC3, data.data())
        {}

    private:
        std::vector<unsigned char> data; // Declaration order matters!
        cv::Mat image;
};