// Traitement numérique du signal
// Projet 2
//
// Binarisation d'image par diffusion d'erreur
//
// Christophe Boyanique
// Emmanuel Pinard
// Mars 1999
//
//
// Calcul du rapport signal/bruit
// Transformation d'une image 256 niveaux de gris (pgm) en image
// monochrome (pbm)


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


// Definition de macros:
#define min(a,b)     (a<b)?(a):(b)
#define abs(a)       (a<0)?(-a):(a)


// Definitions des codes de retour du programme:
#define NO_ERR       0
#define BAD_PARAMS   1
#define NO_FILE      2
#define BAD_FILE     3
#define READ_ERROR   4
#define WRITE_ERROR  5

#define NOHEAD       0
#define HEADER       1

#define RAW          0
#define FLOYD        1


// Prototypage des fonctions:
void usage(char *name);
int calcul(char *input, char *output);
int read_number(FILE *handle);



// Fonction principale du programme
//
int main(int argc, char **argv)
{

  if (argc!=3)
  {
    usage(argv[0]);
    return BAD_PARAMS;
  }

  return calcul(argv[1], argv[2]);

}



// Fonction usage() affichant la syntaxe du programme
//
void usage(char *name)
{
  printf("Usage: %s input.pgm output.pbm\n", name);

}


// Calcul du rapport signal/bruit
//
// Les paramètres sont:
// char *input:   nom du fichier d'entrée
// char *output:  nom du fichier de sortie
//
int calcul(char *input, char *output)
{
  FILE *hin, *hout;      // Handles de fichier
  char buf[80];          // Buffer de lecture
  int err;               // Retour d'erreur de fonctions
  int width, height;     // Dimensions de l'image
  int vpbm, vpgm;        // Valeur d'un pixel
  long cnt;              // Compteur de pixel
  int i, j;              // Indice de boucle for
  double xsb=0,ysb=0,sb; // Rapport signal bruit

  printf("Calcul du rapport signal/bruit\n");

  printf("Opening file %s for reading... ", input);
  hin = fopen(input, "r");        // Ouverture du fichier en lecture
  if (hin == NULL)
  {
    printf("error!\n");
    return NO_FILE;
  }
  printf("Ok\n");

  printf("File type is: ");       // Détermination du type de fichier
  err = (int)fread((void *)buf, (size_t)1, (size_t)2, hin);
  if (err != 2 || buf[0]!='P' || buf[1]!='2')
  {
    printf("unknown!\n");
    fclose(hin);
    return BAD_FILE;
  }
  printf("ASCII PGM (P2)\n");

  printf("Image size is: ");      // Détermination de la taille de l'image
  width = read_number(hin);
  if (width<=0)
  {
    printf("error!\n");
    fclose(hin);
    return BAD_FILE;
  }
  height = read_number(hin);
  if (height<=0)
  {
    printf("error!\n");
    fclose(hin);
    return BAD_FILE;
  }
  printf("%dx%d\n", width, height);

  printf("Grayscale level: ");    // Détermination de la résolution de l'image
  if ( read_number(hin) != 255)
  {
    printf("not 256!\n");
    fclose(hin);
    return BAD_FILE;
  }
  printf("256\n");


  printf("Opening file %s for reading... ", output);
  hout = fopen(output, "r");        // Ouverture du fichier en lecture
  if (hout == NULL)
  {
    printf("error!\n");
    return NO_FILE;
  }
  printf("Ok\n");

  printf("File type is: ");       // Détermination du type de fichier
  err = (int)fread((void *)buf, (size_t)1, (size_t)2, hout);
  if (err != 2 || buf[0]!='P' || buf[1]!='1')
  {
    printf("unknown!\n");
    fclose(hout);
    fclose(hin);
    return BAD_FILE;
  }
  printf("ASCII PGM (P1)\n");

  printf("Image size is: ");      // Détermination de la taille de l'image
  err = read_number(hout);
  if (err != width)
  {
    printf("error!\n");
    fclose(hout);
    fclose(hin);
    return BAD_FILE;
  }
  err = read_number(hout);
  if (err != height)
  {
    printf("error!\n");
    fclose(hout);
    fclose(hin);
    return BAD_FILE;
  }
  printf("%dx%d\n", width, height);


  cnt = 0L;                       // Initialise le compteur de pixel

  for (j=0; j<height; j++)        // Balayage par ligne
  {

    for (i=0; i<width; i++)       // Balayage par colonne
    {
      vpgm = read_number(hin);    // Lit la valeur du pixel
      vpbm = read_number(hout);   // Lit la valeur du pixel
      if (vpgm<0 || vpbm<0)
      {
        printf("\nread error!\n");
        fclose(hout);
        fclose(hin);
        return READ_ERROR;
      }

      // Calcul de x numerateur du rapport signal/bruit
      xsb += vpgm*vpgm;

      // Calcul de y numerateur du rapport signal/bruit
      ysb += (vpgm - (255*(1-vpbm)))*(vpgm - (255*(1-vpbm)));

      cnt++;                          // Incrémente le compteur
    }

  }

  printf("Done\n");

  sb  = 10 * log10( xsb / ysb );
  printf("Rapport Signal/Bruit: %.2f\n", sb);

  fclose(hout);                   // Fermeture des fichiers
  fclose(hin);

  return NO_ERR;
}



// Fonction de lecture d'un nombre dans un fichier
//
int read_number(FILE *handle)
{
  int n = 0;                      // Nombre à renvoyer
  int ok = 0;                     // Drapeau d'erreur
  unsigned char c;                // Charactere lu dans le fichier

  // Première boucle destinée à purger tous les caractères inutiles
  // (fin de ligne, espace, commentaires...)
  do
  {
    fread((void *)&c, (size_t)1, (size_t)1, handle);
    if ( c == '#' )               // Cas d'un commentaire:
    {                             // il faut de nouveau boucler jusqu'à
      do                          // la fin de la ligne
      {
        fread((void *)&c, (size_t)1, (size_t)1, handle);
      } while ( c!='\n' && !feof(handle) );
    }
  } while ( ( c<'0' || c>'9') && !feof(handle) );

  // Deuxième boucle lisant le nombre une fois que l'on est bien
  // positionné dans le fichier
  while ( ( c>='0' && c<='9') && !feof(handle) )
  {
    ok = 1;
    n = n*10 + (int)c - (int)'0';
    fread((void *)&c, (size_t)1, (size_t)1, handle);
  }

  if (ok == 1)
    return n;

  return -1;
}


