web-dev-qa-db-fra.com

Copie complète d'OpenCV cv :: Mat

Le comportement de copie de cv::Mat Me déroute.

Je comprends de la documentation que Mat::copyTo() est une copie complète alors que l'opérateur d'affectation ne l'est pas. Mes questions:

  1. que dois-je faire pour renvoyer un cv::Mat à partir d'une fonction, telle que: cv::Mat func()?

  2. Selon la documentation, si je retourne un cv::Mat Cela n'aura aucune utilité, car après le retour de la fonction, la copie locale du cv::Mat Dans cette fonction sera détruite et donc celle qui accepte le la valeur renvoyée en dehors de la fonction doit pointer vers une adresse aléatoire. La chose étrange est que (la plupart du temps) cela fonctionne correctement. Par exemple, les travaux suivants:

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

Mais pourquoi? Ce n'est pas une copie complète.

Question 3. Et parfois, l'opérateur d'affectation semble également être une copie complète:

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

Ensuite, les deux écrans affichent la même image, a.jpg. Pourquoi? Et d'autres fois, cela ne fonctionne pas. (Le code d'origine est trop long mais il peut également être simplifié dans le cas ci-dessus). À cette époque, l'opérateur d'affectation semble être en fait une copie "superficielle". Pourquoi?

Merci beaucoup!

25
flyinggip

Je pense que l'utilisation de l'affectation n'est pas la meilleure façon de copier une matrice. Si vous souhaitez une nouvelle copie complète de la matrice, utilisez:

Mat a=b.clone(); 

Si vous souhaitez copier la matrice pour remplacer les données d'une autre matrice (pour éviter la réallocation de mémoire), utilisez:

Mat a(b.size(),b.type());
b.copyTo(a);

Lorsque vous affectez une matrice à une autre, le compteur de références du pointeur intelligent aux données de la matrice augmente d'une unité, lorsque vous libérez la matrice (cela peut être fait implicitement lorsque vous laissez le bloc de code), il diminue d'une unité. Lorsqu'il devient égal à zéro, la mémoire allouée est désallouée.

Si vous voulez obtenir le résultat de la fonction utiliser des références, c'est plus rapide:

void Func(Mat& input,Mat& output)
{
 somefunc(input,output);
}

int main(void)
{
...
  Mat a=Mat(.....);
  Mat b=Mat(.....);
  Func(a,b);
...
}
22
Andrey Smorodov

J'utilise OpenCV depuis un certain temps maintenant et le cv :: Mat m'a aussi dérouté, j'ai donc fait quelques lectures.

cv :: Mat est un en-tête qui pointe vers un pointeur de données * qui contient les données d'image réelles. Il implémente également le comptage des références. il contient le nombre d'en-têtes cv::Mat pointant actuellement vers ce * pointeur de données. Ainsi, lorsque vous effectuez une copie régulière telle que:

cv::Mat b; 
cv::Mat a = b;

a pointera vers les données de b et le nombre de références pour celles-ci sera incrémenté. En même temps, le décompte de référence pour les données précédemment signalées par b sera décrémenté (et la mémoire sera libérée si elle est nulle après décrémentation).

Question 1: Cela dépend de votre programme. Veuillez vous référer à cette question pour plus de détails: is-cvmat-class-flawed-by-design

Question 2: la fonction renvoie par valeur. Cela signifie que return image Copiera le tapis et augmentera le nombre de références (maintenant ref_count = 2) et retournera le nouveau tapis. À la fin de la fonction, l'image sera détruite et ref_count sera réduit de un. Mais la mémoire ne sera pas libérée car le ref_count n'est pas 0. Ainsi, le cv :: Mat retourné ne pointe pas vers un emplacement de mémoire aléatoire.

Question 3: Une chose similaire se produit. Lorsque vous dites orgImage2.copyTo(aCopy); Le ref_count pour les données pointées par aCopy sera diminué. Ensuite, une nouvelle mémoire est allouée pour stocker les nouvelles données qui seront copiées. C'est pourquoi copyCopy1 N'a pas été modifié lorsque vous avez fait cela.

7
Indika Pathi

Jetez un oeil à c ++ 11 std :: shared_ptr fonctionne efficacement de la même manière, en utilisant un compteur de référence cv :: Mat se souvient intelligemment chaque fois que le pointeur est référencé, une fois que le nombre atteint 0, il est libéré automatiquement, c'est-à-dire que la mémoire est désallouée et que cv :: Mat n'est plus disponible. Il s'agit effectivement d'une "copie superficielle" et économise des ressources en allouant/désallouant de grandes quantités de mémoire.

D'un autre côté, cv :: Mat :: clone fournira une "copie complète" qui alloue un tout nouveau bloc de mémoire pour que la matrice réside, cela peut être utile si vous effectuez des transformations vers une image que vous voudrez peut-être annuler cependant, plus d'allocation/désallocation de mémoire augmente la quantité de ressources nécessaires.

J'espère que cela aide quelqu'un.

2
user3102241