/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001 Topi Mäenpää and Jaakko Viertola
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.18 $
 *********************************************************************/

#include "Histogram.h"

namespace prapi
{
	List<int> IntegerHistogram::getFeatureVector(const Matrix<int>& mat) throw (FeatureExtractionException&)
	{
		int len = (mat.max()+1) >? _iLength;
		List<int> result(len);
		result.setLength(len);
		result = 0;

		const int* data = mat.getData();
		for (int i=mat.getColumns()*mat.getRows();i--;data++)
			result[*data]++;
		
		return result;
	}

	void MultiFeatureHistogram::Merge::modifyHistogram(const List<int>& pixelValues,
																										 List<int>& histogram) const
	{
		for (int i=pixelValues.getLength();i--;)
			histogram[pixelValues[i]]++;
	}
	

	MultiFeatureHistogram::Concatenate::Concatenate(int channels, int levels) :
		_ilstLevels(channels), _iLength(0)
	{
		_ilstLevels.setLength(channels);
		_ilstLevels = levels;
		for (int i=channels;i--;)
			_iLength += _ilstLevels[i];
	}

	MultiFeatureHistogram::Concatenate::Concatenate(List<int> levels) :
		_ilstLevels(levels), _iLength(0)
	{
		for (int i=levels.getLength();i--;)
			_iLength += _ilstLevels[i];
	}

	void MultiFeatureHistogram::Concatenate::modifyHistogram(const List<int>& pixelValues,
																													 List<int>& histogram) const
	{
		int sum=0;
		for (int i=0;i<pixelValues.getLength();i++)
			{
				histogram[sum+pixelValues[i]]++;
				sum += _ilstLevels[i];
			}
	}

	MultiFeatureHistogram::MultiDimensional::MultiDimensional(int channels, int levels) :
		_ilstMultipliers(channels), _iLength(1)
	{
		_ilstMultipliers.setLength(channels);
		for (int i=channels;i--;)
			{
				_ilstMultipliers[i] = _iLength;
				_iLength *= levels;
			}
	}

	MultiFeatureHistogram::MultiDimensional::MultiDimensional(List<int> levels) :
		_ilstMultipliers(levels.getLength()), _iLength(1)
	{
		_ilstMultipliers.setLength(levels.getLength());
		for (int i=levels.getLength();i--;)
			{
				_ilstMultipliers[i] = _iLength;
				_iLength *= levels[i];
			}
	}

	void MultiFeatureHistogram::MultiDimensional::modifyHistogram(const List<int>& pixelValues,
																																List<int>& histogram) const
	{
		int distIndex=0;
		for (int i=0;i<pixelValues.getLength();i++)
			distIndex += _ilstMultipliers[i] * pixelValues[i];
		histogram[distIndex]++;
	}

	List<int> MultiFeatureHistogram::getFeatureVector(const List<Matrix<int> >& lst) throw (FeatureExtractionException&)
	{
		int histLen = _layerCombiner.getHistogramLength();
		List<int> result(histLen);
		result.setLength(histLen);
		result = 0;

		int len = lst.getLength();
		List<const int*> lstData(len);
		for (int i=0;i<len;i++)
			lstData += lst[i].getData();

		List<int> pixelValues(len);
		pixelValues.setLength(len);
		
		for (int r=lst[0].getRows();r--;)
			for (int c=lst[0].getColumns();c--;)
				{
					for (int i=0;i<len;i++)
						{
							pixelValues[i] = *lstData[i];
							lstData[i]++;
						}
					_layerCombiner.modifyHistogram(pixelValues,result);
				}
		
		return result;
	}
	

	MultiDimensionalHistogram::MultiDimensionalHistogram(List<int> dimensions) : _ilstMultipliers(dimensions.getLength())
	{
		_iLength = 1;
		_ilstMultipliers.setLength(dimensions.getLength());
		for (int i=dimensions.getLength();i--;)
			{
				_ilstMultipliers[i] = _iLength;
				_iLength *= dimensions[i];
			}
	}

	List<int> MultiDimensionalHistogram::getFeatureVector(const List<Matrix<int> >& lst) throw (FeatureExtractionException&)
	{
		List<int> result(_iLength);
		result.setLength(_iLength);
		result = 0;
		for (int r=lst[0].getRows(),index=0;r--;)
			for (int c=lst[0].getColumns();c--;index++)
				{
					int distIndex = 0;
					for (int i=0;i<lst.getLength();i++)
						distIndex += _ilstMultipliers[i] * lst[i].getData()[index];
					result[distIndex]++;
				}
		return result;
	}

	Matrix<int> HistogramOperation::Equalization::getTransformedImage(const Matrix<int>& mat) throw (ImageTransformException&)
	{
		Matrix<int> result(mat.getRows(), mat.getColumns());
		// Calculating the histogram
		IntegerHistogram matrixHistogram;
		List<int> pixels(matrixHistogram.getFeatureVector(mat));
		int levels = pixels.getLength();// get the length of list
		List<int> newValues(levels); // where to put newValues for pixels
		int sum = mat.getRows()*result.getColumns(); // amount of pixels
		int max = mat.max(); // max value of pixels
		int pixelSum = 0; 
		double distance = (double)1/max*sum; //distance between levels
		double halfDistance = distance/2; 
		int j=0;
				
		// calulating the accumulation of the list and at the same time
		// calculatin new values for pixels
		for(int i=0; i<levels;i++)
			{
				pixels[i] = pixelSum + pixels[i];
				pixelSum = pixels[i];
				do
					{ // check if less than 
						if(pixels[i] < j*distance)
							{ // if the value is bigger than value j-1 + half of distance then 
								if(pixels[i] > (j-1)*distance+halfDistance)newValues[i]=j;
								else newValues[i] = j-1;
								// and for every case in this if
								break;
							}//if pixels < j*dist
						// remember to increment th value of i
						j++;
					}while(j<=levels);
			}//for loop

		const int* matData = mat.getData();
		int* resultData = result.getData();
		
		for(int i=0; i< sum;i++,matData++,resultData++)
			*resultData = newValues[*matData];// place the new values
			
		return result;

	} // Equalization
	
	Matrix<int> HistogramOperation::ContrastStretching::getTransformedImage(const Matrix<int>& mat) throw (ImageTransformException&)
	{ 
		Matrix<int> result(mat.getRows(), mat.getColumns());
		// if user does not give new values let's find them
		if( 0 == _iOldMin && 0 == _iOldMax)
			{
				_iOldMin = mat.min();
				_iOldMax = mat.max();
			}
		
		List<int> newValues(_iMax+1);
		newValues.setLength(_iMax+1);
		double conversion = (double)(_iMax - _iMin)/(double)(_iOldMax-_iOldMin);

		for(int i=0; i<newValues.getLength() ; i++)
			{ // checking if the value of i is in the right range 
				if( i >= _iOldMin && i < _iMax )
					newValues[i] = (int)(conversion*(i-_iOldMin)+_iMin);
				else newValues[i] = i;
			}

		// changing the values of pixels
		const int* matData = mat.getData();
		int* resultData = result.getData();
		
		for(int i=0; i< mat.getRows()*mat.getColumns();i++,matData++,resultData++)
			*resultData = newValues[*matData];// place the new values
			
		return result;
	}

	Matrix<int> HistogramOperation::ZNormalization::getTransformedImage(const Matrix<int>& mat) throw (ImageTransformException&)
	{
		Matrix<int> result(mat.getRows(), mat.getColumns());

		double oldMean = 0;
		double oldDev = 0;
		// then calculate the old mean value and old Variance
		Math::meanAndVariance(mat, oldMean, oldDev);
    // calculate the old standard deviation
		oldDev = sqrt(oldDev);
   
		int* resultData = result.getData();
		const int* sourceData = mat.getData();

		for(int i=0;i<result.getRows()*result.getColumns();i++,sourceData++,resultData++)
			{
				int newValue = (int)(_dNewMean+_dNewDev*((double)(*sourceData) - oldMean)/oldDev+0.5);
				// check the limit of newValue
				if(newValue <0) newValue = 0;
				else if(newValue > _iLevels) newValue = _iLevels;

				*resultData = newValue;
			}
		
		return result;
	}
	
}
