#include <stdlib.h>
#include <math.h>

extern "C"
{
#include "image.h"
#include "grille.h"
#include "canal.h"
#include "r.h"
#include "c.h"
#include "contour.h"
#include "dev.h"
//#include "affimage.h"
}


//*****************************************************************************
// Lissage de l'image par moyenne
// w=rayon du carré de calcu de la moyenne (soit un carré de 2*n+1)
// n=nb d'itération
//*****************************************************************************
static Image ConvolMoyenne2R_cg(Image imS,Image imD,int w,int n){

  int i,j,k,l,m,i1,i2,j1,j2,x,y;
  Reel s;
  Image imI ;
  Canal canL ;
  Reel **grilleI ;
  Reel **grilleD ;


  if ((imD=CreeImageDest(imS,imD,T_REEL))==NULL) return(NULL);
  LitTailleFenetre(imS,&x,&y) ;
  if ((grilleD=(Reel**)CreeGrille(imD,Z_FENETRE))==NULL) return (NULL);
  if ((canL=CreeCanal(imS,T_REEL,B_LIGNE,Z_FENETRE))==NULL) return (NULL);
  if ((imI=CreeImageDest(imS,NULL,T_REEL))==NULL) return(NULL);
  if ((grilleI=(Reel**)CreeGrille(imI,Z_FENETRE))==NULL) return (NULL);

  // Initialisation des images
  for(i=0;i<y;i++){
    LitCanal(canL,grilleD[i]);
  }

  for(m=0;m<n;m++){
    // GrilleI <- GrilleD
    for(i=0;i<y;i++)
      for(j=0;j<x;j++)
        grilleI[i][j]=grilleD[i][j] ;
    // Lissage par moyenne sur les lignes
    for(i=0;i<y;i++){
      for(j=0;j<x;j++){
        j1=max(j-w,0); j2=min(j+w+1,x); s=0;
          for(l=j1;l<j2;l++)
            s+=grilleI[i][l] ;
        grilleD[i][j]=s/(j2-j1) ;
      }
    }
    // Lissage par moyenne sur les colonnes
    for(i=0;i<y;i++){
      i1=max(i-w,0); i2=min(i+w+1,y);
      for(j=0;j<x;j++){
        s=0;
        for(k=i1;k<i2;k++)
            s+=grilleI[k][j] ;
        grilleD[i][j]=s/(i2-i1) ;
      }
    }
  }

  LibereGrille(grilleI);
  LibereImage(imI);
  LibereCanal(canL);
  LibereGrille(grilleD);
  return(imD);
}


static Image SupprimeMin4R(Image imaS,Image imaD)
//****************************************************************************
// Suppression de minimums locaux (sur 1 ou 2 pixels de large)
// Utile pour la segmentation (séparation des particules)
//****************************************************************************
{
  int i,j,w,h ;
  Reel sn ;
  Image imaI=NULL;
  Canal canL;
  Reel **grilleI;
  Reel **grilleD;


  if ((imaD=CreeImageDest(imaS,imaD,T_REEL))==NULL) return(NULL);
  LitTailleFenetre(imaS,&w,&h) ;
  if ((grilleD=(Reel**)CreeGrille(imaD,Z_FENETRE))==NULL) return (NULL);
  if ((canL=CreeCanal(imaS,T_REEL,B_LIGNE,Z_FENETRE))==NULL) return (NULL);
  if ((imaI=CreeImageDest(imaS,imaI,T_REEL))==NULL) return(NULL);
  if ((grilleI=(Reel**)CreeGrille(imaI,Z_FENETRE))==NULL) return (NULL);

  // Initialisation des images
  for(i=0;i<h;i++){
    LitCanal(canL,grilleD[i]);
    for(j=0;j<w;j++)
      grilleI[i][j]=grilleD[i][j] ;
  }

  // Supression des minimums locaux de taille 2
  for(i=1;i<(h-2);i++){
    for(j=1;j<(w-2);j++){
       sn=min(grilleI[i-1][j-1],min(grilleI[i][j-1],min(grilleI[i+1][j-1],grilleI[i+2][j-1])));
       sn=min(grilleI[i-1][j],min(grilleI[i+2][j],min(grilleI[i-1][j+1],sn)));
       sn=min(grilleI[i+2][j+1],min(grilleI[i-1][j+2],min(grilleI[i][j+2],sn)));
       sn=min(grilleI[i+1][j+2],min(grilleI[i+2][j+2],sn));
       if (grilleI[i][j]<sn) grilleD[i][j]=sn ;
       if (grilleI[i+1][j]<sn) grilleD[i+1][j]=sn ;
       if (grilleI[i][j+1]<sn) grilleD[i][j+1]=sn ;
       if (grilleI[i+1][j+1]<sn) grilleD[i+1][j+1]=sn ;
    }
  }

  // Suppression des minimums locaux de taille 1
  for(i=1;i<(h-1);i++){
    for(j=1;j<(w-1);j++){
       sn=min(grilleI[i-1][j-1],min(grilleI[i][j-1],grilleI[i+1][j-1]));
       sn=min(grilleI[i-1][j],min(grilleI[i+1][j],sn));
       sn=min(grilleI[i-1][j+1],min(grilleI[i][j+1],min(grilleI[i+1][j+1],sn)));
       if (grilleI[i][j]<sn) grilleD[i][j]=sn ;
    }
  }

  LibereGrille(grilleI);
  LibereImage(imaI);
  LibereCanal(canL);
  LibereGrille(grilleD);
  return(imaD);
}



