【问题标题】:Data in `std::vector` different when using `std::unique_ptr`使用`std::unique_ptr`时`std::vector`中的数据不同
【发布时间】:2020-01-04 14:16:44
【问题描述】:

我编写了一个自定义类来存储图像并最终根据这些图像计算校准,但我遇到了图像存储方式的问题。我有两个执行此操作的重载函数,一个使用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&lt;RGBImage&gt;RGBImage 类将图像数据存储为std::vector&lt;unsigned char&gt;

这是图像从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 的内容有第二个图像作为第一个元素,第三个图像作为第二个元素,第三个元素有一个带有无效数据的cv::Mat

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

【问题讨论】:

  • RGBImage *rgb_image_ptr = snapshot.GetRGBImage(); 你如何将唯一指针转换为原始指针?
  • snapshot.GetRGBImage() 返回一个指向RGBImage 的指针,它在内部将图像数据存储为std::vector&lt;unsigned char&gt;
  • 但是图像集将其存储为唯一指针?一个最小的完整可重现示例怎么样?
  • 是的,ImageSet 将其存储为 std::unique_ptr&lt;RGBImage&gt;
  • 我无法想象用最少 段代码来重现您的问题。请发帖minimal reproducible example

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


【解决方案1】:

这些症状听起来很像发生了浅拷贝,这意味着第二种方法中的未定义行为,因为向量中的cv::Matimg_data 寿命长。让我看看能否找到您使用的构造函数的文档。

找到它here。是的,它做了一个浅拷贝(强调):

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

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

要解决此问题,您需要确保复制数据。您还需要确保在某个时候释放数据以避免内存泄漏。一种方法是定义一个由cv::Mat 和存储数据的东西组成的类,可能是std::vector&lt;unsigned char&gt;。 (使用后一个成员而不是局部变量 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;
};

【讨论】:

  • 这很有帮助,没有意识到内存正在被破坏。我能够将memcpy 的数据导入cv::Mat,然后一切正常。谢谢!
猜你喜欢
  • 1970-01-01
  • 2014-05-12
  • 2019-11-07
  • 2018-02-02
  • 2015-10-11
  • 2017-04-04
  • 1970-01-01
  • 2022-10-21
  • 1970-01-01
相关资源
最近更新 更多