/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001-2003 Topi Mäenpää
 * Copyright (C) 2001 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.15 $
 *********************************************************************/

#include "LBP.h"

#include <Math.h>

using namespace util;
using namespace std;

namespace prapi { namespace texture {
	
	int LBPMapping::getSize(int samples, MappingType type)
	{
		switch (type)
			{
			case MAP_NONE: return 1<<samples;
			case MAP_UNIFORM: return samples*(samples-1)+3;
			case MAP_ROTMIN:
				{
					List<int> patterns(samples);
					patterns.setLength(samples,0);
					patterns[0] = 2;
					int result = 2;
					for (int i=2; i<=samples; i++)
						{
							if (samples % i) continue;
							int sum = 2;
							for (int j=2; j<i; j++)
								if (!(i % j) || i==samples)
									sum += patterns[j-1];
							patterns[i-1] = (1 << i) - sum;
							result += patterns[i-1] / i;
						}
					return result;
				}
			case MAP_UNIFORM_ROTMIN: return samples+2;
			}
		return 0;
	}

	int* LBPMapping::getMapping(int samples, MappingType type)
	{
		int* newMap = new int[1<<samples], index=0;
		int newMax = 0;
		switch (type)
			{
			case MAP_NONE:
				return NULL;
			case MAP_UNIFORM:
				newMax = samples*(samples-1)+3;
				for (unsigned int i=0;i<unsigned(1<<samples);i++)
					{
						if (BitOperation::transitions(i,samples) <= 2) //uniform
							newMap[i] = index++;
						else
							newMap[i] = newMax-1;
					}
				break;

			case MAP_ROTMIN:
				{
					int tmpMap[1<<samples];
					for (int i=0;i<1<<samples;i++)
						tmpMap[i] = -1;
					for (unsigned int i=0;i<unsigned(1<<samples);i++)
						{
							int rotMin = BitOperation::rotmin(i,samples);
							if (tmpMap[rotMin] < 0)
								tmpMap[rotMin] = newMax++;
							
							newMap[i] = tmpMap[rotMin];
						}
				}
				break;
				
			case MAP_UNIFORM_ROTMIN:
				newMax = samples+2;
				for (unsigned int i=0;i<unsigned(1<<samples);i++)
					{
						if (BitOperation::transitions(i,samples) <= 2)
							newMap[i] = BitOperation::onecount(i,samples);
						else
							newMap[i] = samples+1;
					}
				break;
			}
		return newMap;
	}

	List<int> LBP8::getFeatureVector(const Matrix<int>& mat) throw (FeatureExtractionException&)
	{
		int rows = mat.getRows(), columns = mat.getColumns();
		int leap = columns*_uiPredicate;
		const int
			*p0 = mat.getData(),
			*p1 = p0 + _uiPredicate,
			*p2 = p1 + _uiPredicate,
			*p3 = p2 + leap,
			*p4 = p3 + leap,
			*p5 = p4 - _uiPredicate,
			*p6 = p5 - _uiPredicate,
			*p7 = p6 - leap,
			*center = p7 + _uiPredicate;

		List<int> result(256);
		result.setLength(256);
		memset(result.getData(),0,sizeof(int)*256);
		
		unsigned int value;
		int pred2 = _uiPredicate << 1, cntr;
		if (!_bInterpolated)
			{
				for (int r=0;r<rows-pred2;r++)
					{
						for (int c=0;c<columns-pred2;c++)
							{
								value = 0;
								cntr = *center-1;

								//Unrolled loop
								compab_mask_inc(p0,0);
								compab_mask_inc(p1,1);
								compab_mask_inc(p2,2);
								compab_mask_inc(p3,3);
								compab_mask_inc(p4,4);
								compab_mask_inc(p5,5);
								compab_mask_inc(p6,6);
								compab_mask_inc(p7,7);
								center++;

								result[value]++;
							}
						p0 += pred2;
						p1 += pred2;
						p2 += pred2;
						p3 += pred2;
						p4 += pred2;
						p5 += pred2;
						p6 += pred2;
						p7 += pred2;
						center += pred2;
					}
			}
		else
			{
				p0 = center + _pPoints[5].x + _pPoints[5].y * columns;
				p2 = center + _pPoints[7].x + _pPoints[7].y * columns;
				p4 = center + _pPoints[1].x + _pPoints[1].y * columns;
				p6 = center + _pPoints[3].x + _pPoints[3].y * columns;
				
				for (int r=0;r<rows-pred2;r++)
					{
						for (int c=0;c<columns-pred2;c++)
							{
								value = 0;
								cntr = *center-1;

								//Unrolled loop
								compab_mask_inc(p1,1);
								compab_mask_inc(p3,3);
								compab_mask_inc(p5,5);
								compab_mask_inc(p7,7);
								
								compab_mask(intRound(p0,5,columns),0); //p0
								compab_mask(intRound(p2,7,columns),2); //p2
								compab_mask(intRound(p4,1,columns),4); //p4
								compab_mask(intRound(p6,3,columns),6); //p6
								p0++;
								p2++;
								p4++;
								p6++;
								center++;
								
								result[value]++;
							}
						p0 += pred2;
						p1 += pred2;
						p2 += pred2;
						p3 += pred2;
						p4 += pred2;
						p5 += pred2;
						p6 += pred2;
						p7 += pred2;
						center += pred2;
					}
			}
		return result;
	}

