Глубокая копия OpenCV cv:: Mat
Поведение копирования cv::Mat
меня сбивает с толку.
Я понимаю из документации, что Mat::copyTo()
является глубокой копией, в то время как оператор присваивания не является. Мои вопросы:
-
что мне делать, чтобы вернуть cv::Mat
из функции, например: cv::Mat func()
?
-
В соответствии с документацией, если я верну его cv::Mat
, это будет бесполезно, потому что после возвращения функции локальная копия cv::Mat
в этой функции будет уничтожена и, следовательно, возвращаемое значение вне функции должно указывать на некоторый случайный адрес. Странно то, что (в большинстве случаев) он работает правильно. Например, следующие работы:
cv::Mat CopyOneImage(const cv::Mat& orgImage)
{
cv::Mat image;
orgImage.copyTo(image);
return image;
}
int main()
{
std::string orgImgName("a.jpg");
cv::Mat orgImage;
orgImage = cv::imread(orgImgName);
cv::Mat aCopy;
aCopy = CopyOneImage(orgImage);
return 1;
}
Но почему? Это не глубокая копия.
Вопрос 3. А также иногда оператор присваивания тоже выглядит очень грубо:
int main()
{
std::string orgImgName("a.jpg");
cv::Mat orgImage;
orgImage = cv::imread(orgImgName);
cv::Mat aCopy;
orgImage.copyTo(aCopy);
cv::Mat copyCopy1;
copyCopy1 = aCopy;
cv::namedWindow("smallTest", 1);
cv::imshow("smallTest", copyCopy1);
uchar key = (uchar)cv::waitKey();
cv::Mat orgImage2 = cv::imread("b.jpg");
orgImage2.copyTo(aCopy);
cv::imshow("smallTest", copyCopy1);
return 1;
}
Затем на двух дисплеях отображается одинаковое изображение, a.jpg. Зачем? И еще несколько раз это не работает. (Исходный код слишком длинный, но он также может быть упрощен в приведенном выше случае). В те времена оператор присваивания, по-видимому, фактически является "мелким" копированием. Зачем?
Спасибо большое!
Ответы
Ответ 1
Я думаю, что использование присваивания - не лучший способ копирования матрицы. Если вам нужна новая полная копия матрицы, используйте:
Mat a=b.clone();
Если вы хотите скопировать матрицу для замены данных из другой матрицы (во избежание перераспределения памяти), используйте:
Mat a(b.size(),b.type());
b.copyTo(a);
Когда вы присваиваете одну матрицу другой, счетчик ссылок интеллектуального указателя на данные матрицы увеличивается на единицу, а когда вы освобождаете матрицу (это можно сделать неявно, когда вы покидаете блок кода), она уменьшается на единицу. Когда он становится равным нулю, выделенная память освобождается.
Если вы хотите получить результат из ссылки на функцию, это быстрее:
void Func(Mat& input,Mat& output)
{
somefunc(input,output);
}
int main(void)
{
...
Mat a=Mat(.....);
Mat b=Mat(.....);
Func(a,b);
...
}
Ответ 2
Я использую OpenCV какое-то время, и cv:: Mat меня тоже путал, поэтому я немного читал.
cv:: Mat - это заголовок, который указывает на указатель * данных, который содержит фактические данные изображения. Он также выполняет подсчет ссылок. он содержит количество заголовков cv::Mat
, в настоящее время указывающих на этот * указатель данных. Поэтому, когда вы делаете обычную копию, например:
cv::Mat b;
cv::Mat a = b;
a
будет указывать на данные b
, а счетчик ссылок будет увеличен. В то же время счетчик ссылок для данных, ранее отмеченных символом b
, будет уменьшаться (и память будет освобождена, если после декремента будет 0).
Вопрос 1: Это зависит от вашей программы. Пожалуйста, обратитесь к этому вопросу для получения более подробной информации: is-cvmat-class-flawed-by-design
Вопрос 2: функция возвращает значение. Это означает, что return image
скопирует Mat и увеличит счетчик ссылок (теперь ref_count = 2) и вернет новый Mat. Когда функция закончится, изображение будет уничтожено, а ref_count будет уменьшено на единицу. Но память не будет освобождена, так как ref_count не равен 0. Таким образом, возвращенный cv:: Mat не указывает на случайную ячейку памяти.
Вопрос 3: То же происходит. Когда вы скажете orgImage2.copyTo(aCopy);
, ref_count для данных, на которые указывает aCopy
, будет уменьшен. Затем выделяется новая память для хранения новых данных, которые будут скопированы. Вот почему copyCopy1
не был изменен, когда вы это сделали.
Ответ 3
Взгляните на С++ 11 std:: shared_ptr эффективно работает таким же образом, используя счетчик ссылок cv:: Mat ловко запоминается каждый раз, когда указатель ссылается, как только счет достигает 0, он автоматически высвобождается, т.е. память освобождается, а cv:: Mat больше не доступен. Это эффективно "мелкая копия" и экономит ресурсы при распределении/освобождении больших объемов памяти.
С другой стороны, cv:: Mat:: clone предоставит "глубокую копию", которая выделяет целый новый блок памяти для матрицы, в которой она находится, это может быть полезно, если вы делаете преобразования в изображение, которое вы может потребоваться отменить, однако, выделение/освобождение памяти увеличивает объем необходимых ресурсов.
Надеюсь, это поможет кому-то.