/** 
 * @file LocalFeatureCentrist.cpp
 * @brief Implementation of the LocalFeatureCentrist feature published in  "CENTRIST: A Visual Descriptor for Scene Categorization" (PAMI 2011)
 * @author Alexander Freytag
 * @date 12/06/2011
*/

// STL includes
#include <iostream>

// nice-vislearning includes
#include "vislearning/features/localfeatures/LocalFeatureCentrist.h"


using namespace OBJREC;
using namespace std;
using namespace NICE;

/* protected methods*/

/**
 * @brief: perform centrist transformation for a single pixel 
 * @author Alexander Freytag
 * @date 12/06/2011
 */
int LocalFeatureCentrist::CensusTransform(const NICE::Image & img, const int & x, const int & y) const
{
  int index(0);
  if (! img.isWithinImage(x,y)) return index;
  
  if( (img.isWithinImage(x-1,y-1)) && (img.getPixelInt(x,y)<=img.getPixelInt(x-1,y-1)) ) index |= 0x80;
  if( (img.isWithinImage(x-1,y))   && (img.getPixelInt(x,y)<=img.getPixelInt(x-1,y)) )   index |= 0x40;
  if( (img.isWithinImage(x-1,y+1)) && (img.getPixelInt(x,y)<=img.getPixelInt(x-1,y+1)) ) index |= 0x20;
  if( (img.isWithinImage(x,y-1))   && (img.getPixelInt(x,y)<=img.getPixelInt(x,y-1)) )   index |= 0x10;
  if( (img.isWithinImage(x,y+1))   && (img.getPixelInt(x,y)<=img.getPixelInt(x,y+1)) )   index |= 0x08;
  if( (img.isWithinImage(x+1,y-1)) && (img.getPixelInt(x,y)<=img.getPixelInt(x+1,y-1)) ) index |= 0x04;
  if( (img.isWithinImage(x+1,y))   && (img.getPixelInt(x,y)<=img.getPixelInt(x+1,y)) )   index |= 0x02;
  if( (img.isWithinImage(x+1,y+1)) && (img.getPixelInt(x,y)<=img.getPixelInt(x+1,y+1)) ) index |= 0x01;
  return index;
}

/**
 * @brief: perform centrist transformation for a single pixel 
 * @author Alexander Freytag
 * @date 12/06/2011
 */
int LocalFeatureCentrist::CensusTransform(const NICE::ColorImage & img, const int & x, const int & y, const int & channel) const
{
  int index(0);
  if (! img.isWithinImage(x,y)) return index;
  
  if( (img.isWithinImage(x-1,y-1)) && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x-1,y-1, channel)) ) index |= 0x80;
  if( (img.isWithinImage(x-1,y))   && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x-1,y, channel)) )   index |= 0x40;
  if( (img.isWithinImage(x-1,y+1)) && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x-1,y+1, channel)) ) index |= 0x20;
  if( (img.isWithinImage(x,y-1))   && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x,y-1, channel)) )   index |= 0x10;
  if( (img.isWithinImage(x,y+1))   && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x,y+1, channel)) )   index |= 0x08;
  if( (img.isWithinImage(x+1,y-1)) && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x+1,y-1, channel)) ) index |= 0x04;
  if( (img.isWithinImage(x+1,y))   && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x+1,y, channel)) )   index |= 0x02;
  if( (img.isWithinImage(x+1,y+1)) && (img.getPixelInt(x,y, channel)<=img.getPixelInt(x+1,y+1, channel)) ) index |= 0x01;
  return index;
}

/**
 * @brief: generate CT histogram for a given rectangle: pixels in [xi yi]-(xa,ya) -- including (xi,yi) but excluding (xa,ya)
 * @author Alexander Freytag
 * @date 12/06/2011
 */
void LocalFeatureCentrist::GenerateHistForOneRect(const NICE::Image & img, const int & xi, const int & xa, const int & yi, const int & ya, NICE::Vector & feature) const
{
// 	double pixelSum(0.0);
// 	double pixelSquare(0.0);
  feature.resize(256);
  feature.set(0.0);
  
  for (int j = yi; j < ya; j++)
  {
    for (int i = xi; i < xa; i++)
    {
// 			pixelSum += img.getPixelInt(i,j);
// 			pixelSquare += img.getPixelInt(i,j)*img.getPixelInt(i,j);
      feature[CensusTransform(img,i,j)]++;
    }
  }
  
  // normalize histogram to have 0 mean, remove the first and last entry, and normalize to have unit norm
  double sum(feature.Sum()/256.0);//shift more efficient?
  
  // normalize histogram to have 0 mean
  //but why?
  feature -= sum;
  
  //remove the first and last entry
  feature[0] = 0.0;
  feature[255] = 0.0;

  //normalization - here done using unit L2-norm
  feature.normalizeL2();
}

/**
 * @brief: generate CT histogram for a given rectangle: pixels in [xi yi]-(xa,ya) -- including (xi,yi) but excluding (xa,ya)
 * @author Alexander Freytag
 * @date 12/06/2011
 */