//*****************************************************************************
// Sépare les particules par érosion successives
// paramètres: imS1: image binaire
//             maxi: nb maxi d'érosion/amincicement...
//             concav: concavité minimum acceptable
// si maxi=0 poursuite jusqu'à stabilisation
// Correction bug particules rognées
//*****************************************************************************
Image Separe3R(Image imS1,Image imD,float concav,int maxi){

  int i,w,h ;
  Image imT,imT0,imT1,imT2/*,imT3=NULL*/ ;
  Image imT5,imT6,imT7 ;
  Image imT8,imT9,imT10;

  if (imS1==NULL) return(NULL);
  LitTailleFenetre(imS1,&w,&h) ;
  imT=SeuilleSupR(imS1,NULL,0,1);
  if (maxi<=0) maxi=max(w,h);

    // Erosions et création de la carte d'érosion
    imT7=CreeContoursR(imT,NULL,0) ;
    imT8=CarteConcaviteR(imT7,NULL) ;
    //AfficheImage(imT8);
    //Dialogue("Carte concavité");

    imT0=TransfereR(imT,NULL) ;
    imT1=TransfereR(imT,NULL) ;
    for(i=0;i<maxi;i++){
      imT1=ErodeCarreR(imT1,imT1) ;
      imT0=AddR(imT0,imT1,imT0) ;
    }
    imT0=ConvolMoyenne2R_cg(imT0,imT0,1,1) ;
    //AfficheImage(imT0);
    //Dialogue("Carte érosion");
    imT2=DilateCarreR(imT,NULL) ;
    imT2=DilateCarreR(imT2,imT2) ;
//    imT3=TransfereE(imT2,NULL) ;
    if (!TestImageConstR(imT0)) imT0=EtaleR(imT0,imT0,32*maxi,0) ;
    //AfficheImage(imT0);
    //Dialogue("Carte érosion inversée");

    // On Epaissit les sommets des particules
    imT5=SupprimeMin4R(imT0,NULL) ;
    imT5=AmincitCondR(imT5,imT2,imT5,3) ;
    imT5=SupprimeMin4R(imT5,imT5) ;
    imT5=EbarbuleR(imT5,imT5,6) ;
    imT5=AmincitCondR(imT5,imT2,imT5,maxi) ;
    imT5=SupprimeMin4R(imT5,imT5) ;
    imT5=EbarbuleR(imT5,imT5,maxi) ;
    imT5=AmincitCondR(imT5,imT2,imT5,maxi) ;
    imT5=SupprimeMin4R(imT5,imT5) ;
    imT5=EbarbuleR(imT5,imT5,maxi) ;
    imT5=AmincitCondR(imT5,imT2,imT5,maxi) ;
    imT5=SupprimeMin4R(imT5,imT5) ;
    imT5=EbarbuleR(imT5,imT5,maxi) ;
    imT5=SupprimeMin4R(imT5,imT5) ;
    //AfficheImage(imT5);
    //Dialogue("Epaissisement sommets");

    // On repère les frontières des zones d'influences
    imT6=ErodeCroixR(imT5,NULL);
    SoustrR(imT5,imT6,imT6);
//    imT6=ConvolLaplaceR(imT5,NULL) ;
    //AfficheImage(imT6);
    //Dialogue("Convol laplace");
//    imT6=SeuilleInfR(imT6,imT6,0,0) ;
    imT6=SeuilleSupR(imT6,imT6,0,1) ;
    imT6=MultR(imT,imT6,imT6) ;
    //AfficheImage(imT6);
    //Dialogue("Epaissicement seuillé masqué");

    imT8=SeuilleInfR(imT8,imT8,1+10*concav,0) ;
    imT8=SeuilleSupR(imT8,imT8,1+10*concav,1) ;
    //AfficheImage(imT8);
    //Dialogue("Carte concavité seuillée");
    for(i=0;i<max(maxi,16);i++){
      imT8=DilateCarreR(imT8,imT8) ;
      imT8=MultR(imT8,imT6,imT8) ;
    }
    //AfficheImage(imT8);
    //Dialogue("Carte concavité dilatée cond avec épaissicement");

    imT9=SoustrR(imT,imT8,NULL);
    //AfficheImage(imT9);
    //Dialogue("On soustrait au résultat");
    if (!TestImageConstR(imT9)){
      imT9=EtaleR(imT9,imT9,1,0) ;
      imT10=EbarbuleR(imT9,NULL,max(maxi,20)) ;
      imD=EtaleR(imT10,imD,1,0) ;
//      imD=SeuilleSupR(imD,imD,0,1);
      imD=MultR(imS1,imD,imD);
    } else {
      imD=TransfereR(imT9,imD);
//      imD=SeuilleSupR(imD,imD,0,1);
      imD=MultR(imS1,imD,imD);
      }

    LibereImage(imT10) ;
    LibereImage(imT9) ;
    LibereImage(imT6) ;
    LibereImage(imT5) ;
//    LibereImage(imT3) ;
    LibereImage(imT2) ;
    LibereImage(imT1) ;
    LibereImage(imT0) ;
    LibereImage(imT8) ;
    LibereImage(imT7) ;
    LibereImage(imT) ;

  return imD ;
}


