/*
 *	process -- find and crop out frames from
 *		a scan of a strip of 8mm movie film
 *
 *	Copyright 2005 Shay Mozes
 *      Adapted from tiffcine.c by Richard J Kinch
 */

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

/* Grayscale value for a given RGB raster word				*/
#define grayscale(v) ((TIFFGetR(v)+TIFFGetG(v)+TIFFGetB(v))/3)
#define threshold(v) ((grayscale(v)>382))
#define grayscale_white 255*3
#define grayscale_black 0

//parameters for 8mm film
#define FRAME_HEIGHT 1100
#define FRAME_WIDTH  800
#define BLACK_THRESHOLD 50
#define WHITE_THRESHOLD 240
#define WHITE_THRESHOLD2 200
#define FILM_INCREMENT 8000
#define MINIMAL_GAP 580
#define FRAME_TOL 10
#define HOLE_WIDTH 175

/* Number of text columns to use to plot grayscale values		*/
#define PLOT_COLUMNS 75

void
crop(uint32 *raster,uint32 w,uint32 h,
	float xres, float yres, uint16 resunits,
	uint32 x0, uint32 y0, uint32 xN, uint32 yN, char *fname,
	int rotate) {
    /* Given an input RGB raster of dimensions (w,h), write a new TIFF	*/
    /* file named fname, the new image being cropped from the input	*/
    /* raster starting at (x0,y0) and having size (xN,yN).  If "rotate"	*/
    /* we apply a 90 degree clockwise rotation to the output file.	*/
    TIFF *out;
    unsigned char *buf, *p;
    uint32 *q;
    uint32 row, i;
    uint32 cw, ch;		/* Cropped width/height after rotation	*/
    tsize_t scanlinesize;
    if (w==0 || h==0) return;
    if (x0>=w || y0>=h || (x0+xN)>w || (y0+yN)>h) return;
    if (fopen(fname,"rb")) {
	fprintf(stderr,"File \"%s\" already exists\n",fname);
	return;
	}
    out = TIFFOpen(fname,"w");
    if (out==NULL) return;
    if (rotate) { cw = yN ; ch = xN ; } else { cw = xN ; ch = yN ; }
    TIFFSetField(out,TIFFTAG_IMAGEWIDTH,cw);
    TIFFSetField(out,TIFFTAG_IMAGELENGTH,ch);
    TIFFSetField(out,TIFFTAG_PHOTOMETRIC,((uint16)PHOTOMETRIC_RGB));
    TIFFSetField(out,TIFFTAG_COMPRESSION,((uint16)COMPRESSION_NONE));
    TIFFSetField(out,TIFFTAG_SAMPLESPERPIXEL,((uint16)3));
    TIFFSetField(out,TIFFTAG_PLANARCONFIG,((uint16)PLANARCONFIG_CONTIG));
    TIFFSetField(out,TIFFTAG_BITSPERSAMPLE,((uint16)8));
    TIFFSetField(out,TIFFTAG_RESOLUTIONUNIT,(uint16)resunits);
    TIFFSetField(out,TIFFTAG_XRESOLUTION,(float)xres);
    TIFFSetField(out,TIFFTAG_YRESOLUTION,(float)yres);
    scanlinesize =  TIFFScanlineSize(out);
    if (scanlinesize<=0) return;
    buf = (unsigned char *) _TIFFmalloc(scanlinesize);
    if (buf==NULL) return;
    if (rotate) for (row=0; row<ch; row++) {
	p = buf;
	/* q = raster+(x0+ch-1-row)+(y0*w); */
	q = raster+(x0+row)+(y0*w);
	for (i=0; i<cw; i++) {
	    *p++ = TIFFGetR(*q);
	    *p++ = TIFFGetG(*q);
	    *p++ = TIFFGetB(*q);
	    q += w;
	    }
	TIFFWriteScanline(out,buf,row,0);
	}
    else for (row=0; row<ch; row++) {
	p = buf;
	q = raster+w*(y0+ch-1-row)+x0;
	for (i=0; i<cw; i++,q++) {
	    *p++ = TIFFGetR(*q);
	    *p++ = TIFFGetG(*q);
	    *p++ = TIFFGetB(*q);
	    }
	TIFFWriteScanline(out,buf,row,0);
	}
    TIFFClose(out);
    }


