Proposals nms( const Proposals & proposals, const std::vector<int> & order, float max_iou ) {
	IOUSet iou_set( proposals.s );
	
	std::vector< VectorXb > r;
	for( int i: order ) {
		VectorXb p = proposals.p.row( i );
		// Run NMS
		if( p.any() && iou_set.addIfNotIntersects(p,max_iou) )
			r.push_back( p );
	}
	
	Proposals res;
	res.s = proposals.s;
	res.p = RMatrixXb( r.size(), proposals.p.cols() );
	for( int j=0; j<r.size(); j++ )
		res.p.row(j) = r[j];
	return res;
}
std::vector<Proposals> nms( const std::vector<Proposals> & proposals, const std::vector<int> & order, float max_iou ) {
	int N = 0;
	for( const Proposals & p: proposals )
		N += p.p.rows();
	std::vector<int> s_id( N ), p_id( N );
	for( int i=0, k=0; i<proposals.size(); i++ )
		for( int j=0; j<proposals[i].p.rows(); j++, k++ ) {
			s_id[k] = i;
			p_id[k] = j;
		}

	std::vector<RMatrixXs> s;
	std::vector<int> Ns;
	for( int i=0; i<proposals.size(); i++ ) {
		s.push_back( proposals[i].s );
		Ns.push_back( proposals[i].s.maxCoeff()+1 );
	}
	const RMatrixXi ms = mergeOverSegmentations(s);
	const int Nms = ms.maxCoeff()+1;
	
	VectorXu ms_area = VectorXu::Zero( Nms );
	for( int j=0; j<ms.rows(); j++ )
		for( int i=0; i<ms.cols(); i++ )
			ms_area[ ms(j,i) ]++;
	
	std::vector<IOUSet> iou_set;
	std::vector<VectorXs> ids;
	for( int i=0; i<s.size(); i++ ) {
		iou_set.push_back( s[i] );
		ids.push_back( map_id( ms, s[i] ) );
	}
	
	std::vector<int> pb;
	pb.reserve( Nms );
	
	std::vector< std::vector< VectorXb > > r( proposals.size() );
	for( int i: order ) {
		VectorXb p = proposals[ s_id[i] ].p.row( p_id[i] );
		// Run NMS
		if( !p.any() || iou_set[ s_id[i] ].intersects(p,max_iou) )
			continue;
		
		Vector4s bbox = iou_set[ s_id[i] ].computeBBox( p );
		
		// Project each segmentation onto the common OS
		pb.clear();
		for( int j=0; j<Nms; j++ )
			if( p[ ids[ s_id[i] ][j] ] )
				pb.push_back( j );
		
		bool intersects = false;
		for( int k=0; !intersects && k<iou_set.size(); k++ ) 
			if( s_id[i] != k ){
				// Reproject the common OS to the current os
				VectorXu p_area = VectorXu::Zero( Ns[k] );
				for( int j: pb )
					p_area[ ids[ k ][j] ] += ms_area[ j ];
				// Run more NMS
				intersects = iou_set[k].intersects(p_area, bbox, max_iou);
			}
		if( !intersects ) {
			// Add the segment
			iou_set[ s_id[i] ].add( p );
			r[ s_id[i] ].push_back( p );
		}
	}
	
	
	std::vector<Proposals> res( proposals.size() );
	for( int i=0; i<proposals.size(); i++ ) {
		res[i].s = proposals[i].s;
		res[i].p = RMatrixXb( r[i].size(), proposals[i].p.cols() );
		for( int j=0; j<r[i].size(); j++ )
			res[i].p.row(j) = r[i][j];
	}
	return res;
}