使用 std::unique_ptr 时 std::vector 中的数据不同
Data in `std::vector` different when using `std::unique_ptr`
我已经编写了一个自定义 class 来存储图像并最终根据这些图像计算校准,但我在存储图像的方式上遇到了问题。我有两个重载函数可以执行此操作,一个使用 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
class 将图像存储为 std::unique_ptr<RGBImage>
。 RGBImage
class 将图像数据存储为 std::vector<unsigned char>
.
这是图像从 main
加载到 class 的方式:
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
比 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
和存储数据的东西组成的 class,可能是 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;
};
我已经编写了一个自定义 class 来存储图像并最终根据这些图像计算校准,但我在存储图像的方式上遇到了问题。我有两个重载函数可以执行此操作,一个使用 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
class 将图像存储为 std::unique_ptr<RGBImage>
。 RGBImage
class 将图像数据存储为 std::vector<unsigned char>
.
这是图像从 main
加载到 class 的方式:
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
比 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
和存储数据的东西组成的 class,可能是 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;
};