RMatrixXf SeedFeature::computeObjFeatures( const ImageOverSegmentation & ios ) {
	Image rgb_im = ios.image();
	const RMatrixXs & s = ios.s();
	const Edges & g = ios.edges();
	const int Ns = ios.Ns();
	
	RMatrixXf r = RMatrixXf::Zero( Ns, N_OBJ_F );
	if( N_OBJ_F<=1 ) return r;
	VectorXf area  = bin( s, 1, [&](int x, int y){ return 1.f; } );
	VectorXf norm = (area.array()+1e-10).cwiseInverse();
	
	r.col(0).setOnes();
	int o = 1;
	if (N_OBJ_COL>=6) {
		Image lab_im;
		rgb2lab( lab_im, rgb_im );
		r.middleCols(o,6) = norm.asDiagonal() * bin( s, 6, [&](int x, int y){ return makeArray<6>( lab_im(y,x,0), lab_im(y,x,1), lab_im(y,x,2), lab_im(y,x,0)*lab_im(y,x,0), lab_im(y,x,1)*lab_im(y,x,1), lab_im(y,x,2)*lab_im(y,x,2) ); } );
		RMatrixXf col = r.middleCols(o,3);
		if( N_OBJ_COL >= 9)
			r.middleCols(o+6,3) = col.array().square();
		o += N_OBJ_COL;
		
		// Add color difference features
		if( N_OBJ_COL_DIFF ) {
			RMatrixXf bcol = RMatrixXf::Ones( col.rows(), col.cols()+1 );
			bcol.leftCols(3) = col;
			for( int it=0; it*3+2<N_OBJ_COL_DIFF; it++ ) {
				// Apply a box filter on the graph
				RMatrixXf tmp = bcol;
				for( const auto & e: g ) {
					tmp.row(e.a) += bcol.row(e.b);
					tmp.row(e.b) += bcol.row(e.a);
				}
				bcol = tmp.col(3).cwiseInverse().asDiagonal()*tmp;
				r.middleCols(o,3) = (bcol.leftCols(3)-col).array().abs();
				o += 3;
			}
		}
	}
	if( N_OBJ_POS >= 2 ) {
		RMatrixXf xy = norm.asDiagonal() * bin( s, 2, [&](int x, int y){ return makeArray<2>( 1.0*x/(s.cols()-1)-0.5, 1.0*y/(s.rows()-1)-0.5 ); } );
		r.middleCols(o,2) = xy;
		o+=2;
		if( N_OBJ_POS >=4 ) {
			r.middleCols(o,2) = xy.array().square();
			o+=2;
		}
	}
	if( N_OBJ_EDGE ) {
		RMatrixXf edge_map = DirectedSobel().detect( rgb_im );
		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_OBJ_EDGE;
				if ( bin < 0 ) bin = 0;
				if ( bin >= N_OBJ_EDGE ) bin = N_OBJ_EDGE-1;
				r(id,o+bin) += norm[id];
			}
		o += N_OBJ_EDGE;
	}
	const int N_BASIC = o-1;
	// Add in context features
	for( int i=0; i<N_OBJ_CONTEXT; i++ ) {
		const int o0 = o - N_BASIC;
		// Box filter the edges
		RMatrixXf f = RMatrixXf::Ones( Ns, N_BASIC+1 ), bf = RMatrixXf::Zero( Ns, N_BASIC+1 );
		f.rightCols( N_BASIC ) = r.middleCols(o0,N_BASIC);
		for( Edge e: g ) {
			bf.row(e.a) += f.row(e.b);
			bf.row(e.b) += f.row(e.a);
		}
		r.middleCols(o,N_BASIC) = bf.col(0).array().max(1e-10f).inverse().matrix().asDiagonal() * bf.rightCols(N_BASIC);
		o += N_BASIC;
	}
	return r;
}
RMatrixXf GeodesicDistance::compute( const RMatrixXf &start ) const {
	RMatrixXf r = 0*start;
	for( int i=0; i<start.cols(); i++ )
		r.col(i) = compute( (VectorXf)start.col(i) );
	return r;
}