//////////////////////////////////////////////////////////////////////
//                                                                  //
//  effets02.cpp : Ce fichier contient l'implémentation d'effets    //
//                 de bruits                                        //
//                                                                  //
//////////////////////////////////////////////////////////////////////

#include <cmath>
#include <ctime>
#include "tri.h"
#include "type_image.h"



// Effet de bruit aléatoire probabiliste

void type_image::Eff02BruitRand(float Prob)
{
	srand(time(0));
	int nbpixel = int (Prob*Width*Height);
	int x, y;
	bool tour = true;

	for (int i = 0; i < nbpixel; i++)
	{
		x = int (Width * (rand() % 32767) / 32767.0f);
		y = int (Height * (rand() % 32767) / 32767.0f);

		for (enum_type_couleur k = ROUGE; k <= BLEU; k = enum_type_couleur(k + 1))
		{
			if (tour)
			{
				(*this)(x,y,k) *= 2.0f;
				Valide((*this)(x,y,k));
			}
			else
				(*this)(x,y,k) *= 0.5f;
		}

		tour ^= true;
	}
}


// Effet de bruit de Gauss

void type_image::Eff02BruitGauss(float Range)
{
	const float Pi = 4.0f*atan(1.0f);
	const float EPSILON = 1.0e-5f;
	float Uniform, RayLeigh, Gauss;

	for (int i = 0; i < Width; i++)
		for (int j = 0; j < Height; j++)
		{
			Uniform = (rand() % 32767) / 32767.0f;
			if (Uniform < EPSILON)
				RayLeigh = 255.0f;
			else
				RayLeigh = sqrt(-2.0*log(Uniform));

			Gauss = RayLeigh*cos(2.0*Pi*Uniform);

			for (enum_type_couleur k = ROUGE; k <= BLEU; k = enum_type_couleur(k + 1))
			{
				(*this)(i,j,k) = (*this)(i,j,k) + (*this)(i,j,k)*Range*Gauss;
				Valide((*this)(i,j,k));
			}
		}
}


// Bruit additif de Laplace

void type_image::Eff02BruitLaplace(float Coef)
{
	const float EPSILON = 1.0e-5f;
	float u;

	for (int i = 0; i < Width; i++)
		for (int j = 0; j < Height; j++)
		{
			u = (rand() % 32767) / 32767.0f;

			for (enum_type_couleur k = ROUGE; k <= BLEU; k = enum_type_couleur(k + 1))
			{
				if (u <= 0.5)
				{
					if (u < EPSILON)
						(*this)(i,j,k) = 0;
					else
						(*this)(i,j,k) = (*this)(i,j,k)+Coef*log(2.0f*u);
				}
				else
				{
					if (1-u < 0.5*EPSILON)
						(*this)(i,j,k) = 1;
					else
						(*this)(i,j,k) = (*this)(i,j,k)-Coef*log(2.0f*(1-u));
				}

				Valide((*this)(i,j,k));
			}
		}
}


// Effet de moving average filter (bon pour enlever le bruit de Gauss)
// Prend la moyenne des pixels dans un carré autour du pixel

