last-svn-commit-48-gd652519 Import annotating sources from milena green's sandbox.

* README.green: New. * demo/annotating/bic/Makefile.am: New. * demo/annotating/bic/bic.cc: New. * demo/annotating/hsv/Makefile.am: New. * demo/annotating/hsv/hsv.cc: New. * demo/annotating/lep/Makefile.am: New. * demo/annotating/lep/lep.cc: New. * demo/annotating/nb_color/Makefile.am: New. * demo/annotating/nb_color/nb_color.cc: New. * demo/annotating/project/Makefile.am: New. * demo/annotating/project/project.cc: New. * demo/annotating/rgb_64/Makefile.am: New. * demo/annotating/rgb_64/rgb_64.cc: New. * demo/annotating/rgb_64_9/Makefile.am: New. * demo/annotating/rgb_64_9/rgb_64_9.cc: New. * demo/annotating/stddev_color/Makefile.am: New. * demo/annotating/stddev_color/stddev_color.cc: New. * demo/annotating/stddev_color_16/Makefile.am: New. * demo/annotating/stddev_color_16/stddev_color_16.cc: New. * exp/annotating/achromastism/Makefile.am: New. * exp/annotating/achromastism/achromastism.cc: New. * exp/annotating/achromastism/text-color.txt: New. * exp/annotating/achromastism/text-img.txt: New. * exp/annotating/achromastism/text-only.txt: New. * exp/annotating/bench/Makefile.am: New. * exp/annotating/bench/bench.cc: New. * exp/annotating/error/Makefile.am: New. * exp/annotating/error/error.cc: New. * exp/annotating/histo/Makefile.am: New. * exp/annotating/histo/histo.cc: New. * exp/annotating/hsv/Makefile.am: New. * exp/annotating/hsv/hsv.cc: New. * exp/annotating/hue/Makefile.am: New. * exp/annotating/hue/hue.cc: New. * exp/annotating/hue/text-color.txt: New. * exp/annotating/hue/text-img.txt: New. * exp/annotating/hue/text-only.txt: New. * exp/annotating/nb_color/Makefile.am: New. * exp/annotating/nb_color/nb_color.cc: New. * exp/annotating/saturation/Makefile.am: New. * exp/annotating/saturation/saturation.cc: New. * exp/annotating/saturation/text-color.txt: New. * exp/annotating/saturation/text-img.txt: New. * exp/annotating/saturation/text-only.txt: New. * exp/annotating/stddev_color/Makefile.am: New. * exp/annotating/stddev_color/stddev_color.cc: New. * exp/annotating/stddev_color_16/Makefile.am: New. * exp/annotating/stddev_color_16/stddev_color_16.cc: New. * exp/annotating/value/Makefile.am: New. * exp/annotating/value/text-color.txt: New. * exp/annotating/value/text-img.txt: New. * exp/annotating/value/text-only.txt: New. * exp/annotating/value/value.cc: New. * mln/clustering/kmean2d.hh: New. * mln/fun/p2b/achromatic.hh: New. * mln/fun/v2v/hue_concentration.hh: New. * mln/fun/v2v/rgb_to_achromatism_map.hh: New. * mln/fun/v2v/rgb_to_hsv.hh: New. --- scribo/sandbox/green/ChangeLog | 64 + scribo/sandbox/green/README.green | 507 +++++++- .../kmean1d => annotating/bic}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/bic/bic.cc | 122 ++ .../kmean1d => annotating/hsv}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/hsv/hsv.cc | 721 ++++++++++ .../kmean1d => annotating/lep}/Makefile.am | 0 scribo/sandbox/green/demo/annotating/lep/lep.cc | 127 ++ .../kmean1d => annotating/nb_color}/Makefile.am | 0 .../green/demo/annotating/nb_color/nb_color.cc | 143 ++ .../kmean1d => annotating/project}/Makefile.am | 0 .../green/demo/annotating/project/project.cc | 275 ++++ .../kmean1d => annotating/rgb_64}/Makefile.am | 0 .../sandbox/green/demo/annotating/rgb_64/rgb_64.cc | 80 ++ .../kmean1d => annotating/rgb_64_9}/Makefile.am | 0 .../green/demo/annotating/rgb_64_9/rgb_64_9.cc | 132 ++ .../stddev_color}/Makefile.am | 0 .../demo/annotating/stddev_color/stddev_color.cc | 191 +++ .../stddev_color_16}/Makefile.am | 0 .../annotating/stddev_color_16/stddev_color_16.cc | 261 ++++ .../achromastism}/Makefile.am | 0 .../exp/annotating/achromastism/achromastism.cc | 179 +++ .../exp/annotating/achromastism/text-color.txt | 0 .../green/exp/annotating/achromastism/text-img.txt | 0 .../exp/annotating/achromastism/text-only.txt | 0 .../bench}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/bench/bench.cc | 1450 ++++++++++++++++++++ .../error}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/error/error.cc | 833 +++++++++++ .../histo}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/histo/histo.cc | 366 +++++ .../hsv}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/hsv/hsv.cc | 912 ++++++++++++ .../hue}/Makefile.am | 0 scribo/sandbox/green/exp/annotating/hue/hue.cc | 402 ++++++ .../green/exp/annotating/hue}/text-color.txt | 0 .../sandbox/green/exp/annotating/hue}/text-img.txt | 0 .../green/exp/annotating/hue}/text-only.txt | 0 .../nb_color}/Makefile.am | 0 .../green/exp/annotating/nb_color/nb_color.cc | 171 +++ .../saturation}/Makefile.am | 0 .../green/exp/annotating/saturation/saturation.cc | 175 +++ .../exp/annotating/saturation}/text-color.txt | 0 .../green/exp/annotating/saturation}/text-img.txt | 0 .../green/exp/annotating/saturation}/text-only.txt | 0 .../stddev_color}/Makefile.am | 0 .../exp/annotating/stddev_color/stddev_color.cc | 216 +++ .../stddev_color_16}/Makefile.am | 0 .../annotating/stddev_color_16/stddev_color_16.cc | 277 ++++ .../value}/Makefile.am | 0 .../green/exp/annotating/value}/text-color.txt | 0 .../green/exp/annotating/value}/text-img.txt | 0 .../green/exp/annotating/value}/text-only.txt | 0 scribo/sandbox/green/exp/annotating/value/value.cc | 468 +++++++ scribo/sandbox/green/mln/clustering/kmean2d.hh | 4 +- scribo/sandbox/green/mln/fun/p2b/achromatic.hh | 20 +- .../sandbox/green/mln/fun/v2v/hue_concentration.hh | 10 + .../green/mln/fun/v2v/rgb_to_achromatism_map.hh | 5 + scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh | 15 +- 59 files changed, 8105 insertions(+), 21 deletions(-) copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/bic}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/bic/bic.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/hsv}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/hsv/hsv.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/lep}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/lep/lep.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/nb_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/project}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/project/project.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/rgb_64}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/rgb_64_9}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/stddev_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc copy scribo/sandbox/green/demo/{clustering/kmean1d => annotating/stddev_color_16}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/achromastism}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-color.txt (100%) copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-img.txt (100%) copy {milena => scribo}/sandbox/green/exp/annotating/achromastism/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/bench}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/bench/bench.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/error}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/error/error.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/histo}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/histo/histo.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/hsv}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/hsv/hsv.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/hue}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/hue/hue.cc copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/hue}/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/nb_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/saturation}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/saturation/saturation.cc copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/saturation}/text-only.txt (100%) copy scribo/sandbox/green/exp/{regional_maxima => annotating/stddev_color}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/stddev_color_16}/Makefile.am (100%) create mode 100644 scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc copy scribo/sandbox/green/exp/{regional_maxima => annotating/value}/Makefile.am (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-color.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-img.txt (100%) copy {milena/sandbox/green/exp/annotating/achromastism => scribo/sandbox/green/exp/annotating/value}/text-only.txt (100%) create mode 100644 scribo/sandbox/green/exp/annotating/value/value.cc diff --git a/scribo/sandbox/green/ChangeLog b/scribo/sandbox/green/ChangeLog index 5e4cb27..6210a79 100644 --- a/scribo/sandbox/green/ChangeLog +++ b/scribo/sandbox/green/ChangeLog @@ -1,3 +1,67 @@ +2010-09-08 Yann Jacquelet <jacquelet@lrde.epita.fr> + + Import annotating sources from milena green's sandbox. + + * README.green: New. + * demo/annotating/bic/Makefile.am: New. + * demo/annotating/bic/bic.cc: New. + * demo/annotating/hsv/Makefile.am: New. + * demo/annotating/hsv/hsv.cc: New. + * demo/annotating/lep/Makefile.am: New. + * demo/annotating/lep/lep.cc: New. + * demo/annotating/nb_color/Makefile.am: New. + * demo/annotating/nb_color/nb_color.cc: New. + * demo/annotating/project/Makefile.am: New. + * demo/annotating/project/project.cc: New. + * demo/annotating/rgb_64/Makefile.am: New. + * demo/annotating/rgb_64/rgb_64.cc: New. + * demo/annotating/rgb_64_9/Makefile.am: New. + * demo/annotating/rgb_64_9/rgb_64_9.cc: New. + * demo/annotating/stddev_color/Makefile.am: New. + * demo/annotating/stddev_color/stddev_color.cc: New. + * demo/annotating/stddev_color_16/Makefile.am: New. + * demo/annotating/stddev_color_16/stddev_color_16.cc: New. + * exp/annotating/achromastism/Makefile.am: New. + * exp/annotating/achromastism/achromastism.cc: New. + * exp/annotating/achromastism/text-color.txt: New. + * exp/annotating/achromastism/text-img.txt: New. + * exp/annotating/achromastism/text-only.txt: New. + * exp/annotating/bench/Makefile.am: New. + * exp/annotating/bench/bench.cc: New. + * exp/annotating/error/Makefile.am: New. + * exp/annotating/error/error.cc: New. + * exp/annotating/histo/Makefile.am: New. + * exp/annotating/histo/histo.cc: New. + * exp/annotating/hsv/Makefile.am: New. + * exp/annotating/hsv/hsv.cc: New. + * exp/annotating/hue/Makefile.am: New. + * exp/annotating/hue/hue.cc: New. + * exp/annotating/hue/text-color.txt: New. + * exp/annotating/hue/text-img.txt: New. + * exp/annotating/hue/text-only.txt: New. + * exp/annotating/nb_color/Makefile.am: New. + * exp/annotating/nb_color/nb_color.cc: New. + * exp/annotating/saturation/Makefile.am: New. + * exp/annotating/saturation/saturation.cc: New. + * exp/annotating/saturation/text-color.txt: New. + * exp/annotating/saturation/text-img.txt: New. + * exp/annotating/saturation/text-only.txt: New. + * exp/annotating/stddev_color/Makefile.am: New. + * exp/annotating/stddev_color/stddev_color.cc: New. + * exp/annotating/stddev_color_16/Makefile.am: New. + * exp/annotating/stddev_color_16/stddev_color_16.cc: New. + * exp/annotating/value/Makefile.am: New. + * exp/annotating/value/text-color.txt: New. + * exp/annotating/value/text-img.txt: New. + * exp/annotating/value/text-only.txt: New. + * exp/annotating/value/value.cc: New. + * mln/clustering/kmean2d.hh: New. + * mln/fun/p2b/achromatic.hh: New. + * mln/fun/v2v/hue_concentration.hh: New. + * mln/fun/v2v/rgb_to_achromatism_map.hh: New. + * mln/fun/v2v/rgb_to_hsv.hh: New. + + 2010-07-07 Yann Jacquelet <jacquelet@lrde.epita.fr> Import regional maxima sources from milena green's sandbox. diff --git a/scribo/sandbox/green/README.green b/scribo/sandbox/green/README.green index 7886914..5d6e147 100644 --- a/scribo/sandbox/green/README.green +++ b/scribo/sandbox/green/README.green @@ -685,21 +685,6 @@ rechercher dans les derniers répertoires mp00042c et mp00307c. * doc/labeling/mp00307c: Test sur la representativité des couleurs trouvées. -Faire le changelog, il y a de l'aide dans git/doc/Changelog -... et commiter - - -XI ANNOTATING --------------- - -==> to do - - - -* doc/annotating - - - XI AUTRES ASPECTS DOCUMENTAIRES ------------------------------- @@ -732,3 +717,495 @@ l'effort. * doc/formulae: LaTex directory. * doc/quick_tour: LaTex directory. + +XII ANNOTATING +-------------- + +Tout d'abord, voici les notes documentaires qui ont été réalisées sur +la problématique d'annotation d'image. On trouvera un fichier +class.txt qui a pour but de poser quelques réflexions sur les types de +classes de document. Les informations ne sont pas abouties mais +permettent de défricher un peu le terrain. Le document +syntheseMillet2008 est un compte-rendu de lecture des parties +relatives à nos travaux dans la thèse de Millet. Pour bien comprendre +mon travail, il est impératif de lire les travaux de Millet, ou +simplement ce compte-rendu. Le dernier document est moins intéressant, +il s'agit d'une note de travail sur les indicateurs de Millet bruts de +fonderie (testMillet2008). Cette note n'est ni achevée, ni +aboutie. Elle conclue sur le fait que les indicateurs et les seuils +donnés par Millet dans sa thèse sont complètement à revoir pour nos +besoins. Notemment, des tests sur la détection des images par rapport +aux cliparts nous ont convaincu que certaines images AFP contiennent +des de grandes zones homogènes qui induisent des erreurs dans les +prédicteurs de Millet. Néanmoins, les cas particuliers qui +contredisent les aspects opérationnnels de Millet n'enlèvent pas sa +réflexion: Les images noir/blanc (ou monochromes) ont une très faible +saturation, et/ou une forte concentration de la teinte. Les cliparts +ont une forte concentration de niveaux de gris autour d'un pic. + +* doc/annotating: La documentation relative à l'annotation. + + +Après la lecture des descripteurs de Millet, un des premiers réflexe a +été d'en implémenter plusieurs pour voir ce qu'ils pouvaient ressortir +sur les images que nous avions. Le descripteur BIC sépare une image en +deux ensembles de pixels, les points intérieurs et les points +extérieurs. Les points intérieurs ont la propriété d'être de même +couleur que leur 4-voisins. Attention, la couleur est évaluée dans un +espace RGB à 3 bits. + +* demo/annotating/bic: Histogrammes des points intérieurs et extérieurs. + + +Le descripteur LEP, lui, propose de seuiller par sobel l'image et de +faire l'histogramme des configurations du voisinage des points +seuillés. Pour ce faire, on utilise une convolution un peu spéciale +qui va attribué un unique id en fonction du voisinage. + +* demo/annotating/lep: Histogramme des configurations des voisinages des pixels. + + +Un autre descripteur simple est le nombre de couleur dans une +image. Pour cela, il est possible de construire l'histogramme des +couleurs et de compter les cellules pleines. On peut éventuellement +appliquer une quantification sur l'espace des couleurs. La +compilation laisse place à d'étranges warnings sur une comparaison +entre entiers signés et non signés, mais je n'ai pas la main dans mon +code (ou je ne sais pas comment faire) pour enlever ces warnings. Ils +sont récents, je n'avais pas souvenir de les avoir eu. + +* demo/annotating/nb_color: Compte le nombre de couleurs dans une image. +* exp/annotating/nb_color: Adaptation pour fonctionner sur une base d'image. + + +L'histogramme RGB-64 est un descripteur simple qui quantifie les +couleurs sur 2 bits et réalise l'histogramme dans cet espace. C'est +bien sûr une classification gros grain, mais ajouté au reste ... La +version RGB-64-9 ajoute une phase de division de l'image en 9 sous +images. De cette manière, l'histogramme RGB-64 est construit sur les 9 +sous images. Pour former le descripteur final, on fusionne les neufs +histogrammes. + +* demo/annotating/rgb_64: Histogramme couleur dans l'espace RGB-64 (2 bits/axe). +* demo/annotating/rgb_64_9: Histogramme RGB-64 sur les 9 sous images. + + +Le descripteur de projection relaté par Millet est particulier. En +premier lieu l'image est sous échantillonnée pour réduire sa dimension +à 100 x 100 de manière à borner la taille des vecteurs obtenus au +final. Puis l'image est seuillée par sobel (threshold = 100). L'image +est d'abord divisée horizontalement en deux. Puis on établit la +projecton perpendiculairement à la séparation de manière à obtenir +deux vecteurs de 100 valeurs chacunes. On recommence l'opération en +divisant maintenant l'image verticalement. L'union des 4 vecteurs +forme le descripteur de projection. L'information condensée dans ces +vecteurs est simplement la répartition des contours de manière +horizontale ou verticale. + +* demo/annotating/project: Répartition des contours horizontaux et verticaux. + + +La reconnaissance des cliparts s'appuie sur une analyse d'histogramme +qui est fournie dans le code suivant. L'idée est de dire qu'une image +de type clipart va être reconnaissable surtout à l'aide de ces +contours. La couleur existe mais est très grossière. Le faitde +dessiner à la main implique de simplifier énormément, de caricaturer, +le remplissage. Du coup, une analyse en niveau de gris de +l'histogramme révèle très peu de nuances. Il peut cependant en avoir +un peu. Néanmoins, il existe des logiciels pour aider à la fabrication +des cliparts qui proposent l'usage de dégradé, ce qui nuit à cette +méthode de reconnaissance. Millet analyze l'histogramme normalisé +(histogramme divisé par son pic) et regarde si son energie ne serait +pas concentrée autour du pic (5 pixels de chaque côté +maximum). Parfois cette méthode ne fonctionne pas correctement sur des +photographies qui ont un cadre uniforme. La méthode trouve un pic (le +cadre) et vérifie alors qu'une proportion non négligeable des pixels +sont bien autour de ce pic (tout dépend de l'épaisseur du cadre). Pour +palier à cet inconvénient, Millet propose d'utiliser ce test, non plus +sur l'image entière, mais sur chacune des 16 sous images après un +découpage géométrique régulier. De facto, la contribution du cadre +diminue suffisemment pour repasser en dessous du seuil de +reconnaissance. + +* demo/annotating/stddev_color: Descripteur utilisé reconnaitre des cliparts. +* exp/annotating/stddev_color_16: Adaptation pour le travail sur base. +* demo/annotating/stddev_color_16: Descripteur pour cliparts avec 16 imagettes. +* exp/annotating/stddev_color: Adaptation pour le travail sur une base d'image. + + +A partir de maintenant, tous les morceaux de codes réalisés préparent +directement ou indirectement le résultat de la classification des +bases (exp/annotating/bench). + +Plus de temps pour faire le code use correspondant aux fichiers +librairies. Tout le code pour le faire est dans hsv. Rien de +compliqué, mais allons à l'essentiel. + +Le but du code HSV est d'effectuer les tests de Millet ou des +améliorations sur ces tests. Le premier test proposé par Millet est +l'achromaticité. Il s'agit de regarder s'il existe une faible +variation entre les trois canaux (R/G/B) pour un grand nombre de +pixels. Si c'est le cas, c'est que l'image est presque en niveau de +gris et peut être remplacée facilement par une image grisée sans trop +de distorsions au niveau de la couleur. Nous avons essayer de +généraliser un peu le test de manière à produire, non pas seulement +une réponse sur l'achromaticité de l'image, mais aussi avoir une vue +d'ensemble (sous forme d'image) des variations entre les cannaux pour +chaque pixel. Il n'est pas utile d'analyser les différences sur chacun +des canaux, prendre la différence absolue entre le canal min et le +canal max suffit pour définir le test d'achromaticité. + +Les autres tests de Millet sont la faible saturation et la dominance +de la teinte. Pour savoir si la saturation est faible, il faut +utiliser un histogramme du canal dédié à la saturation. Si 95% des +pixels sont en dessous de 100, alors l'image est faiblement saturée, +elle est en noir et blanc. De la même manière, on regarde la dominance +de la teinte. Pour ce faire, il faut voir si l'histogramme de la +teinte ne possède pas un pic avec une très faible variance (tout +rapproché autour du pic). Si c'est le cas, la dominance de la teinte +est avérée et l'on peut calculé la couleur dominante. L'image est en +niveau de gris, mais colorisé autour d'une couleur (par cepia, vert +...). + +Ce programme est très très sujet à changements, il m'a servit de test +et je ne peux pas juré qu'il est complètement sain au niveau du +traitement. D'autres versions sont potentiellement plus +stables. Notemment celle dans exp/annotating/hsv. + +* mln/fun/v2v/rgb_to_achromatism_map.hh : Distance pour l'achromaticité. +* mln/fun/v2v/achromatic.hh : Define the achromatic map. +* mln/fun/v2v/hue_concentration.hh : Define the distance hue/peak map. +* mln/fun/p2b/achromatic.hh : Say if a site is achromatic. +* demo/annotating/hsv: Code des différents tests de Millet. + + +Dans le répertoire exp/annotating/hue, on trouve 3 fichiers textes qui +rassemblent des classes d'images. Tout d'abord les images ICDAR +n'ayant que du texte et des traces de couleurs (lettrine de couleur, +trait, petit bandeau), un fichier où il n'y a que du texte noir & +blanc et un fichier contenant les images couleurs (avec photographies +ou dessins). Cette classification a été effectuée de manière manuelle. +Le code hue test la proportion de pixels étant autour du pic de +teinte. Il s'agit de savoir si la dominance d'une teinte est +avérée. Le code renvoit la proportion de pixels agglomérés autour du +pic. Le but est de généralisé les tests de millets pour qu'ils se +ressemblent le plus possible. + +* exp/annotating/hue: Implémentation de la généralisation du test de Millet. +* mln/fun/v2v/rgb_to_hue_map.hh : Construction de la map de teinte. + + +On retrouve les trois fichiers permettant de classifier la base ICDAR +en trois sous populations. Le test de saturation consiste simplement à +regarder si une certaine quantité de la population de l'histogramme de +saturation est en dessous d'un certain seuil. La généralisation du +test ne porte pas sur le test en lui-même, mais sur la forme dans +lequel le test est fait. + +* exp/annotating/saturation: Implémentation de la généralisation du test. +* mln/fun/v2v/rgb_to_saturation_map.hh : Construction de la map de saturation. + + +Le test de value a déjà été décrit précédemment dans +stddev_color. L'idée est toujours la même, mais cette fois il est +effectué dans l'espace des valeurs (HSV). Cela ne change pas grand +chose, puisqu'il était utilisé sur des images en niveau de gris. C'est +l'un des tests importants car il réagit à la différentiation entre une +image type photographie et une image plus stylisée comme un +clipart. Ce test a aussi des vertus pour la distinction entre du noir +& blanc et de la couleur. Il s'avère que les images type photographie +avec pleins de couleurs ont un histogramme moins sujet aux pics que +les histogrammes noir & blanc. De facto, l'énergie de l'histogramme +est distribué sur l'ensemble de la plage contrairement aux images noir +& blanc où il y a une concentration de chaque côté de l'histogramme +(bipolarité). + +* exp/annotating/value: Implémentation de la généralisation du test de Millet. +* mln/fun/v2v/rgb_to_value_map.hh : Transformation d'espace. + + +Le programme hsv reprend les tests préalablement élaborés auparavant +sur les plans H, S puis V. Il combine tout en un seul programme pour +avoir une vision plus synthétique de ce qui se passe sur les 3 espaces +simultanément. + +Les tests incorpore mes transformations. C'est à dire que l'on +effectue une série de test équivalent à ceux de Millet (au moins dans +l'idée et le plus souvent, il s'agit d'une réecriture sous une autre +forme) en partant des histogrammes normalisés. Un histogramme +normalisé est un histogramme de flottant qui contient l'histogramme +classique divisé par le nombre total de pixels. En faisant cela, on se +déplace dans l'espace des distributions. Si l'histogramme n'a qu'un +seul pic et que ce dernier contient tous les pixels (pic de dirac par +exemple), alors l'histogramme normalisé donnera 1 comme valeur de +proportion à ce pic. En fait, l'intégrale d'une distribution vaut +1. Le but des distributions est de s'affranchir des caractéristiques +de taille de l'image, ce qui permet de comparer les histogrammes entre +eux. On peut ainsi comparer les histogrammes d'une photo AFP petit +format avec celui d'une image grand format de la base ICDAR. Le but du +jeu est de garder les idées de Millet dans ce nouvel espace. Les tests +obtenus sont équivalents mais pas identiques. + +Pour la teinte, une fois l'histogramme normalisé obtenu, on cherche à +savoir s'il existe un pic avec une forte proportion de l'histogramme +autour de ce pic. Plutôt que le pic, nous prenons la moyenne qui est +un opérateur un peu plus robuste et nous calculons la proportion de +l'histogramme autour (un seuil donné en paramètre défini le périmètre +du calcul). L'expression sous forme de proportion plutôt que sous la +forme d'une variance rend le test homogène avec les autres tests. + +Pour la saturation, après normalisation, on regarde la proportion de +l'histogramme en dessous d'un certain seuil. Il n'y a pas de +changement significatif par rapport au test de Millet. Le seuil +initialement proposé par Millet était 100, il a été adapté car nos +bases sont différentes des siennes (le notre est à 25). + +Le test sur les valeurs (cliparts contre photos) a vraiment un intérêt +au delà de ce cas d'utilisation. Il nous renseigne sur la +concentration autour d'un pic de l'histogramme ou non. Nous préferrons +la mesure de similarité à l'équi-répartition des densités, mais l'idée +est exactement la même. Est-ce que notre histogramme est plat ou +inversement, est-ce que les contributions sont rassemblées autour d'un +pic? Si une image est noir et blanc, il existera un pic correspondant +au fond et la densité s'éloignera fortement de la distribution +équiprobable. Dans le cas maintenant d'une image couleur, la +répartition est plus homogène, couvrant un large spectre dans le +domaine de l'histogramme. Du coup, la distribution semblera davantage +equi-répartie. On notera que nous prenons le test à l'envers de ce que +propose Millet. Il essaye de voir si il y a un pic et calcule une +forme de contribution normalisée autour de ce pic. Nous au contraire, +on regarde l'absence de pic et on calcule la différence entre la +densité et cette absence de pic. Notre avantage par rapport à Millet +est démontré particulièrement dans les cas où il existe plusieurs +grosses distributions. A contrario, notre test souffre des cas où il +existe de nombreuses petites distributions. + + +Voici un premier retour sur les expériementations: +La discrimination entre la base AFP et la base ICDAR peut se faire en +étudiant la forme des densités des niveaux de gris. Les images +naturelles semblent avoir un spectre recouvrant en général les 256 +niveaux de gris alors que les images de documents ont une présence +importante du fond. Dans le cadre d'une densité, ce qui est alloué sur +le fond ne peut se retrouver ailleurs. Une comparaison avec la densité +équiprobable nous renseigne donc sur la nature des images. Il semble +néanmoins qu'un certain nombre d'images défient ce dispositif. Par +exemple des gros plans sur des zones mono-teintée (ski, voile,site +web). + +* exp/annotating/hsv: Code unifiant les trois tests sur chacun des plans HSV. +* mln/fun/v2v/rgb_to_hue_map.hh : Transformation d'espace. +* mln/fun/v2v/rgb_to_saturation_map.hh : Transformation d'espace. +* mln/fun/v2v/rgb_to_value_map.hh : Transformation d'espace. + + +Le test sur l'achromatisme des images est décrit dans le code +suivant. Il a été purement et simplement abandonné dans la mesure où +c'est un cas très particulier qui est repris par une saturation très +faible. La saturation s'exprime comme 1 - min(channel)/max(channel), +mais dans le cas où le min(channel) == max(channel), la saturation +vaut 0. Le problème vient plutôt du calcul de la teine qui ne peut pas +admettre que le min soit égal au max. Pour ce calcul, on se débrouille +pour gérer le cas et renvoyer une valeur standardisée, Millet +proposait -1. + +* exp/annotating/achromatism: Détection d'image couleur en niveau de gris. +* mln/fun/v2v/rgb_to_achromatism_map.hh : Transformation d'espace. + + +Ce programme a pour but de créer les histogrammes normalisés en rafale +pour les bases AFP et ICDAR. Il assure leur création dans les six +plans possibles R,G,B,H,S,V. De cette manière il est possible de +vérifier les corrélations éventuelles entre le canal B et G. Par +ailleurs, après visionnement de tous les histogrammes, on note des +spécificités dans les deux bases sur les plans S et V. Certaines +images ont des réactions très fortes sur le plan H, mais ce n'est pas +une caractéristique pour une base (seulement pour ces images en +question). Les résultats ont déjà été sauvegardés dans le répertoire +image du LRDE. Des infos sont notés à ce sujet dans README.result. Le +seul but de ce calcul est de maîtriser csujet dans README.result. Le +seul but de ce calcul est de maîtriser ce qui se passe dans ces +espaces, pas simplement de supposer ce qu'il pourrait s'y passer. La +comparaison des images est rendu possible quelque soit la base +d'origine par le fait qu'elles sont normalisées. Chaque fréquence des +histogrammes est divisée par la somme des fréquences. On obtient donc +une version discrète des densitées. La somme des nouvelles fréquences +vaut 1. On voit davantage si les densités se rapprochent d'une +équi-répartition ou non. La négative implique un ou plusieurs pics. Le +cas défavorable dans mon approche (qui existe pour certaines images) +est une multitude de tout petits pics autour de l'équi-répartition. Ce +n'est pas équi-répartie pour les calculs mais ce n'est clairement pas +la manisfestation d'une concentration de l'énergie quelque part. + +* exp/annotating/histo: Creation des histogrammes normalisés. +* mln/fun/v2v/rgb_to_hue_map.hh: Transformation d'espace. +* mln/fun/v2v/rgb_to_saturation_map.hh: Transformation d'espace. +* mln/fun/v2v/rgb_to_value_map.hh: Transformation d'espace. + + +Dans le programme erreur, on teste l'idée de jonathan. Une référence à +ce sujet peut être trouvée dans README.img. Les bases AFP et ICDAR +sont retravaillées à l'aide de ImageMagick et de Gimp pour diminuer le +nombre de couleurs initiales. On oblige à n'avoir que 30, 20 ou 10 +couleurs pour chaque base. Il y a donc 4 versions de chacune des bases +en comptant la version originale des images. Les algorithmes utilisés +par Gimp et ImageMagick sont très différents. L'idée de Jonathan était +de dire que lorsqu'une image est couleur, plus on réduit le nombre de +couleur, plus elle change vis-à-vis de l'image originale. Inversement, +si une image possède peu de couleurs (noir & blanc), sa dégradation ne +l'altérera pas tant que cela. Prenons une image d'un skieur de la base +AFP, en réduisant le nombre de couleurs, la combinaison marron va +devenir marron strictement homogène et la perception que nous en avons +est visuellement très altérée. A contrario, une image en noir & blanc +ne semble pas bouger d'un pouce. La comparaison avec la discrimination +sur la saturation ou la valeur montrera des résultats un peu meilleur +plus tard mais il impossible de préjuger a priori des résultats +futurs. L'erreur entre l'image initiale et l'image dégradée est +calculée avec le PNSNR (compression p278 Handbook Color). Le programme +calcule la dégradation et essaie de trouver automatiquement des seuils +de séparation pour les deux bases. 4 détection de seuils sont testées. +Ces seuils sont calculées avec deux bases représentatives des bases à +discriminer. En mode production, le seuil est vu comme une constante +pour le programme. Les deux premières classifications renvoient un +seuil calculée comme une moyenne pondérée des moyennes des populations +à discriminer. Le premier détecteur pondère par la déviation standard +et le second par la variance. Le troisième simule deux populations +gaussiennes de variances différentes et résoud l'équation de second +degré qui en résulte. Enfin, le dernier test ne préjuge pas des +distributions statistiques et réalise la minimisation de l'erreur de +classification. Pour cela, il suffit de compter le nombre d'images +bien classées et mal classées sur les bases d'apprentissage des +populations. On utilise pour cela la méthode Otsu. Le détecteur PNSRN +renvoie une valeur. Pour chaque population (AFP, ICDAR), on construit +l'histogramme de des valeurs obtenues. A noter, qu'il faut que les +valeurs du PNSNR soient ramenées entre 0 et 255. En pratique ce n'est +pas un problème, mais il faut le mettre en oeuvre, dans mon code, ce +n'est pas fait (les valeurs ne posaient pas de problème). Donc, pour +chaque seuil possible (de 0 à 256), on étudie la matrice de classement +(groupe 1 - détecté groupe 1, groupe 1 - détecté groupe 2, groupe 2 - +détecté groupe 1, groupe 2 - détecté groupe 2). L'erreur est +simplement la somme des quantités groupe x - détecté groupe y avec x +!= y. Finalement, pour chaque seuil, il est possible de connaitre +l'erreur. Le processus de minimisation de l'erreur revient à chercher +le seuil associé à l'erreur minimale. C'est ce dernier test que nous +préconisons. Il faut mettre en lumière que ces quatre tests renvoient +à peu près les mêmes seuils. La minimisation de l'erreur étant le +meilleur de tous puisqu'il minimise directement l'erreur de +classification et c'est ce que nous cherchions en fin de compte. Un +cinquième test a été fait avec une analyse de fisher sur l'histogramme +des populations mélangées, mais c'est une idée saugrenue car nous +disposons à ce niveau des populations séparées et évidemment les +résultats sont moins bons. + +Petit rappel. Un vieux problème a été mis à jour ici. Lorsqu'on +calcule des informations sur un histogramme, le type temporaire qui +contient les résultats ne doit pas être le type qui encode les valeurs +des résultats. Par exemple, un histogramme de byte, un calcul de +variance tout simple où il faut stocker la valeur des pixels au carré +multiplié par leur occurrence tiendra facilement dans un long mais pas +dans un byte. Le sucre avec les acesseurs des itérateurs tend à +mélanger les genres. Une solution simple est de stocker la valeur de +l'histogramme dans un type pouvant effectuer les calculs et ensuite +retravailler avec ce type. Nous avions déjà discuté de ce problème et +je ne sais pas si tu avais pu corriger le problème. Si la +classification renvoit n'importe quoi, il se peut que cela provienne +de ce problème. Le problème ne se voit pas sur des calculs de +moyennes, il ne s'observe que sur certains calculs de variance (dépend +de la formule utilisée). + +* exp/annotating/error: Test de l'idée de jonathan (dégradation des couleurs) + + +Le travail dans bench.cc reprend toute sorte de travaux déjà réalisés +dans d'autres fichiers. Il a pour but de comparer ensemble tous les +descripteurs pour la reconnaissance de base de données entre l'AFP et +l'ICDAR. + +Le travail commence avec un certain nombres de routines travaillant +sur les histogrammes permettant de trouver le pic, la moyenne, la +variance et d'autres éléments. Les routines étaient éparpillées dans +le code, du coup je les ai regroupées au même endroit. Il y a des +redondances de code correspondant à des copier/coller ou à différents +essais. Prendre les versions les plus génériques et en faire des +accumulateurs serait un riche idée. + +Huit détecteurs sont comparés ensembles: +- hue1, détection d'un pic de teinte très dense par la méthode de Millet. +- sat1, détection d'une densité importante dans les saturations basses (Millet). +- lvl0, comptage du nombre de niveau de gris (idée de Millet). +- hue0, détection d'un pic de teinte par la méthode des densités. +- sat0, détection d'une forte basse saturation par la méthode des densités. +- val0, détection d'un pic de niveau de gris par la méthode des densités. +- val1, détection d'un pic de niveau de gris par la méthode de Millet. +- err, PNSNR (idée de jonathan). + +FIXME: Attention, le PNSNR n'est pas borné, il faut le faire, il doit avoir +au maximum 255 comme valeur. + +Tous les détecteurs ont été expliqués en large, en long et en travers +dans les sources précédentes. LIRE le chapitre XII (ANNOTATING) de ce fichier +en entier. + +Ensuite vient les séparateurs de population statistiques qui ont été +déjà introduit dans le fichier error. Bien que tous soient présent, +c'est la minimisation de l'erreur qui est utilisée. + +Enfin vient le front end, l'un des plus complexes que j'ai écrit cette +année. Le main lui même, n'est pas la partie la plus complexe. Il +définit les actions fonctionnelles à réaliser et la structure de +donnée réalisée pour garder la trace de tous les résultats. +- File_name : Cette variable retient le nom des fichiers. C'est un tableau +de la taille du nombre d'images rencontrées toute base confondue. Comme on +ne parcourt qu'une fois les répertoires, il faut pouvoir retenir cette +information qui servira éventuellement pour la nomenclature des dumps +par la suite. +- Result : Cette variable contient toutes les informations du traitement. +C'est un tableau à deux dimensions, une pour les images et une autre pour +les descripteurs. L'index d'image est le même que pour file_name. +- Size : Cette variable contient le nombre d'images par database. +- Threshold : Cette variable va stocker les seuils calculés sur chacun +des descripteurs. Ces seuils sont sensés effectuer la séparation entre les +deux bases. +- Cxx : variables de comptage des images bien ou mal classées relativement +à la position des seuils et à l'appartenance d'origine des images. Cette +appartenance d'origine est calculée à l'aide Size qui contient le nombre +d'image par base de données. Comme les images sont vues dans l'ordre, les X +premières appartiennent à la base ICDAR et les Y suivantes à la base AFP. +- histo : Variable servant à effectuer par database et par descripteur un +histogramme utilisé ensuite lors de la discrimination des bases. + +La partie compute_descriptors a pour mission de passer une fois sur +toutes les images et de calculer les descripteurs associés. A l'issue +de cette passe file_name, result et size sont remplis et pourront être +utilisés par les autres routines. + +La partie dump_descriptors a pour but de réaliser un tracé en gnuplot, +avec une couleur différente pour chaque base. Le graphe montre les +valeurs utilisées par chaque descripteur pour toutes les images de la +base. + +La partie correction des descripteurs est optionnelle et force un +certain nombre de valeurs pour être inférieures à 256. Le but était de +pouvoir travaillé, même si les descripteurs n'étaient pas complètement +opérationnels. Elle sert donc pour le debug. + +La partie calcul des histogrammes sert à obtenir la distribution des +valeurs de chacun des descripteurs en fonction des bases +utilisées. C'est une étape préliminaire à l'analyse des populations et +au calcul des seuils. + +La partie calcul des seuils repose sur la minimisation des erreurs de +classification. Cette méthode ne fonctionne bien si la classe zéro à +une moyenne inférieure à la classe un. Du coup, il n'est pas possible +de déterminer de manière simple si la classe 0 correspond à l'AFP ou à +l'ICDAR. Pour chaque descripteur, les cartes sont rebattues. + +Enfin, tous les histogrammes sont sauvés pour pouvoir comprendre et +visualiser les résultats. + +Le main2 ne sert à rien, juste pour des essais. + +Attention, modification VAL0/VAL1, vérifiez que le bon descripteur est +au bon endroit dans le tableau. + +* exp/annotating/bench: Comparaison des détecteurs pour la classif. ICDAR/AFP. \ No newline at end of file diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/bic/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/bic/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/bic/bic.cc b/scribo/sandbox/green/demo/annotating/bic/bic.cc new file mode 100644 index 0000000..f63d260 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/bic/bic.cc @@ -0,0 +1,122 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet BIC descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It quantifies the three channels on 3 +/// bits, then builds two histograms with the interior pixels and the +/// exterior ones. The interior pixels are those where color value is +/// the same as their 4 neighbouring pixels. The descriptor is the +/// fusion of the two histograms. + +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> +#include <mln/core/alias/neighb2d.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/var.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> +#include <mln/data/fill.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/colors.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Main entry. +/// +/// Load the images, compute the interior and the exterior with the +/// predicate, then compute the two histograms separately. +int main() +{ + typedef mln::value::rgb<3> t_rgb3; + typedef mln::value::rgb8 t_rgb8; + typedef mln::neighb2d t_neighb2d; + typedef mln::image2d<t_rgb3> t_image2d_rgb3; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::fun::v2v::rgb8_to_rgbn<3> t_rgb8_to_rgb3; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb3 input_rgb3; + t_image2d_rgb3 interior_rgb3; + t_image2d_rgb3 exterior_rgb3; + t_histo3d histo_exterior; + t_histo3d histo_interior; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb3 = mln::data::transform(input_rgb8, t_rgb8_to_rgb3()); + + mln::initialize(interior_rgb3, input_rgb3); + mln::initialize(exterior_rgb3, input_rgb3); + + mln::data::fill(interior_rgb3, mln::literal::black); + mln::data::fill(exterior_rgb3, mln::literal::black); + + mln_piter_(t_image2d_rgb3) p(input_rgb3.domain()); + mln_niter_(t_neighb2d) n(mln::c4(), p); + + for_all(p) + { + bool is_interior = true; + + for_all(n) + is_interior = is_interior && (input_rgb3(p) == input_rgb3(n)); + + if (is_interior) + interior_rgb3(p) = input_rgb3(p); + else + exterior_rgb3(p) = input_rgb3(p); + } + + histo_interior = mln::data::compute(t_histo3d_fun(), interior_rgb3); + histo_exterior = mln::data::compute(t_histo3d_fun(), exterior_rgb3); + + //mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo_interior); + mln::debug::println(histo_exterior); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/hsv/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/hsv/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/hsv/hsv.cc b/scribo/sandbox/green/demo/annotating/hsv/hsv.cc new file mode 100644 index 0000000..e87ab2d --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/hsv/hsv.cc @@ -0,0 +1,721 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet HSV operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to HSV +/// one. Formulae are classical, we can find them on the web. +/// +// Val = max(R,G,B). +// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +/// +/// \fixme: This code is unstable. It was using to make a lot of tests. +/// Don't use it, we have more stable version of it. +#include <iostream> +#include <fstream> + +#include <mln/accu/max_site.hh> +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/point1d.hh> +#include <mln/core/alias/box1d.hh> +#include <mln/core/concept/image.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/transform.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> + +#include <mln/debug/println.hh> + +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/fun/v2v/rgb_to_hsv.hh> +#include <mln/fun/v2v/rgb_to_achromatism_map.hh> +#include <mln/fun/v2v/achromatism.hh> +#include <mln/fun/v2v/hue_concentration.hh> +#include <mln/fun/p2b/component_equals.hh> +#include <mln/fun/p2b/achromatic.hh> +#include <mln/fun/v2v/component.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/img_path.hh> + +#include <mln/io/plot/save_image_sh.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/pbm/save.hh> + +#include <mln/pw/cst.hh> +#include <mln/pw/value.hh> +//#include <mln/trace/quiet.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> +#include <mln/value/hsv.hh> + + +/// \brief Classify a rgb a value with a color of reference. +/// +/// \param[in] rgb a rgb8 value. +/// +/// \return a rgb8 initialize with the selecting literal. +/// +/// This classification of the RGB color space is based on the +/// Millet's work. It uses the hue wheel to do it and make new +/// distinction as brown/orange for instance. +mln::value::rgb8 label_color(const mln::value::rgb8 rgb) +{ + mln::value::hsv_f hsv = mln::fun::v2v::f_rgb_to_hsv_f(rgb); + + mln::value::rgb8 result; + + // Is it a gray level ? + if (0 == hsv.sat()) + { + // which result one ? + if (82 > hsv.sat()) + result = mln::literal::black; + else if (179 > hsv.sat()) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + } + // Is it a true result color ? + else if (14 > hsv.hue()) + result = mln::literal::red; + else if (29 > hsv.hue()) + { + // Is is brown or orange ? + unsigned dist_orange = mln::math::abs(hsv.sat() - 184) + + mln::math::abs(hsv.val() - 65); + + unsigned dist_brown = mln::math::abs(hsv.sat() - 255) + + mln::math::abs(hsv.val() - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else if (45 > hsv.hue()) + { + // Is it green or yellow ? + if (80 > hsv.val()) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else if (113 > hsv.hue()) + result = mln::literal::green; + else if (149 > hsv.hue()) + result = mln::literal::cyan; + else if (205 > hsv.hue()) + result = mln::literal::blue; + else if (235 > hsv.hue()) + result = mln::literal::violet; + else if (242 > hsv.hue()) + result = mln::literal::pink; + else + result = mln::literal::red; + + return result; +} + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +unsigned count_histo(const mln::Image<I>& img_) +{ + const I& img = exact(img_); + + mln_precondition(img.is_valid()); + + unsigned result = 0; + mln_piter(I) p(img.domain()); + + for_all(p) + result += img(p); + + return result; +} + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +template <typename I> +unsigned peak_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + return p_max; +} + + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +// unsigned stddev_color(mln::image2d<mln::value::int_u8> input_int_u8, +// const char *name_histo, +// const char *name_image) +// { +// typedef mln::point1d t_point1d; +// typedef mln::value::rgb8 t_rgb8; +// typedef mln::value::int_u8 t_int_u8; +// typedef mln::image2d<t_rgb8> t_image2d_rgb8; +// typedef mln::image2d<t_int_u8> t_image2d_int_u8; +// typedef mln::image1d<unsigned> t_histo1d; +// typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; +// typedef mln::accu::meta::stat::histo1d t_histo1d_fun; +// typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + +// t_histo1d histo; + +// std::cout << "histo : " << name_histo << std::endl; +// std::cout << "image : " << name_image << std::endl; + +// histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + +// mln::io::pgm::save(input_int_u8, name_image); +// mln::io::plot::save_image_sh(histo, name_histo); +// mln::debug::println(histo); + +// // Find the peak of the histogram +// unsigned v_max = mln::opt::at(histo, 0); +// short p_max = 0; + +// mln_piter_(t_histo1d) p(histo.domain()); + +// for_all(p) +// { +// if (v_max < histo(p)) +// { +// v_max = histo(p); +// p_max = p.ind(); +// } +// } + +// // Compute the specific stddev + +// float stddev_low = 0.0; +// float stddev_up = 0.0; +// float stddev = 0.0; + +// if (250 > p_max) +// for (short i = p_max+1; i < p_max+6; ++i) +// stddev_up += r(p_max, mln::opt::at(histo,p_max), +// i, mln::opt::at(histo,i)); + +// if (5 < p_max) +// for (short i = p_max-1; i > p_max-6; --i) +// stddev_low += r(p_max, mln::opt::at(histo,p_max), +// i, mln::opt::at(histo,i)); + +// stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up : +// (stddev_low + stddev_up)/2; + +// std::cout << "max_site : " << p_max << std::endl; +// std::cout << "h(max_site) : " << v_max << std::endl; +// std::cout << "stddev_up : " << stddev_up << std::endl; +// std::cout << "stddev_low : " << stddev_low << std::endl; +// std::cout << "stddev : " << stddev << std::endl; + +// return 0; +// } + + +// ------------------------------------- +// input image <name>.ppm +// map <name>-<map>.pgm +// thresholded map <name>-<map>.pbm +// histogram <name>-<map>.sh +// decision <name>-<map>.txt +// ------------------------------------- +// Achromatism <name>-achromatism.pgm +// call achromatism(input_rgb8, 7, 99.0) + + +/// \brief Decide if an image is achromatic or not [Millet]. +/// +/// \param[in] input_rgb8 the input image. +/// \param[in] threshold the distance threshold used for equality. +/// \param[in] percentage the percentage of pixels that very the test. +/// +/// This is an improving of the Millet test. The original test +/// compares rgb values to each other and look at differences greater +/// than the threshold. If the number of pixel that pass the test are +/// greater than 99.0, then the image is declared achromatic. In fact, +/// there is few variations between the three channels, so we can say +/// that it is like a grey image. We can with no too distortions +/// replace the color image by the grey one. The improving is in first +/// creating a map of the difference. As we can't keep the free +/// differences between channels, we look at reducing the test and we +/// find an equivalent one based on the difference between minima +/// channel value and the maxima channel value. After the map is +/// ready, we make binarization with the threshold. Then we compute +/// the histogram 1d for every pixels of the map that are greater the +/// threshold. Then, we count pixels in the histogram and traduce the +/// count in percentage to compare to the second threshold. Details +/// are saved in files and printed in the screen. +void achromatism(mln::image2d<mln::value::rgb8> input_rgb8, + mln::value::int_u8 threshold, + float percentage) +{ + typedef mln::fun::v2v::rgb_to_achromatism_map<8> t_rgb_to_achromatism_map; + + mln::image2d<mln::value::int_u8> map; + mln::image2d<mln::value::int_u8> view; + mln::image2d<bool> mask; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + bool result; + + + map = mln::data::transform(input_rgb8, t_rgb_to_achromatism_map()); + view = mln::data::stretch(mln::value::int_u8(), map); + mask = mln::binarization::threshold(map, threshold); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), + map | (mln::pw::value(mask) == true)); + cnt1 = count_histo(histo); + cnt2 = mln::geom::nsites(input_rgb8); + prop = (100.0 * (cnt2 - cnt1) / cnt2); + result = (prop > percentage); + + + std::ofstream txt_stream("achromatism.txt"); + txt_stream << "Achromatism" << std::endl; + + txt_stream << "Nbre pixels : " << cnt2 << std::endl; + txt_stream << "Nbre pixels achromatiques : " << (cnt2-cnt1)<< std::endl; + txt_stream << "Percentage : " << prop << std::endl; + txt_stream << "Image achromatique : " << result << std::endl; + txt_stream << std::endl; + + txt_stream.flush(); + txt_stream.close(); + + mln::io::pgm::save(view, "achromatism.pgm"); + mln::io::plot::save_image_sh(histo, "achromatism.sh"); + mln::io::pbm::save(mask, "achromatism.pbm"); +} + + +/// \brief Decide if an image is low saturate or not and so b/w [Millet]. +/// +/// \param[in] input_hsvf the input image in the HSV space. +/// \param[in] achromatism_mask the mask to prevent computing bad saturation. +/// \param[in] threshold the distance threshold used for equality. +/// \param[in] percentage the percentage of pixels that very the test. +/// +/// As we are in the HSV space, we can just isolate saturation +/// channel. The original idea of Millet is to build histogram bound +/// to 256 for the saturation. If a great percentage (95%) of the +/// population are under a threshold (100), then the image has got low +/// saturation. So, it is b/w image. Low saturation is sometimes +/// redundant with the achromatism test but not every time. + +// call low_saturation(input_rgb8, achromatism_mask, 100, 95.0) +void low_saturation(mln::image2d<mln::value::hsv_f> input_hsvf, + mln::image2d<bool> achromatism_mask, + mln::value::int_u8 threshold, + float percentage) +{ + typedef mln::value::hsv_f t_hsvf; + typedef mln::value::hsv_f::s_type t_sat; + typedef mln::fun::v2v::component<t_hsvf,1> t_component_s; + + mln::image2d<t_sat> map; + mln::image2d<mln::value::int_u8> view; + mln::image2d<bool> mask; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + bool result; + + + map = mln::data::transform(input_hsvf, t_component_s()); + view = mln::data::stretch(mln::value::int_u8(), map); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), + view|(mln::pw::value(achromatism_mask) == true)); + prop = (100.0 * (cnt2 - cnt1) / cnt2); + result = (prop > percentage); + + std::cout << "Saturation" << std::endl; + + cnt1 = count_histo(histo | mln::box1d(mln::point1d(0),mln::point1d(100))); + cnt2= mln::geom::nsites(achromatism_mask| + (mln::pw::value(achromatism_mask)==false)); + + std::ofstream txt_stream("saturation.txt"); + txt_stream << "Saturation" << std::endl; + + txt_stream << "Nbre pixels : " << cnt2 << std::endl; + txt_stream << "Nbre p faiblement saturés : " << cnt1 << std::endl; + txt_stream << "Pourcentage : " << prop << std::endl; + txt_stream << "Image faiblement saturé : " << result << std::endl; + txt_stream << std::endl; + + txt_stream.flush(); + txt_stream.close(); + + mln::io::pgm::save(view, "saturation.pgm"); + mln::io::plot::save_image_sh(histo, "saturation.sh"); + mln::io::pbm::save(mask, "saturation.pbm"); +} + +/* This is a classification of the ICDAR base in 3 modalities. +// COLOR + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00032c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00042c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00076c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00082c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00142c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00215c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00228c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00234c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00248c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00252c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00253c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00255c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00259c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00271c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00290c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00293c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00304c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00307c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00376c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00411c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00419c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00447c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00498c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00510c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00550c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00573c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00589c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00592c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00597c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00599c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00600c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00031c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00034c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00043c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00063c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00065c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00072c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00081c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00083c_20p.ppm"); + +// BLACK AND WHITE + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00329c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00036c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00037c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00039c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00040c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00049c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00055c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00057c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00068c_20p.ppm"); + + +// A LITTLE BIT OF COLOR + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00262c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00263c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00311c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00319c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00440c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00608c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00630c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00631c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00028c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00046c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00073c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00089c_20p.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/ta00090c_20p.ppm"); +*/ + + +/// \brief The main entry and the whole processing routine +/// +/// This is the routine which works on Millet test. First compute the +/// achromatic mask. Then build the transfer between RGB and HSV. Then +/// isolate each channel to work on it. Each separate channel is bound +/// between 0 and 255. Then keep only pixels low saturated (< 100 in S +/// canal) in a specefic map. Build the 3 histograms without the +/// achromatic pixels based on the 3 separated channels (H/S/V). Build the +/// 3 maps (hue_concentration, low_saturation and achromatism). Then do +/// the 3 tests of Millet with the thow thresolded pass (threshold,percentage). +int main() +{ + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::hsv_f t_hsvf; + typedef mln::value::hsv_f::h_type t_hue; + typedef mln::value::hsv_f::s_type t_sat; + typedef mln::value::hsv_f::v_type t_val; + typedef mln::image2d<t_hue> t_image2d_hue; + typedef mln::image2d<t_sat> t_image2d_sat; + typedef mln::image2d<t_val> t_image2d_val; + typedef mln::image2d<t_hsvf> t_image2d_hsvf; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<bool> t_mask; + typedef mln::fun::v2v::f_rgb_to_hsv_f_t t_rgb8_to_hsv; + typedef mln::accu::math::count<t_hsvf> t_count; + typedef mln::fun::v2v::component<t_hsvf,0> t_component_h; + typedef mln::fun::v2v::component<t_hsvf,1> t_component_s; + typedef mln::fun::v2v::component<t_hsvf,2> t_component_v; + typedef mln::fun::p2b::component_equals<t_image2d_hsvf,0> t_component_eq0; + typedef mln::fun::p2b::component_equals<t_image2d_hsvf,1> t_component_eq1; + typedef mln::fun::p2b::component_equals<t_image2d_hsvf,2> t_component_eq2; + typedef mln::fun::p2b::achromatic<t_rgb8> t_achromatic; + + t_image2d_rgb8 input_rgb8; + t_image2d_hsvf input_hsvf; + + t_mask achromatic; + t_mask low_saturation; + + t_image2d_float achromatism1; + t_image2d_int_u8 achromatism2; + t_image2d_float hue_concentration1; + t_image2d_int_u8 hue_concentration2; + + t_image2d_hue input_h; + t_image2d_hue input_h2; + t_image2d_sat input_s; + t_image2d_val input_v; + + t_image2d_int_u8 input_h8; + t_image2d_int_u8 input_s8; + t_image2d_int_u8 input_v8; + + t_histo1d histo_h; + t_histo1d histo_s; + t_histo1d histo_v; + + unsigned cnt1; + unsigned cnt2; + float percentage; + bool result; + + + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; +// mln::io::ppm::load(input_rgb8, ANNOTATING_1_BILL_IMG_PATH"/bill03.ppm"); + mln::io::ppm::load(input_rgb8, ICDAR_20P_PPM_IMG_PATH"/mp00082c_20p.ppm"); + + +// achromatism(input_rgb8,7,99.0); +// exit(-1); + + // REPERAGE DES PIXELS ACHROMATICS + std::cout << "Init achromatic mask ..." << std::endl; + initialize(achromatic, input_rgb8); + mln::data::fill(achromatic, false); + mln::data::fill((achromatic | t_achromatic(input_rgb8, 0.03)).rw(), true); + + mln::io::pbm::save(achromatic, "achromatic.pbm"); + + std::cout << "Achieve canal forking ..." << std::endl; + input_hsvf = mln::data::transform(input_rgb8, t_rgb8_to_hsv()); + input_h = mln::data::transform(input_hsvf, t_component_h()); + input_s = mln::data::transform(input_hsvf, t_component_s()); + input_v = mln::data::transform(input_hsvf, t_component_v()); + + // quid of achromatic pixels ??? + input_h8 = mln::data::stretch(t_int_u8(), input_h); + input_s8 = mln::data::stretch(t_int_u8(), input_s); + input_v8 = mln::data::stretch(t_int_u8(), input_v); + + // IDENTIFY LOW SATURATED PIXELS + std::cout << "Init low saturation mask ..." << std::endl; + initialize(low_saturation, input_s8); + mln::data::fill(low_saturation, false); + mln::data::fill((low_saturation|(mln::pw::value(input_s8) < + mln::pw::cst(100u))).rw(), true); + + mln::io::pbm::save(low_saturation, "low_saturation.pbm"); + + std::cout << "Compute histograms ..." << std::endl; + histo_h = mln::data::compute(mln::accu::meta::stat::histo1d(), + input_h8|(mln::pw::value(achromatic)==false)); + + histo_s = mln::data::compute(mln::accu::meta::stat::histo1d(), + input_s8|(mln::pw::value(achromatic)==false)); + + histo_v = mln::data::compute(mln::accu::meta::stat::histo1d(), + input_v8|(mln::pw::value(achromatic)==false)); + + + // Study the maps + hue_concentration1=mln::data::transform(input_h, + mln::fun::v2v::hue_concentration(histo_h)); + achromatism1=mln::data::transform(input_rgb8,mln::fun::v2v::achromatism()); + + hue_concentration2= mln::data::stretch(t_int_u8(), hue_concentration1); + achromatism2= mln::data::stretch(t_int_u8(), achromatism1); + + mln::io::pgm::save(achromatism2, "achromatism_map.pgm"); + mln::io::pgm::save(hue_concentration2, "hue_concentration_map.pgm"); + mln::io::pgm::save(input_s8, "saturation_map.pgm"); + +// cnt1 = mln::data::compute(t_count(), +// (input_hsvf|t_component_eq0(input_hsvf,-1)).rw()); + + + // (I) ACHROMATISM + std::cout << "Achromatism" << std::endl; + cnt1 = count_histo(histo_h); + cnt2 = mln::geom::nsites(input_h); + + percentage = (100.0 * (cnt2 - cnt1) / cnt2); + result = percentage > 99.0; + + std::cout << "Nbre pixels : " << cnt2 << std::endl; + std::cout << "Nbre pixels achromatiques : " << (cnt2-cnt1)<< std::endl; + std::cout << "Percentage : " << percentage << std::endl; + std::cout << "Image achromatique : " << result << std::endl; + std::cout << std::endl; + + // (II) LOW SATURATION + std::cout << "Saturation" << std::endl; + + cnt1 = count_histo(histo_s | mln::box1d(mln::point1d(0),mln::point1d(100))); + + cnt2= mln::geom::nsites(achromatic | (mln::pw::value(achromatic)==false)); + + percentage = (100.0 * cnt1 / cnt2); + result = percentage > 95.0; + + std::cout << "Nbre pixels : " << cnt2 << std::endl; + std::cout << "Nbre p faiblement saturés : " << cnt1 << std::endl; + std::cout << "Percentage : " << percentage << std::endl; + std::cout << "Image faiblement saturé : " << result << std::endl; + std::cout << std::endl; + + // (III) HIGH HUE CONCENTRATION + mln::debug::println(histo_h); + unsigned peak = peak_histo(histo_h); + + cnt1 = count_histo(histo_h | mln::box1d(mln::point1d(peak-20), + mln::point1d(peak+20))); + + cnt2= count_histo(histo_h); + + percentage = (100.0 * cnt1 / cnt2); + result = percentage > 95.0; + + std::cout << "Position du pic : " << peak << std::endl; + std::cout << "Nbre pixels : " << cnt2 << std::endl; + std::cout << "Nbre pixels proches pic : " << cnt1 << std::endl; + std::cout << "Percentage : " << percentage << std::endl; + std::cout << "Image fortement teintée : " << result << std::endl; + std::cout << std::endl; + + return 0; +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/lep/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/lep/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/lep/lep.cc b/scribo/sandbox/green/demo/annotating/lep/lep.cc new file mode 100644 index 0000000..981a369 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/lep/lep.cc @@ -0,0 +1,127 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet LEP descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works on grey level image. First, +/// Sobel is applied on the image. Then a specific window is build to +/// be convolved on the image. The aim of the convolution is to mark +/// the configuration of the neighbouring for this thresholded image. +/// Histogram of the configuration is then created and that's all. + +#include <iostream> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/w_window2d_int.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/linear/ch_convolve.hh> +#include <mln/linear/convolve.hh> +#include <mln/linear/sobel_2d.hh> + +#include <mln/make/w_window2d.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> + +#include <mln/core/var.hh> + +/// \brief Main entry. +/// +/// Load the pgm image. Threshold it. Identifie the pixel +/// configuration with specific convolution filter. Build 1d histogram. + +int main() +{ + using namespace mln; + + typedef mln::w_window2d_int t_win2d; + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::int_u<9> t_int_u9; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image2d<t_int_u9> t_image2d_int_u9; + typedef mln::image2d<int> t_image2d_int; + typedef mln::image2d<unsigned> t_image2d_unsigned; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<double> t_image2d_double; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + + t_image2d_int_u8 input_int_u8; + t_image2d_int input_int; + t_image2d_unsigned input_unsigned; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + t_image2d_float conf_float; + t_image2d_int_u9 conf_int_u9; + t_histo1d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,ICDAR_50P_PGM_IMG_PATH"/mp00082c_50p.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(input_int_u8); + + //mln::io::plot::save_image_sh(sobel, "sobel.sh"); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(mln::value::int_u8(), thresh_bool); + //mln::io::plot::save_image_sh(thresh, "thresh.sh"); + + int ws[] = { 1, 2, 4, + 8, 256, 16, + 32, 64, 128 }; + + t_win2d win2d = mln::make::w_window2d(ws); + conf_float = mln::linear::convolve(thresh_int_u8, win2d); + conf_int_u9 = mln::data::convert(t_int_u9(), conf_float); + histo = mln::data::compute(t_histo1d_fun(), conf_int_u9); + + mln::io::plot::save_image_sh(histo, "histo.sh"); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/nb_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/nb_color/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc b/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc new file mode 100644 index 0000000..acc64e9 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/nb_color/nb_color.cc @@ -0,0 +1,143 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Compute the number of colors in an image. +/// +/// Build histogram of colors and count the bins different from zero. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/math/sum.hh> +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo3d_rgb.hh> +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/variance.hh> + +#include <mln/algebra/vec.hh> + +#include <mln/arith/diff_abs.hh> + +#include <mln/core/macros.hh> +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/labeling/regional_maxima.hh> +#include <mln/labeling/mean_values.hh> +#include <mln/labeling/compute.hh> + +#include <mln/literal/colors.hh> + +#include <mln/morpho/opening/volume.hh> +#include <mln/morpho/elementary/dilation.hh> + +#include <mln/opt/at.hh> + +#include <mln/pw/cst.hh> + +#include <mln/util/array.hh> +#include <mln/util/timer.hh> + +#include <mln/value/label_8.hh> +#include <mln/value/rgb8.hh> +#include <mln/value/rgb.hh> +#include <mln/value/int_u.hh> + +/// \brief Count the colors. +/// +/// \param[in] image the name of the image to process. +/// +/// \return the number of colors. +/// +/// Count the color by building histogram. Quantification is done to +/// reduce the size of the histogram. +/// +/// \fixme: Strange compilation warning, I don't know how to solve it! + +// n < 8, n is the degree of quantification +template <unsigned n> +unsigned count_image_color(const std::string& image) +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::meta::math::count t_count_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + t_histo3d histo; + t_histo3d opened; + + mln::io::ppm::load(input_rgb8, image.c_str()); + +// unsigned nb_pixel = input_rgb8.ncols() * input_rgb8.nrows(); +// unsigned min_volume = (unsigned)(nb_pixel * 0.054); + unsigned nb_color = 0; + + input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + histo = mln::data::compute(t_histo3d_fun(), input_rgbn); + nb_color = mln::data::compute(t_count_fun(), + (histo | (mln::pw::value(histo) != 0)).rw()); + + return nb_color; +} + + +/// \brief Main entry. +/// +/// Print the number of colors. +int main() +{ + unsigned val=count_image_color<8>(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + + std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/project/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/project/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/project/project.cc b/scribo/sandbox/green/demo/annotating/project/project.cc new file mode 100644 index 0000000..4cab73c --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/project/project.cc @@ -0,0 +1,275 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet PROJECT descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works a grey level image. First, +/// we make subsampling of the image to the size 100x100. Then Sobel +/// is applied. After that, we divide the image in 2 sub-images with +/// horizontal or vertical splitting. Projection (summing along a +/// direction) is done in a way that preserves a vector of size +/// 100. Finally, it results 4 vector of size 100 which are concatened +/// to build the descriptor. + +#include <iostream> +#include <sstream> + +#include <mln/accu/image/init.hh> +#include <mln/accu/image/take.hh> +#include <mln/accu/image/to_result.hh> +#include <mln/accu/stat/mean.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> +#include <mln/core/image/dmorph/unproject_image.hh> + +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/projection.hh> + +#include <mln/img_path.hh> + +#include <mln/io/pgm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/pbm/save.hh> + +#include <mln/linear/sobel_2d.hh> + +#include <mln/opt/at.hh> + +/// \brief get the sub-image name. +/// +/// \param[in] base the common part of the name. +/// \param[in] i the column. +/// \param[in] j the line. +/// +/// \result the complete name of the sub-image. +/// +/// This function builds the complete name of a sub-image by making +/// explicit its position in the array. +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + + +/// \brief Project row data. +/// +/// \param[in] img the binary image converted to u_int8. +/// +/// \ return a vector. +/// +/// This routine "eats" one dimension of the image by making a +/// projection. The projection sums the pixel along a specific +/// direction. It results a vector. +mln::image1d<mln::value::int_u8> +project_row(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,0> t_projection; + + mln::image1d<t_sum> img_accu(img.ncols()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + + +/// \brief Project column data. +/// +/// \param[in] img the binary image converted to u_int8. +/// +/// \ return a vector. +/// +/// This routine "eats" one dimension of the image by making a +/// projection. The projection sums the pixel along a specific +/// direction. It results a vector. +mln::image1d<mln::value::int_u8> +project_col(const mln::image2d<mln::value::int_u8>& img) +{ + typedef mln::accu::math::sum<mln::value::int_u8,mln::value::int_u8> t_sum; + typedef mln::fun::v2v::projection<mln::point2d,1> t_projection; + + mln::image1d<t_sum> img_accu(img.nrows()); + + mln::accu::image::init(img_accu); + + mln::accu::image::take(unproject(img_accu, + img.domain(), + t_projection()).rw(), + img); + + return mln::accu::image::to_result(img_accu); +} + + +/// \brief The image processing routine. +/// +/// Describe the processing chain. First subsample the input image to +/// 100x100. This is done by splitting the image and then compute the +/// mean of the subimage. The result is a pixel of the subsample +/// image. Then apply Sobel and threshold. Then split in two +/// (vertically or horizontally), it results 4 images. Finally, make +/// the projection. +int main() +{ + typedef mln::value::int_u8 t_int_u8; + typedef mln::algebra::vec<3,float> t_vec3f; + typedef mln::algebra::vec<3,unsigned> t_vec3u; + typedef mln::image2d<float> t_image2d_float; + typedef mln::image2d<bool> t_image2d_bool; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::accu::meta::stat::mean t_mean_fun; + typedef mln::accu::math::sum<t_int_u8,t_int_u8> t_sum; + typedef mln::image1d<t_int_u8> t_image1d_int_u8; + + t_image2d_int_u8 input_int_u8; + t_image2d_float sobel; + t_image2d_bool thresh_bool; + t_image2d_int_u8 thresh_int_u8; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::pgm::load(input_int_u8,ICDAR_50P_PGM_IMG_PATH"/mp00082c_50p.pgm"); + + + // IMAGE SPLITTING PHASE + t_image2d_int_u8 subimg_int_u8(mln::box2d(mln::point2d(0,0), + mln::point2d(100,100))); + + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 100; + unsigned sz_col = (pmax.col() - pmin.col())/ 100; + + std::cout << domain << std::endl; + + // Subsampling in 100x100 + // FIXME Test that is it a subsampling, not a upsampling! + for (unsigned i = 0; i < 100; ++i) + for (unsigned j = 0; j < 100; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_int_u8 input_part_int_u8(dom); + + mln::data::paste(input_int_u8 | dom, input_part_int_u8); + //mln::io::pgm::save(input_part_int_u8, get_name("output",i,j)); + float mean = mln::data::compute(t_mean_fun(), input_part_int_u8); + t_int_u8 val = static_cast<t_int_u8>(mean); + mln::opt::at(subimg_int_u8, i,j) = val; + } + + mln::io::pgm::save(subimg_int_u8, "subimg.pgm"); + + sobel = mln::linear::sobel_2d_l1_norm(subimg_int_u8); + thresh_bool = mln::binarization::threshold(sobel, 100); + thresh_int_u8 = mln::data::convert(t_int_u8(), thresh_bool); + + mln::io::pbm::save(thresh_bool, "subimg.pbm"); + + // Define img_up, img_down, img_left, img_right + t_image2d_int_u8 img_up(mln::box2d(mln::point2d(0,0), + mln::point2d(49,99))); + + mln::data::paste(thresh_int_u8 | img_up.domain(), img_up); + + + t_image2d_int_u8 img_down(mln::box2d(mln::point2d(50,0), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_down.domain(), img_down); + + t_image2d_int_u8 img_left(mln::box2d(mln::point2d(0,0), + mln::point2d(99,49))); + + mln::data::paste(thresh_int_u8 | img_left.domain(), img_left); + + + t_image2d_int_u8 img_right(mln::box2d(mln::point2d(0,50), + mln::point2d(99,99))); + + mln::data::paste(thresh_int_u8 | img_right.domain(), img_right); + + + // Define project_up, project_down, project_left, project_right + t_image1d_int_u8 project_up = project_row(img_up); + t_image1d_int_u8 project_down = project_row(img_down); + t_image1d_int_u8 project_left = project_col(img_left); + t_image1d_int_u8 project_right = project_col(img_right); + + mln::io::pgm::save(img_up, "up.pgm"); + mln::io::pgm::save(img_down, "down.pgm"); + mln::io::pgm::save(img_left, "left.pgm"); + mln::io::pgm::save(img_right, "right.pgm"); + + // PRINTING PHASE + + std::cout << std::endl << "UP" << std::endl; + mln::debug::println(project_up); + + std::cout << std::endl << "DOWN" << std::endl; + mln::debug::println(project_down); + + std::cout << std::endl << "LEFT" << std::endl; + mln::debug::println(project_left); + + std::cout << std::endl << "RIGHT" << std::endl; + mln::debug::println(project_right); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/rgb_64/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/rgb_64/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc b/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc new file mode 100644 index 0000000..b49f979 --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/rgb_64/rgb_64.cc @@ -0,0 +1,80 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet RGB-64 descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works by making 2 bits +/// quantification on each channel. It result a processing in RGB-64 +/// space. Then we build the color histogram. + +#include <iostream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Main entry. +/// +/// Loading, Quantifiying in two bits each channel, then building +/// color histogram. +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/rgb_64_9/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/rgb_64_9/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc b/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc new file mode 100644 index 0000000..355cbfd --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/rgb_64_9/rgb_64_9.cc @@ -0,0 +1,132 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet RGB-64 descriptor [millet.phd.2008.pdf] +/// +/// This is an image descriptor. It works by making 2 bits +/// quantification on each channel. It result a processing in RGB-64 +/// space. Then we build the color histogram. + +#include <iostream> +#include <sstream> + +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/core/alias/box2d.hh> +#include <mln/core/alias/point2d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/routine/initialize.hh> + +#include <mln/data/compute.hh> +#include <mln/data/paste.hh> +#include <mln/data/transform.hh> + +#include <mln/debug/println.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +#include <mln/img_path.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/ppm/save.hh> + +#include <mln/value/rgb.hh> +#include <mln/value/rgb8.hh> + +/// \brief Get the sub image name. +/// +/// \return the name of the ith/jth sub image of the array. +const char *get_name(const char *base, const unsigned i, const unsigned j) +{ + std::ostringstream name; + + name << base; + name << i; + name << "_"; + name << j; + name << ".ppm"; + + return name.str().c_str(); +} + +/// \brief Same code as rgb-64 but divide the wall image in nine. +/// +/// Adding a splitting phase for image rgb-64 code reference. +int main() +{ + typedef mln::fun::v2v::rgb8_to_rgbn<2> t_rgb8_to_rgb2; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<2> t_rgb2; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgb2> t_image2d_rgb2; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgb2 input_rgb2; + t_histo3d histo; + + // IMAGE LOADING PHASE + std::cout << "Image loading phase ..." << std::endl; + mln::io::ppm::load(input_rgb8, ICDAR_50P_PPM_IMG_PATH"/mp00082c_50p.ppm"); + + input_rgb2 = mln::data::transform(input_rgb8, t_rgb8_to_rgb2()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_rgb2.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 3; + unsigned sz_col = (pmax.col() - pmin.col())/ 3; + + std::cout << domain << std::endl; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 3; ++i) + for (unsigned j = 0; j < 3; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_rgb2 input_1o9_rgb2(dom); + + mln::data::paste(input_rgb2 | dom, input_1o9_rgb2); + mln::io::ppm::save(input_1o9_rgb2, get_name("output",i,j)); + + histo = mln::data::compute(t_histo3d_fun(), input_1o9_rgb2); + + // PRINTING PHASE + mln::debug::println(histo); + } + +} diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/stddev_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/stddev_color/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc b/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc new file mode 100644 index 0000000..549834f --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/stddev_color/stddev_color.cc @@ -0,0 +1,191 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. + +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +unsigned stddev_color(const std::string& image) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + t_histo1d histo; + t_point1d max_site; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + mln::io::pgm::save(input_int_u8, "tmp.pgm"); + mln::io::plot::save_image_sh(histo, "histo.sh"); + mln::debug::println(histo); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter_(t_histo1d) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + // Compute the specific stddev + + float stddev_low = 0.0; + float stddev_up = 0.0; + float stddev = 0.0; + + if (250 > p_max) + for (short i = p_max+1; i < p_max+6; ++i) + stddev_up += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + if (5 < p_max) + for (short i = p_max-1; i > p_max-6; --i) + stddev_low += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up : + (stddev_low + stddev_up)/2; + + std::cout << "max_site : " << p_max << std::endl; + std::cout << "h(max_site) : " << v_max << std::endl; + std::cout << "stddev_up : " << stddev_up << std::endl; + std::cout << "stddev_low : " << stddev_low << std::endl; + std::cout << "stddev : " << stddev << std::endl; + + return 0; +} + + +/// \brief Main entry. +/// +/// Front end for image processing. +int main() +{ +// unsigned val = stdev_color(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + unsigned val = stddev_color(ANNOTATING_1_LOGO_IMG_PATH "/logo06.ppm"); + +// std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am b/scribo/sandbox/green/demo/annotating/stddev_color_16/Makefile.am similarity index 100% copy from scribo/sandbox/green/demo/clustering/kmean1d/Makefile.am copy to scribo/sandbox/green/demo/annotating/stddev_color_16/Makefile.am diff --git a/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc b/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc new file mode 100644 index 0000000..98d94fd --- /dev/null +++ b/scribo/sandbox/green/demo/annotating/stddev_color_16/stddev_color_16.cc @@ -0,0 +1,261 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. When photographies +/// has some large uniform border, the detection can fail. To improve +/// the method, Millet decide to subdivise the image in 16 sub images +/// on which it applies the test. +#include <iostream> +#include <sstream> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> +#include <mln/data/paste.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] input_int_u8 the 8 bits input image to analyze. +/// \param[in] name_histo the name of the output histogram. +/// \param[in] name_image the name of the ouput sub image. +/// +/// \return the deviation, but at this time nothing.. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +unsigned stddev_color(mln::image2d<mln::value::int_u8> input_int_u8, + const char *name_histo, + const char *name_image) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_histo1d histo; + + std::cout << "histo : " << name_histo << std::endl; + std::cout << "image : " << name_image << std::endl; + + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + mln::io::pgm::save(input_int_u8, name_image); + mln::io::plot::save_image_sh(histo, name_histo); + mln::debug::println(histo); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter_(t_histo1d) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + // Compute the specific stddev + + float stddev_low = 0.0; + float stddev_up = 0.0; + float stddev = 0.0; + + if (250 > p_max) + for (short i = p_max+1; i < p_max+6; ++i) + stddev_up += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + if (5 < p_max) + for (short i = p_max-1; i > p_max-6; --i) + stddev_low += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up : + (stddev_low + stddev_up)/2; + + std::cout << "max_site : " << p_max << std::endl; + std::cout << "h(max_site) : " << v_max << std::endl; + std::cout << "stddev_up : " << stddev_up << std::endl; + std::cout << "stddev_low : " << stddev_low << std::endl; + std::cout << "stddev : " << stddev << std::endl; + + return 0; +} + + +/// \brief Divide the image in 16 sub images. +/// +/// \param[in] image the input image. +/// +/// \result nothing. +/// +/// Divive the input image in 16 by uniform and geometrical +/// method. When a sub image is ready, call the stddev routine to show +/// stats on it. +unsigned stddev_color_16(const std::string& image) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 4; + unsigned sz_col = (pmax.col() - pmin.col())/ 4; + + std::cout << domain << std::endl; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 4; ++i) + for (unsigned j = 0; j < 4; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + std::cout << dom << std::endl; + + // Save it + t_image2d_int_u8 input_1o16_int_u8(dom); + std::ostringstream name_histo(""); + std::ostringstream name_image(""); + + name_histo << "histo" << i << "_" << j << ".sh"; + name_image << "image" << i << "_" << j << ".ppm"; + + mln::data::paste(input_int_u8 | dom, input_1o16_int_u8); + + stddev_color(input_1o16_int_u8, + name_histo.str().c_str(), + name_image.str().c_str()); + } + + return 0; +} + + +/// \brief Main entry. +/// +/// Just a front end for image processing routine. +int main() +{ +// unsigned val = stddev_color_16(ANNOTATING_1_PHOTO_IMG_PATH "/photo01.ppm"); + unsigned val = stddev_color_16(ANNOTATING_1_LOGO_IMG_PATH "/logo06.ppm"); + +// std::cout << "nb color : " << val << std::endl; + + return 0; +} + diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/achromastism/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/achromastism/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc b/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc new file mode 100644 index 0000000..3486b20 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/achromastism/achromastism.cc @@ -0,0 +1,179 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + + +/// \file +/// +/// \brief Implement the Millet achromatism operator [millet.phd.2008.pdf] +/// +/// An image is said to be achromatic when have the RGB channel which +/// are quite identicals. + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_achromatism_map.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +unsigned count_histo(const mln::Image<I>& img_) +{ + const I& img = exact(img_); + + mln_precondition(img.is_valid()); + + unsigned result = 0; + + mln_piter(I) p(img.domain()); + + for_all(p) + result += img(p); + + return result; +} + +/// \brief Decide if an image is achromatic or not [Millet]. +/// +/// \param[in] input the input image. +/// \param[in] output the histogram of the achromatic map. +/// \param[in] threshold the distance threshold used for equality. +/// +/// This is an improving of the Millet test. The original test +/// compares rgb values to each other and look at differences greater +/// than the threshold. If the number of pixel that pass the test are +/// greater than 99.0, then the image is declared achromatic. In fact, +/// there is few variations between the three channels, so we can say +/// that it is like a grey image. We can with no too distortions +/// replace the color image by the grey one. The improving is in first +/// creating a map of the difference. As we can't keep the free +/// differences between channels, we look at reducing the test and we +/// find an equivalent one based on the difference between minima +/// channel value and the maxima channel value. After the map is +/// ready, we make binarization with the threshold. Then we compute +/// the histogram 1d for every pixels of the map that are greater the +/// threshold. Then, we count pixels in the histogram and traduce the +/// count in percentage to compare to the second threshold. Details +/// are saved in files and printed in the screen. +float achromatism_test(const std::string input, + const std::string output, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_achromatism_map<8> t_rgb_to_achromatism_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_achromatism_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + cnt1 = count_histo(histo | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + cnt2 = mln::geom::nsites(input_rgb8); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); + + return prop; +} + + +/// \brief The main entry. +/// +/// Deal with boost library for walking over image database +/// directories. Call the image processing routine and compute some +/// stats on the result. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ICDAR_20P_PPM_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float prop = 0.0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + // concatenation de chaine + t_path directory(ANNOTATING_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + + prop = achromatism_test(dir_iter->path().string(), + output.string(), + 11); + + std::cout << output << " : " << prop << std::endl; + std::cerr << output << " : " << prop << std::endl; + } + } + } + + return 0; +} diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/scribo/sandbox/green/exp/annotating/achromastism/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/achromastism/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/achromastism/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/achromastism/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/bench/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/bench/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/bench/bench.cc b/scribo/sandbox/green/exp/annotating/bench/bench.cc new file mode 100644 index 0000000..102a896 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/bench/bench.cc @@ -0,0 +1,1450 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// This file is the final work to differentiate between the two image +/// databases AFP and ICDAR. Several tests are used to show +/// improvement of detection. The jonathan's tests are put together to +/// compare them to the others (in fact, to the low saturation one). + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/algebra/vec.hh> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.hh> +#include <mln/arith/times.hh> +#include <mln/arith/diff_abs.hh> +#include <mln/arith/div.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/alias/point1d.hh> +#include <mln/core/alias/box1d.hh> + +#include <mln/data/transform.hh> +#include <mln/data/compute.hh> +#include <mln/data/convert.hh> +#include <mln/data/stretch.hh> +#include <mln/data/fill.hh> + +#include <mln/fun/v2v/component.hh> +#include <mln/fun/v2v/rgb_to_hue_map.hh> +#include <mln/fun/v2v/rgb_to_saturation_map.hh> +#include <mln/fun/v2v/rgb_to_value_map.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/zero.hh> + +#include <mln/math/ceil.hh> +#include <mln/math/floor.hh> + +#include <mln/opt/at.hh> + +#include <mln/trait/value_.hh> + +#include <mln/value/rgb8.hh> + + +#include <mln/value/int_u8.hh> + +/// Histogram part. +/// +/// We regroup here the routines that work on histogram, count_histo, +/// sum_frequency_histo, peak_histo, count_null_frequency_histo, +/// cmp_equi_frequency_histo and others (variance +/// operators). count_histo and sum_frequency_histo count the overall +/// frequency in the domain. peak_histo find the peak of the +/// histogram. count_null_frequency_histo compute the number of non +/// grey levels. cmp_equi_frequency_histo compare a histogram with +/// the one with the same frequency everywhere. +/// \{ +template <typename I> +mln_value(I) count_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) result = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + result += histo(p); + + return result; +} + + +template <typename I> +mln_value(I) sum_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + sum += histo(p); + + return sum; +} + + +template <typename I> +mln_coord(mln_site_(I)) peak_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the peak of the histogram + mln_value(I) v_max = mln::opt::at(histo, mln::literal::zero); + mln_coord(mln_site_(I)) p_max = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + return p_max; +} + +template <typename I> +mln_value(I) count_null_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) count = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + if (0 == histo(p)) + count++; + + return count; +} + +template <typename I> +float cmp_equi_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum ++; + var += mln::math::sqr(histo(p) - (1/256.0)); + } + + var = var / sum; + + return var; +} + +template <typename I> +mln_value(I) cnt_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + } + + return sum; +} + +template <typename I> +mln_value(I) sum_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + sum += pos*histo(p); + } + + return sum; +} + +template <typename I> +mln_value(I) avg_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += histo(p); + sum += pos*histo(p); + } + + return (0 == cnt)? 0 : sum/cnt; +} + +template <typename I> +mln_value(I) var3_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + cnt += histo(p); + sum += (mln::math::sqr(p.ind()-mean)*histo(p)); + } + + return (0 == cnt)? 0 : sum/cnt; +} + +template <typename I> +mln_value(I) var2_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) sqr = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_value(I) dlt = mln::literal::zero; + mln_value(I) mxt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += (histo(p)); + sum += (histo(p)*pos); + mxt += (histo(p)*pos*mean); + sqr += (histo(p)*mln::math::sqr(pos)); + dlt += (histo(p)*mln::math::sqr(pos - mean)); + + std::cout << "p = " << pos << std::endl; + std::cout << "p² = " << mln::math::sqr(pos) << std::endl; + std::cout << "p*mean = " << (pos*mean) << std::endl; + std::cout << "p-mean = " << (pos-mean) << std::endl; + std::cout << "(p-mean)² = " << mln::math::sqr(pos-mean) << std::endl; + std::cout << "histo(p) = " << histo(p) << std::endl; + std::cout << "histo(p)*p = " << (pos*histo(p)) << std::endl; + std::cout << "histo(p)*p²= " << (mln::math::sqr(pos)*histo(p)) + << std::endl; + std::cout << "cnt = " << cnt << std::endl; + std::cout << "sum = " << sum << std::endl; + std::cout << "sqr = " << sqr << std::endl; + std::cout << "dlt = " << dlt << std::endl; + std::cout << "mxt = " << mxt << std::endl; + std::cout << std::endl; + } + + std::cout << "sqr/cnt = " << (sqr/cnt) << std::endl; + std::cout << "sum/cnt = " << (sum/cnt) << std::endl; + std::cout << "(sum/cnt)² = " << mln::math::sqr(sum/cnt) << std::endl; + std::cout << "dlt/cnt = " << dlt/cnt << std::endl; + std::cout << "mxt/cnt = " << mxt/cnt << std::endl; + std::cout << std::endl; + + std::cout << "sqr = " + << (sqr) << std::endl; + std::cout << "dlt = " + << (dlt) << std::endl; + std::cout << "cnt*avg² = " + << (mln::math::sqr(sum/cnt)*cnt) << std::endl; + std::cout << "2*mxt = " + << (2*mxt) << std::endl; + std::cout << "sqr - cnt*avg² = " + << (sqr/cnt - mln::math::sqr(sum/cnt)) << std::endl; + std::cout << "(sqr -2*mxt + cnt*avg²) = " + << ((sqr - 2*mxt + mln::math::sqr(sum/cnt))/cnt) << std::endl; + std::cout << std::endl; + return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt); +} + +template <typename I> +mln_value(I) var_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) sqr = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += (histo(p)); + sum += (histo(p)*pos); + sqr += (histo(p)*mln::math::sqr(pos)); + } + + return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt); +} + +template <typename I> +mln_value(I) sqr_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + sum += (mln::math::sqr(p.ind())*histo(p)); + + return sum; +} +/// \} + + +/// \brief Millet Hue detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold a way to adapt the test to the population. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test is used for discrimination between black and white pictures and +/// color ones. Some colored Black and white pictures have their energy near +/// the peak. +float hue1_descriptor(mln::image1d<unsigned> histo, const short threshold) +{ + float cnt1; + float cnt2; + float prop; + short peak; + mln::point1d inf; + mln::point1d sup; + + peak = peak_histo(histo); + inf = mln::point1d(mln::math::max(0, peak-threshold)); + sup = mln::point1d(mln::math::min(255, peak+threshold)); + cnt1 = count_histo(histo|mln::box1d(inf,sup)); + cnt2 = count_histo(histo); + prop = ((255.0 * cnt1) / cnt2); + + return prop; +} + + +/// \brief Saturation detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold a way to adapt the test to the population. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test is used for discrimination between black and white pictures and +/// color ones. Black and white pictures have their energy in the low saturation +/// band. +float sat1_descriptor(mln::image1d<unsigned> histo, const short threshold) +{ + float cnt1; + float cnt2; + float result; + + cnt1 = count_histo(histo | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + cnt2 = count_histo(histo); + result = ((255.0 * cnt1) / cnt2); + + return result; +} + + +/// \brief Counting grey levels descriptor. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the number of grey levels. +/// +/// This test aims at compute the number of grey levels. Photographies tends to +/// use all the levels or many of them. +float lvl0_descriptor(mln::image1d<unsigned> histo) +{ + float result; + + // FIXME 255 + result = 255-count_null_frequency_histo(histo); + + return result; +} + + +/// \brief Density hue detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +float hue0_descriptor(mln::image1d<unsigned> histo) +{ + mln::image1d<float> histo_float; + float sum; + float result; + + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + result = cmp_equi_frequency_histo(histo_float); + + return result*255; +} + + +/// \brief Density saturation detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +float sat0_descriptor(mln::image1d<unsigned> histo) +{ + mln::image1d<float> histo_float; + float sum; + float result; + + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + result = cmp_equi_frequency_histo(histo_float); + + return result*255; +} + + +/// \brief Density value detector. +/// +/// \param[in] histo the histogram image on which applying the detector. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test works on density histogram and compare to the +/// equi-frequency histogram. If the normalized histogram show a peak, +/// it will differ from the reference density. +float val0_descriptor(mln::image1d<unsigned> histo) +{ + mln::image1d<float> histo_float; + float sum; + float result; + + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + result = cmp_equi_frequency_histo(histo_float); + + return result*255; +} + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +template <typename I> +float stddev3(const mln::Image<I>& histo_, unsigned peak) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Compute stddev + + float stddev = 0.0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + stddev += r((short)peak, mln::opt::at(histo,peak), p.ind(), histo(p)); + } + + return stddev; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// \param[in] peak the peak of the histogram. +/// \param[in] limit the threshold to compute the contribution. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +template <typename I> +float stddev2(const mln::Image<I>& histo_, unsigned peak, unsigned limit) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + float stddev_low = 0.0; + float stddev_up = 0.0; + float ret = 0.0; + + // A transformer avec des iterators + + if (250 > peak) + stddev_up = stddev3(histo |mln::box1d(mln::point1d(peak+1), + mln::point1d(peak+limit)), peak); + + if (5 < peak) + stddev_low = stddev3(histo |mln::box1d(mln::point1d(peak-limit), + mln::point1d(peak-1)), peak); + + ret = (250 < peak)? stddev_low : (5 > peak)? stddev_up : + (stddev_low + stddev_up)/2; + + return ret; +} + + +/// \brief Millet value descriptor (cf. Millet.phd.2008) +/// +/// \param[in] histo the histogram image on which applying the detector. +/// \param[in] threshold the limit where to compute the contribution. +/// +/// \return the proportion of population that verify the hue detector. +/// +/// This test aims at compute some deviation on the peak of the histogram of +/// the image. Large deviations mean lots of graduation in colors (such as +/// photos) and small ones mean something like cartoon. +float val1_descriptor(mln::image1d<unsigned> histo, const short threshold) +{ + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_map; + + float result; + short peak; + + peak = peak_histo(histo); + result = stddev2(histo, peak, threshold); + + return result; +} + + +/// \brief Error detector (jonathan idea). +/// +/// \param[in] r_img_map the original red channel. +/// \param[in] g_img_map the original green channel. +/// \param[in] b_img_map the original blue channel. +/// \param[in] r_rdc_map the reduced (in color) red channel. +/// \param[in] g_rdc_map the reduced (in color) green channel. +/// \param[in] b_rdc_map the reduced (in color) blue channel. +/// +/// \return the PNSNR (cf compression p278 Handbook Color). +/// +/// \fixme the actual PNSNR is unbound, we need to fix its max to +/// 255. Two images which are the same produces an infinite PNSNR. +float err_descriptor(mln::image2d<mln::value::int_u8> r_img_map, + mln::image2d<mln::value::int_u8> g_img_map, + mln::image2d<mln::value::int_u8> b_img_map, + mln::image2d<mln::value::int_u8> r_rdc_map, + mln::image2d<mln::value::int_u8> g_rdc_map, + mln::image2d<mln::value::int_u8> b_rdc_map) + + +{ + typedef mln::accu::meta::stat::mean t_mean; + typedef mln::image2d<mln::value::int_u8> t_map; + typedef mln_trait_op_minus_(t_map,t_map) t_minus; + typedef mln_trait_op_times_(t_minus,t_minus) t_times; + + + t_minus minus_red; + t_minus minus_green; + t_minus minus_blue; + + t_times times_red; + t_times times_green; + t_times times_blue; + + float error_red; + float error_green; + float error_blue; + + float error; + + minus_red = (r_img_map - r_rdc_map); + times_red = minus_red * minus_red; + + minus_green = (g_img_map - g_rdc_map); + times_green = minus_green * minus_green; + + minus_blue = (b_img_map - b_rdc_map); + times_blue = minus_blue * minus_blue; + + error_red = mln::data::compute(t_mean(), times_red); + error_green = mln::data::compute(t_mean(), times_green); + error_blue = mln::data::compute(t_mean(), times_blue); + + error = (error_red + error_green + error_blue)/3.0; + error = mln::math::sqrt(error); + error = 20 * log(255/error); + +// FIXME: +// SAME IMAGE PRODUCE PNSNR GOING THROW THE INFINITY. +// DIFFERENT IMAGE PRODUCE PNSNR GOING NEAR ZERO. +// WE SHOULD KEEP THE PNSNR BELOW 255 FOR COMPARAISON. + + return error; +} + +/// Discriminant operators. +/// +/// We have some operators that compute the threshold which separate +/// two sub-populations in a histogram. +/// /{ + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*sqrt(v1)+m2*sqrt(v2))/(sqrt(v1)+sqrt(v2)). +float threshold_histo(float avg1, float var1, float avg2, float var2) +{ + float sigma1 = mln::math::sqrt(var1); + float sigma2 = mln::math::sqrt(var2); + float threshold = (avg1*sigma1+avg2*sigma2)/(sigma1+sigma2); + + return threshold; +} + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*var1+m2*var2)/(sqrt(v1)+sqrt(v2)). + +float threshold3_histo(float avg1, float var1, float avg2, float var2) +{ + float threshold = (avg1*var1+avg2*var2)/(var1+var2); + + return threshold; +} + + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Gaussian discriminant analysis in 1d is done. Compute the +/// discrimanation and solve the parabolic equation. +float threshold2_histo(float avg1, float var1, float avg2, float var2) +{ + float a = var2 - var1; + float b = -2 * (avg1 * var2 - avg2 * var1); + float c = var2 * mln::math::sqr(avg1) - var1 * mln::math::sqr(avg2); + float d = mln::math::sqr(b) - 4 * a * c; + + if (d < 0) + std::cout << "delta negatif" << std::endl; + + float x1 = (-b+mln::math::sqrt(d))/(2*a); + float x2 = (-b-mln::math::sqrt(d))/(2*a); + + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "c = " << c << std::endl; + std::cout << "d = " << d << std::endl; + std::cout << "x1 = " << x1 << std::endl; + std::cout << "x2 = " << x2 << std::endl; + + return x2; +} + +/// \brief Minimisation of the error. +/// +/// \param[in] histo_grp1 the histogram of the first population. +/// \param[in] histo_grp2 the histogram of the second population. +/// +/// \return the threshold. +/// +/// Computes the error and find the minimum error threshold. It's just +/// a counting of element in four categories (0 - but detects 1, 1 - +/// but detects 1, 1 but detects 0, 0 but detects 0). The error is the +/// sum of missclassifications for the classes 0 and 1. As Otsu done, +/// we can iterate on the threshold and compute the error associated +/// to it. The bes threshold is the one that minimize the error. +short min_error(const mln::image1d<float> histo_grp1, + const mln::image1d<float> histo_grp2, + float *_c00, float *_c10, float *_c01, float *_c11) +{ + float c00[256]; + float c10[256]; + float c01[256]; + float c11[256]; + float err[256]; + + for (short p = 0; p < 256; p++) + { + c00[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(0), + mln::point1d(p))); + + c10[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(p+1), + mln::point1d(255))); + + c01[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(0), + mln::point1d(p))); + + c11[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(p+1), + mln::point1d(255))); + } + + short threshold = 0; + float error = c01[0] + c01[0] + c00[0] + c11[0]; + + for(short p = 0; p < 256; p++) + { + err[p] = c10[p] + c01[p]; + +// std::cout << " p = " << p +// << ";c00 = " << c00[p] +// << ";c10 = " << c10[p] +// << ";c01 = " << c01[p] +// << ";c11 = " << c11[p] +// << std::endl; +// std::cout << "err[" << p << "] = " << err[p] << std::endl; + + if (error > err[p]) + { + error = err[p]; + threshold = p; + } + } + + *_c00 = c00[threshold]; + *_c10 = c10[threshold]; + *_c01 = c01[threshold]; + *_c11 = c11[threshold]; + + return threshold; +} + + +/// \brief Fisher analysis. +/// +/// \param[in] histo the histogram of the mixed population. +/// +/// \return the threshold. +/// +/// This routine is less performant than the others because it starts +/// with a mixed population, but we know exactly the two separated +/// population. It was just a test. Don't take it in production +/// environement, it doesn't help you. +short fisher_analysis(const mln::image1d<float> histo) +{ + typedef mln::value::int_u8 t_int_u8; + + // FIXE ME SIZE const short a = mln_min(t_int_u8); + // float cnt1[a]; + + float cnt1[256]; + float sum1[256]; + float sqr1[256]; + float avg1[256]; + float var1[256]; + + float cnt2[256]; + float sum2[256]; + float sqr2[256]; + float avg2[256]; + float var2[256]; + + float cnt0[256]; // count of points + float sum0[256]; // sum of population + float sqr0[256]; // sqr of population + float avg0[256]; // average of the population + float v_in[256]; // variance with-in class + float v_bw[256]; // variance between class + float var0[256]; // variance of the population + short threshold; + float pos; + float min_var; + + // Assign the first elements + cnt1[0] = 0; + sum1[0] = 0; + sqr1[0] = 0; + avg1[0] = 0; + var1[0] = 0; + + // Compute the stats of the wall histogram + cnt2[0] = 0; + sum2[0] = 0; + sqr2[0] = 0; + + for(short p = 0; p < 256; p++) + { + pos = p; + cnt2[0] += mln::opt::at(histo,p); + sum2[0] += (pos * mln::opt::at(histo,p)); + sqr2[0] += (mln::math::sqr(pos) * mln::opt::at(histo,p)); + } + + avg2[0] = (0 == cnt2[0])? 0 : sum2[0] / cnt2[0]; + var2[0] = (0 == cnt2[0])? 0 : sqr2[0] / cnt2[0] - mln::math::sqr(avg2[0]); + + // watch the array limits + for (short p = 1; p < 256; p++) + { + pos = p; + + // Assign the statistics to the primary class + cnt1[p] = cnt1[p-1] + mln::opt::at(histo, p); + sum1[p] = sum1[p-1] + pos * mln::opt::at(histo, p); + sqr1[p] = sqr1[p-1] + mln::math::sqr(pos) * mln::opt::at(histo, p); + avg1[p] = (0 == cnt1[p])? 0 : (sum1[p] / cnt1[p]); + var1[p] = (0 == cnt1[p])? 0 : ((sqr1[p] / cnt1[p])-mln::math::sqr(avg1[p])); + + // Assign the statistics to the second class + cnt2[p] = cnt2[p-1] - mln::opt::at(histo, p);; + sum2[p] = sum2[p-1] - p * mln::opt::at(histo, p);; + sqr2[p] = sqr2[p-1] - mln::math::sqr(p) * mln::opt::at(histo, p);; + avg2[p] = (0 == cnt2[p])? 0 : (sum2[p] / cnt2[p]); + var2[p] = (0 == cnt2[p])? 0 : ((sqr2[p] / cnt2[p])-mln::math::sqr(avg2[p])); + + // Lets compute the invariants + cnt0[p] = cnt1[p] + cnt2[p]; + sum0[p] = sum1[p] + sum2[p]; + sqr0[p] = sqr1[p] + sqr2[p]; + avg0[p] = (cnt1[p] * avg1[p] + cnt2[p] * avg2[p])/cnt0[p]; + v_in[p] = (cnt1[p] * var1[p] + cnt2[p] * var2[p])/cnt0[p]; + v_bw[p] = (cnt1[p] * mln::math::sqr(avg1[p]-avg0[p]) + + cnt2[p] * mln::math::sqr(avg2[p]-avg0[p]))/cnt0[p]; + var0[p] = v_in[p] + v_bw[p]; + } + + // Find the threshold that minimizes the intra-class variance + min_var = cnt2[0]*var2[0]; + threshold = 0; + + for(short p = 0; p < 256; p++) + { + // Compute the intra-class variance + v_in[p] = cnt1[p]*var1[p] + cnt2[p]*var2[p]; +// std::cout << "var intra[" << p << "]= " << v_in[p] << std::endl; + + if (min_var > v_in[p]) + { + min_var = v_in[p]; + threshold = p; + } + } + + return threshold; +} +/// /} + + +/// Launching part. +/// +/// In this part, we have the front end that walk over the image +/// databases and start to compute information that we pass to +/// previous routines. +/// \{ + +#define LVL0_DESCR 0 // Number of grey available +#define HUE0_DESCR 1 // Density histogram hue analysis +#define HUE1_DESCR 2 // Millet Hue analysis +#define SAT0_DESCR 3 // Density histogram saturation analysis +#define SAT1_DESCR 4 // Millet saturation analysis +#define VAL0_DESCR 5 // Density histogram grey level analysis +#define VAL1_DESCR 6 // Millet grey level analysis +#define GMP0_DESCR 7 // PNSNR with GIMP compression +#define GMP1_DESCR 8 // PNSNR with GIMP compression +#define GMP2_DESCR 9 // PNSNR with GIMP compression +#define MGK0_DESCR 9 // PNSNR with ImageMagick compression +#define MGK1_DESCR 10 // PNSNR with ImageMagick compression +#define MGK2_DESCR 11 // PNSNR with ImageMagick compression + +#define MGK_DESCR(version) (MGK0_DESCR + version) +#define GMP_DESCR(version) (GMP0_DESCR + version) + +#define NB_DESCR 12 // Number of descriptors +#define NB_DATABASE 2 // Number of image databases : AFP,ICDAR +#define NB_IMAGE 110 // Number of images +#define NB_VERSION 3 // Number of compression by GIMP or ImageMagick + +/// \brief Make sure that the result is between 0 and 255. +/// +/// \param[in] file_name the name of the analysis files. +/// \param[in] result the values for each image and each descriptors. +/// \param[in] size the number of image by each database. +/// +/// Correct the descriptors if there is overflow values. Just used in +/// debug mode. +void init_descriptors(std::string file_name[], + float result[][NB_DESCR], + int size[]) +{ + for (int i = 0; i < NB_IMAGE; i++) + { + file_name[i] = std::string("PGM"); + + for (int d = 0; d < NB_DESCR; d++) + result[i][d] = (i*d) % 256; + } + + size[0] = 62; + size[1] = 48; +} + + +/// \brief Dump gnuplot file for vizualisation of results. +/// +/// \param[in] file_name the name of the analysis files. +/// \param[in] result the values for each image and each descriptors. +/// \param[in] size the number of image by each database. +/// +/// This routine aimed at plotting the result with one graph. Each +/// database has got its color. We can see the values used by each +/// descriptors along the image of each database. +void dump_descriptors(const std::string file_name[], + const float result[][NB_DESCR], + const int size[]) +{ + std::cout << "#!/usr/bin/gnuplot" << std::endl; + std::cout << "set terminal x11 persist 1" << std::endl; + std::cout << "plot '-' using 2 with point title 'ICDAR',\\" << std::endl; + std::cout << " '-' using 2 with point title 'AFP'" << std::endl; + + int num = 0; + + for (int db = 0; db < NB_DATABASE; db++) + { + for (int i = 0; i < size[db]; i++) + { + std::cout << result[num][LVL0_DESCR] << " "; + std::cout << result[num][HUE0_DESCR] << " "; + std::cout << result[num][HUE1_DESCR] << " "; + std::cout << result[num][SAT0_DESCR] << " "; + std::cout << result[num][SAT1_DESCR] << " "; + std::cout << result[num][VAL0_DESCR] << " "; + std::cout << result[num][VAL1_DESCR] << " "; + std::cout << result[num][GMP0_DESCR] << " "; + std::cout << result[num][GMP1_DESCR] << " "; + std::cout << result[num][GMP2_DESCR] << " "; + std::cout << result[num][MGK0_DESCR] << " "; + std::cout << result[num][MGK1_DESCR] << " "; + std::cout << result[num][MGK2_DESCR] << " "; + std::cout << " # " << file_name[num] << std::endl; + num++; + } + + std::cout << "e" << std::endl; + } +} + + +/// \brief Compute the descriptor histograms for each database. +/// +/// \param[in] result the result values of each descriptors. +/// \param[in] size the number of images by database. +/// \param[out] histo the computed histograms. +/// +/// This routine compute a histograms for each database and each descriptors. +/// So we can see the distribution of descriptor values over each database. +/// Result values are transform from float to int for histograms. +void compute_histo(const float result[][NB_DESCR], + const int size[], + mln::image1d<float> histo[][NB_DATABASE]) +{ + for (int i = 0; i < NB_DESCR; i++) + for (int db = 0; db < NB_DATABASE; db++) + { + histo[i][db].init_(mln::box1d(mln::point1d(0),mln::point1d(255))); + + mln::data::fill(histo[i][db], mln::literal::zero); + } + + short v; + int num = 0; + + for (int db = 0; db < NB_DATABASE; db++) + { + for (int i = 0; i < size[db]; i++) + { + v = (short)mln::math::floor(result[num][VAL0_DESCR]+0.4999); + mln::opt::at(histo[VAL0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][LVL0_DESCR]+0.4999); + mln::opt::at(histo[LVL0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][HUE0_DESCR]+0.4999); + mln::opt::at(histo[HUE0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][HUE1_DESCR]+0.4999); + mln::opt::at(histo[HUE1_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][SAT0_DESCR]+0.4999); + mln::opt::at(histo[SAT0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][SAT1_DESCR]+0.4999); + mln::opt::at(histo[SAT1_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][VAL1_DESCR]+0.4999); + mln::opt::at(histo[VAL1_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][GMP0_DESCR]+0.4999); + mln::opt::at(histo[GMP0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][GMP1_DESCR]+0.4999); + mln::opt::at(histo[GMP1_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][GMP2_DESCR]+0.4999); + mln::opt::at(histo[GMP2_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][MGK0_DESCR]+0.4999); + mln::opt::at(histo[MGK0_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][MGK1_DESCR]+0.4999); + mln::opt::at(histo[MGK1_DESCR][db],v)++; + + v = (short)mln::math::floor(result[num][MGK2_DESCR]+0.4999); + mln::opt::at(histo[MGK2_DESCR][db],v)++; + + num++; + } + } +} + + +/// Compute the threshold for discrimination between ICDAR and AFP. +/// +/// \param[in] histo the histogram for each descriptor and for each database. +/// \param[out] threshold computed values to discriminate between ICDAR/AFP. +/// \param[out] c00 say CLASS 1 but CLASS 1. +/// \param[out] c10 say CLASS 2 but CLASS 1. +/// \param[out] c01 say CLASS 1 but CLASS 2. +/// \param[out] c11 say CLASS 2 but CLASS 2. +/// +/// Compute the threshold by the minimum of error of +/// classification. Population are inverted in function of average, +/// technical solution to prevent min_error crashed. The bad thing is +/// now, the class 0 can be either ICDAR of AFP, depends on descriptors. +void compute_thresholds(const mln::image1d<float> histo[][NB_DATABASE], + short threshold[], + float c00[], + float c10[], + float c01[], + float c11[]) +{ + for (int i = 0; i < NB_DESCR; i++) + { + float avg0 = avg_histo(histo[i][0]); + float avg1 = avg_histo(histo[i][1]); + + if (avg0 < avg1) + { + threshold[i] = min_error(histo[i][0], histo[i][1], + &c00[i], &c10[i], &c01[i], &c11[i]); + } + else + { + threshold[i] = min_error(histo[i][1], histo[i][0], + &c00[i], &c10[i], &c01[i], &c11[i]); + } + + std::cerr << " i = " << i + << "; c00 = " << c00[i] + << "; c10 = " << c10[i] + << "; c01 = " << c01[i] + << "; c11 = " << c11[i] + << "; threshold " << threshold[i] + << std::endl; + + } +} + + +/// \brief Walk over the database directories. +/// +/// \param[out] file_name the array of the image name. +/// \param[out] result the descriptors values for each image. +/// \param[out] size the number of image for each database. +/// +/// This is the front end part to access to the database directories. We use +/// boost library to do it. First we explore ICDAR database en then the AFP one. +/// For each valid image, we compute all the descriptors. Histograms are +/// computed at this level because we can do that thing one time for every +/// density descriptors. Every parameters are updated to retrurn the values. +void compute_descriptors(std::string file_name[], + float result[][NB_DESCR], + int size[]) +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + typedef mln::image1d<unsigned> t_histo; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_int_u8> t_map; + typedef mln::image2d<t_rgb8> t_input; + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_2_hue; + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_2_sat; + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_2_val; + typedef mln::fun::v2v::component<t_rgb8,0> t_rgb_2_red; + typedef mln::fun::v2v::component<t_rgb8,1> t_rgb_2_green; + typedef mln::fun::v2v::component<t_rgb8,2> t_rgb_2_blue; + typedef mln::accu::meta::stat::histo1d t_accu_histo; + + + t_path img_path[2] = { ICDAR_20P_INPUT_IMG_PATH, AFP_PPM_IMG_PATH}; + t_path mgk_path[3][2] = {{ICDAR_20P_MGK30_IMG_PATH, AFP_MGK30_IMG_PATH}, + {ICDAR_20P_MGK20_IMG_PATH, AFP_MGK20_IMG_PATH}, + {ICDAR_20P_MGK10_IMG_PATH, AFP_MGK10_IMG_PATH}}; + t_path gmp_path[3][2] = {{ICDAR_20P_GMP30_IMG_PATH, AFP_GMP30_IMG_PATH}, + {ICDAR_20P_GMP20_IMG_PATH, AFP_GMP20_IMG_PATH}, + {ICDAR_20P_GMP10_IMG_PATH, AFP_GMP10_IMG_PATH}}; + + int num = 0; + int cnt = 0; + + for (int db = 0; db < NB_DATABASE; db++) + { + if (boost::filesystem::exists(img_path[db]) && + boost::filesystem::is_directory(img_path[db])) + { + boost::filesystem::system_complete(img_path[db]); + + const t_iter_path end_iter; + + cnt = 0; + + for (t_iter_path dir_iter(img_path[db]); end_iter != dir_iter; ++dir_iter) + { + t_path img_file = dir_iter->path().leaf(); + t_path dir_file = dir_iter->path(); + t_input img_input; + + mln::io::ppm::load(img_input, dir_file.string().c_str()); + + t_map h_img_map = mln::data::transform(img_input, t_rgb_2_hue()); + t_map s_img_map = mln::data::transform(img_input, t_rgb_2_sat()); + t_map v_img_map = mln::data::transform(img_input, t_rgb_2_val()); + t_map r_img_map = mln::data::transform(img_input, t_rgb_2_red()); + t_map g_img_map = mln::data::transform(img_input, t_rgb_2_green()); + t_map b_img_map = mln::data::transform(img_input, t_rgb_2_blue()); + t_histo h_img_hst = mln::data::compute(t_accu_histo(), h_img_map); + t_histo s_img_hst = mln::data::compute(t_accu_histo(), s_img_map); + t_histo v_img_hst = mln::data::compute(t_accu_histo(), v_img_map); + t_histo r_img_hst = mln::data::compute(t_accu_histo(), r_img_map); + t_histo g_img_hst = mln::data::compute(t_accu_histo(), g_img_map); + t_histo b_img_hst = mln::data::compute(t_accu_histo(), b_img_map); + + std::cerr << dir_iter->path() << std::endl; + + file_name[num] = img_file.string(); + + // descriptors + result[num][LVL0_DESCR] = lvl0_descriptor(v_img_hst); + result[num][HUE0_DESCR] = hue0_descriptor(h_img_hst); + result[num][HUE1_DESCR] = hue1_descriptor(h_img_hst,20); + result[num][SAT0_DESCR] = sat0_descriptor(s_img_hst); + result[num][SAT1_DESCR] = sat1_descriptor(s_img_hst,50); + result[num][VAL0_DESCR] = val0_descriptor(v_img_hst); + //result[num][VAL1_DESCR] = val1_descriptor(v_img_hst, 15); + result[num][VAL1_DESCR] = 0; + + // for gimp and magick + for (int v = 0; v < NB_VERSION; v++) + { + if (boost::filesystem::exists(mgk_path[v][db]) && + boost::filesystem::exists(gmp_path[v][db]) && + boost::filesystem::is_directory(mgk_path[v][db]) && + boost::filesystem::is_directory(gmp_path[v][db])) + { + t_path mgk_file = mgk_path[v][db] / img_file; + t_path gmp_file = gmp_path[v][db] / img_file; + t_input gmp_input; + + mln::io::ppm::load(gmp_input, gmp_file.string().c_str()); + + t_map r_gmp_map = mln::data::transform(gmp_input,t_rgb_2_red()); + t_map g_gmp_map = mln::data::transform(gmp_input,t_rgb_2_green()); + t_map b_gmp_map = mln::data::transform(gmp_input,t_rgb_2_blue()); + + result[num][GMP_DESCR(v)]= err_descriptor(r_img_map, + g_img_map, + b_img_map, + r_gmp_map, + g_gmp_map, + b_gmp_map); + + t_input mgk_input; + + mln::io::ppm::load(mgk_input, mgk_file.string().c_str()); + + t_map r_mgk_map = mln::data::transform(mgk_input,t_rgb_2_red()); + t_map g_mgk_map = mln::data::transform(mgk_input,t_rgb_2_green()); + t_map b_mgk_map = mln::data::transform(mgk_input,t_rgb_2_blue()); + + result[num][MGK_DESCR(v)]= err_descriptor(r_img_map, + g_img_map, + b_img_map, + r_mgk_map, + g_mgk_map, + b_mgk_map); + } + } + + num++; + cnt++; + } + } + + size[db] = cnt; + } +} + +/// \brief Just for debugging purpose and tests. +int main2() +{ + typedef mln::image1d<unsigned> t_histo; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_int_u8> t_map; + typedef mln::image2d<t_rgb8> t_input; + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_2_hue; + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_2_sat; + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_2_val; + typedef mln::fun::v2v::component<t_rgb8,0> t_rgb_2_red; + typedef mln::fun::v2v::component<t_rgb8,1> t_rgb_2_green; + typedef mln::fun::v2v::component<t_rgb8,2> t_rgb_2_blue; + typedef mln::accu::meta::stat::histo1d t_accu_histo; + + t_input img_input; + + mln::io::ppm::load(img_input, ICDAR_20P_INPUT_IMG_PATH"/mp00032c_20p.ppm"); + //mln::io::ppm::load(img_input, AFP_PPM_IMG_PATH"/000_Del218430.ppm"); + + + + t_map h_img_map = mln::data::transform(img_input, t_rgb_2_hue()); + t_map s_img_map = mln::data::transform(img_input, t_rgb_2_sat()); + t_map v_img_map = mln::data::transform(img_input, t_rgb_2_val()); + t_map r_img_map = mln::data::transform(img_input, t_rgb_2_red()); + t_map g_img_map = mln::data::transform(img_input, t_rgb_2_green()); + t_map b_img_map = mln::data::transform(img_input, t_rgb_2_blue()); + t_histo h_img_hst = mln::data::compute(t_accu_histo(), h_img_map); + t_histo s_img_hst = mln::data::compute(t_accu_histo(), s_img_map); + t_histo v_img_hst = mln::data::compute(t_accu_histo(), v_img_map); + t_histo r_img_hst = mln::data::compute(t_accu_histo(), r_img_map); + t_histo g_img_hst = mln::data::compute(t_accu_histo(), g_img_map); + t_histo b_img_hst = mln::data::compute(t_accu_histo(), b_img_map); + + + std::cout << "sat2 : " << sat0_descriptor(s_img_hst) << std::endl; + + return 0; +} + + +/// \brief Main entry. +/// +/// This is the front end for every routines. +/// - compute descriptors. +/// - dump descriptors. +/// - compute histograms. +/// - compute thresholds. +/// - save results. +/// +/// Global data used: +/// - file_name : all the initial images name, mixed every image database. +/// - result : all the result values for every descriptors and for every images. +/// - histo : the histograms ny database and by descriptors. +/// - size : the number of image by database. +/// - threshold : the computed histogram separation on each descriptors. +/// - cxx : the count of well/bad classified images function of threshold info. +int main() +{ + std::string file_name[NB_IMAGE]; + float result[NB_IMAGE][NB_DESCR]; + int size[NB_DATABASE]; + mln::image1d<float> histo[NB_DESCR][NB_DATABASE]; + short threshold[NB_DESCR]; + float c00[NB_DESCR]; + float c10[NB_DESCR]; + float c01[NB_DESCR]; + float c11[NB_DESCR]; + + std::cerr << "DESCRIPTORS" << std::endl; + compute_descriptors(file_name,result,size); +// std::cout << "DUMPING" << std::endl; +// init_descriptors(file_name,result,size); + dump_descriptors(file_name,result,size); + std::cerr << "HISTO" << std::endl; + compute_histo(result,size,histo); + std::cerr << "THRESHOLD" << std::endl; + compute_thresholds(histo,threshold,c00,c10,c01,c11); + + mln::io::plot::save_image_sh(histo[LVL0_DESCR][0], "lvl0_histo1.sh"); + mln::io::plot::save_image_sh(histo[HUE0_DESCR][0], "hue0_histo1.sh"); + mln::io::plot::save_image_sh(histo[HUE1_DESCR][0], "hue1_histo1.sh"); + mln::io::plot::save_image_sh(histo[SAT0_DESCR][0], "sat0_histo1.sh"); + mln::io::plot::save_image_sh(histo[SAT1_DESCR][0], "sat1_histo1.sh"); + mln::io::plot::save_image_sh(histo[VAL0_DESCR][0], "val0_histo1.sh"); + mln::io::plot::save_image_sh(histo[VAL1_DESCR][0], "val1_histo1.sh"); + mln::io::plot::save_image_sh(histo[GMP0_DESCR][0], "gmp0_histo1.sh"); + mln::io::plot::save_image_sh(histo[GMP1_DESCR][0], "gmp1_histo1.sh"); + mln::io::plot::save_image_sh(histo[GMP2_DESCR][0], "gmp2_histo1.sh"); + mln::io::plot::save_image_sh(histo[MGK0_DESCR][0], "mgk0_histo1.sh"); + mln::io::plot::save_image_sh(histo[MGK1_DESCR][0], "mgk1_histo1.sh"); + mln::io::plot::save_image_sh(histo[MGK2_DESCR][0], "mgk2_histo1.sh"); + + mln::io::plot::save_image_sh(histo[LVL0_DESCR][1], "lvl0_histo2.sh"); + mln::io::plot::save_image_sh(histo[HUE0_DESCR][1], "hue0_histo2.sh"); + mln::io::plot::save_image_sh(histo[HUE1_DESCR][1], "hue1_histo2.sh"); + mln::io::plot::save_image_sh(histo[SAT0_DESCR][1], "sat0_histo2.sh"); + mln::io::plot::save_image_sh(histo[SAT1_DESCR][1], "sat1_histo2.sh"); + mln::io::plot::save_image_sh(histo[VAL0_DESCR][1], "val0_histo2.sh"); + mln::io::plot::save_image_sh(histo[VAL1_DESCR][1], "val1_histo2.sh"); + mln::io::plot::save_image_sh(histo[GMP0_DESCR][1], "gmp0_histo2.sh"); + mln::io::plot::save_image_sh(histo[GMP1_DESCR][1], "gmp1_histo2.sh"); + mln::io::plot::save_image_sh(histo[GMP2_DESCR][1], "gmp2_histo2.sh"); + mln::io::plot::save_image_sh(histo[MGK0_DESCR][1], "mgk0_histo2.sh"); + mln::io::plot::save_image_sh(histo[MGK1_DESCR][1], "mgk1_histo2.sh"); + mln::io::plot::save_image_sh(histo[MGK2_DESCR][1], "mgk2_histo2.sh"); + + return 0; +} +/// \} + diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/error/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/error/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/error/error.cc b/scribo/sandbox/green/exp/annotating/error/error.cc new file mode 100644 index 0000000..949b51b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/error/error.cc @@ -0,0 +1,833 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// This file is a prelude just before using the bench source. It puts +/// the light on the Jonathan's idea which wants to compare ICDAR and +/// AFP image database with difference between original image and +/// color reducted one. The key point is to say that when a color +/// image is degraded, the visual quality fall quickly in function of +/// the number of colors left. A contrario, when an image is black and +/// white, this kind of degradation doesn't change the original visual +/// quality. The PNSNR detector is build and learning threshold for +/// automatic classification is done (four algorithms are tested). + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/algebra/vec.hh> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/mean.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.hh> +#include <mln/arith/times.hh> +#include <mln/arith/diff_abs.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> +#include <mln/core/alias/point1d.hh> +#include <mln/core/alias/box1d.hh> + +#include <mln/data/transform.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/fill.hh> + +#include <mln/fun/v2v/component.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/literal/zero.hh> + +#include <mln/math/ceil.hh> +#include <mln/math/floor.hh> + +#include <mln/opt/at.hh> + +#include <mln/trait/value_.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u8.hh> + +/// Work on histogram. +/// \brief Different routines are presented for computing mean, variance ... +/// \{ + +/// \brief Counting the number of pixels in the histogram. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the number of pixels in the histogram. +/// +/// Such routine is a generic and is generally applied on a portion of +/// the real domain. +template <typename I> +mln_value(I) cnt_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + } + + return sum; +} + +/// \brief Summing the values ponderated by their occurences. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the sum ponderated of values by their occurences. +/// +/// This method is not a production routine, it's just a test to debug +/// histogram problem. When we compute something over the histogram, +/// we need to take care of the intermediate type that contains the +/// temporary result. If not the case, the type used is the one of the +/// value of the histogram and we can go to some overflow +/// problems. The trick is to store the value returned by the iterator +/// in some temporary computing type (look at pos in the code). +template <typename I> +mln_value(I) sum_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + sum += pos*histo(p); + } + + return sum; +} + +/// \brief Summing the values ponderated by their occurences. +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the sum ponderated of values by their occurences. +template <typename I> +mln_value(I) avg_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += histo(p); + sum += pos*histo(p); + } + + return (0 == cnt)? 0 : sum/cnt; +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// This computing is safe because the quantity of information stored +/// in sum is low. This formulae doesn't show the overflow problem. +template <typename I> +mln_value(I) var3_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + cnt += histo(p); + sum += (mln::math::sqr(p.ind()-mean)*histo(p)); + } + + return (0 == cnt)? 0 : sum/cnt; +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// This is the debugging code to observe overflow problem. +template <typename I> +mln_value(I) var2_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) sqr = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_value(I) dlt = mln::literal::zero; + mln_value(I) mxt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += (histo(p)); + sum += (histo(p)*pos); + mxt += (histo(p)*pos*mean); + sqr += (histo(p)*mln::math::sqr(pos)); + dlt += (histo(p)*mln::math::sqr(pos - mean)); + + std::cout << "p = " << pos << std::endl; + std::cout << "p² = " << mln::math::sqr(pos) << std::endl; + std::cout << "p*mean = " << (pos*mean) << std::endl; + std::cout << "p-mean = " << (pos-mean) << std::endl; + std::cout << "(p-mean)² = " << mln::math::sqr(pos-mean) << std::endl; + std::cout << "histo(p) = " << histo(p) << std::endl; + std::cout << "histo(p)*p = " << (pos*histo(p)) << std::endl; + std::cout << "histo(p)*p²= " << (mln::math::sqr(pos)*histo(p)) + << std::endl; + std::cout << "cnt = " << cnt << std::endl; + std::cout << "sum = " << sum << std::endl; + std::cout << "sqr = " << sqr << std::endl; + std::cout << "dlt = " << dlt << std::endl; + std::cout << "mxt = " << mxt << std::endl; + std::cout << std::endl; + } + + std::cout << "sqr/cnt = " << (sqr/cnt) << std::endl; + std::cout << "sum/cnt = " << (sum/cnt) << std::endl; + std::cout << "(sum/cnt)² = " << mln::math::sqr(sum/cnt) << std::endl; + std::cout << "dlt/cnt = " << dlt/cnt << std::endl; + std::cout << "mxt/cnt = " << mxt/cnt << std::endl; + std::cout << std::endl; + + std::cout << "sqr = " + << (sqr) << std::endl; + std::cout << "dlt = " + << (dlt) << std::endl; + std::cout << "cnt*avg² = " + << (mln::math::sqr(sum/cnt)*cnt) << std::endl; + std::cout << "2*mxt = " + << (2*mxt) << std::endl; + std::cout << "sqr - cnt*avg² = " + << (sqr/cnt - mln::math::sqr(sum/cnt)) << std::endl; + std::cout << "(sqr -2*mxt + cnt*avg²) = " + << ((sqr - 2*mxt + mln::math::sqr(sum/cnt))/cnt) << std::endl; + std::cout << std::endl; + return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt); +} + +/// \brief Computing the variance +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the variance of the histogram. +/// +/// My standard code for the variance. +template <typename I> +mln_value(I) var_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) pos = mln::literal::zero; + mln_value(I) sum = mln::literal::zero; + mln_value(I) sqr = mln::literal::zero; + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + pos = p.ind(); + cnt += (histo(p)); + sum += (histo(p)*pos); + sqr += (histo(p)*mln::math::sqr(pos)); + } + + return (0 == cnt)? 0 : sqr/cnt - mln::math::sqr(sum/cnt); +} +/// \} + + +/// Classify. +/// \brief Classification routines that compute threshold. +/// \{ + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*sqrt(v1)+m2*sqrt(v2))/(sqrt(v1)+sqrt(v2)). +float threshold_histo(float avg1, float var1, float avg2, float var2) +{ + float sigma1 = mln::math::sqrt(var1); + float sigma2 = mln::math::sqrt(var2); + float threshold = (avg1*sigma1+avg2*sigma2)/(sigma1+sigma2); + + return threshold; +} + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Linear discriminant analysis in 1d is done. When two population +/// have the same variance, the threshold is at (m1+m2)/2. When threre +/// are different variances, we propose the threshold at the position +/// (m1*var1+m2*var2)/(sqrt(v1)+sqrt(v2)). +float threshold3_histo(float avg1, float var1, float avg2, float var2) +{ + float threshold = (avg1*var1+avg2*var2)/(var1+var2); + + return threshold; +} + + + +/// \brief Compute discrimination threshold. +/// \param[in] avg1 the mean of the population 1. +/// \param[in] var1 the variance of the population 1. +/// \param[in] avg2 the mean of the population 2. +/// \param[in] var2 the variance of the population 2. +/// +/// \return the threshold to discriminate the two populations. +/// +/// Gaussian discriminant analysis in 1d is done. Compute the +/// discrimanation and solve the parabolic equation. +float threshold2_histo(float avg1, float var1, float avg2, float var2) +{ + float a = var2 - var1; + float b = -2 * (avg1 * var2 - avg2 * var1); + float c = var2 * mln::math::sqr(avg1) - var1 * mln::math::sqr(avg2); + float d = mln::math::sqr(b) - 4 * a * c; + + if (d < 0) + std::cout << "delta negatif" << std::endl; + + float x1 = (-b+mln::math::sqrt(d))/(2*a); + float x2 = (-b-mln::math::sqrt(d))/(2*a); + + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "c = " << c << std::endl; + std::cout << "d = " << d << std::endl; + std::cout << "x1 = " << x1 << std::endl; + std::cout << "x2 = " << x2 << std::endl; + + return x2; +} + +/// \brief Compute the other proportion of histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the ponderated sum of the sqrt values. +/// +/// This is a test. Don't take this routine in production. +template <typename I> +mln_value(I) sqr_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + sum += (mln::math::sqr(p.ind())*histo(p)); + + return sum; +} + +/// \brief Minimisation of the error. +/// +/// \param[in] histo_grp1 the histogram of the first population. +/// \param[in] histo_grp2 the histogram of the second population. +/// +/// \return the threshold. +/// +/// Computes the error and find the minimum error threshold. It's just +/// a counting of element in four categories (0 - but detects 1, 1 - +/// but detects 1, 1 but detects 0, 0 but detects 0). The error is the +/// sum of missclassifications for the classes 0 and 1. As Otsu done, +/// we can iterate on the threshold and compute the error associated +/// to it. The bes threshold is the one that minimize the error. +short min_error(const mln::image1d<float> histo_grp1, + const mln::image1d<float> histo_grp2) +{ + float c00[256]; + float c10[256]; + float c01[256]; + float c11[256]; + float err[256]; + + for (short p = 0; p < 256; p++) + { + c00[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(0), + mln::point1d(p))); + + c10[p] = cnt_histo(histo_grp1|mln::box1d(mln::point1d(p+1), + mln::point1d(255))); + + c01[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(0), + mln::point1d(p))); + + c11[p] = cnt_histo(histo_grp2|mln::box1d(mln::point1d(p+1), + mln::point1d(255))); + } + + short threshold = 0; + float error = c01[0] + c01[0] + c00[0] + c11[0]; + + for(short p = 0; p < 256; p++) + { + err[p] = c10[p] + c01[p]; + + std::cout << " p = " << p + << ";c00 = " << c00[p] + << ";c10 = " << c10[p] + << ";c01 = " << c01[p] + << ";c11 = " << c11[p] + << std::endl; +// std::cout << "err[" << p << "] = " << err[p] << std::endl; + + if (error > err[p]) + { + error = err[p]; + threshold = p; + } + } + + return threshold; +} + +/// \brief Fisher analysis. +/// +/// \param[in] histo the histogram of the mixed population. +/// +/// \return the threshold. +/// +/// This routine is less performant than the others because it starts +/// with a mixed population, but we know exactly the two separated +/// population. It was just a test. Don't take it in production +/// environement, it doesn't help you. +short fisher_analysis(const mln::image1d<float> histo) +{ + typedef mln::value::int_u8 t_int_u8; + + // FIXE ME SIZE const short a = mln_min(t_int_u8); + // float cnt1[a]; + + float cnt1[256]; + float sum1[256]; + float sqr1[256]; + float avg1[256]; + float var1[256]; + + float cnt2[256]; + float sum2[256]; + float sqr2[256]; + float avg2[256]; + float var2[256]; + + float cnt0[256]; // count of points + float sum0[256]; // sum of population + float sqr0[256]; // sqr of population + float avg0[256]; // average of the population + float v_in[256]; // variance with-in class + float v_bw[256]; // variance between class + float var0[256]; // variance of the population + short threshold; + float pos; + float min_var; + + // Assign the first elements + cnt1[0] = 0; + sum1[0] = 0; + sqr1[0] = 0; + avg1[0] = 0; + var1[0] = 0; + + // Compute the stats of the wall histogram + cnt2[0] = 0; + sum2[0] = 0; + sqr2[0] = 0; + + for(short p = 0; p < 256; p++) + { + pos = p; + cnt2[0] += mln::opt::at(histo,p); + sum2[0] += (pos * mln::opt::at(histo,p)); + sqr2[0] += (mln::math::sqr(pos) * mln::opt::at(histo,p)); + } + + avg2[0] = (0 == cnt2[0])? 0 : sum2[0] / cnt2[0]; + var2[0] = (0 == cnt2[0])? 0 : sqr2[0] / cnt2[0] - mln::math::sqr(avg2[0]); + + // watch the array limits + for (short p = 1; p < 256; p++) + { + pos = p; + + // Assign the statistics to the primary class + cnt1[p] = cnt1[p-1] + mln::opt::at(histo, p); + sum1[p] = sum1[p-1] + pos * mln::opt::at(histo, p); + sqr1[p] = sqr1[p-1] + mln::math::sqr(pos) * mln::opt::at(histo, p); + avg1[p] = (0 == cnt1[p])? 0 : (sum1[p] / cnt1[p]); + var1[p] = (0 == cnt1[p])? 0 : ((sqr1[p] / cnt1[p])-mln::math::sqr(avg1[p])); + + // Assign the statistics to the second class + cnt2[p] = cnt2[p-1] - mln::opt::at(histo, p);; + sum2[p] = sum2[p-1] - p * mln::opt::at(histo, p);; + sqr2[p] = sqr2[p-1] - mln::math::sqr(p) * mln::opt::at(histo, p);; + avg2[p] = (0 == cnt2[p])? 0 : (sum2[p] / cnt2[p]); + var2[p] = (0 == cnt2[p])? 0 : ((sqr2[p] / cnt2[p])-mln::math::sqr(avg2[p])); + + // Lets compute the invariants + cnt0[p] = cnt1[p] + cnt2[p]; + sum0[p] = sum1[p] + sum2[p]; + sqr0[p] = sqr1[p] + sqr2[p]; + avg0[p] = (cnt1[p] * avg1[p] + cnt2[p] * avg2[p])/cnt0[p]; + v_in[p] = (cnt1[p] * var1[p] + cnt2[p] * var2[p])/cnt0[p]; + v_bw[p] = (cnt1[p] * mln::math::sqr(avg1[p]-avg0[p]) + + cnt2[p] * mln::math::sqr(avg2[p]-avg0[p]))/cnt0[p]; + var0[p] = v_in[p] + v_bw[p]; + } + + // Find the threshold that minimizes the intra-class variance + min_var = cnt2[0]*var2[0]; + threshold = 0; + + for(short p = 0; p < 256; p++) + { + // Compute the intra-class variance + v_in[p] = cnt1[p]*var1[p] + cnt2[p]*var2[p]; +// std::cout << "var intra[" << p << "]= " << v_in[p] << std::endl; + + if (min_var > v_in[p]) + { + min_var = v_in[p]; + threshold = p; + } + } + + return threshold; +} +/// \} + +/// \brief ERROR (MSE, PNSNR) compression p278 Handbook Color. +/// \param[in] original the original image. +/// \param[in] reduced the image with a reduced number of colors. +/// +/// \return the PNSNR. +/// +/// Compute the PNSNR. First compute the square difference in each +/// channel. Then compute the mean of those square differences and get +/// the mean over the three channels. Then, get the sqrt of that to +/// have a distance. The PNSNR is a logarithmic quantity of that distance. +float error_test(const std::string original, + const std::string reduced) + +{ + + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::fun::v2v::component<t_rgb8,0> t_component_red; + typedef mln::fun::v2v::component<t_rgb8,1> t_component_green; + typedef mln::fun::v2v::component<t_rgb8,2> t_component_blue; + typedef mln::accu::meta::stat::mean t_mean; + typedef mln::accu::meta::stat::histo1d t_histo; + typedef mln::image2d<t_int_u8> t_img; + typedef mln_trait_op_minus_(t_img,t_img) t_minus; + typedef mln_trait_op_times_(t_minus,t_minus) t_times; + + mln::image2d<mln::value::rgb8> original_rgb8; + mln::image2d<mln::value::rgb8> reduced_rgb8; + + mln::image2d<mln::value::int_u8> original_red; + mln::image2d<mln::value::int_u8> original_green; + mln::image2d<mln::value::int_u8> original_blue; + + mln::image2d<mln::value::int_u8> reduced_red; + mln::image2d<mln::value::int_u8> reduced_green; + mln::image2d<mln::value::int_u8> reduced_blue; + +// mln::image2d<mln::value::int_u8> map_red; +// mln::image2d<mln::value::int_u8> map_green; +// mln::image2d<mln::value::int_u8> map_blue; + +// mln::image1d<unsigned> histo_red; +// mln::image1d<unsigned> histo_green; +// mln::image1d<unsigned> histo_blue; + + t_minus minus_red; + t_minus minus_green; + t_minus minus_blue; + + t_times times_red; + t_times times_green; + t_times times_blue; + + float error_red; + float error_green; + float error_blue; + + float error; + + + mln::io::ppm::load(original_rgb8, original.c_str()); + mln::io::ppm::load(reduced_rgb8, reduced.c_str()); + + original_red = mln::data::transform(original_rgb8, t_component_red()); + original_green = mln::data::transform(original_rgb8, t_component_green()); + original_blue = mln::data::transform(original_rgb8, t_component_blue()); + + reduced_red = mln::data::transform(reduced_rgb8, t_component_red()); + reduced_green = mln::data::transform(reduced_rgb8, t_component_green()); + reduced_blue = mln::data::transform(reduced_rgb8, t_component_blue()); + + minus_red = (original_red - reduced_red); + times_red = minus_red * minus_red; + + minus_green = (original_green - reduced_green); + times_green = minus_green * minus_green; + + minus_blue = (original_blue - reduced_blue); + times_blue = minus_blue * minus_blue; + + error_red = mln::data::compute(t_mean(), times_red); + error_green = mln::data::compute(t_mean(), times_green); + error_blue = mln::data::compute(t_mean(), times_blue); + +// map_red = mln::data::stretch(t_int_u8(), times_red); +// map_green = mln::data::stretch(t_int_u8(), times_blue); +// map_blue = mln::data::stretch(t_int_u8(), times_green); + +// histo_red = mln::data::compute(t_histo(), map_red); +// histo_green = mln::data::compute(t_histo(), map_green); +// histo_blue = mln::data::compute(t_histo(), map_blue); + +// mln::io::plot::save_image_sh(histo_red, "histo_red.sh"); +// mln::io::plot::save_image_sh(histo_green,"histo_green.sh"); +// mln::io::plot::save_image_sh(histo_blue, "histo_blue.sh"); + +// mln::io::pgm::save(map_red, "red.pgm"); +// mln::io::pgm::save(map_green,"green.pgm"); +// mln::io::pgm::save(map_blue, "blue.pgm"); + + error = (error_red + error_green + error_blue)/3.0; + error = mln::math::sqrt(error); + error = 20 * log(255/error); + +// Le PNSNR semble offrir plus d'espace pour la discrimination +// Si les images sont identiques ==> PNSNR = +inf +// Si les images sont très différentes ==> PNSNR = 0 + + return error; +} + + +/// \brief The main entry. +/// +/// This is the front end for error classification. This is the big +/// loop where we walk other image data bases. Computation of +/// automatic thresholds are called from here. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + mln::image1d<float> histo(256); + mln::image1d<float> histo_grp[2]; // histo by group + + histo_grp[0].init_(mln::box1d(mln::point1d(0),mln::point1d(255))); + histo_grp[1].init_(mln::box1d(mln::point1d(0),mln::point1d(255))); + + mln::data::fill(histo, mln::literal::zero); + mln::data::fill(histo_grp[0], mln::literal::zero); + mln::data::fill(histo_grp[1], mln::literal::zero); + + t_path original_path[] = {ICDAR_20P_INPUT_IMG_PATH, + AFP_PPM_IMG_PATH}; + +// t_path reduced1_path[] = {ICDAR_20P_MGK30_IMG_PATH, +// AFP_MGK30_IMG_PATH}; + +// t_path reduced1_path[] = {ICDAR_20P_MGK20_IMG_PATH, +// AFP_MGK20_IMG_PATH}; + + t_path reduced1_path[] = {ICDAR_20P_MGK10_IMG_PATH, + AFP_MGK10_IMG_PATH}; + +// t_path reduced2_path[] = {ICDAR_20P_GMP30_IMG_PATH, +// AFP_GMP30_IMG_PATH}; + +// t_path reduced2_path[] = {ICDAR_20P_GMP20_IMG_PATH, +// AFP_GMP20_IMG_PATH}; + + t_path reduced2_path[] = {ICDAR_20P_GMP10_IMG_PATH, + AFP_GMP10_IMG_PATH}; + + + std::cout << "#!/usr/bin/gnuplot" << std::endl; + std::cout << "set terminal x11 persist 1" << std::endl; + std::cout << "ERROR" << std::endl; + std::cout << "plot '-' using 1 with point notitle,\\" << std::endl; + std::cout << " '-' using 1 with point notitle" << std::endl; + + for (int i = 0; i < 2; i++) + { + if (boost::filesystem::exists(original_path[i]) && + boost::filesystem::exists(reduced1_path[i]) && + boost::filesystem::exists(reduced2_path[i]) && + boost::filesystem::is_directory(original_path[i]) && + boost::filesystem::is_directory(reduced1_path[i]) && + boost::filesystem::is_directory(reduced2_path[i])) + { + boost::filesystem::system_complete(original_path[i]); + const t_iter_path end_iter; + float error1 = 0.0; + float error2 = 0.0; + t_path leaf; + t_path reduced1_file; + t_path reduced2_file; + + for (t_iter_path dir_iter(original_path[i]);end_iter!=dir_iter;++dir_iter) + { + leaf = dir_iter->path().leaf(); + reduced1_file = reduced1_path[i] / leaf; + reduced2_file = reduced2_path[i] / leaf; + + error1 = error_test(dir_iter->path().string(), reduced1_file.string()); + error2 = error_test(dir_iter->path().string(), reduced2_file.string()); + + float a1 = 1; + short v1 = (short)mln::math::floor(error2+0.4999); + mln::opt::at(histo,v1) += a1; + mln::opt::at(histo_grp[i],v1) += a1; + +// float a1 = error2 - mln::math::floor(error2); +// float a2 = mln::math::ceil(error2) - error2; +// short v1 = (short)mln::math::floor(error2); +// short v2 = (short)mln::math::ceil(error2); +// mln::opt::at(histo,v1) += a1; +// mln::opt::at(histo,v2) += a2; +// mln::opt::at(histo_grp[i],v1) += a1; +// mln::opt::at(histo_grp[i],v2) += a2; + + std::cout << error1 << " "; + std::cout << error2 << " "; + std::cout << "# " << dir_iter->path().leaf() << std::endl; + } + std::cout << "e" << std::endl; + } + } + + mln::io::plot::save_image_sh(histo, "histo.sh"); + mln::io::plot::save_image_sh(histo_grp[1], "histo_grp1.sh"); + mln::io::plot::save_image_sh(histo_grp[0], "histo_grp2.sh"); + + float threshold = fisher_analysis(histo); + float threshold2 = threshold_histo(avg_histo(histo_grp[1]), + var_histo(histo_grp[1]), + avg_histo(histo_grp[0]), + var_histo(histo_grp[0])); + float threshold3 = threshold2_histo(avg_histo(histo_grp[1]), + var_histo(histo_grp[1]), + avg_histo(histo_grp[0]), + var_histo(histo_grp[0])); + float threshold4 = min_error(histo_grp[1],histo_grp[0]); + + std::cout << "threshold = " << threshold << std::endl; + std::cout << "threshold2 = " << threshold2 << std::endl; + std::cout << "threshold3 = " << threshold3 << std::endl; + std::cout << "threshold4 = " << threshold4 << std::endl; + std::cout << "avg_grp1 = " << avg_histo(histo_grp[1]) << std::endl; + std::cout << "avg_grp2 = " << avg_histo(histo_grp[0]) << std::endl; + + // compute the classification matrix + // for each sub population + + float c00 = cnt_histo(histo_grp[1] | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + + float c10 = cnt_histo(histo_grp[1] | mln::box1d(mln::point1d(threshold+1), + mln::point1d(255))); + + float c01 = cnt_histo(histo_grp[0] | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + + float c11 = cnt_histo(histo_grp[0] | mln::box1d(mln::point1d(threshold+1), + mln::point1d(255))); + + + std::cout << "pop0 = " << cnt_histo(histo_grp[1]) << std::endl; + std::cout << "pop1 = " << cnt_histo(histo_grp[0]) << std::endl; + std::cout << std::endl; + + std::cout << "c00 = " << c00 << std::endl; + std::cout << "c10 = " << c10 << std::endl; + std::cout << "c01 = " << c01 << std::endl; + std::cout << "c11 = " << c11 << std::endl; + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/histo/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/histo/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/histo/histo.cc b/scribo/sandbox/green/exp/annotating/histo/histo.cc new file mode 100644 index 0000000..90b18e4 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/histo/histo.cc @@ -0,0 +1,366 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Build normalized histograms for visualization only. +/// +/// The goal of this code is to build every normalized histograms of +/// an image database for visual inspection. It enables the +/// comparaison of the densities of each image database and the +/// understanding of the differences in this space. + + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.hh> +#include <mln/arith/div.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/convert.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/zero.hh> +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> +#include <mln/math/sqr.hh> +#include <mln/math/sqrt.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_hue_map.hh> +#include <mln/fun/v2v/rgb_to_saturation_map.hh> +#include <mln/fun/v2v/rgb_to_value_map.hh> +#include <mln/fun/v2v/component.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +//============================================================================// +// HISTOGRAM +//============================================================================// + +/// \brief Count the number of pixel in the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the number of pixels in the domain. +/// +/// This is simple counting routine that compute the number of pixels +/// in the domain of the histogram. For each site, it sums the +/// frequencies associated to its site. +template <typename I> +mln_value(I) cnt_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) cnt = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + cnt += histo(p); + } + + return cnt; +} + +/// \brief The computing of the normalized histogram. +/// +/// \param[in] input the name of the input image. +/// \param[in] output_map the name of the specific map (hue,sat,val,R,G,B). +/// \param[in] output_histo the name of the output normalized histogram. +/// \param[in] space the name of the studied channel (R,G,B,H,S,V). +/// +/// Compute the normalized histogram in the given channel. First get +/// the channel from the RGB space or the HSV space and then compute +/// its normalized histogram. Normalization is done by dividing each +/// frequency by the sum of frequencies. It's a way to make the +/// histogram independant from the dimension of the input image and +/// then to compare different image databases together. +void histo(const std::string input, + const std::string output_map, + const std::string output_histo, + const char space) + +{ + typedef mln::value::rgb8 t_rgb8; + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_map; + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_sat_map; + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_val_map; + typedef mln::fun::v2v::component<t_rgb8,0> t_component_r; + typedef mln::fun::v2v::component<t_rgb8,1> t_component_g; + typedef mln::fun::v2v::component<t_rgb8,2> t_component_b; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float sum; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + switch(space) + { + case 'h': map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); break; + case 's': map = mln::data::transform(input_rgb8, t_rgb_to_sat_map()); break; + case 'v': map = mln::data::transform(input_rgb8, t_rgb_to_val_map()); break; + case 'r': map = mln::data::transform(input_rgb8, t_component_r()); break; + case 'g': map = mln::data::transform(input_rgb8, t_component_g()); break; + case 'b': map = mln::data::transform(input_rgb8, t_component_b()); break; + default: break;// crash + } + + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = cnt_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + + mln::io::pgm::save(map, output_map.c_str()); + mln::io::plot::save_image_sh(histo_float, output_histo.c_str()); +} + +//============================================================================// +// MAIN +//============================================================================// + + +/// \brief The main entry. +/// +/// This a front end to compute histogram with boost plumberies. There +/// is 2 uses cases, the computing over the icdar database and over +/// the afp database. We force the computing over the six channel +/// (H,S,V,R,G,B) in order to select the channel the most specific for +/// a database. It seems that saturation channel and value channel are +/// the most specific to describe each database. Take care to +/// coherence of the output directories for each source of images. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + +// t_path full_path[] = {t_path(ICDAR_20P_TEXT_ONLY_IMG_PATH), +// t_path(ICDAR_20P_TEXT_COLOR_IMG_PATH), +// t_path(ICDAR_20P_TEXT_PHOTO_IMG_PATH)}; + +// t_path full_path[] = {t_path(AFP_INPUT_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP30_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP20_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_GMP10_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK30_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK20_IMG_PATH)}; +// t_path full_path[] = {t_path(AFP_MGK10_IMG_PATH)}; + +// t_path full_path[] = {t_path(ICDAR_20P_INPUT_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_GMP30_IMG_PATH)}; + t_path full_path[] = {t_path(ICDAR_20P_GMP20_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_GMP10_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK30_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK20_IMG_PATH)}; +// t_path full_path[] = {t_path(ICDAR_20P_MGK10_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + t_path directory; + t_path leaf; + t_path output_map; + t_path output_histo; + + std::cerr << "entering " << full_path[i] << std::endl; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + std::cerr << dir_iter->path() << std::endl; + + leaf = dir_iter->path().leaf(); + +// directory = ANNOTATING_AFP_R_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_R_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_R_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_R_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_R_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_R_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_R_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_R_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'r'); + +// directory = ANNOTATING_AFP_G_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_G_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_G_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_G_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_G_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_G_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_G_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_G_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'g'); + +// directory = ANNOTATING_AFP_B_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_B_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_B_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_B_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_B_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_B_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_B_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_B_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'b'); + +// directory = ANNOTATING_AFP_H_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_H_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_H_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_H_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_H_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_H_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_H_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_H_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'h'); + +// directory = ANNOTATING_AFP_S_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_S_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_S_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_S_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_S_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_S_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_S_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_S_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 's'); + +// directory = ANNOTATING_AFP_V_INPUT_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP30_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP20_RET_PATH; +// directory = ANNOTATING_AFP_V_GMP10_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK30_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK20_RET_PATH; +// directory = ANNOTATING_AFP_V_MGK10_RET_PATH; + +// directory = ANNOTATING_ICDAR_V_INPUT_RET_PATH; +// directory = ANNOTATING_ICDAR_V_GMP30_RET_PATH; + directory = ANNOTATING_ICDAR_V_GMP20_RET_PATH; +// directory = ANNOTATING_ICDAR_V_GMP10_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK30_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK20_RET_PATH; +// directory = ANNOTATING_ICDAR_V_MGK10_RET_PATH; + output_histo = change_extension(directory / leaf, ".sh"); + output_map = change_extension(directory / leaf, ".pgm"); + + histo(dir_iter->path().string(), + output_map.string(), + output_histo.string(), + 'v'); + } + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/hsv/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/hsv/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/hsv/hsv.cc b/scribo/sandbox/green/exp/annotating/hsv/hsv.cc new file mode 100644 index 0000000..507d07b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/hsv/hsv.cc @@ -0,0 +1,912 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet HSV operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Hue +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +/// +/// This operator try to integrate many processing in one shot, after +/// HSV transformation. + + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/arith/minus.hh> +#include <mln/arith/div.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/convert.hh> +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/zero.hh> +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> +#include <mln/math/sqr.hh> +#include <mln/math/sqrt.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_hue_map.hh> +#include <mln/fun/v2v/rgb_to_saturation_map.hh> +#include <mln/fun/v2v/rgb_to_value_map.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +//============================================================================// +// HISTOGRAM +//============================================================================// + +/// Histogram +/// \{ +/// \brief Histogram processing methods. +/// +/// In this part, we define methods for histogram processing as average, +/// peak, variance etc ... + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +mln_value(I) count_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) result = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + result += histo(p); + + return result; +} + +/// \brief Sum the whole frequencies. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the number of pixels in the domain. +/// +/// By summing all the frequencies of the domain, we count in fact the +/// number of pixels in the image. +template <typename I> +mln_value(I) sum_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) sum = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + sum += histo(p); + + return sum; +} + +/// \brief Count the null frequencies. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the number of null frequencies. +/// +/// It's a way to access the number of distinct colors in the +/// histogram by comparing it with the histogram domain. +template <typename I> +mln_value(I) count_null_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + mln_value(I) count = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + if (0 == histo(p)) + count++; + + return count; +} + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +template <typename I> +mln_coord(mln_site_(I)) peak_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the peak of the histogram + mln_value(I) v_max = mln::opt::at(histo, mln::literal::zero); + mln_coord(mln_site_(I)) p_max = mln::literal::zero; + mln_piter(I) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + return p_max; +} + +/// \brief Find the maximum frequency of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the maximum frequency of the histogram. +/// +/// Find the peak and return its value, not its position. We use that +/// function to normalize histograms. +template <typename I> +mln_value(I) max_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the value of the peak from the histogram + mln_value(I) max = mln::opt::at(histo, mln::literal::zero); + mln_piter(I) p(histo.domain()); + + for_all(p) + { + max = mln::math::max(histo(p),max); + } + + return max; +} + +/// \brief Find the mean of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the aveerage of the histogram. +/// +/// Compute the mean. +template <typename I> +float mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief Compare the histogram with the equi distributed histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the distance between the equi distributed histogram. +/// +/// Compute the square euclidian distance between histogram and the +/// equi distributed histogram. The equi distributed histogram is an +/// histogram in which each bin has the same frequency (id est 1/256 +/// for instance). The integral equals one for this two +/// histograms. This is a major contribution test to differentiate +/// image database. +template <typename I> +float cmp_equi_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + var += mln::math::sqr(histo(p) - (1/256.0)); + } + + var = var / sum; + + return var; +} + +/// \brief Compute the variance on the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the variance. +/// +/// Compute the variance by substracting the mean. +template <typename I> +float var_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + var += mln::math::sqr(p.ind() - mean) * histo(p); + } + + var = var / sum; + + return var; +} + +/// \brief Find the mean of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the aveerage of the histogram. +/// +/// Compute the mean. +template <typename I> +float mean_frequency_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum++; + mean += histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief Compute the standard deviation on the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the dstandard deviation. +/// +/// Compute the standard deviation by substracting the mean +template <typename I> +float stddev_frequency_histo(const mln::Image<I>& histo_, float mean) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the var of the histogram + float sum = 0; + float var = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum++; + var += mln::math::sqr(histo(p)-mean); + } + + var = mln::math::sqrt(var / sum); + + return var; +} +/// \} + +//============================================================================// +// HUE TEST +//============================================================================// + +/// Hue test +/// \{ +/// \brief Hue test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the hue analysis. + + +/// \brief Label a grey value. +/// +/// \param[in] int_u8 the grey value. +/// +/// \return the reference color for this grey value. +/// +/// Classify grey value in three class, white, black and medium_gray. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the +/// primary colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + + if (10 > hue) + result = mln::literal::red; + else if (32 > hue) + result = mln::literal::orange; + else if (53 > hue) + result = mln::literal::yellow; + else if (74 > hue) + result = mln::literal::green; // chartreuse + else if (96 > hue) + result = mln::literal::green; + else if (116 > hue) + result = mln::literal::green;// turquoise, aigue-marine + else if (138 > hue) + result = mln::literal::green; // cyan + else if (159 > hue) + result = mln::literal::blue; // azur + else if (181 > hue) + result = mln::literal::blue; + else if (202 > hue) + result = mln::literal::violet; + else if (223 > hue) + result = mln::literal::pink; + else // if (244 > hue) + result = mln::literal::red; + + return result; +} + +/// \brief The hue test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// \param[in] threshold the limit to compute the histogram contribution. +/// +/// \return the proportion of pixels that pass the test. +/// +/// This is an augmented test. We first normalized the histogram to +/// obtain a discrete distribution. Then we compute the mean (or the +/// peak, i don't really know what is the best) and we look at the +/// proportion of the pixels around the mean or the peak. This +/// proportion tell us how histogram distribution is concentrated +/// around the modal value. If a hue modal value exist, we can say +/// that the image is colorized. +float hue_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float prop; + short peak; + mln::value::rgb8 color; + float sum; + mln::point1d inf; + mln::point1d sup; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + peak = mean_histo(histo); //peak_histo(histo); + color = label_hue(peak); + inf = mln::point1d(mln::math::max(0, peak-threshold)); + sup = mln::point1d(mln::math::min(255, peak+threshold)); + cnt1 = count_histo(histo_float|mln::box1d(inf,sup)); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); +// std::cout << "peak = " << peak << std::endl; +// std::cout << "color = " << color << std::endl; + + return prop; +} +/// \} + +//============================================================================// +// SATURATION TEST +//============================================================================// + +/// Saturation test +/// \{ +/// \brief Saturation test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the +/// saturation analysis. + + +/// \brief The saturation test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// \param[in] threshold for deciding which is low saturation or not. +/// +/// \return the proportion of pixels which pass the test. +/// +/// This is the augmented test, we normalized the histogram and then +/// we count the proportion of the histogram below the threshold. +float saturation_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_saturation_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float sum; + float prop; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_saturation_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + cnt1 = count_histo(histo_float | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} +/// \} + +//============================================================================// +// VALUE TEST +//============================================================================// + +/// Value test +/// \{ +/// \brief Value test image processing stuff. +/// +/// In this part, we define every thing that is correlated to the +/// value analysis. + + + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +template <typename I> +float stddev3(const mln::Image<I>& histo_, unsigned peak) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Compute stddev + + float stddev = 0.0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + stddev += r((short)peak, mln::opt::at(histo,peak), p.ind(), histo(p)); + } + + return stddev; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +template <typename I> +float stddev2(const mln::Image<I>& histo_, unsigned peak, unsigned limit) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + float stddev_low = 0.0; + float stddev_up = 0.0; + float ret = 0.0; + + // A transformer avec des iterators + + if (250 > peak) + stddev_up = stddev3(histo |mln::box1d(mln::point1d(peak+1), + mln::point1d(peak+limit)), peak); + + if (5 < peak) + stddev_low = stddev3(histo |mln::box1d(mln::point1d(peak-limit), + mln::point1d(peak-1)), peak); + + ret = (250 < peak)? stddev_low : (5 > peak)? stddev_up : + (stddev_low + stddev_up)/2; + + return ret; +} + + +/// \brief This is the Value test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of result histogram. +/// \param[in] tmp the name of the output value (HSV) map. +/// \param[in] threshold the range around the peak where R is computed. +/// +/// \return the proportion of pixels that pass the test. +/// +/// This is the augmented version of the Millet test. This code is not +/// stable. There is a lots of tests on. The Millet test is computed +/// with stddev2 call. We test simple counting around the pic as we +/// do for other tests but it's not really insteresting. The prop4 is +/// the best for me. We test the difference between the distribution and the +/// equi-distribution. +float value_test(const std::string input, + const std::string output, + const std::string tmp, + const short threshold) + +{ + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + mln::image1d<float> histo_float; + float cnt1; + float cnt2; + float prop; + float sum; + float prop4; + short peak; + mln::point1d inf; + mln::point1d sup; + + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_value_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + sum = sum_frequency_histo(histo); + histo_float = mln::data::convert(float(), histo) / sum; + prop4 = cmp_equi_frequency_histo(histo_float); + peak = peak_histo(histo); // mean_histo(histo); + //prop = stddev2(histo, peak, threshold); + inf = mln::point1d(mln::math::max(0, peak-threshold)); + sup = mln::point1d(mln::math::min(255, peak+threshold)); + cnt1 = count_histo(histo_float|mln::box1d(inf,sup)); + cnt2 = count_histo(histo_float); + prop = ((100.0 * cnt1) / cnt2); + + std::cerr << "peak = " << peak << std::endl; + std::cerr << "inf = " << inf << std::endl; + std::cerr << "sup = " << sup << std::endl; + std::cerr << "cnt1 = " << cnt1 << std::endl; + std::cerr << "cnt2 = " << cnt2 << std::endl; + std::cerr << "prop = " << prop << std::endl; + std::cerr << "prop4= " << prop4 << std::endl; + + mln::io::plot::save_image_sh(histo_float, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + +//============================================================================// +// MAIN +//============================================================================// + + +/// \brief The main entry. +/// +/// This is a front end for launching image processing test. We deal +/// with boost library to walk on image database directories. Nothing +/// very interesting here, it is a lot of plumberies. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + +// t_path full_path[] = {t_path(ICDAR_20P_TEXT_ONLY_IMG_PATH), +// t_path(ICDAR_20P_TEXT_COLOR_IMG_PATH), +// t_path(ICDAR_20P_TEXT_PHOTO_IMG_PATH)}; + + t_path full_path[] = {t_path(AFP_PPM_IMG_PATH)}; + + std::cout << "#!/usr/bin/gnuplot" << std::endl; + std::cout << "set terminal x11 persist 1" << std::endl; + std::cout << "#HUE - SATURATION - VALUE" << std::endl; + std::cout << "plot '-' using 1:2 with point notitle,\\" << std::endl; + std::cout << " '-' using 1:2 with point notitle,\\" << std::endl; + std::cout << " '-' using 1:2 with point notitle" << std::endl; + + for (int i = 0; i < 1; ++i) + { + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float prop = 0.0; + t_path directory; + t_path leaf; + t_path output; + t_path tmp; + + std::cerr << "entering " << full_path[i] << std::endl; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + std::cerr << dir_iter->path() << std::endl; + // concatenation de chaine +// directory = (ANNOTATING_ICDAR_H_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_H_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = hue_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 20); + + std::cout << prop << " "; + +// directory = (ANNOTATING_ICDAR_S_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_S_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = saturation_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 25); + + std::cout << prop << " "; + +// directory = (ANNOTATING_ICDAR_V_INPUT_RET_PATH); + directory = (ANNOTATING_AFP_V_INPUT_RET_PATH); + leaf = dir_iter->path().leaf(); + output = change_extension(directory / leaf, ".sh"); + tmp = change_extension(directory / leaf, ".pgm"); + + prop = value_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 15); + + std::cout << prop << " "; + std::cout << "# " << dir_iter->path().leaf() << std::endl; + } + std::cout << "e" << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/hue/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/hue/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/hue/hue.cc b/scribo/sandbox/green/exp/annotating/hue/hue.cc new file mode 100644 index 0000000..6e1195c --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/hue/hue.cc @@ -0,0 +1,402 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet hue operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Hue +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. + + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_hue_map.hh> +#include <mln/value/rgb8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + + + +/// \brief Label a grey value. +/// +/// \param[in] int_u8 the grey value. +/// +/// \return the reference color for this grey value. +/// +/// Classify grey value in three class, white, black and medium_gray. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the +/// primary colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + + if (10 > hue) + result = mln::literal::red; + else if (32 > hue) + result = mln::literal::orange; + else if (53 > hue) + result = mln::literal::yellow; + else if (74 > hue) + result = mln::literal::green; // chartreuse + else if (96 > hue) + result = mln::literal::green; + else if (116 > hue) + result = mln::literal::green;// turquoise, aigue-marine + else if (138 > hue) + result = mln::literal::green; // cyan + else if (159 > hue) + result = mln::literal::blue; // azur + else if (181 > hue) + result = mln::literal::blue; + else if (202 > hue) + result = mln::literal::violet; + else if (223 > hue) + result = mln::literal::pink; + else // if (244 > hue) + result = mln::literal::red; + + +// if (14 > hue) +// result = mln::literal::red; +// else if (29 > hue) +// result = mln::literal::orange; +// else if (45 > hue) +// result = mln::literal::yellow; +// else if (113 > hue) +// result = mln::literal::green; +// else if (149 > hue) +// result = mln::literal::cyan; +// else if (205 > hue) +// result = mln::literal::blue; +// else if (235 > hue) +// result = mln::literal::violet; +// else if (242 > hue) +// result = mln::literal::pink; +// else +// result = mln::literal::red; + + return result; +} + + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +unsigned count_histo(const mln::Image<I>& img_) +{ + const I& img = exact(img_); + + mln_precondition(img.is_valid()); + + unsigned result = 0; + + mln_piter(I) p(img.domain()); + + for_all(p) + result += img(p); + + return result; +} + +/// \brief Find the peak of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the bin which contains the greatest value. +/// +/// Compute the position of the peak of the histogram. To do this, we +/// view evrey bin and we maintain the maxima of the values and the +/// position. At the end, we return the position which contains the +/// greatest value. +template <typename I> +unsigned peak_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + return p_max; +} + +/// \brief Compute the average of the histogram. +/// +/// \param[in] histo_ the histogram. +/// +/// \return the average of the histogram. +/// +/// Compute the mean of the histogram by summing the values of each +/// bin ponderated by its frequency. The result is divided by the sum +/// of the frequencies. +template <typename I> +unsigned mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + + +/// \brief The hue test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] tmp the name of the hue map to output. +/// +/// \return the proportion of pixels that pass the test. +/// +/// Load the input image, transform it to get the hue_map and compute +/// the histogram. Then count the number of pixels that are between +/// peak-threshold and peak +threshold. Print the peak and the +/// percentage of pixels around the pic. +float hue_test(const std::string input, + const std::string output, + const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_hue_map<8> t_rgb_to_hue_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + unsigned peak; + mln::value::rgb8 color; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_hue_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + peak = mean_histo(histo); //peak_histo(histo); + color = label_hue(peak); + cnt1 = count_histo(histo | mln::box1d(mln::point1d(peak-threshold), + mln::point1d(peak+threshold))); + cnt2 = mln::geom::nsites(input_rgb8); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + std::cout << "peak = " << peak << std::endl; + std::cout << "color = " << color << std::endl; + + return prop; +} + + +/// \brief Main entry. +/// +/// It is a front end which managing the iteration through one image +/// data base with boost. Save hue map and hue histogram. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ICDAR_20P_PPM_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float prop = 0.0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + // concatenation de chaine + t_path directory(ANNOTATING_ICDAR_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = hue_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 20); + + std::cout << output << " : " << prop << std::endl; + std::cerr << output << " : " << prop << std::endl; + } + } + } + + return 0; +} diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/scribo/sandbox/green/exp/annotating/hue/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/hue/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/hue/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/hue/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/hue/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/hue/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/nb_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/nb_color/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc b/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc new file mode 100644 index 0000000..2331c61 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/nb_color/nb_color.cc @@ -0,0 +1,171 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Compute the number of colors in an image. +/// +/// Build histogram of colors and count the bins different from zero. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/math/count.hh> +#include <mln/accu/stat/histo3d_rgb.hh> + +#include <mln/binarization/threshold.hh> + +#include <mln/core/alias/neighb3d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_rgbn.hh> + +// #include <mln/morpho/opening/volume.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/pw/value.hh> +#include <mln/pw/cst.hh> + +#include <mln/util/timer.hh> + +#include <mln/value/rgb8.hh> +//#include <mln/value/rgb.hh> + + +/// \brief Count the colors. +/// +/// \param[in] image the name of the image to process. +/// +/// \return the number of colors. +/// +/// Count the color by building histogram. Strange filtering is +/// done. We use also the technique of regional maxima in +/// comment. Quantification is done to reduce the size of the +/// histogram as well. + +// n < 8, n is the degree of quantification +template <unsigned n> +unsigned count_image_color(const std::string& image) +{ + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::rgb<n> t_rgbn; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_rgbn> t_image2d_rgbn; + typedef mln::image3d<unsigned> t_histo3d; + typedef mln::image3d<bool> t_histo3d_bool; + typedef mln::fun::v2v::rgb8_to_rgbn<n> t_rgb8_to_rgbn; + typedef mln::accu::meta::stat::histo3d_rgb t_histo3d_fun; + typedef mln::accu::meta::math::count t_count_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_rgbn input_rgbn; + t_image2d_rgbn output_rgbn; + t_histo3d histo; +// t_histo3d opened; + t_histo3d_bool filtered; + + mln::io::ppm::load(input_rgb8, image.c_str()); + + unsigned nb_pixel = input_rgb8.ncols() * input_rgb8.nrows(); + unsigned min_volume = (unsigned)(nb_pixel * 0.0001); + unsigned nb_color = 0; + +// input_rgbn = mln::data::transform(input_rgb8, t_rgb8_to_rgbn()); + histo = mln::data::compute(t_histo3d_fun(), input_rgb8); +// opened = mln::morpho::opening::volume(histo, mln::c6(), min_volume); + filtered = mln::binarization::threshold(histo, min_volume); + nb_color = mln::data::compute(t_count_fun(), + (filtered|(mln::pw::value(filtered)!=0)).rw()); + + return nb_color; +} + + +/// \brief Main entry. +/// +/// The main routine makes the base driver job. It looks after many +/// directories and lists every images in it. We use the boost library +/// to do it. Statistics are computed on the number of colors. The +/// first idea was to answer the question is this descriptor allow to +/// recognize a specific base. +int main() +{ + typedef boost::filesystem::path t_path; + //typedef boost::filesystem::initial_path<t_path()> t_init_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + unsigned count = 0; + unsigned sum1 = 0; + unsigned sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + unsigned val = count_image_color<8>(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/saturation/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/saturation/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/saturation/saturation.cc b/scribo/sandbox/green/exp/annotating/saturation/saturation.cc new file mode 100644 index 0000000..b5834a1 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/saturation/saturation.cc @@ -0,0 +1,175 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet saturation operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to Sat +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_saturation_map.hh> + +#include <mln/io/ppm/load.hh> +//#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +unsigned count_histo(const mln::Image<I>& img_) +{ + const I& img = exact(img_); + + mln_precondition(img.is_valid()); + + unsigned result = 0; + + mln_piter(I) p(img.domain()); + + for_all(p) + result += img(p); + + return result; +} + +/// \brief The saturation test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of the histogram to output. +/// \param[in] threshold for deciding which is low saturation or not. +/// +/// \return the proportion of pixels that pass the test. +/// +/// Load the input image, transform it to get the saturation_map and compute +/// the histogram. Then count the number of pixels that are under the threshold. +/// Then return the proportion of pixels. +float saturation_test(const std::string input, + const std::string output, +// const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_saturation_map<8> t_rgb_to_saturation_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_saturation_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + cnt1 = count_histo(histo | mln::box1d(mln::point1d(0), + mln::point1d(threshold))); + cnt2 = mln::geom::nsites(input_rgb8); + prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); +// mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + + +/// \brief the main entry. +/// +/// This is a front end for image processing routine. It manages the +/// calling of every image in the database. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ICDAR_20P_PPM_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float prop = 0.0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + // concatenation de chaine + t_path directory(ANNOTATING_ICDAR_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = saturation_test(dir_iter->path().string(), + output.string(), +// tmp.string(), + 25); + + std::cout << output << " : " << prop << std::endl; + std::cerr << output << " : " << prop << std::endl; + } + } + } + + return 0; +} diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/scribo/sandbox/green/exp/annotating/saturation/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/saturation/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/saturation/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/saturation/text-only.txt diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/stddev_color/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/stddev_color/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc b/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc new file mode 100644 index 0000000..365a9c3 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/stddev_color/stddev_color.cc @@ -0,0 +1,216 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. + +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> + +#include <mln/math/sqr.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +float stddev_color(const std::string& image) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + t_histo1d histo; + t_point1d max_site; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter_(t_histo1d) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + // Compute the specific stddev + + float stddev_low = 0.0; + float stddev_up = 0.0; + float stddev = 0.0; + + if (250 > p_max) + for (short i = p_max+1; i < p_max+6; ++i) + stddev_up += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + if (5 < p_max) + for (short i = p_max-1; i > p_max-6; --i) + stddev_low += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up : + (stddev_low + stddev_up)/2; + + return stddev; +} + + +/// \brief Main entry. +/// +/// Front end for image processing. It is in the main routine where we +/// go through the image data base (using boost to do it). +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float count = 0; + float sum1 = 0; + float sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + float val = stddev_color(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/stddev_color_16/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/stddev_color_16/Makefile.am diff --git a/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc b/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc new file mode 100644 index 0000000..fc55547 --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/stddev_color_16/stddev_color_16.cc @@ -0,0 +1,277 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet clipart detection [millet.phd.2008.pdf] +/// +/// The aim of this descriptor is to recognize clipart. To do this, we +/// assume that clipart have their histogram concentrated around their +/// mode. This is particularly true if the clipart is design by hand, +/// because a very small number of grey tones or colors are used to +/// draw it. But sometimes, computer assists their creation and we can +/// find some continuous set of tones and this artefact create noise +/// for the detection technique describe bellow. When photographies +/// has some large uniform border, the detection can fail. To improve +/// the method, Millet decide to subdivise the image in 16 sub images +/// on which it applies the test. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/max_site.hh> +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/macros.hh> +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/image3d.hh> + +#include <mln/debug/println.hh> + +#include <mln/data/compute.hh> +#include <mln/data/fill.hh> +#include <mln/data/transform.hh> +#include <mln/data/paste.hh> + +#include <mln/fun/v2v/rgb8_to_int_u8.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/math/sqr.hh> +#include <mln/math/max.hh> + +#include <mln/opt/at.hh> + +#include <mln/value/rgb8.hh> +#include <mln/value/int_u.hh> + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] input_int_u8 the 8 bits input image to analyze. +/// \param[in] name_histo the name of the output histogram. +/// \param[in] name_image the name of the ouput sub image. +/// +/// \return the deviation, but at this time nothing.. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). +float stddev_color(mln::image2d<mln::value::int_u8> input_int_u8) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_histo1d histo; + + histo = mln::data::compute(t_histo1d_fun(), input_int_u8); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter_(t_histo1d) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + // Compute the specific stddev + + float stddev_low = 0.0; + float stddev_up = 0.0; + float stddev = 0.0; + + if (250 > p_max) + for (short i = p_max+1; i < p_max+6; ++i) + stddev_up += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + if (5 < p_max) + for (short i = p_max-1; i > p_max-6; --i) + stddev_low += r(p_max, mln::opt::at(histo,p_max), + i, mln::opt::at(histo,i)); + + stddev = (250 < p_max)? stddev_low : (5 > p_max)? stddev_up : + (stddev_low + stddev_up)/2; + + return stddev; +} + +/// \brief Divide the image in 16 sub images. +/// +/// \param[in] image the input image. +/// +/// \result nothing. +/// +/// Divive the input image in 16 by uniform and geometrical +/// method. When a sub image is ready, call the stddev routine to show +/// stats on it. +float stddev_color_16(const std::string& image) +{ + typedef mln::point1d t_point1d; + typedef mln::value::rgb8 t_rgb8; + typedef mln::value::int_u8 t_int_u8; + typedef mln::image2d<t_rgb8> t_image2d_rgb8; + typedef mln::image2d<t_int_u8> t_image2d_int_u8; + typedef mln::image1d<unsigned> t_histo1d; + typedef mln::fun::v2v::rgb8_to_int_u8 t_rgb8_to_int_u8; + typedef mln::accu::meta::stat::histo1d t_histo1d_fun; + typedef mln::accu::max_site<t_histo1d> t_max_site_fun; + + t_image2d_rgb8 input_rgb8; + t_image2d_int_u8 input_int_u8; + + mln::io::ppm::load(input_rgb8, image.c_str()); + input_int_u8 = mln::data::transform(input_rgb8, t_rgb8_to_int_u8()); + + // IMAGE SPLITTING PHASE + mln::box2d domain = input_int_u8.domain(); + mln::point2d pmin = domain.pmin(); + mln::point2d pmax = domain.pmax(); + + unsigned sz_row = (pmax.row() - pmin.row())/ 4; + unsigned sz_col = (pmax.col() - pmin.col())/ 4; + float stddev = 0.0; + + // Divide the domain in nine sub-domains. + for (unsigned i = 0; i < 4; ++i) + for (unsigned j = 0; j < 4; ++j) + { + mln::point2d min(pmin.row()+sz_row*i,pmin.col()+sz_col*j); + mln::point2d max(pmin.row()+sz_row*(i+1),pmin.col()+sz_col*(j+1)); + mln::box2d dom(min,max); + + // Save it + t_image2d_int_u8 input_1o16_int_u8(dom); + + mln::data::paste(input_int_u8 | dom, input_1o16_int_u8); + + stddev = mln::math::max(stddev, stddev_color(input_1o16_int_u8)); + } + + return stddev; +} + + +/// \brief Main entry. +/// +/// Just a front end for image processing routine. Use boost to go +/// through the image database. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ANNOTATING_1_BILL_IMG_PATH), + t_path(ANNOTATING_1_FAX_IMG_PATH), + t_path(ANNOTATING_1_HANDWRITTEN_IMG_PATH), + t_path(ANNOTATING_1_LOGO_IMG_PATH), + t_path(ANNOTATING_1_MAP_IMG_PATH), + t_path(ANNOTATING_1_PHOTO_IMG_PATH), + t_path(ANNOTATING_1_SCREENSHOT_IMG_PATH), + t_path(ANNOTATING_1_SLIDE_IMG_PATH), + t_path(ANNOTATING_1_TYPED_IMG_PATH)}; + + for (int i = 0; i < 9; ++i) + { + std::cerr << "entering " << full_path[i] << std::endl; + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float count = 0; + float sum1 = 0; + float sum2 = 0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + float val = stddev_color_16(dir_iter->path().string()); + + ++count; + sum1 += val; + sum2 += val*val; + + std::cout << dir_iter->path().string() << " => " << val << std::endl; + } + + float mean = sum1 / count; + float var = ((float)sum2 / count) - (mean * mean); + + std::cout << "mean : " << mean << std::endl; + std::cout << "var : " << var << std::endl; + } + } + + return 0; +} diff --git a/scribo/sandbox/green/exp/regional_maxima/Makefile.am b/scribo/sandbox/green/exp/annotating/value/Makefile.am similarity index 100% copy from scribo/sandbox/green/exp/regional_maxima/Makefile.am copy to scribo/sandbox/green/exp/annotating/value/Makefile.am diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-color.txt b/scribo/sandbox/green/exp/annotating/value/text-color.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-color.txt copy to scribo/sandbox/green/exp/annotating/value/text-color.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-img.txt b/scribo/sandbox/green/exp/annotating/value/text-img.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-img.txt copy to scribo/sandbox/green/exp/annotating/value/text-img.txt diff --git a/milena/sandbox/green/exp/annotating/achromastism/text-only.txt b/scribo/sandbox/green/exp/annotating/value/text-only.txt similarity index 100% copy from milena/sandbox/green/exp/annotating/achromastism/text-only.txt copy to scribo/sandbox/green/exp/annotating/value/text-only.txt diff --git a/scribo/sandbox/green/exp/annotating/value/value.cc b/scribo/sandbox/green/exp/annotating/value/value.cc new file mode 100644 index 0000000..cade88b --- /dev/null +++ b/scribo/sandbox/green/exp/annotating/value/value.cc @@ -0,0 +1,468 @@ +// Copyright (C) 2007, 2008, 2009, 2010 EPITA LRDE +// +// This file is part of Olena. +// +// Olena is free software: you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation, version 2 of the License. +// +// Olena is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Olena. If not, see <http://www.gnu.org/licenses/>. +// +// As a special exception, you may use this file as part of a free +// software project without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to produce +// an executable, this file does not by itself cause the resulting +// executable to be covered by the GNU General Public License. This +// exception does not however invalidate any other reasons why the +// executable file might be covered by the GNU General Public License. + +/// \file +/// +/// \brief Implement the Millet value operator [millet.phd.2008.pdf] +/// +/// This is the Millet code for moving from RGB space to value +/// one. Formulae are classical, we can find them on the web. +/// +/// Val = max(R,G,B). +/// Sat = (max(R,G,B) - min(R,G,B))/max(R,G,B). +/// IF R = max(R,G,B) THEN Hue = 60 * [(V-B)/(max(R,G,B)-min(R,G,B))]. +/// IF G = max(R,G,B) THEN Hue = 60 * [2 + (B-R)/(max(R,G,B)-min(R,G,B))]. +/// IF B = max(R,G,B) THEN Hue = 60 * [4 + (R-G)/(max(R,G,B)-min(R,G,B))]. +#include <iostream> +#include <sstream> +#include <boost/filesystem.hpp> + +#include <mln/img_path.hh> + +#include <mln/accu/stat/histo1d.hh> + +#include <mln/core/image/image1d.hh> +#include <mln/core/image/image2d.hh> +#include <mln/core/image/dmorph/image_if.hh> + +#include <mln/data/compute.hh> +#include <mln/data/stretch.hh> +#include <mln/data/transform.hh> + +#include <mln/literal/colors.hh> +#include <mln/literal/grays.hh> + +#include <mln/math/max.hh> +#include <mln/math/min.hh> + +#include <mln/opt/at.hh> + +#include <mln/geom/nsites.hh> + +#include <mln/fun/v2v/rgb_to_value_map.hh> + +#include <mln/io/ppm/load.hh> +#include <mln/io/pgm/save.hh> +#include <mln/io/plot/save_image_sh.hh> + +#include <mln/value/rgb8.hh> + +/// \brief Label the value. +/// +/// \param[in] val the value in [0..255]. +/// +/// \return the reference color. +/// +/// Segment the grey value in three classes. +mln::value::rgb8 label_val(const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (82 > val) + result = mln::literal::black; + else if (179 > val) + result= mln::literal::medium_gray; + else + result = mln::literal::white; + + return result; +} + + +/// \brief Label color with orange reference or brown reference. +/// +/// \param[in] color the rgb8 color. +/// \param[in] sat the HSV saturation. +/// \param[in] val the HSV value. +/// +/// \return the reference color with which it has been associated to. +/// +/// Discriminate between orange and brown colors which are at the same +/// position on the hue wheel. +mln::value::rgb8 label_orange_or_brown(const mln::value::rgb8 color, + const mln::value::int_u8 sat, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::orange == color) + { + unsigned dist_orange = mln::math::abs(sat - 184) + + mln::math::abs(val - 65); + + unsigned dist_brown = mln::math::abs(sat - 255) + + mln::math::abs(val - 125); + + if (dist_orange < dist_brown) + result = mln::literal::orange; + else + result = mln::literal::brown; + } + else + result = color; + + return result; +} + +/// \brief Label between yellow and green color. +/// +/// \param[in] color the rgb8 color. +/// \param[in] val the HSV value. +/// +/// \return the reference color associated to that color. +/// +/// Segment between yellow and green because the discrimination is not +/// so easy based on the hue wheel. +mln::value::rgb8 label_yellow_or_green(const mln::value::rgb8 color, + const mln::value::int_u8 val) +{ + mln::value::rgb8 result; + + if (mln::literal::yellow == color) + { + // Is it green or yellow ? + if (80 > val) + result = mln::literal::green; + else + result = mln::literal::yellow; + } + else + return color; + + return result; +} + +/// \brief Label hue color. +/// +/// \param[in] hue the HSV hue canal for a pixel. +/// +/// \return a hue segmentation of the hue wheel. +/// +/// The old classification given by Millet is commented. Mine is ready +/// to use. We need to change reference colors to make the primary +/// colors, the secondary ones, etc... +mln::value::rgb8 label_hue(const mln::value::int_u8 hue) +{ + mln::value::rgb8 result; + + if (14 > hue) + result = mln::literal::red; + else if (29 > hue) + result = mln::literal::orange; + else if (45 > hue) + result = mln::literal::yellow; + else if (113 > hue) + result = mln::literal::green; + else if (149 > hue) + result = mln::literal::cyan; + else if (205 > hue) + result = mln::literal::blue; + else if (235 > hue) + result = mln::literal::violet; + else if (242 > hue) + result = mln::literal::pink; + else + result = mln::literal::red; + + return result; +} + +/// \brief Sum all the bins of the histogram. +/// +/// \param[in] img the histogram based on image. +/// +/// \return the sum of the overall bins. +/// +/// Sum evry bins and return the result. +template <typename I> +unsigned count_histo(const mln::Image<I>& img_) +{ + const I& img = exact(img_); + + mln_precondition(img.is_valid()); + + unsigned result = 0; + + mln_piter(I) p(img.domain()); + + for_all(p) + result += img(p); + + return result; +} + +/// \brief Detect the peak of the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the position of the peak. +/// +/// This is a typical accumulator use case but as it needs the +/// position it couldn't be one of them. Look at the last maxima in +/// the curve and return its position. +template <typename I> +unsigned peak_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the peak of the histogram + unsigned v_max = mln::opt::at(histo, 0); + short p_max = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + if (v_max < histo(p)) + { + v_max = histo(p); + p_max = p.ind(); + } + } + + return p_max; +} + +/// \brief Compute the average of the histogram. +/// +/// \param[in] histo_ the image which represents the histogram. +/// +/// \return the average of the histogram. +/// +/// This code should be an accumulator. It computes the mean of the +/// histogram, just stats in loop. Compute the sum, compute the +/// ponderate sum and divide the second by the first. +template <typename I> +unsigned mean_histo(const mln::Image<I>& histo_) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Find the mean of the histogram + float sum = 0; + float mean = 0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + sum += histo(p); + mean += p.ind()*histo(p); + } + + mean = mean / sum; + + return mean; +} + +/// \brief The R function of Millet +/// +/// \param[in] p the position of the pic. +/// \param[in] histo_p the histo value of the pic. +/// \param[in] x the position of the element which we compute the contrib. +/// \param[in] histo_x the histo value of that element. +/// +/// \result the contribution of the element x. +/// +/// This function compute the variance-like contribution of an element +/// x linked to the pic of the histogram. In fact, every thing is like +/// we compute a square distance-like between the element x and the +/// pic in the normalized histogram. Notice that the normalized +/// histogram is the histogram divide by the value of it's pic. So at +/// the pic, the value equals 1. It's a current representation of the +/// histogram in image processing, we can find it in gimp for exemple. +float r(short p, unsigned histo_p, short x, unsigned histo_x) +{ + float result = mln::math::sqr(((float)histo_x / histo_p) * (x-p)); + + return result; +} + + +/// \brief The stddev3 is an internal stuff. +/// +/// \param[in] histo_ the image which represents the histogram. +/// \param[in] peak the position of the histogram peak. +/// +/// \return simple computing of deviation. +/// +/// This is an internal stuff. It splits the computing for easy +/// reusing practice. Sum the R contribution. +template <typename I> +float stddev3(const mln::Image<I>& histo_, unsigned peak) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + // Compute stddev + + float stddev = 0.0; + + mln_piter(I) p(histo.domain()); + + for_all(p) + { + stddev += r((short)peak, mln::opt::at(histo,peak), p.ind(), histo(p)); + } + + return stddev; +} + + +/// Brief compute the whole deviation of Millet +/// +/// \param[in] image the input image to analyze. +/// \param[in] peak the peak of the histogram. +/// \param[in] limit the threshold to compute the contribution. +/// +/// \return the deviation. +/// +/// The deviation uses the R function. It stats by finding the pic. If +/// the pic is near the rigth border of the histo, compute the R +/// function on the left neighbouring of the pic. If the pic is near +/// the left border of the histo, compute the R function on the right +/// neigbouring. Otherwise, compute the average of the right and left +/// results. The selected neighbouring is composed of five pixels at +/// the right or at the left of the pic. The detection of clipart +/// assumes that the majority of the energy of the histogram is closed +/// to the pic (five pixels around it). The test is generalized by +/// making constants as parameters. +template <typename I> +float stddev2(const mln::Image<I>& histo_, unsigned peak, unsigned limit) +{ + const I& histo = exact(histo_); + + mln_precondition(histo.is_valid()); + + float stddev_low = 0.0; + float stddev_up = 0.0; + float ret = 0.0; + + // A transformer avec des iterators + + if (250 > peak) + stddev_up = stddev3(histo |mln::box1d(mln::point1d(peak+1), + mln::point1d(peak+limit)), peak); + + if (5 < peak) + stddev_low = stddev3(histo |mln::box1d(mln::point1d(peak-limit), + mln::point1d(peak-1)), peak); + + ret = (250 < peak)? stddev_low : (5 > peak)? stddev_up : + (stddev_low + stddev_up)/2; + + return ret; +} + + +/// \brief This is the main Millet test. +/// +/// \param[in] input the name of the input image. +/// \param[in] output the name of result histogram. +/// \param[in] tmp the name of the output value (HSV) map. +/// \param[in] threshold the range around the peak where R is computed. +/// +/// \return the proportion of pixels that pass the test. +float value_test(const std::string input, + const std::string output, + const std::string tmp, + const unsigned threshold) + +{ + typedef mln::fun::v2v::rgb_to_value_map<8> t_rgb_to_value_map; + + mln::image2d<mln::value::rgb8> input_rgb8; + mln::image2d<mln::value::int_u8> map; + mln::image1d<unsigned> histo; + unsigned cnt1; + unsigned cnt2; + float prop; + unsigned peak; + + mln::io::ppm::load(input_rgb8, input.c_str()); + + map = mln::data::transform(input_rgb8, t_rgb_to_value_map()); + histo = mln::data::compute(mln::accu::meta::stat::histo1d(), map); + peak = peak_histo(histo); // mean_histo(histo); + prop = stddev2(histo, peak, threshold); +// cnt1 = count_histo(histo | mln::box1d(mln::point1d(peak-threshold), +// mln::point1d(peak+threshold))); +// cnt2 = mln::geom::nsites(input_rgb8); +// prop = ((100.0 * cnt1) / cnt2); + + mln::io::plot::save_image_sh(histo, output.c_str()); + mln::io::pgm::save(map, tmp.c_str()); + + return prop; +} + + +/// \brief The main entry. +/// +/// This is the front end for using directories with boost. It calls +/// the true image processing routine. +int main() +{ + typedef boost::filesystem::path t_path; + typedef boost::filesystem::directory_iterator t_iter_path; + + t_path full_path[] = {t_path(ICDAR_20P_PPM_IMG_PATH)}; + + for (int i = 0; i < 1; ++i) + { + std::cout << "entering " << full_path[i] << std::endl; + + if (boost::filesystem::exists(full_path[i]) && + boost::filesystem::is_directory(full_path[i])) + { + boost::filesystem::system_complete(full_path[i]); + const t_iter_path end_iter; + float prop = 0.0; + + for (t_iter_path dir_iter(full_path[i]); end_iter != dir_iter; ++dir_iter) + { + // concatenation de chaine + t_path directory(ANNOTATING_RET_PATH); + t_path leaf = dir_iter->path().leaf(); + t_path output = change_extension(directory / leaf, ".sh"); + t_path tmp = change_extension(directory / leaf, ".pgm"); + + prop = value_test(dir_iter->path().string(), + output.string(), + tmp.string(), + 15); + + std::cout << output << " : " << prop << std::endl; + std::cerr << output << " : " << prop << std::endl; + } + } + } + + return 0; +} diff --git a/scribo/sandbox/green/mln/clustering/kmean2d.hh b/scribo/sandbox/green/mln/clustering/kmean2d.hh index 51aaf49..4539083 100644 --- a/scribo/sandbox/green/mln/clustering/kmean2d.hh +++ b/scribo/sandbox/green/mln/clustering/kmean2d.hh @@ -150,7 +150,7 @@ namespace mln { /// Type definitions. /// \brief A few type definitions to limit the refactoring impact. - ///{ + /// \{ typedef value::rgb<8> t_rgb; typedef value::label<8> t_label; typedef value::rg<n> t_value; @@ -179,7 +179,7 @@ namespace mln typedef util::array<t_mean_set> t_mean_cnv; typedef image1d<t_result1d> t_variance_val; typedef util::array<t_variance_val> t_variance_cnv; - ///} + /// \} /// \brief Constructor. /// \param[in] point : the image as the population of pixels. diff --git a/scribo/sandbox/green/mln/fun/p2b/achromatic.hh b/scribo/sandbox/green/mln/fun/p2b/achromatic.hh index 1f45b2d..91a2dc5 100644 --- a/scribo/sandbox/green/mln/fun/p2b/achromatic.hh +++ b/scribo/sandbox/green/mln/fun/p2b/achromatic.hh @@ -44,15 +44,29 @@ namespace mln namespace p2b { - /// \brief Functor that compare the i-th component of a value. - // V is for the type of the value received - // i is the ith component to select + /// \brief Functor that compare the i-th component with a threshold. + /// + /// T_rgb is the kind of RGB we use. template <typename T_rgb> struct achromatic : public Function_v2b< achromatic<T_rgb> > { typedef bool result; + + /// \brief Operator that makes the threshold [millet.phd.2008.pdf] + /// + /// \param[in] p the selected site + /// + /// \return if the site is achromatic or not. + /// + /// This a try for opimized the call to the achromatic + /// routine. The goal is to decide if a site has got an + /// achromatic status or not. This is the true Millet test. bool operator()(const point2d& p) const; + /// \brief Cstor of the object. + /// + /// \param[in] img the RGB input image. + /// \param[in] threshold the value to compare with. achromatic(const image2d<T_rgb>& img, const float threshold); const float threshold_; diff --git a/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh b/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh index 84d26c8..e776c9a 100644 --- a/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh +++ b/scribo/sandbox/green/mln/fun/v2v/hue_concentration.hh @@ -32,6 +32,10 @@ # include <mln/value/hsv.hh> # include <mln/value/rgb8.hh> +/// \file +/// +/// This is the code for building hue_concentration_map. + namespace mln { @@ -41,12 +45,18 @@ namespace mln namespace v2v { + /// \brief internal method for detecting the histogram peak. + /// + /// \param[in] hue_histo the histogram of hue. + /// + /// \return the seed of the peek. unsigned peak_histo(const mln::image1d<unsigned>& hue_histo); struct hue_concentration : public Function_v2v< hue_concentration > { typedef float result; + /// \brief Gibe the distance map between actual hue and the peak. float operator()(const float hue) const; hue_concentration(const mln::image1d<unsigned>& hue_histo) diff --git a/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh b/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh index 6de63ec..126f9bd 100644 --- a/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh +++ b/scribo/sandbox/green/mln/fun/v2v/rgb_to_achromatism_map.hh @@ -49,6 +49,11 @@ namespace mln /// \brief Convert rgb value to achromatism map. /// + /// Convert rgb value to binary achromastism map using the + /// rebuilded Millet phd formulae [millet.phd.2008.pdf]. The + /// idea is to look at the minimum and maximum of the channels + /// and to return the difference. + /// /// \ingroup modfunv2v template <unsigned n> diff --git a/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh b/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh index 3dd8eb0..a191095 100644 --- a/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh +++ b/scribo/sandbox/green/mln/fun/v2v/rgb_to_hsv.hh @@ -34,6 +34,11 @@ #include <mln/value/hsv.hh> #include <mln/value/rgb.hh> +/// \fiie +/// +/// This is the millet [millet.phd.2008.pdf] transformation from RGB +/// space to HSV space. + namespace mln { @@ -57,7 +62,15 @@ namespace mln typedef T_hsv result; - /// HSV implementation from millet.2008.phd.pdf p67 + /// \brief HSV implementation from millet.2008.phd.pdf p67 + /// + /// \param[in] rgb the input rgb pixel. + /// + /// \return a HSV pixel. + /// + /// This is the Millet implementation of its transformation + /// operator to go from RGB space to HSV space. When pixels + /// are achromatic, hue equals -1. template <typename T_rgb> T_hsv operator()(const T_rgb& rgb) const; -- 1.5.6.5
participants (1)
-
Yann Jacquelet