/* EulerNumber
   The input image should be a binary image
*/
long EulerNumber(const ImageMatrix &Im, int mode) {  
	unsigned long x, y;
	size_t i;
	// quad-pixel match patterns
	unsigned char Px[] = {
		// P1 - single pixel
		(1 << 3) | (0 << 2) |
		(0 << 1) | (0 << 0),
		(0 << 3) | (1 << 2) |
		(0 << 1) | (0 << 0),
		(0 << 3) | (0 << 2) |
		(1 << 1) | (0 << 0),
		(0 << 3) | (0 << 2) |
		(0 << 1) | (1 << 0),
		// P3 - 3-pixel
		(0 << 3) | (1 << 2) |
		(1 << 1) | (1 << 0),
		(1 << 3) | (0 << 2) |
		(1 << 1) | (1 << 0),
		(1 << 3) | (1 << 2) |
		(0 << 1) | (1 << 0),
		(1 << 3) | (1 << 2) |
		(1 << 1) | (0 << 0),
		// Pd - diagonals
		(1 << 3) | (0 << 2) |
		(0 << 1) | (1 << 0),
		(0 << 3) | (1 << 2) |
		(1 << 1) | (0 << 0)
	};
	unsigned char Imq;
	// Pattern match counters
	long C1 = 0, C3 = 0, Cd = 0;
	readOnlyPixels pix_plane = Im.ReadablePixels();
	
	assert ( (mode == 4 || mode == 8) && "Calling EulerNumber with mode other than 4 or 8");

	// update pattern counters by scanning the image.
	for (y = 1; y < Im.height; y++) {
		for (x = 1; x < Im.width; x++) {
			// Get the quad-pixel at this image location
			Imq = 0;
			if (pix_plane(y-1,x-1) > 0) Imq |=  (1 << 3);
			if (pix_plane(y-1,x  ) > 0) Imq |=  (1 << 2);
			if (pix_plane(y  ,x-1) > 0) Imq |=  (1 << 1);
			if (pix_plane(y  ,x  ) > 0) Imq |=  (1 << 0);
			// find the matching pattern
			for (i = 0; i < 10; i++) if (Imq == Px[i]) break;
			if      (i >= 0 && i <= 3) C1++;
			else if (i >= 4 && i <= 7) C3++;
			else if (i == 8 && i == 9) Cd++;
		}
	}
	
	if (mode == 4)
		return ( (C1 - C3 + (2*Cd)) / 4);
	else
		return ( (C1 - C3 - (2*Cd)) / 4);
}
/* the number of pixels that are above the threshold
   the input image is a binary image
*/
unsigned long area(const ImageMatrix &Im) {
	unsigned int x,y,w = Im.width, h = Im.height;
	unsigned long sum=0;
	readOnlyPixels pix_plane = Im.ReadablePixels();
	for (y = 0; y < h; y++)
		for (x = 0; x < w; x++)
			sum += (pix_plane(y,x) > 0 ? 1 : 0);
	return(sum);
}
/* find the centroid of a certain feature
   the input image should be a bwlabel transform of a binary image
   the retruned value is the area of the feature
*/
unsigned long FeatureCentroid(const ImageMatrix &Im, double object_index,double *x_centroid, double *y_centroid) {
	unsigned int x,y,w = Im.width, h = Im.height;
	unsigned long x_mass=0,y_mass=0,mass=0;
	readOnlyPixels pix_plane = Im.ReadablePixels();

	for (y = 0; y < h; y++)
		for (x = 0; x < w; x++)
			if (pix_plane(y,x) == object_index) {
				x_mass=x_mass+x+1;      /* the "+1" is only for compatability with matlab code (where index starts from 1) */
				y_mass=y_mass+y+1;      /* the "+1" is only for compatability with matlab code (where index starts from 1) */
				mass++;
			}
	if (x_centroid) *x_centroid=(double)x_mass/(double)mass;
	if (y_centroid) *y_centroid=(double)y_mass/(double)mass;
	return(mass);
}
/* the input should be a binary image */
void GlobalCentroid(const ImageMatrix &Im, double *x_centroid, double *y_centroid) {
	unsigned int x,y,w = Im.width, h = Im.height;
	double x_mass=0,y_mass=0,mass=0;
	readOnlyPixels pix_plane = Im.ReadablePixels();

	for (y = 0; y < h; y++)
		for (x = 0; x < w; x++)
			if (pix_plane(y,x) > 0) {
				x_mass=x_mass+x+1;    /* the "+1" is only for compatability with matlab code (where index starts from 1) */
				y_mass=y_mass+y+1;    /* the "+1" is only for compatability with matlab code (where index starts from 1) */
				mass++;
			}
	if (mass) {
		*x_centroid=x_mass/mass;
		*y_centroid=y_mass/mass;
	} else *x_centroid=*y_centroid=0;
}
/*  BWlabel
    label groups of 4-connected pixels.
    This is an implementation of the Matlab function bwlabel
*/
unsigned long bwlabel(ImageMatrix &Im, int level) {
	long x, y, base_x,base_y,stack_count, w = Im.width, h = Im.height;
	unsigned long group_counter = 1;
	point *stack=new point[w*h];
	pixData &pix_plane = Im.WriteablePixels();

	for (y = 0; y < h; y++) {
		for (x = 0; x < w; x++) {
			if ( pix_plane(y,x) == 1) {
				/* start a new group */
				group_counter++;
				pix_plane(y,x) = group_counter;
				stack[0].x=x;
				stack[0].y=y;
				stack_count=1;
				while (stack_count > 0) {
					base_x=stack[0].x;
					base_y=stack[0].y;

					if (base_x > 0 && pix_plane(base_y,base_x-1) == 1) {
						pix_plane(base_y,base_x-1) = group_counter;
						stack[stack_count].x=base_x-1;
						stack[stack_count].y=base_y;
						stack_count++;
					}

					if (base_x < w-1 && pix_plane(base_y,base_x+1) == 1) {
						pix_plane(base_y,base_x+1) = group_counter;
						stack[stack_count].x=base_x+1;
						stack[stack_count].y=base_y;
						stack_count++;
					}

					if (base_y > 0 && pix_plane(base_y-1,base_x) == 1) {
						pix_plane(base_y-1,base_x) = group_counter;
						stack[stack_count].x=base_x;
						stack[stack_count].y=base_y-1;
						stack_count++;
					}

					if (base_y < h-1 && pix_plane(base_y+1,base_x) == 1) {
						pix_plane(base_y+1,base_x) = group_counter;
						stack[stack_count].x=base_x;
						stack[stack_count].y=base_y+1;
						stack_count++;
					}

					/* look for 8 connected pixels */
					if (level==8) {
						if (base_x > 0 && base_y > 0 && pix_plane(base_y-1,base_x-1) == 1) {
							pix_plane(base_y-1,base_x-1) = group_counter;
							stack[stack_count].x=base_x-1;
							stack[stack_count].y=base_y-1;
							stack_count++;
						}

						if (base_x < w-1 && base_y > 0 && pix_plane(base_y-1,base_x+1) == 1) {
							pix_plane(base_y-1,base_x+1) = group_counter;
							stack[stack_count].x=base_x+1;
							stack[stack_count].y=base_y-1;
							stack_count++;
						}

						if (base_x > 0 && base_y < h-1 && pix_plane(base_y+1,base_x-1) == 1) {
							pix_plane(base_y+1,base_x-1) = group_counter;
							stack[stack_count].x=base_x-1;
							stack[stack_count].y=base_y+1;
							stack_count++;
						}

						if (base_x < w-1 && base_y < h-1 && pix_plane(base_y+1,base_x+1) == 1) {
							pix_plane(base_y+1,base_x+1) = group_counter;
							stack[stack_count].x=base_x+1;
							stack[stack_count].y=base_y+1;
							stack_count++;
						}
					}
			  
					stack_count-=1;
					memmove(stack,&(stack[1]),sizeof(point)*stack_count);
				}
			}
		}
	}

	/* now decrease every non-zero pixel by one because the first group was "2" */
	for (y=0;y<h;y++)
		for (x=0;x<w;x++)
			if (pix_plane(y,x) != 0)
				pix_plane(y,x) -= 1;

	delete [] stack;
	return(group_counter-1);
}
void haralick2D(ImageMatrix &Im, double distance, double *out) {
	unsigned int a,x,y;
	unsigned char **p_gray;
	TEXTURE *features;
	int angle;
	double min[14],max[14],sum[14];
	double min_value,max_value;
	double scale255;
	readOnlyPixels pix_plane = Im.ReadablePixels();

	if (distance <= 0) distance = 1;

	p_gray = new unsigned char *[Im.height];
	for (y = 0; y < Im.height; y++)
		p_gray[y] = new unsigned char[Im.width];

	Im.BasicStatistics(NULL, NULL, NULL, &min_value, &max_value, NULL, 0);
	scale255 = (255.0/(max_value-min_value));
	for (y = 0; y < Im.height; y++)
		for (x = 0; x < Im.width; x++)
			p_gray[y][x] = (unsigned char)((pix_plane(y,x) - min_value) * scale255);

	for (a = 0; a < 14; a++) {
		min[a] = INF;
		max[a] = -INF;
		sum[a] = 0;
	}
	for (angle = 0; angle <= 135; angle = angle+45) {
		features = Extract_Texture_Features((int)distance, angle, p_gray, Im.height,Im.width);
		/*  (1) Angular Second Moment */
		sum[0] += features->ASM;
		if (features->ASM < min[0]) min[0] = features->ASM;
		if (features->ASM > max[0]) max[0] = features->ASM;
		/*  (2) Contrast */
		sum[1] += features->contrast;
		if (features->contrast < min[1]) min[1] = features->contrast;
		if (features->contrast > max[1]) max[1] = features->contrast;
		/*  (3) Correlation */
		sum[2] += features->correlation;
		if (features->correlation < min[2]) min[2] = features->correlation;
		if (features->correlation > max[2]) max[2] = features->correlation;
		/*  (4) Variance */
		sum[3] += features->variance;
		if (features->variance < min[3]) min[3] = features->variance;
		if (features->variance > max[3]) max[3] = features->variance;
		/*  (5) Inverse Diffenence Moment */
		sum[4] += features->IDM;
		if (features->IDM < min[4]) min[4] = features->IDM;
		if (features->IDM > max[4]) max[4] = features->IDM;
		/*  (6) Sum Average */
		sum[5] += features->sum_avg;
		if (features->sum_avg < min[5]) min[5] = features->sum_avg;
		if (features->sum_avg > max[5]) max[5] = features->sum_avg;
		/*  (7) Sum Variance */
		sum[6] += features->sum_var;
		if (features->sum_var < min[6]) min[6] = features->sum_var;
		if (features->sum_var > max[6]) max[6] = features->sum_var;
		/*  (8) Sum Entropy */
		sum[7] += features->sum_entropy;
		if (features->sum_entropy < min[7]) min[7] = features->sum_entropy;
		if (features->sum_entropy > max[7]) max[7] = features->sum_entropy;
		/*  (9) Entropy */
		sum[8] += features->entropy;
		if (features->entropy < min[8]) min[8] = features->entropy;
		if (features->entropy > max[8]) max[8] = features->entropy;
		/* (10) Difference Variance */
		sum[9] += features->diff_var;
		if (features->diff_var < min[9]) min[9] = features->diff_var;
		if (features->diff_var > max[9]) max[9] = features->diff_var;
		/* (11) Diffenence Entropy */
		sum[10] += features->diff_entropy;
		if (features->diff_entropy < min[10]) min[10] = features->diff_entropy;
		if (features->diff_entropy > max[10]) max[10] = features->diff_entropy;
		/* (12) Measure of Correlation 1 */
		sum[11] += features->meas_corr1;
		if (features->meas_corr1 < min[11]) min[11] = features->meas_corr1;
		if (features->meas_corr1 > max[11]) max[11] = features->meas_corr1;
		/* (13) Measure of Correlation 2 */
		sum[12] += features->meas_corr2;
		if (features->meas_corr2 < min[12]) min[12] = features->meas_corr2;
		if (features->meas_corr2 > max[12]) max[12] = features->meas_corr2;
		/* (14) Maximal Correlation Coefficient */
		sum[13] += features->max_corr_coef;
		if (features->max_corr_coef < min[13]) min[13] = features->max_corr_coef;
		if (features->max_corr_coef > max[13]) max[13] = features->max_corr_coef;
		free(features);
	}

	for (y = 0; y < Im.height; y++)
		delete [] p_gray[y];
	delete [] p_gray;

	/* copy the values to the output vector in the right output order */
	double temp[28];
	for (a = 0; a < 14; a++) {
		temp[a] = sum[a]/4;
		temp[a+14] = max[a]-min[a];
	}

	out[ 0] = temp[ 0];
	out[ 1] = temp[14];
	out[ 2] = temp[ 1];
	out[ 3] = temp[15];
	out[ 4] = temp[ 2];
	out[ 5] = temp[16];
	out[ 6] = temp[ 9];
	out[ 7] = temp[23];
	out[ 8] = temp[10];
	out[ 9] = temp[24];
	out[10] = temp[ 8];
	out[11] = temp[22];
	out[12] = temp[11];
	out[13] = temp[25];
	out[14] = temp[ 4];
	out[15] = temp[18];
	out[16] = temp[13];
	out[17] = temp[27];
	out[18] = temp[12];
	out[19] = temp[26];
	out[20] = temp[ 5];
	out[21] = temp[19];
	out[22] = temp[ 7];
	out[23] = temp[21];
	out[24] = temp[ 6];
	out[25] = temp[20];
	out[26] = temp[ 3];
	out[27] = temp[17];
}
//---------------------------------------------------------------------------
int CombFirst4Moments2D(const ImageMatrix &Im, std::vector<double> vec) {
	double **I,**J,**J1,z4[4]={0,0,0,0},z[4];
	double matr4moments[4][N_COMB_SAMPLES];
	long m,n,n2,m2;
	long a,x,y,ii;
	int matr4moments_index;
	int vec_count=0;
	readOnlyPixels pix_plane = Im.ReadablePixels();
	long step;
	Moments4 tmpMoments;
	for (a = 0; a < 4; a++)    /* initialize */
		for (matr4moments_index = 0; matr4moments_index < N_COMB_SAMPLES; matr4moments_index++)
			matr4moments[a][matr4moments_index] = 0;

	m=Im.height;
	n=Im.width;
	I=new double*[n];
	J=new double*[n];
	J1=new double*[n];
	for (a = 0; a < n; a++) {
		I[a] = new double[m];
		J[a] = new double[m];
		J1[a] = new double[m];
	}

	for (y = 0; y < m; y++) {
		for (x = 0; x < n; x++) {
			I[x][y] = y+1;
			J[x][y] = x+1;
		}
	}

	n2 = (int)(round(n/2));
	m2 = (int)(round(m/2));

	/* major diag -45 degrees */
	matr4moments_index=0;
	step = (int)(round((double)m/10));
	if (step < 1) step = 1;
	for (ii = 1-m; ii <= m; ii = ii+step) {
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index]=z4[a];

		tmpMoments.reset();
		for (y = 0; y < m; y++) {
			for (x = 0; x < n; x++) {
				if (fabs(I[x][y] + ii - J[x][y]) < 1)
					tmpMoments.add (pix_plane(y,x));
			}
		}

		tmpMoments.momentVector(z);
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index] = z[a];
		matr4moments_index++;
	}
	vec_count=matr4moments_to_hist(matr4moments,vec,vec_count);

	/* major diag +45 degrees */
	/* fliplr J */
	for (y = 0; y < m; y++)
		for (x = 0; x < n; x++)
			J1[x][y] = J[n-1-x][y];

	matr4moments_index=0;
	step = (int)(round((double)m/10));
	if (step < 1) step = 1;
	for (ii = 1-m; ii <= m; ii = ii+step) {
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index]=z4[a];

		tmpMoments.reset();
		for (y = 0; y < m; y++) {
			for (x = 0; x < n; x++) {
				if (fabs(I[x][y] + ii - J1[x][y]) < 1)
					tmpMoments.add (pix_plane(y,x));
			}
        }

		tmpMoments.momentVector(z);
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index] = z[a];
		matr4moments_index++;
	}
	vec_count=matr4moments_to_hist(matr4moments,vec,vec_count);

	/* vertical comb */
	matr4moments_index=0;
	step = (int)(round((double)n/10));
	if (step < 1) step = 1;
	for (ii = 1-n; ii <= n; ii = ii+step) {
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index]=z4[a];

		tmpMoments.reset();
		for (y = 0; y < m; y++) {
			for (x = 0; x < n; x++) {
				if (fabs(J[x][y] + ii - n2) < 1)
					tmpMoments.add (pix_plane(y,x));
			}
		}

		tmpMoments.momentVector(z);
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index] = z[a];
		matr4moments_index++;
	}
	vec_count=matr4moments_to_hist(matr4moments,vec,vec_count);

	/* horizontal comb */
	matr4moments_index=0;
	step = (int)(round((double)m/10));
	if (step < 1) step = 1;
	for (ii = 1-m; ii <= m; ii = ii+step) {
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index] = z4[a];

		tmpMoments.reset();
		for (y = 0; y < m; y++) {
			for (x = 0; x < n; x++) {
				if (fabs(I[x][y] + ii - m2) < 1)
					tmpMoments.add (pix_plane(y,x));
			}
        }

		tmpMoments.momentVector(z);
		for (a = 0; a < 4; a++) matr4moments[a][matr4moments_index] = z[a];
		matr4moments_index++;
	}
	vec_count=matr4moments_to_hist(matr4moments,vec,vec_count);

	/* free the memory used by the function */
	for (a=0;a<n;a++) {
		delete [] I[a];
		delete [] J[a];
		delete [] J1[a];
	}
	delete [] I;
	delete [] J;
	delete [] J1;

	return(vec_count);
}