// Auteurs
// Matthieu Aubry
// Anthony Combe
// Licence
// Domaine publique
Unit outils_unit;

INTERFACE

Uses image_unit;


type
    // type masque pour la fonction convol_image
    masque = record
    matrix : array[1..3,1..3] of integer;
    N:integer
    end;
    
    // d�inition d'une zone de roi
    roi = record
        x0 : integer;
        y0 : integer;
        x1 : integer;
        y1 : integer
        end;
    
    // diff�entes fonctions  disponibles pour applique_lut
    fonctions = (assombrir, eclaircir, seuillage, puit, negatif, recadrage, egalisation);
    
    // diff�ents filtres disponibles pour convol_image
    filtres = (filtre_flou, filtre_net, filtre_contourx, filtre_contoury);
    
    // tableau pour le stockage des donn�s de la fonction histogram
    Thisto = array[0..255] of longint;


// d�laration des fonctions et procedures
function cabs(n:integer):integer;
function dilate(img: pTimage; n:integer): pTimage;
function erode(img: pTimage; n:integer): pTimage;
function erode_soft(img: pTimage; n:integer): pTimage;
function applique_lut(img: pTimage; fonc: fonctions; arg1, arg2: byte) : pTimage;
function convol_image(img:pTimage; zone:roi; filter:filtres):pTimage;
function detection_contour(img: pTimage; seuil: integer) : pTimage;
procedure histogram(img:pTimage;zone:roi;var histog:Thisto);


// d�laration d'une variable globale de roi
var zone:roi;

IMPLEMENTATION

// 
// fonction interne �la unit qui renvoie le plus grand byte parmi 9 byte pass� en argument
// @param x1..x9 : bytes �comparer
// @return byte correspondant au maximum
function max9(x1,x2,x3,x4,x5,x6,x7,x8,x9 : byte ) : byte;
var maxi : integer;
begin
     if x1>x2 then maxi:=x1
        else maxi:=x2;
     if x3>maxi then maxi:=x3;
     if x4>maxi then maxi:=x4;
     if x5>maxi then maxi:=x5;
     if x6>maxi then maxi:=x6;
     if x7>maxi then maxi:=x7;
     if x8>maxi then maxi:=x8;
     if x9>maxi then maxi:=x9;
max9 := maxi;
end;



// 
// fonction interne �la unit qui renvoie le plus petit byte parmi 9 byte pass� en argument
// @param x1..x9 : bytes �comparer
// @return byte correspondant au minimum
function min9(x1,x2,x3,x4,x5,x6,x7,x8,x9 : byte ) : byte;
var mini : integer;
begin
     if x1<x2 then mini:=x1
        else mini:=x2;
     if x3<mini then mini:=x3;
     if x4<mini then mini:=x4;
     if x5<mini then mini:=x5;
     if x6<mini then mini:=x6;
     if x7<mini then mini:=x7;
     if x8<mini then mini:=x8;
     if x9<mini then mini:=x9;
min9 := mini;
end;



// 
// fonction read_pixel l��ement modifi� elle renvoie un pixel blanc si les coordonn�s sont en dehors de l'image
// @param x,y : coordonn�s du pixel �lire
// @param img : pointeur vers l'image source
// @return pixel lu
function read_pixel2(x,y:integer;img:pTimage):Tpixel;
var pix: Tpixel;
begin
    if (x < 0) or (y>img^.hauteur) or (y < 0) or (x>img^.largeur) then
    begin
       // si les coordonn�s sont erron�s on renvoie un pixel blanc
       pix.blue:=255;
       pix.red:=255;
       pix.green:=255;
    end
    else pix := read_pixel(x, y, img);
read_pixel2 := pix;
end;




// 
// fonction erode qui permet de diminuer la taille des ��ents blancs
// @param img : pointeur vers l'image source
// @param n : nombre de fois �appliquer la fonction sur l'image
// @return pointeur vers l'image obtenue
function erode(img: pTimage; n:integer): pTimage;
var img2, img3: pTimage;
    i,j, k : integer;
    comp : byte;
