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 ;