2015-09-17 7 views
1

У меня есть список изображений, которые хранят его несколько объектов Mat, и мне нужно вставить их в вектор Mat.Создает ли std :: vector push_back глубокую копию аргумента?

vector<Mat> images; 
Mat image; 
for (i = 0; i < n; i++) 
{ 
    \\ importing the ith image into a mat image; 
    images.push_back(image); 
} 

Это создает глубокую копию изображения?

Sure. 
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(); 

Это все еще заставляет меня есть утечки памяти, что может быть причиной этого?

+1

'.push_back' скопирует объект. Но здесь объект представляет собой объект 'cv :: Mat', который сам является заголовком матрицы, что является чем-то вроде« умного указателя ». Таким образом, вы копируете умный указатель, но не значения матричных данных! И «изображение», и копия «изображения» будут разделять одни и те же элементы матрицы, пока одна из них не переназначит новую память (что может случиться, если для функции openCV требуется больше данных). Таким образом, здесь нет глубокой копии (матричных элементов), но вы не можете точно сказать, что обе ссылки на одни и те же данные навсегда. – Micka

+0

Тогда я смущен. Так как я всегда .push_back (изображение), т. Е. Та же матрица с разными значениями, как векторные изображения по-прежнему содержат разные матрицы (что я проверил). –

+0

можете ли вы предоставить некоторый минимальный код для проверки этого поведения? – Micka

ответ

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 не разделяет данные со всеми другими матрицами, так как размеры целевого изображения НЕ УСТАНАВЛИВАЮТСЯ, поэтому в рамках метода medianBlur необходимо было выделить новую память для images[3]. Таким образом, изображения [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 

Здесь данные совместно и каждый раз при изменении данных одной из этих матриц, остальные будут изменены, тоже. Но, очевидно, если вы назначили новую память данных одной из этих матриц, другие все еще ссылаются на другую память данных.

+0

Таким образом, деструктор Mat :: release фактически уничтожает «заголовок». Как я фактически уничтожаю значения объектов Mat? –

+0

OpenCV использует подсчет ссылок для данных. Поэтому, когда последний заголовок одна матрица данных будет уничтожена, память будет выпущена. – Micka

+0

Hi Micka, У меня есть еще один вопрос относительно этой настройки. Я толкаю объекты-маты в вектор, а затем освобождаю их один за другим, но все же, похоже, имеет утечку памяти. Я думал, что когда refcount = 0, память освобождается. –

2

Это не делает глубокие копии, так как cv::Mat - общие указатели. Вы должны использовать clone() или подобное при добавлении к вектору images.

5

std::vector::push_back использует конструктор копирования объекта для вставки элемента в вектор. Поэтому, если конструктор Mat Mat создает глубокую копию объекта Mat, вы получаете глубокую копию.

Смежные вопросы