Auteur: Niels Van Vliet / LRDE. Pour toute remarque: niels@lrde.epita.fr. *============================================================================= || Comment utiliser Olena en 30 minutes. *============================================================================= Pour suivre le tutoriel, vous devez ouvrir dans un explorateur WEB les pages suivantes: /----------------------------------------------------------------------------- | http://www.lrde.epita.fr/~palma_g/olena/readme | http://www.lrde.epita.fr/~palma_g/olena/html/index.html \----------------------------------------------------------------------------- Afin de compiler dans les salles machines EPITA, vous devez lire le readme ci-dessus. Plan: I. Charger une image II. Copier une image a. Alias b. Clonage c. Point Dpoint Window III. Appel d'algo *============================================================================= || I. Charger une image *============================================================================= Prendre une image couleur. La convertir en *.ppm. /----------------------------------------------------------------------------- | $> convert toto.png co.ppm | | ou bien téléchargez co.ppm à partir de: | | http://www.lrde.epita.fr/~palma_g/olena/co.ppm \----------------------------------------------------------------------------- Vous avez besoin des en-têtes suivantes: /---------------------------------------------------------------------------- | #include // Pour avoir les image2d, et plein de choses. | #include // Pour avoir le ntg::int_u8. | #include // Pour avoir le ntg::rgb_8. \---------------------------------------------------------------------------- Le type d'une image 2d est: /---------------------------------------------------------------------------- | oln::image2d c; //c est une image couleur (24 bits). | oln::image2d g; //g est une image en niv. de gris (8 bits). \---------------------------------------------------------------------------- Pour charger une image dans c: /---------------------------------------------------------------------------- | c = oln::io::load("co.ppm"); | | if (!c.has_impl()) { | std::cerr << "Fail" << std::endl; | exit(1); | } | | oln::io::save(c, "co2.ppm"); \---------------------------------------------------------------------------- Le code complet est: /---------------------------------------------------------------------------- | #include // Pour avoir les image2d, et plein de choses. | #include // Pour avoir le ntg::int_u8. | #include // Pour avoir le ntg::rgb_8. | | using namespace oln; | using namespace ntg; | | int main(){ | image2d c; //c est une image couleur (24 bits). | image2d g; //g est une image en niv. de gris (8 bits). | | c = io::load("co.ppm"); | | if (!c.has_impl()) { | std::cerr << "Fail" << std::endl; | exit(1); | } | | io::save(c, "co2.ppm"); | } \---------------------------------------------------------------------------- *============================================================================= || II. Copier une image *============================================================================= a. Alias Soit 2 images, i et j: /---------------------------------------------------------------------------- | oln::image2d i = oln::io::load("co.ppm"); | oln::image2d j; \---------------------------------------------------------------------------- Si vous écrivez: /---------------------------------------------------------------------------- | i = j; \---------------------------------------------------------------------------- alors les pixels de i sont les MEMES que ceux de j. Toute modification de i modifie j et vice-versa. i est un alias sur j. b. Clonage En écrivant: /---------------------------------------------------------------------------- | i = j.clone(); \---------------------------------------------------------------------------- i et j sont deux images différentes, mais les valeurs de leurs pixels sont les mêmes. c. Changer la valeur d'un point Pour changer la valeur du niveau de gris de g, et pour changer la valeur du pixel situé en (y, x) de i : /------------------------------------------------------------------------ 1 - | oln::coord y = 2, x = 10; | g(y, x) = 14; //en niveau de gris | c(y, x) = ntg::rgb_8(16, 6, 4); //en couleur | c(y, x)[ntg::rgb_R] = 255; //change uniquement la composante Red. \---------------------------------------------------------------------------- On peut préférablement utiliser des points: /------------------------------------------------------------------------ 2 - | oln::coord y = 2, x = 10; | oln::point2d p(y,x); | g[p] = 14; | c[p] = ntg::rgb_8(16, 6, 4); | c[p][ntg::rgb_R] = 255; \---------------------------------------------------------------------------- Les delta-points sont souvent utilisés pour donner la position relative entre deux points: /---------------------------------------------------------------------------- | oln::dpoint2d v(4, 3); | g[p + v] = 14; // le point (y + 4, x + 3) est modifié. \---------------------------------------------------------------------------- Un ensemble de delta-points est une window. C'est très utile en traitement d'image. En effet cela permet de dire que des points sont voisins. Exemple : On dit que les points sont voisins s'ils sont à droite, à gauche, en haut ou en bas du point, ou le point lui même : /---------------------------------------------------------------------------- | oln::window2d v; | v.add(0, 1).add(0, -1).add(-1, 0).add(1, 0).add(0, 0); | // But you can write: | oln::window2d v2 = oln::win_c4p(); \---------------------------------------------------------------------------- Voici comment afficher tous les points voisins au point p: /---------------------------------------------------------------------------- | oln::point2d p(10, 5); | oln::window2d v2 = oln::win_c4p(); | for (unsigned i = 0; i < v2.card(); ++i) | std::cout << p + v2.dp(i) << std::endl; \---------------------------------------------------------------------------- Si on remplace oln::win_c4p() par oln::mk_win_disc(3), on affiche tous les points à une distance de 3 du point p. d) Faire une boucle sur tous les points de l'image i et c. On peut utiliser deux boucles for : /------------------------------------------------------------------------ 1 - | assert(c.size() == g.size()); | for (oln::coord y = 0; y < c.nrows(); ++y) | for (oln::coord x = 0; x < c.ncols(); ++x) | g(y,x) = c(y, x)[ntg::rgb_R] / 3 + | c(y, x)[ntg::rgb_G] / 3 + | c(y, x)[ntg::rgb_B] / 3; \---------------------------------------------------------------------------- Mais il est plus propre d'utiliser un iterateur. Un iterateur ressemble à un point. Contrairement aux itérateurs de la STL (vector::iterator), ce ne sont pas des pointeurs, et un iterateur peut être utilisé sur plusieurs images. Attention! Il faut que les différentes images aient la même size(). Un iterateur se construit à partir d'une image, ce qui lui permet de récupérer des informations. /------------------------------------------------------------------------ 2 - | assert(c.size() == g.size()); | oln_iter_type_(oln::image2d) it(c); | for_all(it) | g[it] = c[it][ntg::rgb_R] / 3 + | c[it][ntg::rgb_G] / 3 + | c[it][ntg::rgb_B] / 3; \---------------------------------------------------------------------------- On note l'utilisation de la macro: oln_iter_type_(type_d_image). Pourquoi il y a un underscore alors que lorsque j'ouvre un fichier d'Olena le plus souvent il n'y en a pas? - Il ne faut pas mettre d'underscore si le type_d_image dépend d'un paramètre template. Ceci ne te semble pas clair ? Bon si tu ne codes pas très bien et pas générique, ne met pas d'underscore et si le compilateur affiche une erreur, tu ajoutes un underscore. Le code complet est: /---------------------------------------------------------------------------- | #include // Pour avoir les image2d, et plein de choses. | #include // Pour avoir le ntg::int_u8. | #include // Pour avoir le ntg::rgb_8. | | using namespace oln; | using namespace ntg; | | int main(){ | | // Window2d | | const point2d p(10, 5); | const window2d v2 = win_c4p(); | for (unsigned i = 0; i < v2.card(); ++i) | std::cout << p + v2.dp(i) << std::endl; | | // Color to gray | | image2d c = io::load("co.ppm"); | if (!c.has_impl()) { | std::cerr << "Fail" << std::endl; | exit(1); | } | | image2d g(c.size()); | | assert(c.size() == g.size()); //This is not neccessary. | oln_iter_type_(image2d) it(c); | for_all(it) | g[it] = c[it][rgb_R] / 3 + | c[it][rgb_G] / 3 + | c[it][rgb_B] / 3; | | io::save(g, "co.pgm"); | } \---------------------------------------------------------------------------- Notez que le fichier de sortie est un pgm. - co.ppm : RGB 3*8 bits : oln::image2d - co.pgm : Gris 8 bits : oln::image2d - co.pbm : N/B 1 bit : oln::image2d *============================================================================= * III. Appel d'algo : une façon de retirer les lettres de la BD. *============================================================================= On suppose que vous êtes allés en cours. Vous savez qu'une fermeture (closing en anglais) retire les petits éléments foncés d'une image. Nous allons effectuer une fermeture sur co.ppm. Pour cela nous allons utiliser l'aide. /---------------------------------------------------------------------------- | http://www.lrde.epita.fr/~palma_g/olena/html/index.html | Tapez 'closing' dans search. Premier lien. | Regardez les images en noir et blanc. \---------------------------------------------------------------------------- On remarque que le texte ('Olena') est effacé dans la pseudo-bulle blanche. L'exemple est appliqué sur une image binaire, or l'image en 'co.ppm' est une image couleur. Il faut aller voir les arguments de la fonction pour voir ce qui est attendu : /---------------------------------------------------------------------------- | closing (const abstract::non_vectorial_image< I > & input, | const abstract::struct_elt< E > & se \---------------------------------------------------------------------------- Une couleur est un type vectoriel (3 composantes au vecteur: R, G, B). Cet algo fonctionne par contre en niveau de gris. Dans un premier temps nous allons transformer notre image en image en niveau de gris: /---------------------------------------------------------------------------- | #include | #include | #include | | using namespace oln; | using namespace ntg; | | int main() | { | image2d c("co.ppm"); | if (!c.has_impl()) { | std::cerr << "Fail" << std::endl; | exit(1); | } | image2d g(c.size()); | | oln_iter_type_(image2d) it(c); | for_all(it) | g[it] = c[it][rgb_R] / 3 + | c[it][rgb_G] / 3 + | c[it][rgb_B] / 3; | } \---------------------------------------------------------------------------- Il faut alors ajouter une en-tête: /---------------------------------------------------------------------------- | #include \---------------------------------------------------------------------------- Puis appeler l'algorithme: /---------------------------------------------------------------------------- | g = oln::morpho::closing(g, oln::win_c8p()); \---------------------------------------------------------------------------- Le code complet est: /---------------------------------------------------------------------------- | #include | #include | #include | #include | | using namespace oln; | using namespace ntg; | | int main() | { | oln::image2d c("co.ppm"); | if (!c.has_impl()) { | std::cerr << "Fail" << std::endl; | exit(1); | } | oln::image2d g(c.size()); | | oln_iter_type_(image2d) it(c); | for_all(it) | g[it] = c[it][rgb_R] / 3 + | c[it][rgb_G] / 3 + | c[it][rgb_B] / 3; | g = oln::morpho::fast::closing(g, oln::mk_win_disc(3)); | io::save(g, "co_closed.pgm"); | } \----------------------------------------------------------------------------