void type_image::Eff02FilterMovAv(int sizex, int sizey)
{
	float *PtrNouveau = new float[Width*Height*3];
	float Temp;

	for (enum_type_couleur c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
		for (int k = sizex / 2; k < Width - sizex / 2; k++)
			for (int l = sizey / 2; l < Height - sizey / 2; l++)
			{
				Temp = 0;

				for (int i = 0; i < sizex; i++)
					for (int j = 0; j < sizey; j++)
						Temp+= (*this)(k - sizex/2 + i, l - sizey/2 + j, c);

				PtrNouveau[(k + l*Width)*3 + c] = Temp / sizex / sizey;
			}

	delete [] PtrImage;

	PtrImage = PtrNouveau;
}


// Effet de median filter (bon pour enlever le bruit impulsive)
// Prend la médiane des pixels dans un carré autour du pixel

void type_image::Eff02FilterMedian(int sizex, int sizey)
{
	float *Liste = new float[sizex*sizey];

	float *PtrNouveau = new float[Width*Height*3];

	for (enum_type_couleur c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
		for (int k = sizex / 2; k < Width - sizex / 2; k++)
			for (int l = sizey / 2; l < Height - sizey / 2; l++)
			{
				for (int i = 0; i < sizex; i++)
					for (int j = 0; j < sizey; j++)
						Liste[i + sizex*j] = (*this)(k - sizex/2 + i, l - sizey/2 + j, c);

				// Tri de la liste
				Tri(Liste, 0, sizex*sizey - 1);

				PtrNouveau[(k + l*Width)*3 + c] = Liste[sizex*sizey/2];
			}

	delete [] Liste;

	delete [] PtrImage;

	PtrImage = PtrNouveau;
}


// Effet de multistage median filter
// Prend la médiane sur plusieurs rectangles

void type_image::Eff02FilterMedianMulti(int size)
{
	float *s = new float[size];
	float *t = new float[size];
	float *u = new float[size];
	float *v = new float[size];
	float e[5];

	float *PtrNouveau = new float[Width*Height*3];

	int size2 = size / 2;
	
	for (enum_type_couleur c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
		for (int k = size / 2; k < Width - size / 2; k++)
			for (int l = size / 2; l < Height - size / 2; l++)
			{
				for (int j = 0; j < size; j++)
				{
					s[j] = (*this)(k,l+j-size2,c);
					t[j] = (*this)(k+j-size2,l,c);
					u[j] = (*this)(k-j+size2,l-j+size2,c);
					v[j] = (*this)(k-j+size2,l+j-size2,c);
				}
				Tri(s,0,size-1);
				Tri(t,0,size-1);
				Tri(u,0,size-1);
				Tri(v,0,size-1);

				e[0] = s[size2];
				e[1] = t[size2];
				e[2] = u[size2];
				e[3] = v[size2];
				e[4] = (*this)(k,l,c);

				Tri(e, 0, 4);
				PtrNouveau[(k + l*Width)*3 + c] = e[2];
			}

	delete []s;
	delete []t;
	delete []u;
	delete []v;

	delete [] PtrImage;

	PtrImage = PtrNouveau;
}


// Effet de max filter
// prend la valeur maximale du carré

void type_image::Eff02FilterMax(int sizex, int sizey)
{
	float *PtrTemp = new float[Width*Height*3];
	float *PtrTemp2 = new float[Width*Height*3];
	float Tampon;
	int i, k, l;
	enum_type_couleur c;

	for (c = ROUGE; c <= BLEU; c = enum_type_couleur(c+1))
	{
		// Calcul du maximum sur les rangées
		for (k = 0; k < Width; k++)
		{
			// Calcule le premier maximum
			Tampon = (*this)(k, 0, c);

			for (i = 1; i < sizey; i++)
				if (Tampon < (*this)(k, i, c))
					Tampon = (*this)(k, i, c);

			PtrTemp[(k +(sizey/2*Width))*3 + c] = Tampon;

			// Calcule le maximum pour le reste de la ligne
			for (l = sizey/2 + 1; l < Height - sizey / 2; l++)
			{
				if ((*this)(k, l+sizey/2, c) >= Tampon)
					Tampon = (*this)(k, l+sizey/2, c);
				else
				{
					if ((*this)(k, l - sizey/2 - 1, c) == Tampon)
					{
						Tampon = (*this)(k, l - sizey/2, c);
						for (i = 1; i < sizey; i++)
							if (Tampon < (*this)(k, l + i - sizey/2, c))
								Tampon = (*this)(k, l + i - sizey/2, c);
					}
				}

				PtrTemp[(k + l*Width)*3 + c] = Tampon;
			}
		}
	}

	delete []PtrImage;
	PtrImage = PtrTemp;

	for (c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
	{
		// Calcul du maximum sur les colonnes
		for (l = sizey/2; l < Height - sizey / 2; l++)
		{
			// Calcule le premier maximum
			Tampon = (*this)(0, l, c);

			for (i = 1; i < sizex; i++)
				if (Tampon < (*this)(i, l, c))
					Tampon = (*this)(i, l, c);

			PtrTemp2[(sizex/2 + l*Width)*3 + c] = Tampon;

			// Calcule le maximum pour le reste des colonnes
			for (k = sizey/2 + 1; k < Width - sizex / 2; k++)
			{
				if ((*this)(k + sizex/2, l, c) >= Tampon)
					Tampon = (*this)(k+sizex/2, l, c);
				else
				{
					if ((*this)(k - sizex/2 - 1, l, c) == Tampon)
					{
						Tampon = (*this)(k - sizex/2, l, c);
						for (i = 1; i < sizex; i++)
							if (Tampon < (*this)(k + i - sizex/2, l, c))
								Tampon = (*this)(k + i - sizex/2, l, c);
					}
				}

				PtrTemp2[(k + l*Width)*3 + c] = Tampon;
			}
		}
	}

	delete []PtrImage;
	PtrImage = PtrTemp2;
}


// Effet de min filter
// prend la valeur minimale du carré

void type_image::Eff02FilterMin(int sizex, int sizey)
{
	float *PtrTemp = new float[Width*Height*3];
	float *PtrTemp2 = new float[Width*Height*3];
	float Tampon;
	int i, k, l;
	enum_type_couleur c;

	for (c = ROUGE; c <= BLEU; c = enum_type_couleur(c+1))
	{
		// Calcul du minimum sur les rangées
		for (k = 0; k < Width; k++)
		{
			// Calcule le premier minimum
			Tampon = (*this)(k, 0, c);

			for (i = 1; i < sizey; i++)
				if (Tampon > (*this)(k, i, c))
					Tampon = (*this)(k, i, c);

			PtrTemp[(k +(sizey/2*Width))*3 + c] = Tampon;

			// Calcule le maximum pour le reste de la ligne
			for (l = sizey/2 + 1; l < Height - sizey / 2; l++)
			{
				if ((*this)(k, l+sizey/2, c) <= Tampon)
					Tampon = (*this)(k, l+sizey/2, c);
				else
				{
					if ((*this)(k, l - sizey/2 - 1, c) == Tampon)
					{
						Tampon = (*this)(k, l - sizey/2, c);
						for (i = 1; i < sizey; i++)
							if (Tampon > (*this)(k, l + i - sizey/2, c))
								Tampon = (*this)(k, l + i - sizey/2, c);
					}
				}

				PtrTemp[(k + l*Width)*3 + c] = Tampon;
			}
		}
	}

	delete []PtrImage;
	PtrImage = PtrTemp;

	for (c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
	{
		// Calcul du maximum sur les colonnes
		for (l = sizey/2; l < Height - sizey / 2; l++)
		{
			// Calcule le premier maximum
			Tampon = (*this)(0, l, c);

			for (i = 1; i < sizex; i++)
				if (Tampon > (*this)(i, l, c))
					Tampon = (*this)(i, l, c);

			PtrTemp2[(sizex/2 + l*Width)*3 + c] = Tampon;

			// Calcule le maximum pour le reste des colonnes
			for (k = sizey/2 + 1; k < Width - sizex / 2; k++)
			{
				if ((*this)(k + sizex/2, l, c) <= Tampon)
					Tampon = (*this)(k+sizex/2, l, c);
				else
				{
					if ((*this)(k - sizex/2 - 1, l, c) == Tampon)
					{
						Tampon = (*this)(k - sizex/2, l, c);
						for (i = 1; i < sizex; i++)
							if (Tampon > (*this)(k + i - sizex/2, l, c))
								Tampon = (*this)(k + i - sizex/2, l, c);
					}
				}

				PtrTemp2[(k + l*Width)*3 + c] = Tampon;
			}
		}
	}

	delete []PtrImage;
	PtrImage = PtrTemp2;
}


// Effet de local adaptive filter

void type_image::Eff02FilterLocalAdapt(int sizex, int sizey, float NoiseVar)
{
	float *PtrNouveau = new float[Width*Height*3];
	int i, j, bm;
	long s;
	float sx, snx, xy = float(sizex*sizey);

	for (enum_type_couleur c = ROUGE; c <= BLEU; c = enum_type_couleur(c + 1))
		for (int k = sizex / 2; k < Width - sizex / 2; k++)
			for (int l = sizey / 2; l < Height - sizey / 2; l++)
			{
				s = 0;

				// Trouver le "local mean"
				for (i = 0; i < sizex; i++)
					for (j = 0; j < sizey; j++)
						s+= (*this)(k - sizex/2 + i, l - sizey/2 + j, c);

				bm = int( (float)s/xy);

				// Trouver le "local standard deviation"
				s = 0;
				for (i = 0; i < sizex; i++)
					for (j = 0; j < sizey; j++)
						s+= ( (*this)(k+i-sizex/2,l+j-sizey/2,c) - bm ) *
						    ( (*this)(k+i-sizex/2,l+j-sizey/2,c) - bm );

				sx = (float) s / xy;

				// Perform adaptation
				if (sx == 0.0f)
					PtrNouveau[(k + l*Width)*3 + c] = bm;
				else
				{
					snx = NoiseVar/sx;
					if (snx > 1.0f)
						snx = 1.0f;
					PtrNouveau[(k + l*Width)*3 + c] = (int)((1.0f-snx)*(*this)(k,l,c)+ snx*bm);
				}

				Valide(PtrNouveau[(k + l*Width)*3 + c]);
			}

	delete [] PtrImage;

	PtrImage = PtrNouveau;
}