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

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

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

这是工作函数:

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

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

最佳答案

这些症状听起来很像正在进行浅拷贝,这意味着自 cv::Mat 以来第二种方法中的未定义行为在 vector 中超过img_data .让我看看能否找到您使用的构造函数的文档。

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

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.

所以当第二种方法将图像推送到 _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;
};

关于c++ - 使用 `std::vector` 时 `std::unique_ptr` 中的数据不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57741764/

相关文章:

c++ - OpenMP:为 'shared' 预先确定 'shared' ?

c++ - 为什么 sizeof() 我的双数组 [4] 只有 4?

python - 在opencv python中加速每个像素循环

c++ - 包装类的静态初始化列表

c++ - 为什么在 GCC/C++ 中弹出 "pragma GCC diagnostic push"警告?

python - CUDA(GPU) 作为 OpenCV 后端

python - 在选择图像轮廓时如何避免对图像(条形图)进行分组?

c++ - 在 C++ 中使用智能指针声明双向矩阵

sockets - 当所有 IP 地址都相同时,指定用于 UDP 写入的以太网端口

c++ - 使用 LLVM 在 OSX 上将问题与 boost::program_options 联系起来