c++ - copyto - Las diferencias de uso de "const cv:: Mat &", "cv:: Mat &", "cv:: Mat" o "const cv:: Mat" como parámetros de función?
opencv mat library (2)
He buscado a fondo y no he encontrado una respuesta directa a esto.
Pasando matrices de opencv ( cv::Mat
) como argumentos a una función, estamos pasando un puntero inteligente. Cualquier cambio que hagamos a la matriz de entrada dentro de la función también altera la matriz fuera del alcance de la función.
Lo leí al pasar una matriz como referencia constante, no se altera dentro de la función. Pero un simple ejemplo muestra que sí:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input;
Output += 1;
}
int main( int argc, char** argv ){
cv::Mat A = cv::Mat::ones(3,3,CV_8U);
std::cout<<"A = /n"<<A<<"/n/n";
cv::Mat B;
sillyFunc(A,B);
std::cout<<"A = /n"<<A<<"/n/n";
std::cout<<"B = /n"<<B<<"/n/n";
}
Claramente, A
se altera a pesar de que se envía como una const cv::Mat&
.
Esto no me sorprende ya que dentro de la función I2
es una copia simple del puntero inteligente de I1
y, por lo tanto, cualquier cambio en I2
alterará I1
.
Lo que me desconcierta es que no entiendo qué diferencia práctica existe entre enviar cv::Mat
, const cv::Mat
, const cv::Mat&
o cv::Mat&
como argumentos para una función.
Sé cómo anular esto (reemplazando Output = Input;
con Output = Input.clone();
resolvería el problema) pero aún no entiendo la diferencia mencionada anteriormente.
¡Gracias chicos!
Cuando pasa un puntero inteligente como referencia, puede, teóricamente, ahorrar algo de tiempo de proceso, ya que no se invoca el constructor de copia del puntero inteligente.
Todo es porque OpenCV utiliza la administración automática de memoria .
OpenCV maneja toda la memoria automáticamente.
En primer lugar,
std::vector
,Mat
y otras estructuras de datos utilizadas por las funciones y métodos tienen destructores que desasignan los búferes de memoria subyacentes cuando es necesario. Esto significa que los destructores no siempre desasignan los búferes como en el caso deMat
. Toman en cuenta el posible intercambio de datos. Un destructor disminuye el contador de referencia asociado con el almacenamiento intermedio de datos de la matriz. El búfer se desasigna si y solo si el contador de referencia llega a cero, es decir, cuando ninguna otra estructura se refiere al mismo búfer. De manera similar, cuando se copia una instancia deMat
, no se copian datos reales. En cambio, el contador de referencia se incrementa para memorizar que hay otro propietario de los mismos datos. También existe el método deMat::clone
que crea una copia completa de los datos de la matriz.
Dicho esto, para hacer que dos cv::Mat
s señalen cosas diferentes, debe asignarles memoria por separado. Por ejemplo, lo siguiente funcionará como se espera:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input.clone(); // Input, Output now have seperate memory
Output += 1;
}
PS : cv::Mat
contiene un int* refcount
que apunta al contador de referencia. Consulte Gestión de memoria y recuento de referencias para obtener más información:
Mat
es una estructura que mantiene las características de matriz / imagen (número de filas y columnas, tipo de datos, etc.) y un puntero a los datos. Entonces, nada nos impide tener varias instancias deMat
correspondan a los mismos datos. UnaMat
mantiene un conteo de referencias que indica si los datos deben ser desasignados cuando se destruye una instancia particular deMat
.
Diferencias entre el envío de cv::Mat
, const cv::Mat
, const cv::Mat&
o cv::Mat&
como argumentos para una función:
cv::Mat Input
: pasa una copia del encabezado deInput
. Su encabezado no se cambiará fuera de esta función, pero se puede cambiar dentro de la función. Por ejemplo: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
: pasa una copia del encabezado deInput
. Su encabezado no se cambiará fuera o dentro de la función. Por ejemplo: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
: pasa una referencia del encabezado deInput
. Garantiza que el encabezado deInput
no se cambiará fuera o dentro de la función. Por ejemplo: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
: pasa una referencia del encabezado deInput
. Los cambios al encabezado de entrada ocurren fuera de y dentro de la función. Por ejemplo:void sillyFunc(cv::Mat& Input, cv::Mat& Output){ Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change ... }
PS2 : debo señalar que, en las cuatro situaciones ( cv::Mat
, const cv::Mat
, const cv::Mat&
o cv::Mat&
), solo el acceso al encabezado del Mat está restringido, no al datos a los que apunta. Por ejemplo, puede cambiar sus datos en las cuatro situaciones y sus datos cambiarán fuera de y dentro de la función:
/*** 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
}