//*****************************************************************************
// Sépare les particules par érosion successives, puis Epaississements
// contrôlés par niveaux, en sélectionnant les particules présentant une concavité
// paramètres: imS1: image binaire
//             imS2: image en niveau de gris
//             maxi: nb maxi d'érosion/amincicement...
// Correction bug particules rognées
//*****************************************************************************
Image SepareSelect3R(Image imS1,Image imD,float concav,int maxi){


  int i,ox,oy,w,h,dim ;
  float cv;
  Liste source,s2,dest,c;
  Image imC,imT0,imT1;
  Fenetre fen ;
  //int rien=1 ;
  //char *m ;


  if (imS1==NULL) return(NULL);
  LitFenetre(imS1,&ox,&oy,&w,&h) ;

  imD=CreeContoursR(imS1,imD,0);
  if ((imC=imD->suivant)==NULL) return (NULL);
  if (CreeListe(imC,&source)) return(NULL);
  dim=source.Dimension();

//  imD=TransfereR(imS1,imD) ;
  for(i=0;i<dim;i++){
     Lire(&source,&c) ;
     cv=Concavite(&c) ;
     if (Niveau(&c)>0){
       if ((cv>=0.0)&&(cv<concav)){
         Ecrire(&c,&dest);// rien=0;
       }else {
         // Cas ou la particule a une assez forte concavite locale:
         // on essaye une separation morphologique
         imT0=TraceImagetteContR(NULL,&c,0) ;
         FenetreCirc(&c,&fen) ;
         EcritFenetre(imD,fen.origine.x-1,fen.origine.y-1,fen.taille.x+2,fen.taille.y+2) ;
         imD=SoustrR(imD,imT0,imD) ;
         imT1=Separe3R(imT0,NULL,concav/2,maxi) ;
         imD=AddR(imD,imT1,imD) ;
         LibereImage(imT1) ;
         LibereImage(imT0) ;
       }
     }
  }

  EcritFenetre(imD,ox,oy,w,h) ;
  LibereImage(SupprimeImageL(&imD,1));
  return(imD) ;
}


