All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.factory.feature.associate.FactoryAssociation Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2020, 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.factory.feature.associate;

import boofcv.abst.feature.associate.*;
import boofcv.abst.feature.describe.DescriptorInfo;
import boofcv.alg.descriptor.KdTreeTuple_F64;
import boofcv.alg.feature.associate.*;
import boofcv.concurrency.BoofConcurrency;
import boofcv.struct.feature.*;
import org.ddogleg.nn.FactoryNearestNeighbor;
import org.ddogleg.nn.NearestNeighbor;

import javax.annotation.Nullable;


/**
 * Creates algorithms for associating {@link boofcv.struct.feature.TupleDesc_F64} features.
 *
 * @author Peter Abeles
 */
@SuppressWarnings("unchecked")
public class FactoryAssociation {

	public static  AssociateDescription generic( ConfigAssociate config, DescriptorInfo info )
	{
		int DOF = info.createDescription().size();

		switch( config.type ) {
			case GREEDY: {
				ScoreAssociation scorer = FactoryAssociation.defaultScore(info.getDescriptionType());
				return FactoryAssociation.greedy(config.greedy,scorer);
			}
			case KD_TREE: return (AssociateDescription)FactoryAssociation.kdtree(config.nearestNeighbor,DOF);
			case RANDOM_FOREST: return (AssociateDescription)FactoryAssociation.kdRandomForest(
					config.nearestNeighbor,DOF, 10, 5, 1233445565);
			default: throw new IllegalArgumentException("Unknown association: "+config.type);
		}
	}

	/**
	 * Checks and if neccisary wraps the association to ensure that it returns only unique associations
	 */
	public static  AssociateDescription ensureUnique( AssociateDescription associate )
	{
		if( !associate.uniqueDestination() || !associate.uniqueSource() ) {
			return new EnforceUniqueByScore.Describe<>(associate,true,true);
		} else {
			return associate;
		}
	}

	/**
	 * Checks and if neccisary wraps the association to ensure that it returns only unique associations
	 */
	public static  AssociateDescription2D ensureUnique( AssociateDescription2D associate )
	{
		if( !associate.uniqueDestination() || !associate.uniqueSource() ) {
			return new EnforceUniqueByScore.Describe2D<>(associate,true,true);
		} else {
			return associate;
		}
	}

	/**
	 * Returns an algorithm for associating features together which uses a brute force greedy algorithm.
	 * See {@link AssociateGreedy} for details.
	 * 
	 * @param score Computes the fit score between two features.
	 * @param config Configuration
	 * @param  Data structure being associated
	 * @return AssociateDescription
	 */
	public static  AssociateDescription
	greedy( ConfigAssociateGreedy config, ScoreAssociation score )
	{
		AssociateGreedyBase alg;

		if(BoofConcurrency.USE_CONCURRENT ) {
			alg = new AssociateGreedy_MT<>(score, config.forwardsBackwards);
		} else {
			alg = new AssociateGreedy<>(score, config.forwardsBackwards);
		}
		alg.setMaxFitError(config.maxErrorThreshold);
		alg.setRatioTest(config.scoreRatioThreshold);
		return new WrapAssociateGreedy<>(alg);
	}


	/**
	 * Approximate association using a K-D tree degree of moderate size (10-15) that uses a best-bin-first search
	 * order.
	 *
	 * @see AssociateNearestNeighbor_ST
	 * @see org.ddogleg.nn.alg.searches.KdTreeSearch1Bbf
	 *
	 * @param dimension Number of elements in the feature vector
	 * @return Association using approximate nearest neighbor
	 */
	public static AssociateDescription kdtree(
			@Nullable ConfigAssociateNearestNeighbor configNN , int dimension ) {
		NearestNeighbor nn = FactoryNearestNeighbor.kdtree(new KdTreeTuple_F64(dimension),configNN.maxNodesSearched);

		return associateNearestNeighbor(configNN,nn);
	}

	/**
	 * Approximate association using multiple random K-D trees (random forest) for descriptors with a high degree of
	 * freedom, e.g. > 20
	 *
	 * @see AssociateNearestNeighbor_ST
	 * @see org.ddogleg.nn.wrap.KdForestBbfNearestNeighbor
	 *
	 * @param dimension Number of elements in the feature vector
	 * @param numTrees Number of trees that are considered.  Try 10 and tune.
	 * @param numConsiderSplit Number of nodes that are considered when generating a tree.  Must be less than the
	 *                         point's dimension.  Try 5
	 * @param randomSeed Seed used by random number generator
	 * @return Association using approximate nearest neighbor
	 */
	public static AssociateDescription kdRandomForest( @Nullable ConfigAssociateNearestNeighbor configNN ,
																	  int dimension,
																	  int numTrees ,
																	  int numConsiderSplit ,
																	  long randomSeed) {
		if( configNN == null )
			configNN = new ConfigAssociateNearestNeighbor();
		NearestNeighbor nn = FactoryNearestNeighbor.kdRandomForest(
				new KdTreeTuple_F64(dimension),
				configNN.maxNodesSearched,numTrees,numConsiderSplit,randomSeed);

		return associateNearestNeighbor(configNN,nn);
	}