begin
    img2 := new_image;
    img3 := new_image;
    img2^.hauteur := img^.hauteur;
    img2^.largeur := img^.largeur;
    img3^.hauteur := img^.hauteur;
    img3^.largeur := img^.largeur;
    
    // on copie l'image pass�en arguments pour ne pas la modifier
    for i:=0 to img^.largeur Do
        for j:= 0 to img^.hauteur Do
            write_pixel(i, j, img2, read_pixel(i, j, img).red, read_pixel(i, j, img).blue, read_pixel(i, j, img).blue );
    
    // on lit sur l'image 2 et on �rit sur l'image 3, ensuite on copie de 3 vers 2
    for k:=0 to n Do
        begin
        for i:=0 to img2^.largeur Do
            for j:= 0 to img2^.hauteur Do
                begin
                // on cherche la composante minimale
                comp := min9( read_pixel2(i-1, j-1, img2).red, read_pixel2(i-1, j, img2).red, read_pixel2(i-1, j+1, img2).red, read_pixel2(i, j-1, img2).red, read_pixel2(i, j, img2).red, read_pixel2(i, j+1, img2).red, read_pixel2(i+1, j-1, img2).red, read_pixel2(i+1, j, img2).red, read_pixel2(i+1, j+1, img2).red );
                write_pixel(i, j, img3, comp, comp, comp);
                end;
        // on copie de 3 vers 2
        for i:=0 to img3^.largeur Do
            for j:= 0 to img3^.hauteur Do
                write_pixel(i, j, img2, read_pixel(i, j, img3).red, read_pixel(i, j, img3).blue, read_pixel(i, j, img3).blue );
    
        end;
    
    erode:= img2;
end;



// 
// fonction erode_soft identique �erode sauf qu'elle ne prends en compte que les 4 pixels autour du pixel consid��// @param img : pointeur vers l'image source
// @param n : nombre de fois �appliquer la fonction sur l'image
// @return pointeur vers l'image obtenue
function erode_soft(img: pTimage; n:integer): pTimage;
var img2, img3: pTimage;
    i,j, k : integer;
    comp : byte;
begin
    img2 := new_image;
    img3 := new_image;
    img2^.hauteur := img^.hauteur;
    img2^.largeur := img^.largeur;
    img3^.hauteur := img^.hauteur;
    img3^.largeur := img^.largeur;
    
    // on copie l'image pass�en arguments pour ne pas la modifier
    for i:=0 to img^.largeur Do
        for j:= 0 to img^.hauteur Do
            write_pixel(i, j, img2, read_pixel(i, j, img).red, read_pixel(i, j, img).blue, read_pixel(i, j, img).blue );
    
    // on lit sur l'image 2 et on �rit sur l'image 3, ensuite on copie de 3 vers 2
    for k:=0 to n Do
        begin
        for i:=0 to img2^.largeur Do
            for j:= 0 to img2^.hauteur Do
                begin
                // on cherche la composante minimale parmi les 4 pixels autour du pixel consid��                comp := min9( 255, read_pixel2(i-1, j, img2).red, 255, read_pixel2(i, j-1, img2).red, read_pixel2(i, j, img2).red, read_pixel2(i, j+1, img2).red, 255, read_pixel2(i+1, j, img2).red, 255 );
                write_pixel(i, j, img3, comp, comp, comp);
                end;
        // on copie de 3 vers 2
        for i:=0 to img3^.largeur Do
            for j:= 0 to img3^.hauteur Do
                write_pixel(i, j, img2, read_pixel(i, j, img3).red, read_pixel(i, j, img3).blue, read_pixel(i, j, img3).blue );
    
        end;
    
    erode_soft:= img2;
end;




// 
// fonction dilate qui permet d'augmenter la taille des ��ents blancs
// @param img : pointeur vers l'image source
// @param n : nombre de fois �appliquer la fonction sur l'image
// @return pointeur vers l'image obtenue
function dilate(img: pTimage; n:integer): pTimage;
var img2, img3: pTimage;
    i,j, k : integer;
    comp : byte;
begin
    img2 := new_image;
    img3 := new_image;
    img2^.hauteur := img^.hauteur;
    img2^.largeur := img^.largeur;
    img3^.hauteur := img^.hauteur;
    img3^.largeur := img^.largeur;
    
    // on copie l'image pass�en arguments pour ne pas la modifier
    for i:=0 to img^.largeur Do
        for j:= 0 to img^.hauteur Do
            write_pixel(i, j, img2, read_pixel(i, j, img).red, read_pixel(i, j, img).blue, read_pixel(i, j, img).blue );
    
    // on lit sur l'image 2 et on �rit sur l'image 3, ensuite on copie de 3 vers 2
    for k:=0 to n Do
        begin
        for i:=0 to img2^.largeur Do
            for j:= 0 to img2^.hauteur Do
                begin
                // on cherche la composante maximale
                comp := max9( read_pixel(i-1, j-1, img2).red, read_pixel(i-1, j, img2).red, read_pixel(i-1, j+1, img2).red, read_pixel(i, j-1, img2).red, read_pixel(i, j, img2).red, read_pixel(i, j+1, img2).red, read_pixel(i+1, j-1, img2).red, read_pixel(i+1, j, img2).red, read_pixel(i+1, j+1, img2).red );
                write_pixel(i, j, img3, comp, comp, comp);
                end;
        // on copie de 3 vers 2
        for i:=0 to img3^.largeur Do
            for j:= 0 to img3^.hauteur Do
                write_pixel(i, j, img2, read_pixel(i, j, img3).red, read_pixel(i, j, img3).blue, read_pixel(i, j, img3).blue );
    
        end;
    
    dilate:= img2;
