// Time-stamp: <03.06.1999, 19:07:25, mrz@isun34>


/**
 * (C) 1999 René Scholz, <www.thur.de/~Voland/>
 *
 *
 * Bestimmt die Bild-Differenz zweier PGM5-Bilder
 * und gibt diesen als RMSE-Wert und PSNR-Wert aus.
 *
 * Außerdem kann das Differenzbild (ABS der Grauwerte der entsprechenden
 * Pixel) in eine Datei geschrieben werden.
 *
 * Es wird zusätzlich noch die Entropie der Bilder berechnet, allerdings
 * nur 1dim (nach der Def. der Entropie = -\sum p_i ld(p_i) )
 *
 * Kommentare nach "P5" sind möglich.



   @author <A HREF="http://www.thur.de/~Voland/">René Scholz</A>.
   @version 1.0
*/


import java.text.*;
import java.util.*;
import java.io.*;


class pgmdiff {

  final static String   VERSION="1.0";
  static boolean        Debug=false;
  static String         Pic1=null;
  static String         Pic2=null;
  static String         DiffPic=null;

  /** Used for nicer printing of numerical output. */
  static NumberFormat form=NumberFormat.getNumberInstance();


  public static void main (String args[]) {

    form.setMaximumFractionDigits(4);
    form.setMinimumFractionDigits(4);
    form.setGroupingUsed(false);

    scan_options(args);

    PicData P1=new PicData(Pic1);  P1.Dim=1; 
    PicData P2=new PicData(Pic2);  P2.Dim=1; 
    PicData DP=null;

    System.out.println("Reading pictures ...");
    pgm.read(P1); pgm.read(P2);

    if(P1.X!=P2.X || P1.Y!=P2.Y) {
      System.out.println("ERROR: The pictures have not the same dimensions!");
      System.exit(-1);
    }

    if(DiffPic!=null) {
      DP=new PicData(DiffPic);
      DP.X=P1.X; DP.Y=P1.Y;
      DP.Dim=1;
      DP.data1D=new byte[DP.X*DP.Y];
      DP.Comment="# difference picture of the pictures:\n#\t" + P1.Name +
	"\n#\t" + P2.Name + "\n";
    }

    System.out.println("Calculating ...");
    calc_psnr(P1.data1D, P2.data1D, DP);

    if(DiffPic!=null) pgm.write(DP);

  }





  /**
   * Methode zum Berechnen der PSNR zweier gleichgroßer Graustufenbilder
     (PGM5).
   *
   * @param ORG    Originalbild
   * @param OTHER  Das andere (entpackte) Bild
   * @param dp     Das Differenzbild (null falls nicht benutzt)
   * @return       Die PSNR beider Bilder zueinander.<BR>
                   (PSNR=Peak Signal Noise Ratio)

   Author: René Scholz
  */
  public static float calc_psnr(byte[] ORG, byte[] OTHER, PicData dp) {
    long sum=0;  double rmse=0; double psnr=0;
    int  pixels=ORG.length;
    final double M=0.43429448190325182765d;  // Math.log ist ln != log_10
    int Max_Gray_Value=0;
    int Hellster_Pixel=0;
    int A; int B;

    int t;
    if(dp==null)
      for(int i=0; i<pixels; i++) {
	t=ORG[i] - OTHER[i];
	sum+=t*t;
      }
    else {
      for(int i=0; i<pixels; i++) {
	A=ORG[i]; B=OTHER[i];
	t=(A>=B) ? A-B : B-A;
	/*
	  // Code needed for calculating entrophy:
	  if(A>Max_Gray_Value) Max_Gray_Value=A;
	  if(B>Max_Gray_Value) Max_Gray_Value=B;
	*/
	sum+=t*t;
	if(t>Hellster_Pixel) Hellster_Pixel=t;
	dp.data1D[i]=(byte)(t-128);
      }
      if(Hellster_Pixel<=0)   Hellster_Pixel=1;
      if(Hellster_Pixel>255)  Hellster_Pixel=255;
      dp.Colors=Hellster_Pixel;
    }

    rmse=Math.sqrt((double)sum / (double)pixels);
    if(rmse>0) psnr=20.0d*M*Math.log(255.0d / rmse);
    else psnr=0;

    System.out.println("\nRMSE: " + form.format(rmse));
    if(psnr!=0)
      System.out.println("PSNR: "   + form.format(psnr));
    else
      System.out.println("PSNR: Inf");
    
    return((float)psnr);
  }






  /** Shows the possible options for the command line. */
  static void show_options () {

    System.out.println("\nVersion: " +VERSION);
    System.out.println("Usage:   java pgmdiff -p1 picture1 -p2 picture2  [Optionen]\n");
    System.out.println("Options:\n");
    System.out.println("  -h | --help     Show this help.");

    System.out.println("  -p1 <pic1>      Read picture 1 from file <pic1>.");
    System.out.println("  -p2 <pic2>      Read picture 2 from file <pic2>.");
    System.out.println("  [-dp diffpic]   Write the difference picture <diffpic> (as PGM5).");
    //System.out.println("-debug       Anzeige von Debug-Informationen.");
  }



  /**
     Reads the command line input from the user and checks for
     bad parameter constallations.

     @param <TT>args[]</TT> The arguments from the command line.
  */
  static void scan_options (String args[]) {
    String s; int L=args.length;
    if(L==0) { show_options(); System.exit(1); }

    //System.out.println("args.length="+L);
    for(int i=0; i<L; i++) {

      //System.out.println("ARG["+i+"]="+args[i]);
      s=args[i];
      //System.out.println("ARG["+i+"]="+s);
      if(s.compareTo("-debug")==0) { Debug=true; continue; }

      if(s.compareTo("-h")==0 || s.compareTo("--help")==0)
        { show_options(); System.exit(0); }


      if(s.compareTo("-p1")==0) {
        if(++i<L) Pic1=args[i];
        else {
          System.err.println("ERROR: Filename of picture 1 missing!");
          show_options(); System.exit(1);
        }
        continue;
      }

      if(s.compareTo("-p2")==0) {
        if(++i<L) Pic2=args[i];
        else {
          System.err.println("ERROR: Filename of picture 2 missing!");
          show_options(); System.exit(1);
        }
        continue;
      }

      if(s.compareTo("-dp")==0) {
        if(++i<L) DiffPic=args[i];
        else {
          System.err.println("ERROR: Filename of diffpic missing!");
          show_options(); System.exit(1);
        }
        continue;
      }


      // else it's a wrong option:
      System.err.println("\nERROR: wrong option "+s+" !\n\n");
      show_options(); System.exit(1);
    }


    // Nachbearbeitungen:
    if(Pic1==null) {
      System.err.println("ERROR: Filename of picture 1 missing!");
      show_options(); System.exit(1);
    }

    if(Pic2==null) {
      System.err.println("ERROR: Filename of picture 2 missing!");
      show_options(); System.exit(1);
    }


  }



}