	Matrix<int> LBP8::getTransformedImage(const Matrix<int>& mat) throw (ImageTransformException&)
	{
		int rows = mat.getRows(), columns = mat.getColumns();
		int pred2 = _uiPredicate << 1;
		Matrix<int> result(rows-pred2,columns-pred2);
		int leap = columns*_uiPredicate;
		const int
			*p0 = mat.getData(),
			*p1 = p0 + _uiPredicate,
			*p2 = p1 + _uiPredicate,
			*p3 = p2 + leap,
			*p4 = p3 + leap,
			*p5 = p4 - _uiPredicate,
			*p6 = p5 - _uiPredicate,
			*p7 = p6 - leap,
			*center = p7 + _uiPredicate;
		int
			*resultPtr = result.getData();

		unsigned int value;
		int cntr;
		if (!_bInterpolated)
			{
				for (int r=0;r<rows-pred2;r++)
					{
						for (int c=0;c<columns-pred2;c++)
							{
								value = 0;
								cntr = *center-1;

								//Unrolled loop
								compab_mask_inc(p0,0);
								compab_mask_inc(p1,1);
								compab_mask_inc(p2,2);
								compab_mask_inc(p3,3);
								compab_mask_inc(p4,4);
								compab_mask_inc(p5,5);
								compab_mask_inc(p6,6);
								compab_mask_inc(p7,7);
								center++;

								*resultPtr = value;
								resultPtr++;
							}
						p0 += pred2;
						p1 += pred2;
						p2 += pred2;
						p3 += pred2;
						p4 += pred2;
						p5 += pred2;
						p6 += pred2;
						p7 += pred2;
						center += pred2;
					}
			}
		else
			{
				p0 = center + _pPoints[5].x + _pPoints[5].y * columns;
				p2 = center + _pPoints[7].x + _pPoints[7].y * columns;
				p4 = center + _pPoints[1].x + _pPoints[1].y * columns;
				p6 = center + _pPoints[3].x + _pPoints[3].y * columns;

				for (int r=0;r<rows-pred2;r++)
					{
						for (int c=0;c<columns-pred2;c++)
							{
								value = 0;
								cntr = *center-1;

								//Unrolled loop
								compab_mask_inc(p1,1);
								compab_mask_inc(p3,3);
								compab_mask_inc(p5,5);
								compab_mask_inc(p7,7);
								
								compab_mask(intRound(p0,5,columns),0); //p0
								compab_mask(intRound(p2,7,columns),2); //p2
								compab_mask(intRound(p4,1,columns),4); //p4
								compab_mask(intRound(p6,3,columns),6); //p6
								p0++;
								p2++;
								p4++;
								p6++;
								center++;
								
								*resultPtr = value;
								resultPtr++;
							}
						p0 += pred2;
						p1 += pred2;
						p2 += pred2;
						p3 += pred2;
						p4 += pred2;
						p5 += pred2;
						p6 += pred2;
						p7 += pred2;
						center += pred2;
					}
			}
		return result;
	}
}}
