简体   繁体   English

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

[英]Data in `std::vector` different when using `std::unique_ptr`

I have written a custom class that stores images and eventually computes a calibration based on those images, but I am encountering an issue in the way that the images are stored. 我编写了一个自定义类来存储图像,并最终根据这些图像计算校准值,但是在存储图像的方式上遇到了问题。 I have two overloaded functions that do this, with one reading the images from file using cv::imread , and the other using an intermediate Snapshot data structure for holding the data. 我有两个重载函数可以执行此操作,一个函数使用cv::imread从文件中读取图像,另一个函数使用中间的Snapshot数据结构来保存数据。 The function using cv::imread works fine, but the one using the custom data structure does not. 使用cv::imread的功能可以cv::imread工作,但是使用自定义数据结构的功能则不能。 I am trying to store three images right now, and the issue is that as I push the images into a vector, the data for the second images is copied into the first one. 我正在尝试存储三个图像,问题是当我将图像推入矢量时,第二个图像的数据被复制到第一个图像中。

This is the working function: 这是工作功能:

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;
}

This is the function that is not working: 这是不起作用的功能:

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;
}

The ImageSet class stores images as an std::unique_ptr<RGBImage> . ImageSet类将图像存储为std::unique_ptr<RGBImage> The RGBImage class stores the image data as a std::vector<unsigned char> . RGBImage类将图像数据存储为std::vector<unsigned char>

This is how the images are loaded into the class from the main : 这是将图像从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);

When I put a break point inside the function and check the content of the _snapshots , the first element is the second image, and the second and third elements are the third image. 当我在函数中放置一个断点并检查_snapshots的内容时,第一个元素是第二个图像,第二个和第三个元素是第三个图像。 When I set a break point after all the AddImage() calls, the content of _snapshots has the second image as the first element, the third image as the second element, and the third element has a cv::Mat with invalid data. 当我设置断点的所有后AddImage()调用,内容_snapshots具有第二图像作为第一元件,作为第二元件的第三图像,以及第三元件具有cv::Mat与无效数据。

What is the reason why the two methods are storing the images differently? 两种方法存储图像的方式不同的原因是什么? What would be the way to fix this issue? 解决该问题的方法是什么?

Those symptoms sound a lot like there is a shallow copy going on, which would mean undefined behavior in the second approach since the cv::Mat in the vector outlives img_data . 这些症状听起来很像是正在进行浅拷贝,这意味着第二种方法中的不确定行为,因为向量中的cv::Mat超过了img_data Let me see if I can find documentation for the constructor you used. 让我看看是否可以找到您使用的构造函数的文档。

Found it here . 在这里找到它。 Yes, it does a shallow copy (emphasis added): 是的,它做了一个浅表副本(添加了强调):

Matrix constructors that take data and step parameters do not allocate matrix data. 带有数据和步骤参数的矩阵构造函数不分配矩阵数据。 Instead, they just initialize the matrix header that points to the specified data, which means that no data is copied . 相反,他们只是初始化指向指定数据的矩阵头,这意味着没有数据被复制

So when the second approach pushes an image onto _snapshots , that image's data lives in the local variable img_data . 因此,当第二种方法将图像推送到_snapshots ,该图像的数据将存储在局部变量img_data Then the function ends, making that data invalid. 然后函数结束,使该数据无效。 Thus you get undefined behavior when you look at the data. 因此,当您查看数据时,您将获得未定义的行为。

To solve this, you would need to make sure the data gets copied. 为了解决这个问题,您需要确保复制了数据。 You would also want to make sure the data gets freed at some point to avoid a memory leak. 您还希望确保在某些时候释放数据以避免内存泄漏。 One approach is to define a class consisting of a cv::Mat and something to store the data, perhaps a std::vector<unsigned char> . 一种方法是定义一个由cv::Mat和一些用于存储数据的类组成的类,也许是std::vector<unsigned char> (Use the latter member instead of the local variable img_data .) A starting point might be the following: (使用后一个成员代替局部变量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;
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM