# mission2b.adb

    --
    -- Ce programme affiche les contours d'une image (filtre de Sobel)
    -- On en profite pour tester différents types de rendus (N&B, colorés, modulés par un log, ...)
    --

    with JPG, GAda.Graphics, Gada.Plus ;

    -- Pour avoir le log
    with Ada.Numerics.Generic_Elementary_Functions;

    procedure Mission2b is

       package Gr renames GAda.Graphics ;

       -- Pour avoir le log...
       package math is new Ada.Numerics.Generic_Elementary_Functions (Float);

       -- Affiche l'image IMG aux coordonnées x,y de la fenêtre graphique.
       procedure Afficher_Image(Img : JPG.T_Image ; X,Y : Integer) is
          Haut : Integer := Img'Length(1) ;
       begin
          for Ligne in Img'Range(1) loop
             for Colonne in Img'Range(2) loop
                Gr.ColorPoint(X + Colonne, Y + Haut - 1 - Ligne,
                              Img(Ligne, Colonne)) ;
             end loop ;
          end loop ;
       end Afficher_Image ;

       -- Transforme une image en niveaux de gris
       function Niveau_Gris(Img : JPG.T_Image) return JPG.T_Image Is
          Result : JPG.T_Image(Img'Range(1), Img'Range(2)) ;
          Pixel :Gr.T_Couleur ;
          Gris : Integer ;
       begin
          for Ligne in Img'Range(1) loop
    	 for Colonne in Img'Range(2) loop
    	    -- Le pixel coloré situé aux coordonnées ligne, colonne
    	    Pixel := Img(Ligne, Colonne) ;

    	    -- Sa luminosité
    	    Gris := (Pixel.Rouge + Pixel.Vert + Pixel.Bleu) / 3 ;

    	    -- L'image résultat en niveau de gris.
    	    Result(Ligne,Colonne) := (Gris, Gris, Gris) ;
    	 end loop ;
          end loop ;

          return Result ;
       end Niveau_Gris ;

       -- Pour le filtre de Sobel, on essaie plusieurs rendus différents
       type T_Rendu is (Seuil, NB, NBlog, Coul, CoulLog) ;

       --
       -- Voici les fonctions calculant ces différents rendus
       --

       -- Valeur maximale atteignable par Norme2
       Norme2Max : constant Integer := 2* ( (255*4)**2 ) ;
       LogNorme2Max : constant Integer := Integer(Math.Log(Float(Norme2Max))) ;

       -- Au dessus d'un seuil, c'est blanc. En dessous, c'est noir.
       function Sobel_Seuil (Norme2 : Integer) return Gr.T_Couleur is
          Color : Gr.T_Couleur ;
       begin
          -- Seuil choisi en tatonnant
          if Norme2 > 10000 then
    	 Color := (255,255,255) ;
          else
    	 Color := (0,0,0) ;
          end if ;
          return Color ;
       end Sobel_Seuil ;

       -- Saturation : si ça dépasse 255, prend 255.
       function Sature(X : Integer) return Integer is
       begin
          if X > 255 then return 255 ;
          else return X ;
          end if ;
       end Sature ;

       -- Niveau de gris proportionnel à la norme2 (amplifié par un certain coefficient)
       function Sobel_NB(Norme2 : Integer) return Gr.T_Couleur is
          Amplif : Integer := 4 ;
          Gris : Integer ;
       begin
          Gris := Sature ((Norme2 * 255 * Amplif) / Norme2Max) ;
          return (Gris,Gris,Gris) ;
       end Sobel_NB ;

       -- Niveau de gris proportionnel au log de la norme2
       function Sobel_NBLog(Norme2 : Integer) return Gr.T_Couleur is
          Gris : Integer ;
       begin
          Gris := (Integer(Math.Log(Float(1 + Norme2))) * 255) / LogNorme2Max ;
          return (Gris,Gris,Gris) ;
       end Sobel_NBLog ;

       -- Couleur originale modulée par la norme2 (amplifiée)
       function Sobel_Coul(Coul : Gr.T_Couleur ; Norme2 : Integer) return Gr.T_Couleur is
          Coef : Float ;
          Amplif : Float := 4.0 ;

       begin
          Coef := Amplif * Float(Norme2) / Float(Norme2Max) ;

          return ( Sature(Integer( Float(Coul.Rouge) * Coef )) ,
    	       Sature(Integer( Float(Coul.Vert) * Coef )) ,
    	       Sature(Integer( Float(Coul.Bleu) * Coef )) ) ;
       end Sobel_Coul ;

       -- Couleur originale modulée par le log de la norme2
       function Sobel_CoulLog(Coul : Gr.T_Couleur ; Norme2 : Integer) return Gr.T_Couleur is
          Coef : Float ;
       begin
          Coef := (Math.Log(Float(1 + Norme2))) / Float(LogNorme2Max) ;

          return ( Integer( Float(Coul.Rouge) * Coef ) ,
    	       Integer( Float(Coul.Vert) * Coef ) ,
    	       Integer( Float(Coul.Bleu) * Coef ) ) ;
       end Sobel_CoulLog ;

       -- Fonction qui choisit le rendu à effectuer.
       function Choix_Couleur( Couleur_Originale : Gr.T_Couleur ; Norme2 : Integer ; Rendu : T_Rendu) return Gr.T_Couleur is
          Resultat : Gr.T_Couleur ;
       begin
          case Rendu is
    	 when Seuil => Resultat := Sobel_Seuil(Norme2) ;
    	 when NB => Resultat := Sobel_NB(Norme2) ;
    	 when NBlog => Resultat := Sobel_NBlog(Norme2) ;
    	 when Coul => Resultat := Sobel_Coul(Couleur_Originale, Norme2) ;
    	 when CoulLog => Resultat := Sobel_CoulLog(Couleur_Originale, Norme2) ;
          end case ;

          return Resultat ;
       end Choix_Couleur ;

       -- Affiche les contours (filtre de Sobel)
       procedure Afficher_Sobel(Img : JPG.T_Image ; X,Y : Integer ; Rendu : T_Rendu) is

          -- Le filtre fonctionne sur la luminosité, i.e. une image en niveau de gris
          ImageGris : JPG.T_Image := Niveau_Gris(Img) ;

          Haut : Integer := Img'Length(1) ;
          GradX, GradY : Integer ;
          Norme2 : Integer ;

       begin
          for Ligne in Img'First(1)+1 .. Img'Last(1)-1 loop
    	 for Colonne in Img'First(2)+1 .. Img'Last(2)-1 loop
    	    -- On calcule le gradient en prenant au choix l'une des composantes (ici rouge),
    	    -- car toutes les composantes sont égales.
    	    GradX := ImageGris(Ligne-1, Colonne+1).Rouge - ImageGris(Ligne-1, Colonne-1).Rouge
    	      + 2 * (ImageGris(Ligne, Colonne+1).Rouge - ImageGris(Ligne, Colonne-1).Rouge)
    	           + ImageGris(Ligne+1, Colonne+1).Rouge - ImageGris(Ligne+1, Colonne-1).Rouge ;

    	    GradY := ImageGris(Ligne+1, Colonne-1).Rouge - ImageGris(Ligne-1, Colonne-1).Rouge
    	      + 2 * (ImageGris(Ligne+1, Colonne).Rouge - ImageGris(Ligne-1, Colonne).Rouge)
    	           + ImageGris(Ligne+1, Colonne+1).Rouge - ImageGris(Ligne-1, Colonne+1).Rouge ;

    	    Norme2 := GradX * GradX + GradY * GradY ;

    	    -- Choix de la couleur à afficher à ce point

    	    Gr.ColorPoint(X + Colonne, Y + Haut - 1 - Ligne,
    			  Choix_Couleur(Img(Ligne, Colonne), Norme2, Rendu) ) ;
    	 end loop ;
          end loop ;
       end Afficher_Sobel ;

       procedure Tester_Afficher_Image(Nom_Image : String) is
          Image : JPG.T_Image := JPG.Lire_Image(Nom_Image) ;

          Largeur, Hauteur : Integer ;
       begin
          -- La hauteur est le nombre de lignes
          Hauteur := Image'Length(1) ;

          -- La largeur est le nombre de colonnes
          Largeur := Image'Length(2) ;

          Gr.Resize(3 * Largeur, 2 * Hauteur) ;

          Afficher_Image(Niveau_Gris(Image), 0, 0) ;

          Afficher_Sobel(Image, 0, Hauteur, Seuil) ;
          Afficher_Sobel(Image, Largeur, 0, NB) ;
          Afficher_Sobel(Image, Largeur, Hauteur, NBlog) ;
          Afficher_Sobel(Image, 2*Largeur, 0, Coul) ;
          Afficher_Sobel(Image, 2*Largeur, Hauteur, CoulLog) ;

       end Tester_Afficher_Image ;

    begin

       Tester_Afficher_Image(Gada.Plus.Choisir_Fichier) ;

    end Mission2b ;