/** 
* @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) );
    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.xsize;
    int tm_ysize = img.ysize;

#if 0
    int xtl = example->x - window_size_x/2;
    int ytl = example->y - window_size_y/2;
    int xrb = example->x + window_size_x/2;
    int yrb = example->y + window_size_y/2;

    xtl = xtl * tm_xsize / xsize;
    ytl = ytl * tm_ysize / ysize;
    xrb = xrb * tm_xsize / xsize;
    yrb = yrb * tm_ysize / ysize;
#endif

    
    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.numChannels );
    assert ( img.data[bin] != NULL );

    int kA = xtl + ytl * tm_xsize;
    int kB = xrb + ytl * tm_xsize;
    int kC = xtl + yrb * tm_xsize;
    int kD = xrb + yrb * tm_xsize;
    A = img.data[bin][ kA ];
    B = img.data[bin][ kB ];
    C = img.data[bin][ kC ];
    D = img.data[bin][ kD ];

    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.numChannels );

	double val1 =  (D - B - C + A);
	A = img.data[bin2][ kA ];
	B = img.data[bin2][ kB ];
	C = img.data[bin2][ kC ];
	D = img.data[bin2][ kD ];

	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.numChannels ; b++)
	{
	    if ( b == bin ) continue;
	    A = img.data[b][ kA ];
	    B = img.data[b][ kB ];
	    C = img.data[b][ kC ];
	    D = img.data[b][ kD ];
	    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 ()
{
}