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

raf*_*010 0 c++ opencv smart-pointers c++11

我编写了一个自定义类来存储图像,并最终基于这些图像计算校准,但是在存储图像的方式上遇到了问题。我有两个重载函数可以执行此操作,一个函数使用读取文件中的图像,另一个函数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;
}
Run Code Online (Sandbox Code Playgroud)

这是不起作用的功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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

两种方法存储图像的方式不同的原因是什么?解决该问题的方法是什么?

JaM*_*MiT 5

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

在这里找到它。是的,它做了一个浅表副本(添加了强调):

带有数据和步骤参数的矩阵构造函数不分配矩阵数据。相反,他们只是初始化指向指定数据的矩阵头,这意味着没有数据被复制

因此,当第二种方法将图像压入时_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;
};
Run Code Online (Sandbox Code Playgroud)