我编写了一个自定义类来存储图像,并最终基于这些图像计算校准,但是在存储图像的方式上遇到了问题。我有两个重载函数可以执行此操作,一个函数使用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<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
的内容将第二个图像作为第一个元素,将第三个图像作为第二个元素,而第三个元素将一个{{1 }}中包含无效数据。
这两种方法存储图像的原因为何?解决此问题的方法是什么?
答案 0 :(得分:5)
这些症状听起来很像是在进行浅表复制,这意味着第二种方法的行为不确定,因为向量中的cv::Mat
比img_data
寿命更长。让我看看是否可以找到您使用的构造函数的文档。
找到了here。是的,它会进行浅表复制(添加了强调):
采用数据和步骤参数的矩阵构造函数不分配矩阵数据。相反,他们只是初始化指向指定数据的矩阵标题,这意味着不复制任何数据。
因此,当第二种方法将图像推送到_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;
};