std :: vector的push_back是否创建了参数的深层副本?

时间:2015-09-17 14:24:06

标签: c++ opencv vector copy-constructor mat

我有一个图像列表存储了几个Mat对象,我需要将它们推入Mat的矢量。

Array [ 1, 2, 3, 4, 5 ]

这是否会创建图像的深层副本?

不确定

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); 

这仍然让我有内存泄漏,背后可能是什么原因?

4 个答案:

答案 0 :(得分:7)

std::vector::push_back使用对象的复制构造函数将元素插入向量中。因此,如果Mat复制构造函数创建了Mat对象的深层副本,则会获得深层副本。

答案 1 :(得分:3)

这是一个小小的测试程序,用于演示特殊的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()进行深层复制。

enter image description here

现在继续这个:

    // 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;
}

看起来像这样: enter image description here

这一次,medianBlur的调用没有与所有其他矩阵共享数据,因为目标图像的尺寸DID NOT FIT,因此必须为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 分配新的内存。 medianBlur方法。所以images [3]引用了不同的数据元素!

所有这些可能有点棘手,因为用户可能无法直接看到,哪些函数调用将分配新数据,哪些不分配,所以如果你想确保分配新数据,你应该在开始时这样做对于每个垫子,或使用空垫作为目的地(或者不要在开头共享任何数据)。

还有一件事:

// 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

这两个都是保存使用,以便不共享数据,因为所有emptyMat在开头都没有任何数据,因此不能共享任何数据。每当向任何矢量元素分配任何数据时,另一个数据都不知道,因此他们不会共享该数据。

    /// <summary>
    /// Helper class for having a object's constructor automatically assigned by a "GetService" request.
    /// </summary>
    /// <param name="resolver">The resolver.</param>
    /// <param name="type">The type to register.</param>
    public static void Register<TConcrete, TInterface>(this IMutableDependencyResolver resolver)
        where TConcrete : class
    {
        var concreteType = typeof(TConcrete);

        // Must be a single constructor
        var constructors = concreteType.GetConstructors().Single();

        IList<object> values = new List<object>();

        foreach (var parameter in constructors.GetParameters())
        {
            if (parameter.ParameterType.IsInterface == false)
            {
                throw new InvalidOperationException($"The type {concreteType.Name} has constructor paramters that are not interfaces.");
            }

            values.Add(resolver.GetService(parameter.ParameterType));
        }

        resolver.Register(() => Activator.CreateInstance(concreteType, values.ToArray()), typeof(TInterface));
    }

此处,数据是共享的,每当您更改其中一个矩阵的DATA时,其他矩阵也会被更改。但显然,如果将新数据存储器分配给其中一个矩阵,其他矩阵仍会引用其他数据存储器。

答案 2 :(得分:2)

它不会生成深层副本,因为cv::Mat是共享指针。添加到向量clone()时,您必须使用images或类似内容。

答案 3 :(得分:0)

std::vector::push_back会将对象复制或移动到向量中,这意味着将调用Mat的复制文件或移动文件。所以它取决于Mat

请参阅CopyInsertableMoveInsertable