/* Computes the weight W of the edge f1->f2 using the float parameters as coefficients for the expression:
     * W = alpha * (f1->Q()+f2->Q())/2 + beta * f1\/f2 + delta * f1<-->start/end

     * f1= pos.f ; f2= pos.FFlip ;
     * f1->Q() = ambient occlusion value for the face f1
     ** The final weight of the edge should be lower for faces with higher ambient occlusion value.
     * f1\/f2 = dihedral angle between face f1 and f2: concave edges are priviledged.
     ** The final weight of the edge should be higher for convex edges and lower for concave ones.
     * f1<-->start = geodesic distance "dist" from the current face to the start face chosen by the user.
                     Using the "geomin" and "geomax" coefficients, edges that are at a distance
                     lower than geomin*dist or higher geomax*dist are given a higher weight
                     ..this will prevent trivial cuts
     ** weighted also on the length of the edge shared by f1 and f2.
    */
    double operator() (const vcg::face::Pos<CutFace> pos)
    {
        //sub-weights are normalized using previously computed max values
        return ((_elength_weight * vcg::Norm(pos.V()->P() - pos.VFlip()->P()))
                + (_geodesic_weight * GeodesicDistance(pos))
                + (_ambient_weight * (pos.f->Q() + pos.FFlip()->Q())/(2.0f*_maxq))
                + (_dihedral_weight * ( (M_PI + vcg::face::DihedralAngleRad<CutFace>(*(pos.f), pos.E())) / (2*M_PI))) );
    }
SeedFeature::SeedFeature( const ImageOverSegmentation & ios, const VectorXf & obj_param ) {
	Image rgb_im = ios.image();
	const RMatrixXs & s = ios.s();
	const int Ns = ios.Ns(), W = rgb_im.W(), H = rgb_im.H();
	
	// Initialize various values
	VectorXf area  = bin( s, 1, [&](int x, int y){ return 1.f; } );
	VectorXf norm = (area.array()+1e-10).cwiseInverse();
	pos_ = norm.asDiagonal() * bin( s, 6, [&](int i, int j){ float x=1.0*i/(W-1)-0.5,y=1.0*j/(H-1)-0.5; return makeArray<6>( x, y, x*x, y*y, fabs(x), fabs(y) ); } );
	if (N_DYNAMIC_COL) {
		Image lab_im;
		rgb2lab( lab_im, rgb_im );
		col_ = norm.asDiagonal() * bin( s, 6, [&](int x, int y){ return makeArray<6>( rgb_im(y,x, 0), rgb_im(y,x,1), rgb_im(y,x,2), lab_im(y,x,0), lab_im(y,x,1), lab_im(y,x,2) ); } );
	}
	
	const int N_GEO = sizeof(EDGE_P)/sizeof(EDGE_P[0]);
	for( int i=0; i<N_GEO; i++ )
		gdist_.push_back( GeodesicDistance(ios.edges(),ios.edgeWeights().array().pow(EDGE_P[i])+1e-3) );
	
	// Compute the static features
	static_f_ = RMatrixXf::Zero( Ns, N_STATIC_F );
	int o=0;
	// Add the position features
	static_f_.block( 0, o, Ns, N_STATIC_POS ) = pos_.leftCols( N_STATIC_POS );
	o += N_STATIC_POS;
	// Add the geodesic features
	if( N_STATIC_GEO >= N_GEO ) {
		RMatrixXu8 bnd = findBoundary( s );
		RMatrixXf mask = (bnd.array() == 0).cast<float>()*1e10;
		for( int i=0; i<N_GEO; i++ )
			static_f_.col( o++ ) = gdist_[i].compute( mask );
		for( int j=1; (j+1)*N_GEO<=N_STATIC_GEO; j++ ) {
			mask = (bnd.array() != j).cast<float>()*1e10;
			for( int i=0; i<N_GEO; i++ )
				static_f_.col( o++ ) = gdist_[i].compute( mask );
		}
	}
	if( N_STATIC_EDGE ) {
		RMatrixXf edge_map = DirectedSobel().detect( ios.image() );
		for( int j=0; j<s.rows(); j++ )
			for( int i=0; i<s.cols(); i++ ) {
				const int id = s(j,i);
				int bin = edge_map(j,i)*N_STATIC_EDGE;
				if ( bin < 0 ) bin = 0;
				if ( bin >= N_STATIC_EDGE ) bin = N_STATIC_EDGE-1;
				static_f_(id,o+bin) += norm[id];
			}
		o += N_STATIC_EDGE;
	}
	if( N_OBJ_F>1 )
		static_f_.col(o++) = (computeObjFeatures(ios)*obj_param).transpose();
	
	// Initialize the dynamic features
	dynamic_f_ = RMatrixXf::Zero( Ns, N_DYNAMIC_F );
	n_ = 0;
	min_dist_ = RMatrixXf::Ones(Ns,5)*10;
	var_      = RMatrixXf::Zero(Ns,6);
}