void LocalFeatureCentrist::GenerateHistForOneRect(const NICE::ColorImage & img, const int & xi, const int & xa, const int & yi, const int & ya, NICE::Vector & feature) const
{
// 	double pixelSum(0.0);
// 	double pixelSquare(0.0);
  int nrChannel (img.channels());
  feature.resize(256*nrChannel);
  feature.set(0.0);
  
  for (int channel = 0; channel < nrChannel; channel++)
  {
    for (int j = yi; j < ya; j++)
    {
      for (int i = xi; i < xa; i++)
      {
// 				pixelSum += img.getPixelInt(i,j, channel);
// 				pixelSquare += img.getPixelInt(i,j, channel)*img.getPixelInt(i,j, channel);
        feature[256*channel+this->CensusTransform(img,i,j,channel)]++;
      }
    }
  }

  // normalize histogram to have 0 mean, remove the first and last entry, and normalize to have unit norm
  double sum(feature.Sum()/256.0);//shift more efficient?
  
  // normalize histogram to have 0 mean
  //but why?
  feature -= sum;
  
  //remove the first and last entry
  feature[0] = 0.0;
  feature[255] = 0.0;

  //normalization - here done using unit L2-norm
  feature.normalizeL2();
}

/**
 * @brief Computes several CENTRIST descriptors for each of the given positions om a local neighborhood
 * @author Alexander Freytag
 * @date 12/06/2011
 */
void LocalFeatureCentrist::computeDesc( const NICE::Image & img, NICE::VVector & positions, NICE::VVector & descriptors ) const
{
  descriptors.clear();
  for (NICE::VVector::const_iterator it = positions.begin(); it != positions.end(); it++)
  {
    NICE::Vector descriptor;
    
    this->GenerateHistForOneRect(img, (*it)[0]-i_sizeNeighborhood/2, (*it)[0]+i_sizeNeighborhood/2, (*it)[1]-i_sizeNeighborhood/2, (*it)[1]+i_sizeNeighborhood/2, descriptor);
    
    descriptors.push_back(descriptor);
  }
}

/**
 * @brief Computes several CENTRIST descriptors for each of the given positions om a local neighborhood
 * @author Alexander Freytag
 * @date 12/06/2011
 */
void LocalFeatureCentrist::computeDesc( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors ) const
{
  descriptors.clear();
  for (NICE::VVector::const_iterator it = positions.begin(); it != positions.end(); it++)
  {
    NICE::Vector descriptor;
    
    this->GenerateHistForOneRect(img, (*it)[0]-i_sizeNeighborhood/2, (*it)[0]+i_sizeNeighborhood/2, (*it)[1]-i_sizeNeighborhood/2, (*it)[1]+i_sizeNeighborhood/2, descriptor);
    
    descriptors.push_back(descriptor);
  }
}

/* public methods*/

/**
 * @brief Default constructor
 * @author Alexander Freytag
 * @date 12/06/2011
 */
LocalFeatureCentrist::LocalFeatureCentrist()
{
  this->i_sizeNeighborhood = 16;
}

/**
 * @brief Recommended constructor
 * @author Alexander Freytag
 * @date 12/06/2011
 */
LocalFeatureCentrist::LocalFeatureCentrist( const Config *conf, const std::string & section )
{
  this->i_sizeNeighborhood = conf->gI(section, "i_sizeNeighborhood", 16);
}

/**
 * @brief Default destructor
 * @author Alexander Freytag
 * @date 12/06/2011
 */
LocalFeatureCentrist::~LocalFeatureCentrist()
{
}

int LocalFeatureCentrist::getDescSize() const
{
  // we have 8 neighbors, so the size is always 256 with the given implementation (without spatial levels, ...)
  return 256;
}

/**
 * @brief Computes several CENTRIST descriptors for each of the given positions on a local neighborhood
 * @author Alexander Freytag
 * @date 12/06/2011
 */
int LocalFeatureCentrist::getDescriptors ( const NICE::Image & img, VVector & positions, VVector & descriptors ) const
{
  this->computeDesc( img, positions, descriptors );
  return 0;
}

/**
 * @brief Computes several CENTRIST descriptors for each of the given positions on a local neighborhood
 * @author Alexander Freytag
 * @date 12/06/2011
 */
int LocalFeatureCentrist::getDescriptors ( const NICE::ColorImage & img, NICE::VVector & positions, NICE::VVector & descriptors) const
{
  this->computeDesc( img, positions, descriptors );
  return 0;
}

/**
 * @brief Visualizes CENTRIST descriptors, not implemented yet!
 * @author Alexander Freytag
 * @date 12/06/2011
 */
void LocalFeatureCentrist::visualizeFeatures ( NICE::Image & mark,
                                               const VVector & positions,
                                               size_t color ) const
{
  throw NICE::Exception ( "LocalFeatureCentrist::visualizeFeatures -- currently not implemented." );
}

