std::vector 的 push_back 是否创建了参数的深层副本?
Does std::vector's push_back create a deep copy of the argument?
我有一个图像列表,它存储了几个 Mat 对象,我需要将它们推入 Mat 的向量中。
vector<Mat> images;
Mat image;
for ( i = 0; i < n; i++)
{
\ importing the i-th image into a mat image;
images.push_back(image);
}
这会创建图像的深拷贝吗?
当然
vector<Mat> images;
Mat image (100, 100, CV_8UC(1), Scalar::all(255));
images.push_back(image);
image.release();
Mat temp (100,100, CV_8UC(1), Scalar::all(0));
image = temp;
images.push_back(image);
imshow("black", images[0]);
waitKey(0);
imshow("White",images[1]);
waitKey(0);
这应该显示一张黑白图像。
另一个问题
Mat img;
vector<mat> images;
for (i = 1; i < 5, i++)
{
img.create(h,w,type); // h,w and type are given correctly
// input an image from somewhere to img correctly.
images.push_back(img);
img.release();
}
for (i = 1; i < 5; i++) images[i].release();
这仍然让我有内存泄漏,这背后的原因可能是什么?
它不会进行深度复制,因为 cv::Mat
是共享指针。添加到向量 images
时,您必须使用 clone()
或类似的方法。
std::vector::push_back
使用对象的复制构造函数将一个元素插入向量中。所以如果 Mat 复制构造函数创建了 Mat 对象的深拷贝,你得到了一个深拷贝。
std::vector::push_back 会将对象复制或移动到向量中,这意味着将调用 Mat
的复制构造函数或移动构造函数。所以这取决于Mat
.
参见CopyInsertable and MoveInsertable。
这里有一个小测试程序,用于演示 cv::Mat 对象(矩阵头)的数据共享属性!
int main()
{
// create input of size 512x512
cv::Mat input = cv::imread("../inputData/Lenna.png");
// create a second input of size 256x256
cv::Mat modifiedInput;
cv::resize(input, modifiedInput, cv::Size(256,256));
std::vector<cv::Mat> images;
// first element will be a "deep copy" where the matrix elements will be copied to a new memory location and a new header will be created, referecing those matrix elements.
images.push_back(input.clone());
// 6 times copy the "input" to "images".
// All the copies will (deep) copy the matrix header but they will share the matrix elements (because their memory LOCATION will be copied)
for(unsigned int i=0; i<6; ++i)
images.push_back(input);
// now some experiments:
// draw a circle to input variable. At this point it should share it's matrix elements with images[1-5]
cv::circle(input, cv::Point(100,100), 30, cv::Scalar(0,0,0), -1);
// draw a circle to a vector element:
cv::circle(images[5], cv::Point(300,100), 30, cv::Scalar(0,0,0), -1);
// use a openCV function that will allocate new memory, if the destination dimensions don't fit:
// to a mat whose dimensions fit:
// remember that input.size() == vector[0..5].size
// compute median blur and target one of the matrices that share their data at the moment:
cv::medianBlur(input, images[3], 11);
cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);
cv::waitKey(0);
此时它看起来像这样:除了第一个矩阵,所有矩阵都共享其元素的数据,因为 .clone()
.
强制进行了深度复制
现在继续:
// to a mat whose dimensions don't fit (new memory will be allocated, not shared by the other matrix headers anymore):
// images[3] will not share the data with other matrix headers afterwards
cv::medianBlur(modifiedInput, images[3], 11);
// now images[3] and images[4] will share matrix elements
images[4] = images[3];
cv::circle(images[4], cv::Point(128,128), 20, cv::Scalar(255,255,255), 3);
// create a deep-copy of 256x256 input to overwrite images[5] (not modifying any other image's matrix elements)
images[5] = modifiedInput.clone();
cv::circle(images[5], cv::Point(0,0), 30, cv::Scalar(0,255,0), -1);
cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);
//cv::imshow("input", input);
//cv::imwrite("../outputData/MainBase.png", input);
cv::waitKey(0);
return 0;
}
看起来像这样:
这一次,medianBlur
的调用没有与所有其他矩阵共享数据,因为目标图像的尺寸不适合,所以必须为 images[3]
分配新内存在 medianBlur 方法中。所以 images[3] 引用了不同的数据元素!
所有这一切可能有点棘手,因为用户可能无法直接看到哪些函数调用将分配新数据而哪些不会,所以如果您想确保分配新数据,您应该这样做在每个垫子的开头,或使用空垫子作为目的地(或者在开始时不共享任何数据)。
还有一件事:
cv::Mat emptyMat;
std::vector<cv::Mat> images(n, emptyMat); // insert n copies of emptyMat header
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(emptyMat) // same result
这两个都是存着用的,不共享数据,因为所有的emptyMat一开始都没有数据,所以不能共享数据。每当将任何数据分配给任何矢量元素时,其他元素都不知道,因此他们不会共享该数据。
// BUT:
cv::Mat notEmptyMat = cv::Mat::zeros(height, width, type);
std::vector<cv::Mat> images(n, notEmptyMat ); // insert n copies of emptyMat header which references the assigned zeroes data of size width x height
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(notEmptyMat ) // same result
在这里,数据是共享的,只要您更改其中一个矩阵的数据,其他矩阵也会更改。但很明显,如果您将新的数据内存分配给其中一个矩阵,其他矩阵仍会引用它们的其他数据内存。
我有一个图像列表,它存储了几个 Mat 对象,我需要将它们推入 Mat 的向量中。
vector<Mat> images;
Mat image;
for ( i = 0; i < n; i++)
{
\ importing the i-th image into a mat image;
images.push_back(image);
}
这会创建图像的深拷贝吗?
当然
vector<Mat> images;
Mat image (100, 100, CV_8UC(1), Scalar::all(255));
images.push_back(image);
image.release();
Mat temp (100,100, CV_8UC(1), Scalar::all(0));
image = temp;
images.push_back(image);
imshow("black", images[0]);
waitKey(0);
imshow("White",images[1]);
waitKey(0);
这应该显示一张黑白图像。
另一个问题
Mat img;
vector<mat> images;
for (i = 1; i < 5, i++)
{
img.create(h,w,type); // h,w and type are given correctly
// input an image from somewhere to img correctly.
images.push_back(img);
img.release();
}
for (i = 1; i < 5; i++) images[i].release();
这仍然让我有内存泄漏,这背后的原因可能是什么?
它不会进行深度复制,因为 cv::Mat
是共享指针。添加到向量 images
时,您必须使用 clone()
或类似的方法。
std::vector::push_back
使用对象的复制构造函数将一个元素插入向量中。所以如果 Mat 复制构造函数创建了 Mat 对象的深拷贝,你得到了一个深拷贝。
std::vector::push_back 会将对象复制或移动到向量中,这意味着将调用 Mat
的复制构造函数或移动构造函数。所以这取决于Mat
.
参见CopyInsertable and MoveInsertable。
这里有一个小测试程序,用于演示 cv::Mat 对象(矩阵头)的数据共享属性!
int main()
{
// create input of size 512x512
cv::Mat input = cv::imread("../inputData/Lenna.png");
// create a second input of size 256x256
cv::Mat modifiedInput;
cv::resize(input, modifiedInput, cv::Size(256,256));
std::vector<cv::Mat> images;
// first element will be a "deep copy" where the matrix elements will be copied to a new memory location and a new header will be created, referecing those matrix elements.
images.push_back(input.clone());
// 6 times copy the "input" to "images".
// All the copies will (deep) copy the matrix header but they will share the matrix elements (because their memory LOCATION will be copied)
for(unsigned int i=0; i<6; ++i)
images.push_back(input);
// now some experiments:
// draw a circle to input variable. At this point it should share it's matrix elements with images[1-5]
cv::circle(input, cv::Point(100,100), 30, cv::Scalar(0,0,0), -1);
// draw a circle to a vector element:
cv::circle(images[5], cv::Point(300,100), 30, cv::Scalar(0,0,0), -1);
// use a openCV function that will allocate new memory, if the destination dimensions don't fit:
// to a mat whose dimensions fit:
// remember that input.size() == vector[0..5].size
// compute median blur and target one of the matrices that share their data at the moment:
cv::medianBlur(input, images[3], 11);
cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);
cv::waitKey(0);
此时它看起来像这样:除了第一个矩阵,所有矩阵都共享其元素的数据,因为 .clone()
.
现在继续:
// to a mat whose dimensions don't fit (new memory will be allocated, not shared by the other matrix headers anymore):
// images[3] will not share the data with other matrix headers afterwards
cv::medianBlur(modifiedInput, images[3], 11);
// now images[3] and images[4] will share matrix elements
images[4] = images[3];
cv::circle(images[4], cv::Point(128,128), 20, cv::Scalar(255,255,255), 3);
// create a deep-copy of 256x256 input to overwrite images[5] (not modifying any other image's matrix elements)
images[5] = modifiedInput.clone();
cv::circle(images[5], cv::Point(0,0), 30, cv::Scalar(0,255,0), -1);
cv::imshow("0", images[0]);
cv::imshow("1", images[1]);
cv::imshow("2", images[2]);
cv::imshow("3", images[3]);
cv::imshow("4", images[4]);
cv::imshow("5", images[5]);
//cv::imshow("input", input);
//cv::imwrite("../outputData/MainBase.png", input);
cv::waitKey(0);
return 0;
}
看起来像这样:
这一次,medianBlur
的调用没有与所有其他矩阵共享数据,因为目标图像的尺寸不适合,所以必须为 images[3]
分配新内存在 medianBlur 方法中。所以 images[3] 引用了不同的数据元素!
所有这一切可能有点棘手,因为用户可能无法直接看到哪些函数调用将分配新数据而哪些不会,所以如果您想确保分配新数据,您应该这样做在每个垫子的开头,或使用空垫子作为目的地(或者在开始时不共享任何数据)。
还有一件事:
cv::Mat emptyMat;
std::vector<cv::Mat> images(n, emptyMat); // insert n copies of emptyMat header
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(emptyMat) // same result
这两个都是存着用的,不共享数据,因为所有的emptyMat一开始都没有数据,所以不能共享数据。每当将任何数据分配给任何矢量元素时,其他元素都不知道,因此他们不会共享该数据。
// BUT:
cv::Mat notEmptyMat = cv::Mat::zeros(height, width, type);
std::vector<cv::Mat> images(n, notEmptyMat ); // insert n copies of emptyMat header which references the assigned zeroes data of size width x height
// or
for(unsigned int i=0; i<n; ++i)
images.push_back(notEmptyMat ) // same result
在这里,数据是共享的,只要您更改其中一个矩阵的数据,其他矩阵也会更改。但很明显,如果您将新的数据内存分配给其中一个矩阵,其他矩阵仍会引用它们的其他数据内存。