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

boofcv.alg.feature.describe.llah.LlahHasher Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 1.1.7
Show newest version
/*
 * 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.alg.feature.describe.llah;

import boofcv.alg.geo.PerspectiveOps;
import georegression.struct.point.Point2D_F64;
import lombok.Getter;
import lombok.Setter;
import org.ddogleg.combinatorics.Combinations;
import org.ddogleg.util.PrimitiveArrays;

import java.util.List;

/**
 * Functions related to computing the hash values of a LLAH feature. This is done by looking at the invariant
 * geometry between points and computing a hash function from their values.
 *
 * @author Peter Abeles
 */
public abstract class LlahHasher {

	/**
	 * Recommended K from the paper
	 */
	public static int DEFAULT_HASH_K = 25;
	/**
	 * The recommended hash size from the paper
	 */
	public static int DEFAULT_HASH_SIZE = 12_800_000;

	/**
	 * Defines the look up table. A binary search is used to effectively find the index of a value
	 */
	@Setter @Getter protected double[] samples;

	/**
	 * k^i in the hash function
	 */
	private long hashK;
	/**
	 * The maximum value of the hash code
	 */
	private int hashSize;

	// Used to compute all the combinations of a set
	private Combinations combinator = new Combinations<>();

	/**
	 * Configures the hash function. See JavaDoc for info on variables
	 */
	public LlahHasher(long hashK, int hashSize) {
		this.hashK = hashK;
		this.hashSize = hashSize;
	}

	/**
	 * Returns the number of invariants given the number of points.
	 * @param numPoints Number of points the hash function is computed from
	 * @return Number of invariants the feature will have
	 */
	public int getNumberOfInvariants(int numPoints ) {
		return (int)Combinations.computeTotalCombinations(numPoints, getInvariantSampleSize());
	}

	/**
	 * Computes the hashcode and invariant values. Stores result in output
	 *
	 * @param points Set of points. Must be ≥ 4.
	 */
	public void computeHash(List points , LlahFeature output ) {
		int N = getInvariantSampleSize();
		if( points.size() < N )
			throw new IllegalArgumentException("Must be at least 5 points and not "+points.size());
		combinator.init(points,N);
		long hash = 0;
		int i = 0;
		long k = 1;
		do {
			double invariant = computeInvariant(combinator);
			int r = output.invariants[i++] = discretize(invariant);
			hash += r*k;
			k *= hashK;
		} while( combinator.next() );

		output.hashCode = (int)(hash % hashSize);
	}

	/**
	 * Stores the computed invariants into an array
	 */
	public void computeInvariants( List points , double[] invariants, int offset ) {
		int N = getInvariantSampleSize();
		combinator.init(points,N);
		int i = 0;
		do {
			invariants[offset+i++] = computeInvariant(combinator);
		} while( combinator.next() );
	}

	/**
	 * Number of points required to compute the invariants
	 */
	protected abstract int getInvariantSampleSize();

	/**
	 * Computes the invariants given the set of points
	 */
	protected abstract double computeInvariant(Combinations combinator);

	/**
	 * Computes the discrete value from the continuous valued invariant
	 */
	public int discretize( double invariant ) {
		return PrimitiveArrays.lowerBound(samples,0,samples.length,invariant);
	}

	/**
	 * Create a lookup table by sorting then sampling the invariants. This will have the desired property of
	 * having a denser set of points where there is a higher density of values. A histogram is required instead
	 * of raw values because it becomes intractable quickly for even only a few documents if it's an array..
	 *
	 * @param histogram Histogram of invariant values from 0 to maxValue
	 * @param histLength Histogram length.
	 * @param histMaxValue The maximum value in the histogram
	 * @param numDiscrete Number of possible discrete values. Larger values indicate higher resolution in discretation
	 */
	public void learnDiscretization( int[] histogram , int histLength , double histMaxValue, int numDiscrete ) {
		this.samples = new double[numDiscrete-1];

		// Number of hits in the histogram
		int total = 0;
		for (int i = 0; i < histLength; i++) {
			total += histogram[i];
		}

		// samples is designed so that any value form 0.0 to samples[0] will have a value of 0
		// then any value greater than samples[N-1] wil have a value of N
		int locHist = 0;
		for (int i = 1, j = 0; i < numDiscrete; i++) {
			int target = (total-1)*i/numDiscrete;

			while( locHist < target ) {
				locHist += histogram[j++];
			}
			samples[i-1] = j*histMaxValue/histLength;
		}
	}

	/**
	 * Returns the number of possible values
	 */
	public int getNumValues() {
		return samples.length;
	}

	public static class Affine extends LlahHasher {

		public Affine(long hashK, int hashSize) {
			super(hashK, hashSize);
			// computed from random data
			samples = new double[]{
					0.044, 0.0876, 0.1334, 0.1813, 0.2332,
					0.2885, 0.3465, 0.4099, 0.4779, 0.5522,
					0.6353, 0.7279, 0.8316, 0.9477, 1.0751,
					1.223, 1.3926, 1.5891, 1.8183, 2.0855,
					2.4067, 2.8084, 3.3036, 3.9727, 4.9149,
					6.2906, 8.5293, 13.0366, 25.6325};
		}

		@Override
		protected int getInvariantSampleSize() {
			return 4;
		}

		@Override
		protected double computeInvariant(Combinations combinator) {
			Point2D_F64 p1 = combinator.get(0);
			Point2D_F64 p2 = combinator.get(1);
			Point2D_F64 p3 = combinator.get(2);
			Point2D_F64 p4 = combinator.get(3);
			return PerspectiveOps.invariantAffine(p1,p2,p3,p4);
		}
	}

	public static class CrossRatio extends LlahHasher {

		public CrossRatio(long hashK, int hashSize) {
			super(hashK, hashSize);
			// computed from random data
			samples = new double[]{
					0.01434, 0.03408, 0.05712, 0.08384, 0.11374,
					0.14714, 0.18358, 0.2239, 0.26832, 0.31724,
					0.37016, 0.4261, 0.48634, 0.5486, 0.6153,
					0.68642, 0.75742, 0.8274, 0.90072, 0.96994,
					1.0305, 1.12668, 1.26924, 1.48098, 1.80568,
					2.35426, 3.29888, 5.3724, 12.1995};
		}

		@Override
		protected int getInvariantSampleSize() {
			return 5;
		}

		@Override
		protected double computeInvariant(Combinations combinator) {
			Point2D_F64 p1 = combinator.get(0);
			Point2D_F64 p2 = combinator.get(1);
			Point2D_F64 p3 = combinator.get(2);
			Point2D_F64 p4 = combinator.get(3);
			Point2D_F64 p5 = combinator.get(4);
			return PerspectiveOps.invariantCrossRatio(p1,p2,p3,p4,p5);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy