#+TITLE: Création d’images par algorithme génétique avec référence #+SUBTITLE: Rapport de projet #+AUTHOR: Lucien Cartier-Tilet #+EMAIL: phundrak@phundrak.fr #+CREATOR: Lucien Cartier-Tilet #+LANGUAGE: fr #+LATEX_CLASS: article #+LaTeX_CLASS_OPTIONS: [a4paper,twoside] #+LATEX_HEADER: \usepackage{xltxtra,fontspec,xunicode}\usepackage[total={6.5in,9.5in}]{geometry}\setromanfont[Numbers=Lowercase]{Charis SIL} #+LATEX_HEADER: \usepackage{xcolor} \usepackage{hyperref} #+LATEX_HEADER: \hypersetup{colorlinks=true,linkbordercolor=red,linkcolor=blue,pdfborderstyle={/S/U/W 1}} #+STARTUP: latexpreview #+OPTIONS: toc:nil * Sujet Le sujet de ce projet est la création d’un logiciel pouvant recréer une image fournie grâce à des générations aléatoires et successives de formes aux, positions, couleurs et taille aléatoires. L’algorithme commence par créer une image vide aux dimensions identiques à l’image de référence, puis applique une de ces formes aléatoires. Si la ressemblance de l’image ainsi générée augmente par rapport à sa version précédente par rapport à l’image de référence, alors cette modification est conservée, sinon elle est annulée. Répéter jusqu’à satisfaction. * Les méthodes utilisées Plusieurs approches au problème sont possibles, allant de la simple implémentation naïve du problème à des moyen pouvant au moins décupler la vitesse de génération de l’image. Sauf indication contraire, j’ai utilisé dans l’implémentation de chaque méthode des carrés comme forme d’éléments appliqués aléatoirement à l’image. Pour évaluer la ressemblance entre deux image, j’évalue une distance euclidienne entre le vecteur de leurs pixels qui peut se résumer à ceci : #+begin_export latex $$\sqrt{\sum_{i=0}^n(v_i - w_i)^2}$$ #+end_export ~V~ étant le vecteur de pixels de l’image de référence, ~W~ étant le vecteur de pixels de l’image générée, et ~n~ la taille de ces deux vecteurs. Les tests de temps sont réalisés sur un Lenovo Ideapad Y700, disposant d’un processeur Intel® Core™ i7-6700HQ à 2.6GHz et un turbo à 3.5GHz, composé de quatre cœurs supportant chacun deux threads, et de 16Go de RAM. Le programme est compilé avec les options d’optimisation ~-O3~ et ~-flto~. Voici également ci-dessous la liste des options et arguments possibles concernant l’exécution du logiciel. #+begin_src text $ ./bin/genetic-image -h Allowed options: -h [ --help ] Display this help message -i [ --input ] arg Input image -o [ --output ] arg Image output path (default: input path + "_output") -m [ --method ] arg Method number to be used (default: 1) -n [ --iterations ] arg Number of iterations (default: 5000) -v [ --verbose ] Enables verbosity #+end_src Voici le script grâce auquel les valeurs de temps d’exécution ont été obtenues : #+INCLUDE: ../benchmarks.fish src shell -n Quelques-unes de ces lignes commençasont là uniquement pour de la mise en forme des données afin que je puisse ** Méthode naïve J’ai tout d’abord implémenté la méthode naïve afin d’avoir une référence en matière de temps. Cette dernière est implémentée dans ~src/methods.cc~ avec la fonction ~method1()~. Comme ce à quoi je m’attendais, cette méthode de génération d’images est très lente, principalement dû au fait que l’algorithme en l’état essaiera d’appliquer des couleurs n’existant pas dans l’image de référence, voire complètement à l’opposées de la palette de couleurs de l’image de référence. Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. | / | < | < | | Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | |--------------------+-----------------+-----------------------| | 10 | 200 | 0.065881 | | 50 | 100 | 0.130041 | | 100 | 50 | 0.186012 | | 200 | 20 | 0.385982 | | 500 | 10 | 1.437486 | | 1000 | 5 | 3.608983 | Naturellement, la variation en temps d’exécution croît en même temps que le nombre d’améliorations nécessaires à apporter à l’image à améliorer, dû à la nature aléatoire de l’algorithme. Cependant, on constate également une croissance importante du temps d’exécution suivant également ce nombre d’itérations réussies. Vous trouverez en Annexes (§[[*M%C3%A9thode 1]]) un exemple d’image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations via cette méthode. ** Réduction du panel des couleurs Constatant que la majorité des échecs d’ajout de formes de couleur par la première méthode échouent dû à une couleur incorrecte, voire n’appartenant pas à l’image de référence, j’ai décidé de restreindre les possibilités de couleurs parmis lesquelles le hasard peut choisir à la liste des couleurs présentes dans l’image de référence uniquement. Ce choix se fait donc via l’implémentation d’un set de valeurs uniques représentant les couleurs trouvées dans l’image de référence, leur détection étant réalisée avec des threads parallèles pour plus de rapidité à l’exécution. Cette méthode est celle implémentée dans la fonction ~method2()~ dans ~src/methods.cc~. Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. | / | < | < | | Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | |--------------------+-----------------+-----------------------| | 10 | 200 | 0.072979 | | 50 | 100 | 0.114426 | | 100 | 50 | 0.157965 | | 200 | 20 | 0.290475 | | 500 | 10 | 0.785426 | | 1000 | 5 | 2.664046 | On peut remarquer une amélioration quant à la rapidité d’exécution du logiciel. Cependant, le résultat n’est pas aussi important qu’escompté. Je suppose que cela est dû au fait que l’algorithme précédent peut considérer un rapprochement d’une zone déjà colorée vers la couleur d’origine comme une amélioration, avec une possibilité plus large sur ce plan-là que pour le second algorithme qui se doit d’être plus précis concernant les couleurs. Une nette amélioration du résultat est toutefois visibles, voir Annexes (§[[*M%C3%A9thode 2]]) pour une image générée à partir de ~img/mahakala-monochrome.png~ via la méthode 2 et avec 2000 améliorations. Étant donné que cette modification ne sera à priori pas en conflit avec d’autres méthodes, cette amélioration sera conservée pour toutes les autres avancées suivantes. ** Une taille des formes aléatoire mais contrôlée Une autre méthode peut être de contrôler la taille des éléments en spécifiant une taille minimale et maximale selon le nombre d’éléments posés et le nombre total d’éléments à poser. Ainsi, on pourrait privilégier tout d’abord de grandes formes en début de génération pour encourager petit à petit les formes à réduire en taille. Cela permettrait d’obtenir rapidement une représentation grossière de l’image pour ensuite pouvoir progressivement afiner les détails. La taille de la forme à appliquer est définie comme suit : #+begin_export latex $$coef=\frac{nbIterRestantes}{totalIter}$$ $$tailleMinimale=coef \frac{min(Width,Height)}{2}$$ $$tailleMaximale=tailleMinimale*2+1$$ $$taille=Rand([\![tailleMinimale;tailleMaximale[\![)$$ #+end_export Voici les moyennes de temps d’exécution selon le nombre d’itérations réussies sur le nombre d’exécutions indiqué. | / | < | < | | Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | |--------------------+-----------------+-----------------------| | 10 | 200 | 0.082068 | | 50 | 100 | 0.244236 | | 100 | 50 | 0.418075 | | 200 | 20 | 1.453703 | | 500 | 10 | 4.777205 | | 1000 | 5 | 20.33209 | Cette version du logiciel est nettement plus lente que ses versions précédentes du fait de la contrainte de taille pour les formes pouvant potentiellement améliorer l’image, cependant la qualité des images générées est plus haute que celle des version précédentes, voir en Annexes (§[[*M%C3%A9thode 3]]). Cette méthode ne me semble que moyennement concluante, certes la vitesse d’exécution du logiciel est beaucoup plus faible, mais il est également vrai que la qualité des images générées est supérieure aux deux autres méthodes. Ainsi, il sera possible d’utiliser les modifications apportées par cette méthode en utilisant une option ~-s [ --size ]~ avec les méthodes suivantes pour activer cette modification de l’algorithme. ** Concurrence entre threads Une utilisation de calculs parallèles pourrait être intéressante afin d’accélerer la génération des images : l’utilisation de threads mis en concurrence. À chaque tentative d’amélioration de l’image, plusieurs threads sont lancés, et chacun créera sa propre amélioration possible de l’image. Ces résultats sont récupérés et évalués, et parmi les résultats améliorant l’image générée, celle avec le meilleur score est conservée. Cela permet ainsi de multiplier les chances d’avoir une amélioration de l’image par tentative. Voici les benchmarks d’exécution de cette méthode sans contrôle de la taille des formes aléatoires : | / | < | < | | Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | |--------------------+-----------------+-----------------------| | 10 | 200 | 0.080525 | | 50 | 100 | 0.139892 | | 100 | 50 | 0.169113 | | 200 | 20 | 0.273342 | | 500 | 10 | 0.610812 | | 1000 | 5 | 1.403816 | Et voici les benchmarks d’exécution de cette même méthode avec contrôle de la taille des formes aléatoires : | / | < | < | | Nb d’améliorations | Nb d’exécutions | Temps d’exécution (s) | |--------------------+-----------------+-----------------------| | 10 | 200 | 0.085981 | | 50 | 100 | 0.156099 | | 100 | 50 | 0.29183 | | 200 | 20 | 0.59844 | | 500 | 10 | 2.513782 | | 1000 | 5 | 6.457168 | Pour résumer, ces deux tableaux montrent la parallélisation de la seconde méthode et de la troisième méthode respectivement via des threads concurrentiels. On peut remarquer que le temps d’exécution s’est nettement amélioré, avec un temps d’exécution à peu près deux fois plus rapide pour l’exécution sans contrôle de taille des formes que la seconde méthode, et pouvant être jusqu’à trois fois plus rapide que la troisième méthode avec le contrôle de la taille des formes activée. On a donc une véritable amélioration significative avec cette nouvelle version parallèle. ** Collaboration entre threads Une différente approche au parallélisme peut être réalisée : plutôt que d’essayer de mettre en concurrence plusieurs threads, il serait possible d’essayer de plutôt les mettre en collaboration. Cela implique par exemple de diviser l’image d’entrée en plusieurs zones sur laquelle chacun des threads lancés travailleraient, appliquant chacun le nombre d’améliorations demandé sur sa zone dédiée. Puis, une fois que chacun des threads a terminé son travail, les différentes zones sont unifiées en une seule image. * Annexes ** Images *** Image de référence #+CAPTION: Image de référence utilisée pour les tests du logiciel [[../img/mahakala-monochrome.jpg]] *** Méthode 1 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la première méthode [[./output1.png]] *** Méthode 2 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la seconde méthode [[./output2.png]] *** Méthode 3 #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la troisième méthode [[./output3.png]] *** Méthode 4 **** Taille des formes non contrôlée #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la quatrième méthode sans l’option ~-s~ [[./output4-1.png]] **** Taille des formes contrôlée #+CAPTION: Image générée à partir de ~img/mahakala-monochrome.png~ avec 2000 améliorations avec la quatrième méthode avec l’option ~-s~ [[./output4-2.png]]