end;




// 
// procedure histogram, permet d'analyser les pixels d'une roi en comptant les pixels d'une meme couleur
// @param img : pointeur vers l'image source
// @param zone : roi
// @param histog : tableau destin��recevoir les donn�s
procedure histogram(img:pTimage;zone:roi;var histog:Thisto);
var i,j,l,taille,N:integer;
    k:byte;
begin
    // on initialise le tableau avec des 0
    for k:=0 to 255 do histog[k]:=0;

    // on parcours toute la zone de roi
    for i:=zone.x0 to zone.x1 do
        for j:=zone.y0 to zone.y1 do
        begin
            // on ajoute �l'index du tableau correspondant au pixel
            k:=read_pixel(i,j,img).red;
            histog[k]:=histog[k]+1;
          end;

    if(DEBUG) then 
    begin
        // on affiche les r�ultats obtenus
        N:=0;
        for k:=0 to 255 do N:=N+histog[k];
        for k:=0 to 255 do
        begin
            writeln;
            i:=k;
            write(i);
            taille:=histog[k] div 200;
            for l:=0 to taille do write('*');
        end;
    end;
end; // histogram 


              

// 
// fonction clip permettant de limiter les valeurs d'un integer entre 0 et 255
// @return integer born�function clip(n:integer):integer;
begin
    if n<0 then clip:=0 else if n>255 then clip:=255 else clip:=n;
end;


// 
// fonction cabs identique �clip mais renvoyant la valeur absolue dans le cas d'un integer n�atif
// @return integer born�function cabs(n:integer):integer;
begin
    if n<0 then n:=abs(n);
    if n>255 then n:=255;
    cabs:=n;
end;



// 
// fonction convol_image permettant d'appliquer un filtre sur une image
// @param img : pointeur vers l'image source
// @param zone : roi
// @param filter : type de filtre �appliquer
// @return pointeur vers l'image obtenue
function convol_image(img:pTimage; zone:roi; filter : filtres):pTimage;
var i,j,h,k:integer;
    compr,compg,compb:byte;
    intr,intg,intb:integer;
    img2:pTimage;
    mask:masque;
begin

     img2 := new_image;  
     img2^.hauteur := img^.hauteur;
     img2^.largeur := img^.largeur;
    
     // chargement du filtre

     case filter of

     // FILTRE FLOU
     filtre_flou :
        begin
             mask.matrix[1,1]:=1;
             mask.matrix[1,2]:=1;
             mask.matrix[1,3]:=1;
             mask.matrix[2,1]:=1;
             mask.matrix[2,2]:=1;
             mask.matrix[2,3]:=1;
             mask.matrix[3,1]:=1;
             mask.matrix[3,2]:=1;
             mask.matrix[3,3]:=1;
             mask.N:=9;
        end;


     // FILTRE NET
     filtre_net    :
             begin
        mask.matrix[1,1]:=-1;
             mask.matrix[1,2]:=-1;
             mask.matrix[1,3]:=-1;
        mask.matrix[2,1]:=-1;
        mask.matrix[2,2]:=17;
        mask.matrix[2,3]:=-1;
        mask.matrix[3,1]:=-1;
        mask.matrix[3,2]:=-1;
        mask.matrix[3,3]:=-1;
        mask.N:=9;
        end;

     // FILTRE CONTOURX
     filtre_contourx    :
             begin
        mask.matrix[1,1]:=1;
        mask.matrix[1,2]:=0;
        mask.matrix[1,3]:=-1;
        mask.matrix[2,1]:=1;
        mask.matrix[2,2]:=0;
        mask.matrix[2,3]:=-1;
        mask.matrix[3,1]:=1;
        mask.matrix[3,2]:=0;
        mask.matrix[3,3]:=-1;
        mask.N:=1;
        end;

     // FILTRE CONTOURY
     filtre_contoury    :
             begin
        mask.matrix[1,1]:=1;
        mask.matrix[1,2]:=1;
        mask.matrix[1,3]:=1;
        mask.matrix[2,1]:=0;
        mask.matrix[2,2]:=0;
        mask.matrix[2,3]:=0;
        mask.matrix[3,1]:=-1;
        mask.matrix[3,2]:=-1;
        mask.matrix[3,3]:=-1;
        mask.N:=1;
        end;

    end;

    // DEBUG, la zone de roi est en fait toute l'image
    zone.x0:=0;
    zone.y0:=0;
    zone.x1:=img^.largeur;
    zone.y1:=img^.hauteur;
     
     
     for i:=zone.x0 to zone.x1 Do
     begin
         for j:= zone.y0 to zone.y1 Do
         begin
             intr:=0;
             intg:=0;
             intb:=0;

             for h:=1 to 3 do
             begin
                 for k:=1 to 3 do
                 begin
                      intr := intr + (read_pixel(i-2+k,j-2+h,img).red * mask.matrix[h,k]);
                      intg := intg + (read_pixel(i-2+k,j-2+h,img).green * mask.matrix[h,k]);
                      intb := intb + (read_pixel(i-2+k,j-2+h,img).blue * mask.matrix[h,k]);
                 end;
             end;

             intr := intr DIV mask.N;
             intg := intg DIV mask.N;
             intb := intb DIV mask.N;

             compr:=cabs(intr);
             compg:=cabs(intg);
             compb:=cabs(intb);            

             write_pixel(i,j,img2,compr,compg,compb);

         end;
     end;

     convol_image := img2;
end;





// 
// fonction applique_lut permettant d'appliquer un traitement aux pixels
// @param img : pointeur vers l'image source
// @param fonc : type de traitement �appliquer
// @param arg1, arg2 : argument integer utilis�pour les diff�ents filtres (parfois inutilis�)
// @return pointeur vers l'image obtenue
function applique_lut(img: pTimage; fonc: fonctions; arg1, arg2: byte) : pTimage;
var img2: pTimage;
    i,j : integer;
    comp, tmp : byte;
begin
img2 := new_image;
img2^.hauteur := img^.hauteur;
img2^.largeur := img^.largeur;

for i:=0 to img^.largeur Do
     for j:= 0 to img^.hauteur Do
         begin

         comp := read_pixel(i,j,img).red;

         // on applique le bon traitement suivant le param�re fonc
         case fonc of
              assombrir:if comp <= arg1 then
                   write_pixel(i,j,img2, 0, 0, 0)
                else write_pixel(i,j,img2, comp - arg1, comp - arg1, comp - arg1);

              eclaircir:if comp >= arg1 then
                   write_pixel(i,j,img2, 255, 255, 255)
                else write_pixel(i,j,img2, comp + arg1, comp + arg1, comp + arg1);

              seuillage:if comp <= arg1 then
                   write_pixel(i,j,img2, 0, 0, 0)
                else write_pixel(i,j,img2, 255, 255, 255);

              puit:if (comp <= arg1) or (comp >= arg2) then
                      write_pixel(i,j,img2, 255, 255, 255)
                else write_pixel(i,j,img2, 0, 0, 0);

              negatif:write_pixel(i,j,img2, 255-comp, 255-comp, 255-comp);

              recadrage:begin
                        tmp := (255 DIV (arg2-arg1) ) * comp - 255 * arg1 DIV (arg2-arg1);
                        if tmp < 0 then tmp := 0;
                        if tmp > 255 then tmp := 255;
                        write_pixel(i,j,img2, comp, comp, comp);
                        end
         end;
    end;



applique_lut := img2;
end;




// 
// fonction detection_contour qui permet de d�ecter les contour d'une image en couleur
// @param img : pointeur vers l'image source
// @param seuil : valeur du seuil �appliquer pr l'obtention de l'image en noir et blanc par seuillage
// @return pointeur vers l'image obtenue
function detection_contour(img: pTimage; seuil: integer) : pTimage;
var img2, img3, img4 : pTimage;
begin
    img2 := grey_level(img);
    img2 := applique_lut(img2, seuillage, seuil, 0);

    img3 := convol_image(img2, zone, filtre_contourx);
    img4 := convol_image(img2, zone, filtre_contoury);

    img2 := somme_image(img3,img4);

    delete_image(img3);
    delete_image(img4);

    detection_contour := img2;
end;



Begin
End.