//Image Grad3x3C(Image imS,Image imD)
/*********************************/
/*                                                  |-101|
   Gradient calculé à partir de 2 masques 3x3 : 1/8 |-202|
                                                    |-101|
   Le résultat est mis dans une image complexe.
*/
/*{
Image imT0;
Image imT1;
imT0=ConvolGradHorR(imS,NULL);
imT1=ConvolGradVerR(imS,NULL);
ConstMultR(imT0,imT0,1./8);
ConstMultR(imT1,imT1,-1./8);
imD=TransfereXYC(imT0,imT1,imD);
LibereImage(imT0);
LibereImage(imT1);
return(imD);
} */

Image SeuilHysteresisR(Image imS,Image imD,float sHaut, float sBas)
/****************************************************************/
/* Seuillage hystérésis : on garde tous le points de niveau > sBas
  qui appartiennent à un objet ayant au moins un point de niveau > sHaut
  On considère des voisinages V8
*/
{
Image imT;
imT=SeuilleInfR(imS,NULL,sHaut,0);
SeuilleSupR(imT,imT,sHaut,1);
imD=SeuilParGermesV8R(imS,imT,imD,sBas,1);
LibereImage(imT);
return(imD);
}

//*****************************************************************************
// Efface les bords de l'image (particules qui touchent le bord)
// Propagation du germe sur un V8
//*****************************************************************************
Image EffaceBordV8R(Image imaS,Image imaD)
{

  int w,h ;

  if (imaS==NULL) return(NULL);
  LitTailleFenetre(imaS,&w,&h) ;
  if (TestImageConstR(imaS)) return (imaD=TransfereR(imaS,imaD)) ;
//  imaD=EtaleR(imaS,imaD,1,0);
  EcritFenetre(imaD,0,0,w,1);
  imaD=NiveauR(imaD,imaD,1);
  EcritFenetre(imaD,0,0,1,h);
  imaD=NiveauR(imaD,imaD,1);
  EcritFenetre(imaD,0,h-1,w,1);
  imaD=NiveauR(imaD,imaD,1);
  EcritFenetre(imaD,w-1,0,1,h);
  imaD=NiveauR(imaD,imaD,1);
  EcritFenetre(imaD,0,0,w,h);
  imaD=RempliRegionV8R(imaD,imaD,0,0,0,0);
//  if (!TestImageConstR(imaD)) imaD=EtaleR(imaD,imaD,1,0);
  return (imaD) ;
}

//Image SegParGradient3x3R(Image imS,Image imD,float sHaut,float sBas,int remplis)
/******************************************************************************/
/* Segmentation à base de gradient calculé par masque 3x3.
  Convient pour des images présentant des objets pas trop petits ayant des contours
  assez bien marqués.
  On effectue un seuillage par hystérésis, une fermeture des contours, un
  ébarbulage, et éventuellement une suppression des objets touchant le bord et
  un remplissage.
  sHaut et sBas : seuils haut et bas du seuillage hystérésis
  remplis : booléen 1/0 indiquant s'il y a ou non suppression des objets touchant
   les bords et remplissage.
*/
/*{
Image imT0;
Image imT1;
Image imT2;
imT1=Grad3x3C(imS,NULL);
imT0=MaxModInterpolC(imT1,NULL);
imT2=ModuleR(imT0,NULL);
LibereImage(imT0);
imD=SeuilHysteresisR(imT2,NULL,sHaut,sBas);
LibereImage(imT2);
AmincitV8R(imD,imD,0);
FermeContR(imD,imT1,imD,2,2);
LibereImage(imT1);
AmincitV8R(imD,imD,0);
EbarbuleV8R(imD,imD);
if (remplis)
  {
  EffaceBordV8R(imD,imD);
  CreeContoursR(imD,imD,0);
  FiltreNiveauR(imD,imD,1,2);
  LibereImage(SupprimeImageL(&imD,1));
  }
return(imD);
} */

