/**
* @file EOHFeature.cpp
* @brief edge orientation histograms (Levi and Weiss 2004)
* @author Erik Rodner
* @date 05/07/2008
*/
#include <iostream>

#include "EOHFeature.h"
#include "vislearning/cbaselib/FeaturePool.h"

using namespace OBJREC;

const double epsilon = 10e-8;

using namespace std;
using namespace NICE;



/** simple constructor */
EOHFeature::EOHFeature ( const Config *conf )
{
  window_size_x = conf->gI ( "EOHFeature", "window_size_x", 21 );
  window_size_y = conf->gI ( "EOHFeature", "window_size_y", 21 );
  scaleStep = conf->gD ( "EOHFeature", "scale_step", sqrt ( 2.0f ) );
  numScales = conf->gI ( "EOHFeature", "num_scales", 5 );
  numBins = conf->gI ( "EOHFeature", "num_bins", 9 );

  bin  = 0;
  bin2 = 0;
  type = EOH_VALUE;
}

/** simple destructor */
EOHFeature::~EOHFeature()
{
}

double EOHFeature::val ( const Example *example ) const
{
  const NICE::MultiChannelImageT<double> & img = example->ce->getDChannel (
        CachedExample::D_INTEGRALEOH );

  int xsize;
  int ysize;
  example->ce->getImageSize ( xsize, ysize );
  int tm_xsize = img.width();
  int tm_ysize = img.height();

  int wsx2, wsy2;
  int xx, yy;

  int exwidth = example->width;
  if ( exwidth == 0 ) {
    wsx2 = window_size_x * tm_xsize / ( 2 * xsize );
    wsy2 = window_size_y * tm_ysize / ( 2 * ysize );
  } else {
    int exheight = example->height;
    wsx2 = exwidth * tm_xsize / ( 2 * xsize );
    wsy2 = exheight * tm_ysize / ( 2 * ysize );
  }

  xx = ( example->x ) * tm_xsize / xsize;
  yy = ( example->y ) * tm_ysize / ysize;

  int xtl = xx - wsx2;
  int ytl = yy - wsy2;
  int xrb = xx + wsx2;
  int yrb = yy + wsy2;

#define BOUND(x,min,max) (((x)<(min))?(min):((x)>(max)?(max):(x)))
  xtl = BOUND ( xtl, 0, tm_xsize - 1 );
  ytl = BOUND ( ytl, 0, tm_ysize - 1 );
  xrb = BOUND ( xrb, 0, tm_xsize - 1 );
  yrb = BOUND ( yrb, 0, tm_ysize - 1 );
#undef BOUND

  double A, B, C, D;

  assert ( bin < ( int ) img.channels() );

  A = img.get ( xtl, ytl, bin );
  B = img.get ( xrb, ytl, bin );
  C = img.get ( xtl, yrb, bin );
  D = img.get ( xrb, yrb, bin );

  if ( type == EOH_VALUE )
  {
    int area = ( xrb - xtl ) * ( yrb - ytl );

    if ( area == 0 )
      return 0.0;
    else {
      /* A B
         C D  */
      double value = ( D - B - C + A ) / area;

      return value;
    }
  }
  else if ( type == EOH_RATIO )
  {
    assert ( bin2 < ( int ) img.channels() );

    double val1 = ( D - B - C + A );
    A = img.get ( xtl, ytl, bin2 );
    B = img.get ( xrb, ytl, bin2 );
    C = img.get ( xtl, yrb, bin2 );
    D = img.get ( xrb, yrb, bin2 );

    double val2 = ( D - B - C + A );

    return ( val1 + epsilon ) / ( val2 + epsilon );
  }
  else if ( type == EOH_DOMINANT_ORIENTATION )
  {
    double val1 = ( D - B - C + A );
    double sum = val1;
    for ( int b = 0 ; b < ( int ) img.channels() ; b++ )
    {
      if ( b == bin ) 
        continue;
      A = img.get ( xtl, ytl, b );
      B = img.get ( xrb, ytl, b );
      C = img.get ( xtl, yrb, b );
      D = img.get ( xrb, yrb, b );
      sum += ( D - B - C + A );
    }

    return ( val1 + epsilon ) / ( sum + epsilon );
  }

  assert ( 1 == 0 );
}

void EOHFeature::explode ( FeaturePool & featurePool, bool variableWindow ) const
{
  int nScales = ( variableWindow ? numScales : 1 );

  for ( int i = 0 ; i < nScales ; i++ )
  {
    int wsy = window_size_y;
    int wsx = window_size_x;
    for ( int _type = 0 ; _type < EOH_NUMTYPES; _type++ )
    {
      if ( ( _type == EOH_VALUE ) || ( _type == EOH_DOMINANT_ORIENTATION ) )
      {
        for ( int _bin = 0 ; _bin < numBins ; _bin++ )
        {
          EOHFeature *f = new EOHFeature();
          f->window_size_x = wsx;
          f->window_size_y = wsy;
          f->bin = _bin;
          f->type = _type;
          featurePool.addFeature ( f, 1.0 / ( EOH_NUMTYPES * numBins * nScales ) );
        }
      }

      if ( ( _type == EOH_RATIO ) )
      {
        for ( int _bin = 0 ; _bin < numBins ; _bin++ )
        {
          for ( int _bin2 = 0 ; _bin2 < numBins ; _bin2++ )
          {
            if ( bin == bin2 ) continue;

            EOHFeature *f = new EOHFeature();
            f->window_size_x = wsx;
            f->window_size_y = wsy;
            f->bin = _bin;
            f->bin2 = _bin2;
            f->type = _type;
            featurePool.addFeature ( f, 1.0 / ( EOH_NUMTYPES * ( numBins - 1 ) * numBins * nScales ) );
          }
        }
      }
    }

    wsx = ( int ) ( scaleStep * wsx );
    wsy = ( int ) ( scaleStep * wsy );
  }
}

Feature *EOHFeature::clone() const
{
  EOHFeature *f = new EOHFeature();
  f->window_size_x = window_size_x;
  f->window_size_y = window_size_y;
  f->bin = bin;
  f->bin2 = bin2;
  f->type = type;

  return f;
}

Feature *EOHFeature::generateFirstParameter () const
{
  return clone();
}

void EOHFeature::restore ( istream & is, int format )
{
  is >> window_size_x;
  is >> window_size_y;
  is >> type;
  is >> bin;
  is >> bin2;
}

void EOHFeature::store ( ostream & os, int format ) const
{
  os << "EOHFEATURE "
  << window_size_x << " "
  << window_size_y << " "
  << type << " "
  << bin << " "
  << bin2;
}

void EOHFeature::clear ()
{
}