	public static AssociateNearestNeighbor
	associateNearestNeighbor( @Nullable ConfigAssociateNearestNeighbor config ,  NearestNeighbor nn )
	{
		if( config == null )
			config = new ConfigAssociateNearestNeighbor();

		config.checkValidity();

		AssociateNearestNeighbor assoc;
		if( BoofConcurrency.USE_CONCURRENT ) {
			assoc = new AssociateNearestNeighbor_MT<>(nn);
		} else {
			assoc = new AssociateNearestNeighbor_ST<>(nn);
		}
		assoc.setRatioUsesSqrt(config.distanceIsSquared);
		assoc.setMaxScoreThreshold(config.maxErrorThreshold);
		assoc.setScoreRatioThreshold(config.scoreRatioThreshold);
		return assoc;
	}

	/**
	 * Given a feature descriptor type it returns a "reasonable" default {@link ScoreAssociation}.
	 *
	 * @param tupleType Class type which extends {@link boofcv.struct.feature.TupleDesc}
	 * @return A class which can score two potential associations
	 */
	public static 
	ScoreAssociation defaultScore( Class tupleType ) {
		if( NccFeature.class.isAssignableFrom(tupleType) ) {
			return (ScoreAssociation)new ScoreAssociateNccFeature();
		} else if( TupleDesc_F64.class.isAssignableFrom(tupleType) ) {
			return (ScoreAssociation)new ScoreAssociateEuclideanSq_F64();
		} else if( tupleType == TupleDesc_F32.class ) {
			return (ScoreAssociation)new ScoreAssociateEuclideanSq_F32();
		} else if( tupleType == TupleDesc_U8.class ) {
			return (ScoreAssociation)new ScoreAssociateSad_U8();
		} else if( tupleType == TupleDesc_B.class  ) {
			return (ScoreAssociation)new ScoreAssociateHamming_B();
		} else {
			throw new IllegalArgumentException("Unknown tuple type: "+tupleType);
		}
	}

	/**
	 * Scores features based on Sum of Absolute Difference (SAD).
	 *
	 * @param tupleType Type of descriptor being scored
	 * @return SAD scorer
	 */
	public static 
	ScoreAssociation scoreSad( Class tupleType ) {
		if( TupleDesc_F64.class.isAssignableFrom(tupleType) ) {
			return (ScoreAssociation)new ScoreAssociateSad_F64();
		} else if( tupleType == TupleDesc_F32.class ) {
			return (ScoreAssociation)new ScoreAssociateSad_F32();
		} else if( tupleType == TupleDesc_U8.class ) {
			return (ScoreAssociation)new ScoreAssociateSad_U8();
		} else if( tupleType == TupleDesc_S8.class ) {
			return (ScoreAssociation)new ScoreAssociateSad_S8();
		} else {
			throw new IllegalArgumentException("SAD score not supported for type "+tupleType.getSimpleName());
		}
	}

	/**
	 * Scores features based on their Normalized Cross-Correlation (NCC).
	 *
	 * @return NCC score
	 */
	public static ScoreAssociation scoreNcc() {
		return new ScoreAssociateNccFeature();
	}

	/**
	 * Scores features based on the Euclidean distance between them.  The square is often used instead
	 * of the Euclidean distance since it is much faster to compute.
	 *
	 * @param tupleType Type of descriptor being scored
	 * @param squared IF true the distance squared is returned.  Usually true
	 * @return Euclidean distance measure
	 */
	public static 
	ScoreAssociation scoreEuclidean( Class tupleType , boolean squared ) {
		if( TupleDesc_F64.class.isAssignableFrom(tupleType) ) {
			if( squared )
				return (ScoreAssociation)new ScoreAssociateEuclideanSq_F64();
			else
				return (ScoreAssociation)new ScoreAssociateEuclidean_F64();
		} else if( tupleType == TupleDesc_F32.class ) {
			if( squared )
				return (ScoreAssociation)new ScoreAssociateEuclideanSq_F32();
		}

		throw new IllegalArgumentException("Euclidean score not yet supported for type "+tupleType.getSimpleName());
	}

	/**
	 * Hamming distance between two binary descriptors.
	 *
	 * @param tupleType Type of descriptor being scored
	 * @return Hamming distance measure
	 */
	public static 
	ScoreAssociation scoreHamming( Class tupleType ) {
		if( tupleType == TupleDesc_B.class ) {
			return (ScoreAssociation)new ScoreAssociateHamming_B();
		}

		throw new IllegalArgumentException("Hamming distance not yet supported for type "+tupleType.getSimpleName());
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy