it-swarm.com.de

Unterschiede bei der Verwendung von "const cv :: Mat &", "cv :: Mat &", "cv :: Mat" oder "const cv :: Mat" als Funktionsparameter?

Ich habe gründlich gesucht und keine einfache Antwort darauf gefunden.

Wenn Sie opencv-Matrizen (cv::Mat) als Argumente an eine Funktion übergeben, übergeben wir einen intelligenten Zeiger. Jede Änderung an der Eingabematrix innerhalb der Funktion ändert die Matrix auch außerhalb des Funktionsumfangs.

Ich habe gelesen, dass durch das Übergeben einer Matrix als const-Referenz diese nicht innerhalb der Funktion geändert wird. Aber ein einfaches Beispiel zeigt:

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

Natürlich wird A geändert, obwohl es als const cv::Mat& gesendet wird.

Dies überrascht mich nicht, da sich innerhalb der Funktion I2 eine einfache Kopie des intelligenten Zeigers von I1 befindet und daher jede Änderung in I2I1 verändert.

Was mich verblüfft, ist, dass ich nicht verstehe, welche praktischen Unterschiede zwischen dem Senden von cv::Mat, const cv::Mat, const cv::Mat& oder cv::Mat& als Argumente an eine Funktion bestehen.

Ich weiß, wie ich das überschreiben kann (das Ersetzen von Output = Input; durch Output = Input.clone(); würde das Problem lösen), aber ich verstehe den oben genannten Unterschied immer noch nicht.

Danke Leute!

25
CV_User

Das liegt daran, dass OpenCV Automatic Memory Management verwendet.

OpenCV behandelt den gesamten Speicher automatisch.

Zunächst haben std::vector, Mat und andere Datenstrukturen, die von den Funktionen und Methoden verwendet werden, Destruktoren, die bei Bedarf die zugrunde liegenden Speicherpuffer aufheben. Dies bedeutet, dass die Destruktoren die Puffer nicht immer wie bei Mat freigeben. Sie berücksichtigen den möglichen Datenaustausch. Ein Destruktor dekrementiert den Referenzzähler, der dem Matrixdatenpuffer zugeordnet ist. Der Puffer wird nur dann freigegeben, wenn der Referenzzähler Null erreicht, dh wenn keine anderen Strukturen auf denselben Puffer verweisen. Wenn eine Mat-Instanz kopiert wird, werden auch keine tatsächlichen Daten wirklich kopiert. Stattdessen wird der Referenzzähler erhöht, um zu speichern, dass ein anderer Besitzer derselben Daten vorhanden ist. Es gibt auch die Methode Mat::clone, mit der eine vollständige Kopie der Matrixdaten erstellt wird.

Damit zwei cv::Mats auf verschiedene Dinge verweisen, müssen Sie den Speicher für sie separat zuordnen. Zum Beispiel wird Folgendes wie erwartet funktionieren:

void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
    Output = Input.clone(); // Input, Output now have seperate memory
    Output += 1;
}

P.S: cv::Mat enthält einen int* refcount, der auf den Referenzzähler verweist. Check out Speicherverwaltung und Referenzzählung für weitere Details:

Mat ist eine Struktur, die Matrix-/Bildmerkmale (Zeilen- und Spaltennummer, Datentyp usw.) und einen Zeiger auf Daten enthält. Nichts hindert uns also daran, mehrere Instanzen von Mat zu haben, die den gleichen Daten entsprechen. Eine Mat führt einen Referenzzähler, der angibt, ob Daten freigegeben werden müssen, wenn eine bestimmte Instanz von Mat zerstört wird. 


Unterschiede zwischen dem Senden von cv::Mat, const cv::Mat, const cv::Mat& oder cv::Mat& als Argumente an eine Funktion:

  1. cv::Mat Input: Übergeben Sie eine Kopie der Kopfzeile von Input. Sein Header wird nicht außerhalb dieser Funktion geändert, sondern kann innerhalb der Funktion geändert werden. Zum Beispiel:

    void sillyFunc(cv::Mat Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function
        //...
    }
    
  2. const cv::Mat Input: Übergeben Sie eine Kopie der Kopfzeile von Input. Sein Header wird nicht außerhalb oder innerhalb der Funktion geändert. Zum Beispiel:

    void sillyFunc(const cv::Mat Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function
        //...
    }
    
  3. const cv::Mat& Input: übergeben Sie einen Verweis auf den Header der Input. Garantiert, dass der Header von Input nicht außerhalb der Funktion oder innerhalb der Funktion geändert wird. Zum Beispiel:

    void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // Error when trying to change the header
        ...
    }
    
  4. cv::Mat& Input: übergeben Sie einen Verweis auf den Header der Input. Änderungen an der Kopfzeile von Input finden außerhalb und innerhalb der Funktion statt. Zum Beispiel:

    void sillyFunc(cv::Mat& Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change
        ...
    }
    

P.S.2: Ich muss darauf hinweisen, dass in allen vier Situationen (cv::Mat, const cv::Mat, const cv::Mat& oder cv::Mat&) nur der Zugriff auf den Mat-Header beschränkt ist, nicht auf die Daten, auf die er verweist. Beispielsweise können Sie seine Daten in allen vier Situationen ändern, und ihre Daten ändern sich tatsächlich außerhalb und innerhalb der Funktion:

/*** 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
}
52
herohuyongtao

Wenn Sie einen intelligenten Zeiger als Referenz übergeben, können Sie theoretisch etwas Verarbeitungszeit einsparen, da der Kopierkonstruktor des intelligenten Zeigers nicht aufgerufen wird.

0
ivg