#define MAX_FRAMES 13

main(int argc, char **argv) {
    TIFF *tif;
    uint32 w, h;
    size_t npixels;
    uint32 *raster, v;
    float xres, yres;
    uint16 resunits;
    int i,j,black,white, start_row, tmpmax,tmpmin;
    int *rstats = NULL , *cstats = NULL, *mstats = NULL;
    int ymin,ymax,minmax, imin,imax, discard, lastnum=0;
    int xmin[MAX_FRAMES], xmax[MAX_FRAMES], seps[MAX_FRAMES];

    char path[255];

    char *prefix = "frame";
    int fileno = 2;
    int numframes=0,cont;

    char filename[255], tmpfilename[255];
    char fname[255];
    char command[255];
    char number[10];
    
    if (argc!=4) {
	fprintf(stderr,"usage: %s <from> <to> <roll#>\n",argv[0]);
	return(-1);
    }
    path[0] = '\0';
    strcat(path,"/cygdrive/c/8mm/");
    strcat(path,argv[3]);
    for (fileno=atoi(argv[1]); fileno<atoi(argv[2]); fileno ++) {

    filename[0] = '\0';
    command[0] = '\0';
    number[0] = '\0';
    black = 0;

    /* Open input TIFF file */
    strcat(command,"convert -compress Lossless ");
    strcat(command,path);
    strcat(command,prefix);
    sprintf(number,"%d",fileno);
    strcat(command,number);
    strcat(command,".jpg ");
    strcat(command,path);
    strcat(command,prefix);
    strcat(command,number);
    strcat(command,".tif");

//    printf("%s\n",command);
    system(command);
//    system("convert D:/Shay/8mm/roll2/frame1.jpg D:/Shay/8mm/roll2/frame1.tif");

    strcat(filename,path);
    strcat(filename,prefix);
    strcat(filename,number);
    strcat(filename,".tif");

    printf("%s",filename);

    
    
    tif = TIFFOpen(filename,"r");
//    tif = TIFFOpen("D:\\Shay\\8mm\\roll2\\frame1.tif","r");
    if (tif==NULL) {
	fprintf(stderr,"Cannot open input TIFF file %s\n", filename);
	return(-1);
	}

//    printf("jolly good!\n");
    /* Load and characterize TIFF image as an RGB raster		*/
    TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&w);
    TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&h);
    TIFFGetField(tif,TIFFTAG_RESOLUTIONUNIT,&resunits);
    TIFFGetField(tif,TIFFTAG_XRESOLUTION,&xres);
    TIFFGetField(tif,TIFFTAG_YRESOLUTION,&yres);
    npixels = w * h;
//    printf("# file=%s w=%ld h=%ld npixels=%ld\n",argv[1],(long)w,(long)h,(long)npixels);
    raster = (uint32*) _TIFFmalloc(npixels * sizeof(uint32));
    if (raster==NULL) { fprintf(stderr,"Out of memory!\n"); return(-1); }
    if (TIFFReadRGBAImage(tif,w,h,raster,0)==0) {
	fprintf(stderr,"Cannot read image from TIFF file!\n");
	return(-1);
	}

    v = raster[0];
//    printf("%d %d %d\n",TIFFGetR(v),TIFFGetG(v),TIFFGetB(v));

    if (NULL == cstats){
	cstats = (int*) calloc(w,sizeof(int));
	mstats = (int*) calloc(w,sizeof(int));
	rstats = (int*) calloc(h,sizeof(int));
	}

    if (NULL == rstats || NULL == cstats || NULL == mstats){ fprintf(stderr,"Out of memory!\n"); return(-1); }

    //avoid blank lines in the beginning
    i=0;
    do {
	i++;
	rstats[i] = 0;
	for(j=0;j<w;j++){
                rstats[i] += grayscale(raster[i*w+j]);
		}
	rstats[i]/=w;
//	printf("%d, ",rstats[i]);
       } while (i<h && rstats[i] > BLACK_THRESHOLD);
    start_row = i;

//    printf("after white lines\n");

    /* refine black-threshold */

    black = 0;
    for (i=start_row*w; i<(start_row+10)*w; i++) black += grayscale(raster[i]);
    black /= 10*w;
    black += 10;
//    printf("black point is: %d\n", black);


//    black = BLACK_THRESHOLD;


    /* find horizontal beginning of frames*/
    /*
    i=start_row;
    do {
	i++;
	rstats[i] = 0;
	for(j=0;j<w;j++){
                rstats[i] += grayscale(raster[i*w+j]);
		}
	rstats[i]/=w;
//	printf("%d, ",rstats[i]); fflush(stdout);
       } while (i<h && rstats[i] < black);

    if (i>h) {
        printf("could not find frams!!!\n");
//	exit(0);
	}

    ymax = i + FRAME_HEIGHT;
    ymin = i;
    printf("frame Y position: %d - %d.\n", ymin, ymax);
    */


    //find horizontal end of frames by identifying sprocket holes

    white = WHITE_THRESHOLD;
    for (i=(w-10)*h-1;i>w*(h-500);i--){
	if (grayscale(raster[i]) > white) {
		i-=FRAME_WIDTH;
		for (j=0;j<HOLE_WIDTH/2;j++) {
			if (grayscale(raster[i-j]) < white) {
				i-=j;
				break;
				}
			}
		if (j==HOLE_WIDTH/2) {
			i -= j;
			break;
			}
		}
	}
    if (i>w*(h-500)) for(;grayscale(raster[i])>white&&grayscale(raster[i-15*w])>white||grayscale(raster[i+40])>white;i-=w);
    ymax = i/w-15;
    ymin = ymax - FRAME_HEIGHT;
//    printf("frame Y position: %d - %d.\n", ymin, ymax);




    for(j=0;j<w;j++){
	cstats[j] = mstats[j] = 0;
	}

/*
    for(i=ymin;i<=ymax;i++){
	for(j=0;j<w;j++){
		if (mstats[j] < grayscale(raster[i*w+j])) mstats[j] = grayscale(raster[i*w+j]);
		}
	}
*/
    tmpmax = ymax+150;
    tmpmin = ymax+20;
    if (ymax+150 >= h) tmpmax = h;

    for(i=tmpmin;i<h;i++){
	for(j=0;j<w;j++){
		cstats[j]+=grayscale(raster[i*w+j]);
		}
	}

    for(j=0;j<w;j++){
	cstats[j] /= (tmpmax-tmpmin);
	}


/* identify frames by finding black separators between frames */
/*
    numframes = 0;
    cont = 1;
    for (j=10;j<w;j++) if (mstats[j] < black) black = mstats[j];

    while (cont && numframes<MAX_FRAMES-3 && black<100) {
	black += 2;
//	printf("trying black level %d\n",black);
	numframes = 0;
    	i=0;
    	minmax=0;
    	for(j=10;j<w && i<MAX_FRAMES;j++){
		if (minmax==0 && mstats[j] < black ) {		
			xmin[i] = j;
			minmax = 1;
			if (i>0) {
				printf("%d, ",xmin[i]-xmax[i-1]);
//
//				if (xmin[i]-xmax[i-1] > FRAME_WIDTH + FRAME_TOL) break;
//				if (xmin[i]-xmax[i-1] < FRAME_WIDTH - FRAME_TOL) {
//					cont = 0; //failure...
//					break;
//					}

				numframes++;
				}
//			printf("%d -> ",j);
			}
		else if(minmax==1) {
//	   		printf("%d , ",mstats[j]);
	   		if (mstats[j] > black){
				xmax[i] = j;
				minmax = 0;
//				printf("-> %d (%d)\n",j,xmax[i]-xmin[i]);
				i++;
				}
	   		}
		}
	printf(" - number of frames is %d\n",numframes);
	
	}

    printf("number of frames is %d\n",numframes);
*/

/* identifiy frames by finding sprocket holes */

    numframes = 0;
    cont = 1;
    i=0;
    minmax=0;
    for(j=10;j<w && i<MAX_FRAMES;j++){
	if (minmax==0 && cstats[j] > white && (j+50>w ||cstats[j+50] > white) ) {		
		xmin[i] = j;
		minmax = 1;
		if (i>0) {
			if (xmin[i]-xmax[i-1] < MINIMAL_GAP) {
				printf("*%d*", xmin[i]-xmax[i-1]);
				minmax = 0;
				j+= 15;
				continue;
			}
			seps[i-1] = (xmin[i]+xmax[i-1])/2;
//			printf("%d, ",xmin[i]-xmax[i-1]);
//			printf("seps[%d] = %d , ",i-1,seps[i-1]);
			numframes++;
			}
//		printf("%d -> ",j);
		}
	else if(minmax==1) {
//   		printf("%d , ",cstats[j]);
   		if (cstats[j] < white){
			if (i>0 && j-xmin[i] < HOLE_WIDTH) {
				printf("@%d@", j-xmin[i]);
				j+=25;
				continue;
				}
			xmax[i] = j;
			minmax = 0;
//			printf("-> %d (%d)\n",j,xmax[i]-xmin[i]);
			i++;
			}
   		}
	}

   numframes--;

// find additional frames
   if (seps[0] > FRAME_WIDTH) {
	for (i=numframes;i>=0;i--) seps[i+1] = seps[i];
	seps[0] = seps[1] - (seps[2]-seps[1]);
	numframes++;
	}
   if (seps[numframes] + FRAME_WIDTH < w){
	seps[numframes+1] = seps[numframes] + (seps[numframes]-seps[numframes-1]);
	numframes++;
   }


    printf("  - number of frames is %d. ",numframes);

    imin = discard = 0;
//discard frame in case there are\were 11 frames in file.
    if (numframes == 11 || lastnum == 11) imin++;
    printf("%d\n",imin);

    tmpfilename[0] = '\0';
    strcat(tmpfilename,path);
    strcat(tmpfilename,"video/");
    strcat(tmpfilename,prefix);
    sprintf(number,"%03d_",fileno);
    strcat(tmpfilename,number);
    for (i=imin;i<numframes;i++){
	fname[0] = '\0';
	strcat(fname,tmpfilename);
//	strcat(fname,"video2/");
	sprintf(number,"%02d",i);
	strcat(fname,number);
	strcat(fname,".tif");
//	printf("writing frame %d starting at %d",i, seps[i]+3);
	crop(raster,w,h,xres,yres,resunits, seps[i]-10, ymin, FRAME_WIDTH, FRAME_HEIGHT, fname, 0);

	command[0] = '\0';

	strcat(command,"mogrify -format jpg -quality 90 -flop -rotate 270 -level 20 -modulate 115 ");

//	strcat(command,"mogrify -format jpg -compress Lossless -flop -rotate 270 -level 20 -modulate 115 ");
//	strcat(command,"mogrify -format jpg -compress Lossless -flop -rotate 270 ");
   	strcat(command,fname);

//	strcat(command," ");
//	strcat(command,tmpfilename);
//	strcat(command,number);
//	strcat(command,".jpg");

//	printf("%s\n",command);
//	printf("remove %s\n",fname);

	system(command);
	remove(fname);

	}
	



    lastnum = numframes;

    /* Discard image and close TIFF file.				*/
    _TIFFfree(raster); raster = NULL;
    TIFFClose(tif); tif = NULL;

    remove(filename);

    }

    return(0);
	
    }
