/*  PGM Image Cropper v1.1.0 Ver.: OK (c) 10/09 - Christos Iosifides */
/*
   Name:			PGM_Cropper
   Version:		    1.1.0
   Copyright:	    Ch Iossif @ 2009
   Author:		    Christos Iosifidis
   Date:			08/10/09 12:55
   Modified:		30/03/10 21:10
   Description:     PGM Image Cropper
   Usage:           $PGM_Cropper ImageIn.pgm win_width win_height offsetX offsetY ImageOut.pgm

   Copyright (C) June 2009 Ch Iossif <chiossif@yahoo.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

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

void error(const char *);
void warning(const char *);
int swap(short int);

int main(int argc,char **argv) {
    FILE *fp;
    int PGM_mode, PGM_row, PGM_col, PGM_scale, ch;
    int frow, fcol, offx, offy, k, mini, maxi, histo[65536], d, med, sum;
    double s, ss;
    char buff[BUFSIZ];
    unsigned short *image;
    register int r, c;

    if (argc!=7) {
        sprintf(buff,"Bad Usage.\n\nPGM_Cropper Usage:\n$PGM_Cropper ImageIn.pgm win_width win_height offsetX offsetY ImageOut.pgm\n\nPGM_Cropper by Ch Iossif (c) Oct 2009");
        error(buff);
    }

    if ((fp=fopen(argv[1],"rb"))==NULL) {
        sprintf(buff,"Bad input file name: <%s>", argv[1]);
        error(buff);
    }

//  Reading PGM header info - Checking for correct file version
    if (getc(fp)!='P')
        error("This file is not a PGM file.\n");
    if ((ch=getc(fp))==EOF)
        error("Bad file read");
    PGM_mode=ch-'0';
    if (PGM_mode!=5)
        error("This file is not a PGM5 file");
    do {
        if (fgets(buff,BUFSIZ,fp)!=buff)
            error("Bad read at filename.");
    } while (buff[0]=='#' || buff[0]<' ');
    sscanf(buff, "%d %d", &PGM_col, &PGM_row);
    do {
        if (fgets(buff,BUFSIZ,fp)!=buff)
            error("Bad read at filename.");
    } while (buff[0]=='#' || buff[0]<' ');
    sscanf(buff, "%d", &PGM_scale);

//  Check image dimensions and allocate memory
    if (PGM_row<0)
        error("Bad rows value");
    if (PGM_col<0)
        error("Bad columns value");
    if ((image=malloc(PGM_row*PGM_col*sizeof(short*)))==NULL)
        error("Not enough memory avaliable");

//  Read image and close file
    if (fread(image, sizeof(short), PGM_row*PGM_col, fp)!=PGM_row*PGM_col)
        error("End Of File encountered");
    fclose(fp);

// Check command line arguments
    fcol=atoi(argv[2]);
    frow=atoi(argv[3]);
    offx=atoi(argv[4]);
    offy=atoi(argv[5]);

    if (offx<0 || offy<0 || frow<1 || fcol<1 || offx+fcol>PGM_col || offy+frow>PGM_row) {
        sprintf(buff,"Bad arguments.\nInput image size is: %4d %4d.\nOutput image size is:%4d %4d.\nStarting pixel at x:%4d y:%4d\nEnding pixel at x:%4d y:%4d\n",PGM_row, PGM_col, frow, fcol, offx, offy, offx+fcol, offy+frow);
        error(buff);
    }

//  Initialize statistics
    k=s=ss=0;
    mini=65536;
    maxi=0;
    for (r=0;r<65536;r++)
        histo[r]=0;

//  Calculate statistics
    for (r=0;r<frow;r++) {
        for (c=0;c<fcol;c++) {
            if ((d=swap(image[(r+offy)*PGM_col+c+offx]))<=PGM_scale) {
                k++;
                s+=(double)d;
                ss+=(double)d*d;
                histo[d]++;
                if (mini>(int)d)
                    mini=(int)d;
                if (maxi<(int)d)
                    maxi=(int)d;
            }
        }
    }
    if (k<1||k>PGM_row*PGM_col)
        error("Bad image data");

    med=k/2;
    sum=0;
    for (c=mini;sum<med;r=c++)
        sum+=histo[c];
    med=r;

//  Display statistics
    printf( "\nPGM_Cropper reports output image statistics :\n" );
    printf( "Minimum             = %7d\n", mini );
    printf( "Maximum             = %7d\n", maxi );
    printf( "Average             = %9.1lf\n", s/k );
    printf( "Median              = %7d\n", med );
    printf( "Standard Deviation  = %12.4lf\n", sqrt((ss-s*s/k)/k) );

//Write results
    if ((fp=fopen(argv[6],"wb"))==NULL) {
        sprintf(buff,"Bad output file name: <%s>", argv[6]);
        error(buff);
    }
    if (fprintf(fp,"P5\n#PGM_Cropper by Ch Iossif (c) Oct 2009.\n%d %d\n%d\n", fcol, frow, maxi)<=0) {
        sprintf(buff,"Bad write at file: <%s>", argv[6]);
        error(buff);
    }

    for (r=0;r<frow;r++) {
        if (fwrite(&image[(r+offy)*PGM_col+offx],sizeof(short),fcol,fp)!=fcol) {
            sprintf(buff,"Bad write at file: <%s>", argv[6]);
            error(buff);
        }
    }

    fclose(fp);

//  Free'em all and exit
    free(image);
    printf("\n\nPGM_Cropper by Ch Iossif (c) Oct 2009.\n\n");
    return 0;
}

void error(const char *s) {
    printf("PGM_Cropper reports: Error: %s.\n",s);
    exit(1);
}

void warning(const char *s) {
    printf("PGM_Cropper reports: Warning: %s.\n",s);
}

int swap(short int sbv) {
    int tmp1, tmp2;
    tmp1= (sbv&0x00ff)<<8;
    tmp2= (sbv&0xff00)>>8;
    return (tmp1 | tmp2);
}

/*
A PGM file consists of a sequence of one or more PGM images. There are no data, delimiters, or padding before, after, or between images.

Each PGM image consists of the following:

   1. A "magic number" for identifying the file type. A pgm image's magic number is the two characters "P5".
   2. Whitespace (blanks, TABs, CRs, LFs).
   3. A width, formatted as ASCII characters in decimal.
   4. Whitespace.
   5. A height, again in ASCII decimal.
   6. Whitespace.
   7. The maximum gray value (Maxval), again in ASCII decimal. Must be less than 65536, and more than zero.
   8. A single whitespace character (usually a newline).
   9. A raster of Height rows, in order from top to bottom. Each row consists of Width gray values, in order from left to right. Each gray value is a number from 0 through Maxval, with 0 being black and Maxval being white. Each gray value is represented in pure binary by either 1 or 2 bytes. If the Maxval is less than 256, it is 1 byte. Otherwise, it is 2 bytes. The most significant byte is first.

      A row of an image is horizontal. A column is vertical. The pixels in the image are square and contiguous.

      Each gray value is a number proportional to the intensity of the pixel, adjusted by the ITU-R Recommendation BT.709 gamma transfer function. (That transfer function specifies a gamma number of 2.2 and has a linear section for small intensities). A value of zero is therefore black. A value of Maxval represents CIE D65 white and the most intense value in the image and any other image to which the image might be compared.

[http://netpbm.sourceforge.net/doc/pgm.html]
*/

