#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <list>
#include <cv.h>
#include <highgui.h>

using namespace std;

#define BACKGROUND 0
#define OBJECT 255


typedef struct{
	int x;
	int y;
}posicion;

typedef struct{
	int mano;
	int val0, val1, val2;
}pixel;

int trackbar=0;


void dilate( IplImage  **img, int num);	
void erode(	IplImage  **img, int num);	
void cierre( IplImage  **img, int num);
void apertura (IplImage  **img, int num);
void distancia(IplImage **imagen);
void ejes( CvBox2D box, CvPoint2D32f pt[4] );

void my_mouse_callback(int event, int x, int y, int flags, void* param){
	IplImage** imgs = (IplImage**) param;
	IplImage *image=imgs[trackbar];
	uchar *data=(uchar *)image->imageData;
	int height,width;
	int step,channels;	
	height    = image->height;
	width     = image->width;
	step	  = image->widthStep;
	channels  = image->nChannels;
	if(event==CV_EVENT_LBUTTONUP){
		int R,G,B;
		R=data[y*step+x*channels+2];
		G=data[y*step+x*channels+1];
		B=data[y*step+x*channels];

		printf("R=%d G=%d B=%d\n\tR-B=%d   R-G=%d   B-G=%d\n\n",R,G,B,R-B,R-G,B-G);
	}
}
void visor(int position){}

int main(){

	IplImage * aux_img;
	int c;
	uchar *data3;
	pixel aux;

	IplImage* img[19];
	char name[]="bscap000.jpg";
	
	for(int i=0; i<19;i++){
		if(i<10){
			name[7]=i+48;
		}
		else{
			name[6]=49;
			name[7]=i-10+48;
		}
		img[i] = cvLoadImage(name,1);		

	}
	if(img[0]==NULL) return -1;

	
	
	
	cvNamedWindow( "Original", CV_WINDOW_AUTOSIZE );
	cvSetMouseCallback("Original",my_mouse_callback,(void*) img);
	cvCreateTrackbar("Foto","Original",&trackbar,18, visor);	


	uchar *data;
	int height,width;
	int step,channels,step2;
	
	CvFont font = cvFont(8,1);
	cvInitFont(&font,CV_FONT_VECTOR0,0.5,0.5,0,1);
	char buffer[200];

	IplImage* img2[19];
	IplImage* img3[19];
	posicion pos[19];
	posicion pos2[19];
	int area[19];
	for(int i=0; i<19;i++){

		img2[i] = cvCreateImage(cvGetSize(img[0]),8,1);
		area[i] = 0;
	}

	
	cvNamedWindow( "Clasificada", CV_WINDOW_AUTOSIZE );
	cvShowImage("Clasificada",img2[trackbar]);

	cvNamedWindow( "Distancia", CV_WINDOW_AUTOSIZE );
	
	cvSetMouseCallback("Clasificada",my_mouse_callback,(void*) img);


	int dif_v2_v0; //Rojo_Azul
	int dif_v2_v1; //Rojo_Verde
	int dif_v0_v1; //Azul_Verde


	for(int n=0; n<19;n++){

		// get the image data
		height    = img[n]->height;
		width     = img[n]->width;
		step	  = img[n]->widthStep;
		channels  = img[n]->nChannels;
		data      = (uchar *)img[n]->imageData;
		uchar *data2    =  (uchar *)img2[n]->imageData;
		step2	  = img2[n]->widthStep;


		for(int i= 0; i < height; i++){
			for(int j= 0; j < width; j++){ 

				aux.val0 = data[(i)*step+(j)*channels+0];
				aux.val1 = data[(i)*step+(j)*channels+1];
				aux.val2 = data[(i)*step+(j)*channels+2];

				dif_v2_v0 = aux.val2-aux.val0;
				dif_v2_v1 = aux.val2-aux.val1;
				dif_v0_v1 = aux.val0-aux.val1;

				
				if( (dif_v2_v0> 27 && dif_v2_v0<100)  && (dif_v2_v1>-2 && dif_v2_v1<78) && (dif_v0_v1>-55 && dif_v0_v1<0)  ){
					data2[i*step2+j]=255;
				}else {
					data2[i*step2+j]=0;
				}

			}
		}

		//cierre(&img2[n],1);
		//apertura(&img2[n],1);
		dilate(&img2[n],1);
		erode(&img2[n],1);

		CvMemStorage *mem;
		CvSeq *contours, *ptr, *ptr2;

		mem = cvCreateMemStorage(0);
		cvFindContours(img2[n], mem, &contours, sizeof(CvContour), CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
		cvZero(img2[n]);
	
		int cont=1;
		int valor=0;
		img3[n]=cvCreateImage(cvGetSize(img2[n]),8,1);
		cvZero(img3[n]);

		for (ptr = contours ; ptr != NULL; ptr = ptr->h_next, cont++) {
			if(valor < ptr->total){
				valor=ptr->total;
			}
		}
		for (ptr = contours ; ptr != NULL; ptr = ptr->h_next) {
			CvScalar color = cvScalar(255);
			if(ptr->total==valor){
				ptr2=ptr;
				cvDrawContours(img3[n], ptr, cvScalar(255), cvScalar(255), -1, CV_FILLED, 8, cvPoint(0,0));
				cvDrawContours(img2[n], ptr, cvScalar(255), cvScalar(255), -1, CV_FILLED, 8, cvPoint(0,0));
			}
			else{
				color = cvScalar(  rand()&200);
				cvDrawContours(img2[n], ptr, color, cvScalar(255), -1, CV_FILLED, 8, cvPoint(0,0));
			}
		}

		
		distancia(&img3[n]);

		data3    =  (uchar *)img3[n]->imageData;

		int index;
		int min=0;
		
		pos[n].x=0;
		pos[n].y=0;

		for(int i= 0; i < height; i++){
			for(int j= 0; j < width; j++){
				index = (i)*step2+(j);
				if( data3[index]>min){
					min=data3[index];
					pos2[n].x=pos[n].x;
					pos2[n].y=pos[n].y;

					pos[n].x=j;
					pos[n].y=i;
				}

				if( data2[index] != 0){
					area[n]++;
				}

			}
		}

		
		
		sprintf(buffer,"A=%d",area[n]);
		cvPutText(img2[n],buffer,cvPoint(width-80,height-20), &font, cvScalar(128));

		CvBox2D box = cvMinAreaRect2( ptr2);

		//Pinta caja minima
			CvPoint2D32f points[4];
			CvPoint2D32f points2[4];
			CvPoint pt0, pt;
			CvPoint pt2;
			ejes(box, points2);
			cvBoxPoints(box, points);

			float X,Y,x,y, prop;
			x = points2[0].x-points2[2].x;
			x = x*x;
			y = points2[0].y-points2[2].y;
			y = y*y;
			X = sqrt( x+y );
			x = points2[1].x-points2[3].x;
			x = x*x;
			y = points2[1].y-points2[3].y;
			y = y*y;
			Y = sqrt( x+y );
			prop = Y/X;

			printf("Imagen %d:  X= %f, Y= %f   Proporcion=%f   Area=%d \n", n, X, Y, prop, area[n] );

			pt0.x = cvRound(points[3].x);
			pt0.y = cvRound(points[3].y);
			for(int i = 0; i < 4; i++ )
			{
				pt.x = cvRound(points[i].x);
				pt.y = cvRound(points[i].y);
				pt2.x = cvRound(points2[i].x);
				pt2.y = cvRound(points2[i].y);
				cvLine(img2[n], pt0, pt, cvScalar(175), 1, CV_AA, 0);
				cvCircle(img2[n],pt2,2,cvScalar(150,0,0),2);
				pt0 = pt;
			}
		//
		

		cvCircle(img[n],cvPoint(pos[n].x,pos[n].y), int( 0.5*sqrt( double(area[n]) ) ),cvScalar(255,0,0),1);
		cvCircle(img2[n],cvPoint(pos[n].x,pos[n].y),min,cvScalar(150,0,0),1);
		//cvCircle(img3[n],cvPoint(pos2[n].x,pos2[n].y),3,cvScalar(150,0,0),3);
		cvCircle(img3[n],cvPoint(box.center.x,box.center.y),3,cvScalar(150,0,0),3);
		cvCircle(img2[n],cvPoint(box.center.x,box.center.y),3,cvScalar(150,0,0),3);


	}


	int key;
	while(1){
		key=(cvWaitKey(10) & 255);
		cvShowImage("Original",img[trackbar]);
		cvShowImage("Clasificada",img2[trackbar]);
		cvShowImage("Distancia",img3[trackbar]);

		if(key==27) break;
	}


	return 0;
}



void erode(	IplImage  **img, int num)
{
	int      i, j,k;
	int height,width,step,chan;
	uchar *data,*data_tmp;
	IplImage *tmp;

	height	= (*img)->height;
	width		= (*img)->width;
	step      = (*img)->widthStep;
	chan		= (*img)->nChannels;
	data      = (uchar *)(*img)->imageData;

	for (int n =0; n<num; n++){
		tmp = cvCloneImage((*img));
		data_tmp      = (uchar *)tmp->imageData;

		for(i=1;i<height-1;i++)
			for(j=1;j<width-1;j++) 
				for(k=0;k<chan;k++)
					if(data[i*step+j*chan+k]==OBJECT &&
						( 
						data_tmp[(i-1)*step+(j-1)*chan+k]==BACKGROUND ||
						data_tmp[(i-1)*step+(j)*chan+k]==BACKGROUND ||
						data_tmp[(i-1)*step+(j+1)*chan+k]==BACKGROUND ||
						data_tmp[(i)*step+(j-1)*chan+k]==BACKGROUND ||
						data_tmp[(i)*step+(j+1)*chan+k]==BACKGROUND ||
						data_tmp[(i+1)*step+(j-1)*chan+k]==BACKGROUND ||
						data_tmp[(i+1)*step+(j)*chan+k]==BACKGROUND ||
						data_tmp[(i+1)*step+(j+1)*chan+k]==BACKGROUND 
						)
						)data[i*step+j*chan+k]=BACKGROUND;

		cvReleaseImage(&tmp);
	}
} 

void dilate(	IplImage  **img, int num)
{
	int      i, j,k;
	int height,width,step,chan;
	uchar *data,*data_tmp;
	IplImage *tmp;

	height	= (*img)->height;
	width		= (*img)->width;
	step      = (*img)->widthStep;
	chan		= (*img)->nChannels;
	data      = (uchar *)(*img)->imageData;

	for (int n =0; n<num; n++){
		tmp = cvCloneImage((*img));
		data_tmp      = (uchar *)tmp->imageData;

		for(i=1;i<height-1;i++)
			for(j=1;j<width-1;j++) 
				for(k=0;k<chan;k++)
					if(data[i*step+j*chan+k]==BACKGROUND &&
						( 
						data_tmp[(i-1)*step+(j-1)*chan+k]==OBJECT ||
						data_tmp[(i-1)*step+(j)*chan+k]==OBJECT ||
						data_tmp[(i-1)*step+(j+1)*chan+k]==OBJECT ||
						data_tmp[(i)*step+(j-1)*chan+k]==OBJECT ||
						data_tmp[(i)*step+(j+1)*chan+k]==OBJECT ||
						data_tmp[(i+1)*step+(j-1)*chan+k]==OBJECT ||
						data_tmp[(i+1)*step+(j)*chan+k]==OBJECT ||
						data_tmp[(i+1)*step+(j+1)*chan+k]==OBJECT 
						)
						)data[i*step+j*chan+k]=OBJECT;

		cvReleaseImage(&tmp);
	}
} 
void cierre (IplImage  **img, int num){
	erode(img,num);
	dilate(img,num+1);
	erode(img,1);
}

void apertura (IplImage  **img, int num){
	
	dilate(img,num);
	erode(img,num+1);
	dilate(img,1);
}


void distancia(IplImage **imagen){
	
	uchar *data;
	int height,width;
	int step;
	IplImage *img= cvCloneImage(*imagen);


	height    = img->height;
	width     = img->width;
	step	  = img->widthStep;
	data      = (uchar *)img->imageData;
	int index;
	int min;

	//bool finish;

	for(int i= 1; i < height-1; i++){
		for(int j= 1; j < width-1; j++){

			index = (i)*step+(j);
			if( data[index] != 0){
				min=data[index];
				for(int m = -1; m <= 1; m+=1){
					for(int n = -1; n <= 1; n++){
						if( data[(i+m)*step+(j+n)]< min)
							min=data[(i+m)*step+(j+n)];
					}
				}

				data[index]=min+1;
			}


		}
	}

	for(int i= height-1; i > 0; i--){
		for(int j= width-1; j>0; j--){

			index = (i)*step+(j);
			if( data[index] != 0){
				min=data[index];
				for(int m = -1; m <= 1; m+=1){
					for(int n = -1; n <= 1; n++){
						if( data[(i+m)*step+(j+n)]< min)
							min=data[(i+m)*step+(j+n)];
					}
				}

				data[index]=min+1;
			}


		}
	}

	*imagen=cvCloneImage(img);

	cvReleaseImage(&img);


}


void ejes( CvBox2D box, CvPoint2D32f pt[4] )
{
    double angle = box.angle*CV_PI/180.;
    float a = (float)cos(angle)*0.5f;
    float b = (float)sin(angle)*0.5f;
	CvPoint2D32f pt_aux;

    if( !pt )
        CV_Error( CV_StsNullPtr, "NULL vertex array pointer" );

	pt_aux.x=pt[0].x = box.center.x - a*box.size.height - b*box.size.width;
	pt_aux.y=pt[0].y = box.center.y + b*box.size.height - a*box.size.width;
    pt[1].x = box.center.x + a*box.size.height - b*box.size.width;
    pt[1].y = box.center.y - b*box.size.height - a*box.size.width;
    pt[2].x = 2*box.center.x - pt[0].x;
    pt[2].y = 2*box.center.y - pt[0].y;
    pt[3].x = 2*box.center.x - pt[1].x;
    pt[3].y = 2*box.center.y - pt[1].y;


	pt[0].x += (pt[1].x-pt[0].x)/2;
	pt[0].y += (pt[1].y-pt[0].y)/2;
	pt[1].x += (pt[2].x-pt[1].x)/2;
	pt[1].y += (pt[2].y-pt[1].y)/2;
	pt[2].x += (pt[3].x-pt[2].x)/2;
	pt[2].y += (pt[3].y-pt[2].y)/2;
	pt[3].x += (pt_aux.x-pt[3].x)/2;
	pt[3].y += (pt_aux.y-pt[3].y)/2;

}