/*
 * Copyright (c) 2011-2014, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.feature.associate;

import boofcv.abst.feature.associate.AssociateDescription2D;
import boofcv.abst.feature.associate.ScoreAssociation;
import boofcv.struct.feature.AssociatedIndex;
import boofcv.struct.feature.MatchScoreType;
import georegression.struct.point.Point2D_F64;
import org.ddogleg.struct.FastQueue;
import org.ddogleg.struct.GrowQueue_I32;

/**
 * Base class for algorithms which consider all possible associations but perform a quick distance calculation
 * to remove unlikely matches before computing the more expensive fit score between two descriptions.  The
 * maxDistance is the upper limit and features with a distance greater than maxDistance are rejected.  Maximum
 * error is exclusive and a match must have an error which is less than the max error.
 *
 * By default the max-distance and max error are set to Double.MAX_VALUE.
 *
 * @author Peter Abeles
 */
public abstract class BaseAssociateLocation2DFilter implements AssociateDescription2D {
	// computes association score
	private ScoreAssociation scoreAssociation;

	// maximum allowed distance from the epipolar line
	protected double maxDistance = Double.MAX_VALUE;

	// the largest allowed error
	protected double maxError = Double.MAX_VALUE;

	// input lists
	private FastQueue locationSrc;
	private FastQueue descSrc;
	private FastQueue locationDst;
	private FastQueue descDst;

	// list of source features not associated
	private GrowQueue_I32 unassociatedSrc = new GrowQueue_I32();

	// list of features that have been matched with each other
	private FastQueue matched = new FastQueue(10,AssociatedIndex.class,true);

	// creates a list of unassociated features from the list of matches
	private FindUnassociated unassociated = new FindUnassociated();

	// is backwards validation performed during association?
	private boolean backwardsValidation = true;

	/**
	 * Specifies score mechanism
	 *
	 * @param scoreAssociation How features are scored.
	 * @param backwardsValidation Require that matches are mutual in forward/backwards directions
	 * @param maxError Maximum allowed association error
	 */
	protected BaseAssociateLocation2DFilter( ScoreAssociation scoreAssociation ,
											 boolean backwardsValidation ,
											 double maxError )
	{
		this.scoreAssociation = scoreAssociation;
		this.backwardsValidation = backwardsValidation;
		this.maxError = maxError;
	}

	public double getMaxDistance() {
		return maxDistance;
	}

	public void setMaxDistance(double maxDistance) {
		this.maxDistance = maxDistance;
	}

	@Override
	public void setSource(FastQueue location, FastQueue descriptions) {
		if( location.size() != descriptions.size() )
			throw new IllegalArgumentException("The two lists must be the same size");

		this.locationSrc = location;
		this.descSrc = descriptions;
	}

	@Override
	public void setDestination(FastQueue location, FastQueue descriptions) {
		if( location.size() != descriptions.size() )
			throw new IllegalArgumentException("The two lists must be the same size");

		this.locationDst = location;
		this.descDst = descriptions;
	}

	protected abstract void setActiveSource( Point2D_F64 p );

	protected abstract double computeDistanceToSource( Point2D_F64 p );

	@Override
	public void associate() {

		unassociatedSrc.reset();
		matched.reset();

		for( int i = 0; i < locationSrc.size(); i++ ) {
			Point2D_F64 p_s = locationSrc.get(i);
			D d_s = descSrc.get(i);
			setActiveSource(p_s);

			double bestScore = maxError;
			int bestIndex = -1;

			// find the best match in destination list
			for( int j = 0; j < locationDst.size(); j++ ) {
				D d_d = descDst.get(j);

				// compute distance between the two features
				double distance = computeDistanceToSource(locationDst.get(j));
				if( distance > maxDistance )
					continue;

				double score = scoreAssociation.score(d_s,d_d);
				if( score < bestScore ) {
					bestScore = score;
					bestIndex = j;
				}
			}

			if( bestIndex == -1 ) {
				unassociatedSrc.add(i);
				continue;
			}

			if( backwardsValidation &&  !backwardsValidation(i, bestIndex)) {
				unassociatedSrc.add(i);
				continue;
			}

			AssociatedIndex m = matched.grow();
			m.src = i;
			m.dst = bestIndex;
			m.fitScore = bestScore;
		}
	}

	/**
	 * Finds the best match for an index in destination and sees if it matches the source index
	 *
	 * @param indexSrc The index in source being examined
	 * @param bestIndex Index in dst with the best fit to source
	 * @return true if a match was found and false if not
	 */
	private boolean backwardsValidation(int indexSrc, int bestIndex) {
		double bestScoreV = maxError;
		int bestIndexV = -1;

		D d_forward = descDst.get(bestIndex);
		setActiveSource(locationDst.get(bestIndex));

		for( int j = 0; j < locationSrc.size(); j++ ) {

			// compute distance between the two features
			double distance = computeDistanceToSource(locationSrc.get(j));
			if( distance > maxDistance )
				continue;

			D d_v = descSrc.get(j);

			double score = scoreAssociation.score(d_forward,d_v);
			if( score < bestScoreV ) {
				bestScoreV = score;
				bestIndexV = j;
			}
		}

		return bestIndexV == indexSrc;
	}

	@Override
	public FastQueue getMatches() {
		return matched;
	}

	@Override
	public GrowQueue_I32 getUnassociatedSource() {
		return unassociatedSrc;
	}

	@Override
	public GrowQueue_I32 getUnassociatedDestination() {
		return unassociated.checkDestination(matched,locationDst.size());
	}

	@Override
	public void setThreshold(double score) {
		maxError = score;
	}

	@Override
	public MatchScoreType getScoreType() {
		return scoreAssociation.getScoreType();
	}

	@Override
	public boolean uniqueSource() {
		return true;
	}

	@Override
	public boolean uniqueDestination() {
		return false;
	}
}

 

Ads help maintain this website.