IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

manières de retourner une image opencv


Sujet :

C++

  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2016
    Messages : 15
    Par défaut manières de retourner une image opencv
    J'ai une classe Operation qui a entre autres un attribut opimg de type cv::Mat.
    Cette classe dispose de plusieurs méthodes permettant d'effectuer des traitements sur cette image.
    Je veux que l'attribut cv::Mat soit l'image traitée.

    Dans une des méthodes, je crée une image intermédiaire img_blur puis à la fin de cette méthode je fais:
    opimg = img_blur.
    Est-ce correct ? Vu que l'img_blur est sensé s'effacer à la fin de la méthode, est-ce que opimg garde une valeur cohérente ?


    De manière plus générale, je me demande comment construire mes méthodes. Faut-il leur passer un pointeur vers une image en paramètre (image que l'on aurait initialisée dans le main) ? ou directement modifier l'attribut de la classe?

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Je n'ai, pour être tout à fait honnête, jamais utilsié OpenCV, et il se peut donc que je dise de grosses conneries

    Cependant, un rapide coup d'oeil sur la page de documentation associée à Cv::mat, il y a pas mal d'informations à glâner:
    • D'abord, on remarque la présence d'un constructeur de copie (Mat (const Mat &m)) et d'un constructeur de copie par déplacement (Mat (Mat &&m))
    • Ensuite, on constate que le destructeur n'est pas virtuel
    • Enfin, on constate la présence d'un opérateur d'affectation (Mat & operator= (const Mat &m))

    Et surtout, on constate que ni l'opérateur d'affectation, ni les constructeurs de copie ne sont =delete, et qu'il existe une description bien clair de chacune de ces fonction.

    A partir de là, on peut donc déduire que la classe cv::Mat a -- bel et bien -- sémantique de valeur, et qu'on peut donc assigner les valeurs de l'une à l'autre.

    Tu peux donc effectivement sans problème affecter une matrice à une autre sous la forme d'un code proche de opimg = img_blur;, à ceci près qu'il faut voir à quel point cela pourrait impacter sur les performance, car, a priori, copier une matrice, ca risque toujours de prendre du temps

    Dans l'idéal, tu aurais sans doute intérêt à utiliser la sémantique de déplacement qui pourrait sans doute t'éviter bien des copies

    De manière plus générale, je me demande comment construire mes méthodes. Faut-il leur passer un pointeur vers une image en paramètre (image que l'on aurait initialisée dans le main) ? ou directement modifier l'attribut de la classe?
    Déjà, au lieu de travailler avec un pointeur, je te conseillerais plutôt d'utiliser des références, ce sera beaucoup plus simple à l'emploi.

    En outre, à moins que tu ne veuille modifier la matrice initialisée dans le main, et que la modification ne soit accessible dans main et / ou que tu veuilles récupérer des information de cette matrice comme base des modifications à apporter à la matrice de ta classe, autant que tu travailles sur la matrice directement contenue par ta classe (ou sur une copie de celle-ci, si tu veux pouvoir annuler une modification).
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2016
    Messages : 15
    Par défaut
    Merci beaucoup pour ta réponse complète.
    J'ai tout passé en référence du coup, et effectivement cela m'a aidé. Quel est la raison technique derrière ce conseil (plutôt que d'utiliser des pointeurs) ?
    Ensuite concernant l'opérateur =, j'ai vérifié dans la doc d'opencv et il semblerait que la complexité soit en O(1) donc ça devrait aller.

    Mon objet Operation a maintenant cette forme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Operation
    {
    	private:
    		int height;
    		int width;
    		cv::Mat opimg;
    		cv::Mat opimg_src;
     
    	public:
    	bool op_rgbtogray(cv::Mat& img, cv::Mat& gray);
    	bool op_blur(cv::Mat& img, cv::Mat& blur);
    	bool op_get_src_img(cv::Mat& img);
    	bool op_get_current_img(cv::Mat& img);
    	bool op_add_img_src(cv::Mat& img);
    	Operation();
    	Operation(cv::Mat& imgptr);
    	~Operation();
    };
    Les fonctions principales op_rgbtogray et op_blur sont pour résumer de cette forme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    bool Operation::op_rgbtogray(cv::Mat& img, cv::Mat& gray)
    {
    	//convert a rgb image to gray
    	//New grayscale image = ( (0.3 * R) + (0.59 * G) + (0.11 * B) )
    	for (int i = 0; i < height; i++)
    	{
    		for (int j = 0; j < width; j++)
    		{
    			Point3_<uchar>* p = img.ptr<Point3_<uchar> >(i, j);
    			opimg.at<uchar>(i, j) = (0.3 * p->z) + (0.59 * p->y) + (0.11 * p->x); //utiliser foreach
    			//grayptr->at<uchar>(i, j) = ((p->z) + (p->y) + (p->x))/3;
    		}
    	}
    	gray = opimg; //a revoir car copie de matrice?
    	return true;
    }
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    bool Operation::op_blur(cv::Mat& img, cv::Mat& blur)
    {
    	//blur an image with a convolution(gaussian kernel)
    	/// Declare variables
    	Mat src, dst, kernel;
    	Mat img_blur(height, width, CV_8UC1, Scalar(0, 0, 0));
    	Point anchor;
    	double delta;
    	int ddepth, kernel_size;
    	/// Initialize arguments for the filter
    	anchor = Point(-1, -1);
    	delta = 0;
    	ddepth = -1;
    	kernel_size = 5;
    	kernel = 1.0f/159.0f*(Mat_<double>(5, 5) << 2, 4, 5, 4, 2, 
    												4, 9, 12, 9, 4, 
    												5, 12, 15, 12, 5, 
    												4, 9, 12, 9, 4, 
    												2, 4, 5, 4, 2);
     
    	/// Apply filter
    	filter2D(img, img_blur, ddepth, kernel, anchor, delta, BORDER_DEFAULT);
    	blur = img_blur;  //a revoir car copie de matrice
    	opimg = img_blur; //a revoir car copie de matrice
    	return true;
    }
    Ainsi pour convertir une image en noir et blanc je fais , en gros:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        Mat gray;
        Mat img = imread("lena.jpg");
     
        Operation opimage(img);
     
        opimage.op_rgbtogray(img, gray);
    cela te semble être dans les normes ?
    J'aimerais utiliser le catch error de c++ au lieu de mettre des return true partout. Je vais améliorer ça
    ps: je sais que des fonctions existent déjà pour faire ce que je fais, je fais ça pour m’entraîner.

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 757
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 757
    Par défaut
    Citation Envoyé par Azghar Voir le message
    Quel est la raison technique derrière ce conseil (plutôt que d'utiliser des pointeurs) ?
    2 raisons
    • la syntaxe : plus besoin de déférencer ta variable (*XX) (même si entre la syntaxe point X.A et flèche X->A il n'y a pas une grosse différence )
    • les tests : une référence ne peut pas être nulle ... même si une référence peut être invalide (exemple classique, le retour référence d'une variable locale). Cela peut même aller assez loin comme par exemple, un membre référence doit être initialisé obligatoirement et uniquement dans la liste d'initialisation du constructeur.

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Pour comprendre la raison technique, il faut djéà savoir ce qu'est un pointeur:

    Un pointeur n'est en réalité jamais qu'une valeur numérique entière (généralement non signée) qui représente l'adresse à laquelle on devrait trouver une donnée du type indiqué.

    Le problème, c'est qu'il existe aussi une valeur (nullptr) qui indique l'absence de cette donnée et qui est considérée par le compilateur comme étant "une adresse invalide". Du coup, avant d'essayer d'accéder à la donnée, il faut -- systématiquement -- vérifier si le pointeur dont on dispose ne représente pas cette "adresse invalide", autrement, le programme va planter sur une "erreur de segmentation.

    En soi, cela présente malgré tout un avantage majeur: celui de pouvoir faire savoir que la donnée n'existe purement et simplement pas (quand on considère la donnée comme "optionnelle", par exemple).

    Cependant, et c'est là que les choses se compliquent, même si on pense effectivement à vérifier si le pointeur ne représente pas la valeur nullptr, il est très difficile (1), (entre autres) en raison des règles de portée (de "durée de vie") des données créées sur la pile de garantir que l'adresse mémoire (sensément valide) indiquée par le pointeur contient effectivement une donnée du type indiqué.

    par exemple le code suivant semblera tout à fait correct. Mais les commentaires t'aideront à comprendre pourquoi le programme plantera
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(){
        int * ptr{nullptr}; // on déclare un pointeur
        {  //remarque l'accolade que l'on ouvre ici: elle crée une portée qui s'achève avec l'accolade fermante
            int i = 8; // on déclare une variable qui n'existe q'entre les  deux accolades
           ptr = & i; // ptr représente maintenant l'adresse à laquelle se trouve la variable i
        }  // mais i est détruit ici, parce que l'on sort de la portée dans laquelle elle a été déclarée.
        /* à partir d'ici, ptr ne vaut pas nullptr, mais bien l'adresse mémoire à laquelle i se trouvait 
         * mais i n'existe plus!!!
         * Qu'y a-t-il donc à cette adresse mémoire alors?  Personne ne peut le dire
         */
        *ptr += 5; /* si i avait encore existé, cela aurait augmenté sa valeur de 5
                    * mais comme i n'existe plus, quel sera le résultat de cette commande?
                    */
    }
    Et encore, cet exemple est "relativement simple". Il y a des cas où les choses sont très loin d'être aussi claires

    De leur côté, les références doivent obligatoirement être définies sur un objet qui existe forcément.

    Un code similaire utilisant une référence et prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main(){
        /* je veux attendre de disposer de i pour définir la valeur de ref */
        int & ref; // ben non, le compilateur ne l'acceptera pas
        {
            int i = 8;
            int & refOK =i; // ici, ca fonctionne, parce que l'on dit explicitement que refOK référence la variable i
        } /* mais ce n'est pas grave, parce que la règle veut que les variables soient détruite dans l'ordre inverse
           * de leur déclaration.  refOK et i seront donc détruite en arrivant à l'accolade ici,
           * mais refOK aura forcément été détruite avant i
           */
    }
    En plus, comme l'a si bien dit foetus, la syntaxe d'utilisation d'une référence est quand même beaucoup plus simple que celle d'un pointeur

    (1) Bon, je l'avoue volontiers : j'exagère volontairement sur l'inconvénient pour te faire peur, car il existe effectivement quelques techniques permettant de s'assurer qu'un pointeur représentera effectivement toujours l'adresse d'une donnée qui existe.

    Il n'empêche que, si cela peut te faire partir du principe qu'il ne faut utiliser un pointeur que lorsque tu n'as vraiment pas d'autre choix, cela pourra t'éviter bien des soucis
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2016
    Messages : 15
    Par défaut
    Merci pour vos réponses, cela me permet d'en apprendre beaucoup et de revoir les bases.
    J'ai cependant essayé d’exécuter le programme exemple sur les pointeurs que tu as écris et qui était sensé planté et cela a bien marché : *ptr renvoit 13.
    Ai-je de la chance et ptr pointe vers un int (par un coup de chance ?) j'ai essayé plusieurs fois et ça marche systématiquement.

    Je cherche un exemple de cas où on serait obligé d'utiliser les pointeurs plutôt que les références. Je pense aux tableaux ?

    Enfin, dans mon troisième morceau de code, je fais:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    blur = img_blur;  //a revoir car copie de matrice
    opimg = img_blur; //a revoir car copie de matrice
    où blur est un paramètre, img_blur une variable locale, et opimg un attribut de ma classe Operation.
    Comment ça se fait que ça marche alors qu'on assigne par référence à opimg et blur une valeur qui va se détruire juste après car locale?

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par Azghar Voir le message
    Merci pour vos réponses, cela me permet d'en apprendre beaucoup et de revoir les bases.
    J'ai cependant essayé d’exécuter le programme exemple sur les pointeurs que tu as écris et qui était sensé planté et cela a bien marché : *ptr renvoit 13.
    Ai-je de la chance et ptr pointe vers un int (par un coup de chance ?) j'ai essayé plusieurs fois et ça marche systématiquement.
    Oui, je sais...

    C'est parce que l'exemple que j'ai donné est vraiment trop simple et à cause du mode de fonctionnement tout particulier de la pile.

    Mais, je peux t'assurer que, dans un code réel (avec un appel de fonction entre l'accolade fermante et la modification de la valeur du pointeur, ou avec la déclaration d'une nouvelle variable d'un type différent, par exeemple), pour le même prix, ou bien ton code aurait planté, ou bien tu aurais eu un résultat tellement aberrant que tu aurais souhaité qu'il plante

    Parce que c'est ce que l'on appelle en réalité un comportement indéfini (undefined behaviour que l'on désigne souvent par UB, tout simplement). Si bien que, pour le même prix, cela pourrait appeler un sous-programme planqué quelque part, qui provoquerait le lancement de missiles nucléaires sur moscou
    Je cherche un exemple de cas où on serait obligé d'utiliser les pointeurs plutôt que les références. Je pense aux tableaux ?
    Même pas...

    En fait, les seuls cas où l'utilisation de pointeurs devient indispensable (en dehors, bien sur du fait d'avoir la possibilité d'indiquer explicitement que la donnée n'existe absolument pas), ont trait aux classes dites "à sémantique d'entité", aux hiérarchies de classes (l'héritage d'inclusion) et au polymorphisme
    Enfin, dans mon troisième morceau de code, je fais:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    blur = img_blur;  //a revoir car copie de matrice
    opimg = img_blur; //a revoir car copie de matrice
    Si vraiment la copie des matrices posent des problèmes de performances, ce qui ne pourra être vérifié qu'à l'aide de benchmark, tu as toujours la possibilité d'utiliser la sémantique de déplacement (et std::move) si (et seulement si) tu es sur que la matrice d'origine ne sera plus utilisée par la suite
    où blur est un paramètre, img_blur une variable locale, et opimg un attribut de ma classe Operation.
    Comment ça se fait que ça marche alors qu'on assigne par référence à opimg et blur une valeur qui va se détruire juste après car locale?
    Parce que blur, en tant qu'argument reçu par référence, n'est en réalité qu'un alias de la matrice que tu as transmis à la fonction lors de l'appel.

    Cela revient donc exactement au même que si tu... modifiait en réalité la matrice qui a servi de paramètre lors de l'appel au niveau de la fonction appelante: Voici à quoi ressemblerait sans doute la fonction appelante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(){
        cv::Mat laMatrice; // voici la matrice qui sera effectivement modifiée par ta fonction
                           // et qui sera connue, dans Operation::op_blur sous le nom de blur
        cv::Mat lImage; // et voici celle qui sera connue dans la fonction sous le nom de
                        // img
        /* ... */
        Operation uneOperation;
        /* ... */
        uneOperation.op_blur (lImage, laMatrice);
        /* ...           ^       ^
         *               |       |
         *             img     blur
         */
    }
    Quant à opimg, c'est une donnée membre de ta classe. Cela signifie qu'elle existera aussi longtemps que l'instance (uneOperation, ici) n'aura pas été détruite et donc, au moins jusqu'à l'accolade fermante de la fonciton (main, dans le cas présent)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Retourner une image
    Par Medde dans le forum Imagerie
    Réponses: 7
    Dernier message: 21/07/2009, 16h25
  2. retourner une image avec tkinter
    Par kokliklo dans le forum Tkinter
    Réponses: 1
    Dernier message: 30/04/2009, 13h35
  3. Récupérer une image OpenCV dans Allegro
    Par karistouf dans le forum OpenCV
    Réponses: 0
    Dernier message: 04/12/2008, 14h45
  4. Retourner une image grace à SDL
    Par ced236 dans le forum SDL
    Réponses: 4
    Dernier message: 23/10/2007, 19h36
  5. Retourner une image
    Par Aldur dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 26/12/2005, 10h14

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo