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

//Ocho vecinos
#define A0 data[i*step+j*channels+k]
#define A1 data[i*step+(j+1)*channels+k]
#define A2 data[(i-1)*step+(j+1)*channels+k]
#define A3 data[(i-1)*step+(j)*channels+k]
#define A4 data[(i-1)*step+(j-1)*channels+k]
#define A5 data[i*step+(j-1)*channels+k]
#define A6 data[(i+1)*step+(j-1)*channels+k]
#define A7 data[(i+1)*step+(j)*channels+k]
#define A8 data[(i+1)*step+(j+1)*channels+k]

#define BACKGROUND 0
#define OBJECT 255
#define NCIERRE 2
#define NBORDE 1
#define H 119
#define HMIN 100
#define S 255
#define SMIN 75

int h_max=H;
int h_min=HMIN;
int s_max=S;
int s_min=SMIN;
int v_value=0;

int n_img=0;


typedef struct{
	int x,y;
}posicion;

int media_h=0; int media_s=0; int media_v=0;
int media_r=0; int media_g=0; int media_b=0;


void dilate( IplImage  **img, int num);	
void dilate_blackholes(	IplImage  **img, int num);
void erode(	IplImage  **img, int num);		
void cierre( IplImage  **img, int num);
void borde_a_fondo(IplImage  **img, int num);
void captura_pos(int event, int x, int y, int flags, void* param);
void umbralizando_hsv(IplImage * img);
void antialiasing(IplImage **img);
void vaciar_ruido(IplImage **img, int n, float mult=0.5);

void cambiarH(int position){
}
void cambiarS(int position){
	
}

// A Simple Camera Capture Framework
int main() {
	//int i, j, k;
	int height,width;
	//int step,channels;		//frame variables					

	
	CvCapture* capture = cvCaptureFromCAM( CV_CAP_ANY );
	if( !capture ) {
		fprintf( stderr, "ERROR: capture is NULL \n" );
		getchar();
		return -1;
	}

	// Create a window in which the captured images will be presented
	cvNamedWindow( "ventana", CV_WINDOW_AUTOSIZE );
	

	//cvSetMouseCallback("ventana",(CvMouseCallback) captura_pos);
	cvCreateTrackbar("Hmax","ventana",&h_max,255, cambiarH);
	cvCreateTrackbar("Hmin","ventana",&h_min,255, cambiarH);
	cvCreateTrackbar("Smax","ventana",&s_max,255, cambiarS);
	cvCreateTrackbar("Smin","ventana",&s_min,255, cambiarS);
	IplImage* frame = cvQueryFrame( capture );
		
	// get the image data
	height    = frame->height;
	width     = frame->width;
	//cvSetMouseCallback("ventana",captura_pos,(void*) frame);
	// Show the image captured from the camera in the window and repeat
	while( 1 ) {
		// Get one frame
		IplImage* frame = cvQueryFrame( capture );
		if( !frame ) {
			fprintf( stderr, "ERROR: frame is null...\n" );
			getchar();
			break;
		}
		
		umbralizando_hsv(frame);
		
		cvShowImage("ventana",frame);
		// Do not release the frame!

		//If ESC key pressed, Key=0x10001B under OpenCV 0.9.7(linux version),
		//remove higher bits using AND operator
		if( (cvWaitKey(10) & 255) == 27 ) break;
	}


	// Release the capture device housekeeping
	cvReleaseCapture( &capture );

	cvDestroyWindow( "ventana" );
	return 0;
}





void captura_pos(int event, int x, int y, int flags, void* param){
	IplImage* imagen=(IplImage*) param;
	char * name= "mano";
	char mixname[15];
	char number[3];
	if(event==CV_EVENT_LBUTTONUP){
		printf("H=%d, S=%d, V=%d\n", media_h,media_s,media_v);
		printf("R=%d, G=%d, B=%d\n\n", media_r,media_g,media_b);
		strcpy(mixname,name);
		itoa(n_img,number,10);
		strcat(mixname,number);
		strcat(mixname,".png");
		cvSaveImage(mixname,imagen);
		n_img++;
	}
}

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 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 cierre (IplImage  **img, int num){
	erode(img,num);
	dilate(img,num+1);
	erode(img,1);
}

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

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

	for(j=0;j<width;j++) 
		for(k=0;k<chan;k++){
			data[0+j*chan+k]=BACKGROUND;
			data[(height-1)*step+j*chan+k]=BACKGROUND;
		}
	for(i=0;i<height;i++) 
		for(k=0;k<chan;k++){
			data[i*step+k]=BACKGROUND;
			data[i*step+(width-1)*chan+k]=BACKGROUND;
		}

}

void umbralizado_doble(IplImage ** img, int min, int max){

	int i,j,k;
	int height,width,step,channels;
	uchar *data;

	// get the image data
	height    = (*img)->height;
	width     = (*img)->width;
	step      = (*img)->widthStep;
	channels  = (*img)->nChannels;
	data      = (uchar *)(*img)->imageData;

	

	for(i=1;i<height-1;i++)
		for(j=1;j<width-1;j++) 
			for(k=0;k<channels;k++){
				if(  (data[i*step+j*channels+k] > max) || ( data[i*step+j*channels+k] ) < min )
					data[i*step+j*channels+k] = BACKGROUND;
				//else data[i*step+j*channels+k] = OBJECT;
			}
}
void umbralizando_hsv(IplImage * img){

	int height,width,step,channels;		//tono variables
	uchar *rgb_data;
	uchar *tono_data;
	uchar *sat_data;
	uchar *valor_data;
	uchar *gray_data;
	uchar *bordes_data;
	int i,j,k;

	media_h=0;
	media_s=0;
	media_v=0;

	media_r=0;
	media_g=0;
	media_b=0;

	CvSize size = cvGetSize(img);

	IplImage * hsv= cvCreateImage(size,8,3);
	IplImage *gray2;
	IplImage* h = cvCreateImage(size,8,1);
	IplImage* s = cvCreateImage(size,8,1);
	IplImage* v = cvCreateImage(size,8,1);
	

	cvCvtColor(img,hsv,CV_RGB2HSV);
	cvSplit(hsv,h,s,v,0);	

	//IplImage* r = cvCreateImage(size,8,1); //Probando con rgb
	//IplImage* g = cvCreateImage(size,8,1); //Probando con rgb
	//IplImage* b = cvCreateImage(size,8,1); //Probando con rgb
	//cvSplit(img,r,g,b,0);					//Probando con rgb
	//umbralizado_doble(&r,h_min,h_max);
	//cvNamedWindow( "r", CV_WINDOW_AUTOSIZE );
	//cvShowImage( "r", r);
	//umbralizado_doble(&g,s_min,s_max);
	//cvNamedWindow( "g", CV_WINDOW_AUTOSIZE );
	//cvShowImage( "g", g);
	//cvNamedWindow( "b", CV_WINDOW_AUTOSIZE );
	//cvShowImage( "b", b);

	height    = h->height;
	width     = h->width;
	step      = h->widthStep;
	channels  = h->nChannels;
	rgb_data      = (uchar *)img->imageData;
	tono_data      = (uchar *)h->imageData;
	sat_data      = (uchar *)s->imageData;
	valor_data      = (uchar *)v->imageData;
	
	/*for(i=0;i<height;i++)
		for(j=0;j<width;j++) 
			for(k=0;k<channels;k++){
				media_h += tono_data[i*step+j*channels+k];
				media_s += sat_data[i*step+j*channels+k];
				media_v += valor_data[i*step+j*channels+k];

				media_r += rgb_data[i*step+j*3];
				media_g += rgb_data[i*step+j*3+1];
				media_b += rgb_data[i*step+j*3+2];
			}
	int tam=height*width;
	media_h=media_h/tam;
	media_s=media_s/tam;
	media_v=media_v/tam;

	media_r=media_r/tam;
	media_g=media_g/tam;
	media_b=media_b/tam;*/


	
	//IplImage* tono_u = cvCreateImage( size, IPL_DEPTH_8U, 1 );

	
	umbralizado_doble(&h,h_min,h_max);
	/*cvNamedWindow( "h", CV_WINDOW_AUTOSIZE );
	cvShowImage( "h", h );
	*/
	/*dilate(&h,1);
	erode(&h,2);
	dilate(&h,1);
	*/
	
	
	//borde_a_fondo(&tono_u,NBORDE);
	//erode(&tono_u,1);

	/*cvNamedWindow( "tono", CV_WINDOW_AUTOSIZE );
	cvMoveWindow("tono", 100, 100);
	cvShowImage( "tono", h);*/

	
	
	//cvNamedWindow( "s", CV_WINDOW_AUTOSIZE );
	//cvShowImage( "s", s );
	//umbralizado_doble(&s,s_min,s_max);
	umbralizado_doble(&s,0,255);

	IplImage* valor_u = cvCreateImage( size, IPL_DEPTH_8U, 1 );
	/*cvNamedWindow( "v", CV_WINDOW_AUTOSIZE );
	cvShowImage( "v", v);*/
	float vals[]={-1,-1,-1, -1,9,-1, -1,-1,-1};
	CvMat rotmat;
	cvInitMatHeader(&rotmat,3,3,CV_32FC1,vals);
	cvFilter2D(v,valor_u,&rotmat);	


	tono_data      = (uchar *)h->imageData;
	sat_data      = (uchar *)s->imageData;
	valor_data      = (uchar *)valor_u->imageData;


	
	cvAdaptiveThreshold(v,valor_u,255,CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY,7,5);
	erode(&valor_u,2);
	
	cvNamedWindow( "vu", CV_WINDOW_AUTOSIZE );
	cvShowImage( "vu", valor_u );
	borde_a_fondo(&valor_u,NBORDE);
	/*cvSmooth(valor_u,valor_u,CV_BLUR,7,7);

	dilate(&valor_u,1);*/
	
	IplImage* bordes = cvCloneImage(valor_u);
	
	/*cvNamedWindow( "vu2", CV_WINDOW_AUTOSIZE );
	cvShowImage( "vu2", valor_u );*/

	bordes_data      = (uchar *)bordes->imageData;

	/*for(i=0;i<height;i++)
		for(j=0;j<width;j++) 
			for(k=0;k<channels;k++){
				bordes_data[i*step+j*channels+k]=255-bordes_data[i*step+j*channels+k];
			}
	borde_a_fondo(&bordes,NBORDE);*/
	
	

	IplImage* gray = cvCreateImage( size, IPL_DEPTH_8U, 1 );
	gray_data      = (uchar *)gray->imageData;
	
	for(i=0;i<height;i++)
		for(j=0;j<width;j++) 
			for(k=0;k<channels;k++){
				if(tono_data[i*step+j*channels+k]==0 || sat_data[i*step+j*channels+k]==0 ) //SIN TENER EN CUENTA EL VALOR
				//if(tono_data[i*step+j*channels+k]==0 || sat_data[i*step+j*channels+k]==0 ||valor_data[i*step+j*channels+k]==0)
					gray_data[i*step+j*channels+k]=0;
				else{
					gray_data[i*step+j*channels+k]=255;
				}
			}
	borde_a_fondo(&gray,NBORDE);
	/*dilate(&gray,1);
	erode(&gray,2);
	dilate(&gray,1);*/
	
	
	


	CvMemStorage *mem;
	CvSeq *contours, *ptr;

	mem = cvCreateMemStorage(0);
	cvFindContours(bordes, mem, &contours, sizeof(CvContour), CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
	cvZero(bordes);
	//CvScalar color = CV_RGB( 255, 255, 255);

	for (ptr = contours; ptr != NULL; ptr = ptr->h_next) {

		CvScalar color = CV_RGB(  rand()&255, rand()&255, rand()&255 );
        cvDrawContours(bordes, ptr, color, CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0));
  }
	cvNamedWindow( "bordes", CV_WINDOW_AUTOSIZE );
	cvShowImage( "bordes", bordes );
	cvReleaseImage(&bordes );
	

	cvNamedWindow( "gris", CV_WINDOW_AUTOSIZE );
	cvMoveWindow("gris", 327, 375);
	cvShowImage( "gris", gray );

	gray2=cvCloneImage(gray);
	erode(&gray2,4);
	dilate(&gray2,6);
	cvNamedWindow( "gris2", CV_WINDOW_AUTOSIZE );

	cvShowImage( "gris2", gray2 );

	// release the image
	cvReleaseImage(&h );
	cvReleaseImage(&s );
	cvReleaseImage(&v);
	cvReleaseImage(&hsv );
	cvReleaseImage(&gray );
	cvReleaseImage(&gray2 );	
	cvReleaseImage(&valor_u);

}
void dilate_blackholes(	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=3;i<height-3;i++)
			for(j=3;j<width-3;j++) 
				for(k=0;k<chan;k++)
					if(data_tmp[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-1)*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+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-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 antialiasing(IplImage **img){
	int      i, j,k;
	int color;
	int height,width,step,channels;
	uchar *data,*data2;
	IplImage *auxiliar;
	auxiliar = cvCloneImage(*img);

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

	
	
	for(i=1;i<height-1;i++){
		for(j=1;j<width-1;j++){ 
			for(k=0;k<channels;k++){
				color =(A0 + A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8)/9;
				data2[i*step+j*channels+k]= color;
			}
		}
					
	}
	cvReleaseImage(&auxiliar);

}

void vaciar_ruido(IplImage **img, int n, float mult){
	int height,width,step,channels;		//imagen variables
	uchar *data; 
	uchar * aux_data;
	IplImage * aux =cvCloneImage((*img));

	height    = (*img)->height;
	width     = (*img)->width;
	step      = (*img)->widthStep;
	channels  = (*img)->nChannels;
	data      = (uchar *)(*img)->imageData;
	aux_data  = (uchar *)aux->imageData;
	int limit=int(n*n*mult);

	int borde=int(n/2);

	int cont;

	for(int i=borde;i<height-borde;i++)
		for(int j=borde;j<width-borde;j++) 
			for(int k=0;k<channels;k++){
				cont=0;

				for(int l=i-borde;l<i+borde+1;l++)
					for(int m=i-borde;m<i+borde+1;m++){
						cont+=aux_data[l*step+m*channels+k];
					}	
				cont=int(cont/255);

				if(cont>=limit)
					data[i*step+j*channels+k]=255;
				else
					data[i*step+j*channels+k]=0;
			}

			cvReleaseImage(&aux);

}