Ответ 1
Все потому, что OpenCV использует Автоматическое управление памятью.
OpenCV автоматически обрабатывает всю память.
Прежде всего,
std::vector
,Mat
и другие структуры данных, используемые функциями и методами, имеют деструкторы, которые освобождают базовые буферы памяти, когда это необходимо. Это означает, что деструкторы не всегда освобождают буфер, как в случаеMat
. Они учитывают возможность совместного использования данных. Деструктор уменьшает опорный счетчик, связанный с буфером матричных данных. Буфер освобождается, если и только если контрольный счетчик достигает нуля, то есть, когда никакие другие структуры не относятся к одному и тому же буферу. Аналогично, когда экземплярMat
копируется, фактические данные на самом деле не копируются. Вместо этого счетчик ссылок увеличивается, чтобы запомнить, что есть еще один владелец тех же данных. Существует также методMat::clone
, который создает полную копию данных матрицы.
Тем не менее, чтобы сделать две cv::Mat
точки на разные вещи, вам нужно выделить память отдельно для них. Например, следующее будет работать, как ожидалось:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input.clone(); // Input, Output now have seperate memory
Output += 1;
}
P.S: cv::Mat
содержит int* refcount
, который указывает на счетчик ссылок. Подробнее см. Управление памятью и подсчет ссылок.
Mat
- это структура, которая сохраняет характеристики матрицы/изображения (количество строк и столбцов, тип данных и т.д.) и указатель на данные. Поэтому ничто не мешает нам иметь несколько экземпляровMat
, соответствующих тем же данным. AMat
хранит счетчик ссылок, который сообщает, что данные должны быть освобождены при уничтожении конкретного экземпляраMat
.
Различия между отправкой cv::Mat
, const cv::Mat
, const cv::Mat&
или cv::Mat&
в качестве аргументов функции:
-
cv::Mat Input
: передать копию заголовкаInput
. Его заголовок не будет изменен вне этой функции, но может быть изменен внутри функции. Например:void sillyFunc(cv::Mat Input, cv::Mat& Output){ Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function //... }
-
const cv::Mat Input
: передать копию заголовкаInput
. Его заголовок не будет изменен вне функции или внутри нее. Например:void sillyFunc(const cv::Mat Input, cv::Mat& Output){ Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function //... }
-
const cv::Mat& Input
: передать ссылку заголовкаInput
. Гарантирует, что заголовокInput
не будет изменен вне функции или внутри нее. Например:void sillyFunc(const cv::Mat& Input, cv::Mat& Output){ Input = cv::Mat::ones(4, 4, CV_32F); // Error when trying to change the header ... }
-
cv::Mat& Input
: передать ссылку заголовкаInput
. Изменения в заголовкеInput
происходят вне и внутри функции. Например:void sillyFunc(cv::Mat& Input, cv::Mat& Output){ Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change ... }
PS2: я должен указать, что во всех четырех ситуациях (cv::Mat
, const cv::Mat
, const cv::Mat&
или cv::Mat&
) доступ к заголовку Mat сдержанными, а не данными, на которые он указывает. Например, вы можете изменить свои данные во всех четырех ситуациях, и его данные действительно будут меняться вне и внутри функции:
/*** will work for all the four situations ***/
//void sillyFunc(cv::Mat Input){
//void sillyFunc(const cv::Mat Input){
//void sillyFunc(const cv::Mat &Input){
void sillyFunc(cv::Mat &Input){
Input.data[0] = 5; // its data will be changed here
}