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

boofcv.alg.feature.describe.DescribeSiftCommon Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2016, 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;

import boofcv.alg.descriptor.UtilFeature;
import boofcv.alg.filter.kernel.KernelMath;
import boofcv.factory.filter.kernel.FactoryKernelGaussian;
import boofcv.struct.convolve.Kernel2D_F32;
import boofcv.struct.feature.TupleDesc_F64;
import georegression.metric.UtilAngle;

/**
 * Base class for {@link DescribePointSift SIFT} descriptors.  Provides common functionality between sparse and
 * dense computations.
 *
 * @author Peter Abeles
 */
public class DescribeSiftCommon {

	// width of a subregion, in samples
	protected int widthSubregion;
	// width of the outer grid, in sub-regions
	protected int widthGrid;

	// number of bins in the orientation histogram
	protected int numHistogramBins;
	protected double histogramBinWidth;

	// maximum value of an element in the descriptor
	protected double maxDescriptorElementValue;

	// weight applied across the entire image
	protected float gaussianWeight[];

	/**
	 * Configures the descriptor.
	 *
	 * @param widthSubregion Width of sub-region in samples.  Try 4
	 * @param widthGrid Width of grid in subregions.  Try 4.
	 * @param numHistogramBins Number of bins in histogram.  Try 8
	 * @param weightingSigmaFraction Sigma for Gaussian weighting function is set to this value * region width.  Try 0.5
	 * @param maxDescriptorElementValue Helps with non-affine changes in lighting. See paper.  Try 0.2
	 */
	public DescribeSiftCommon(int widthSubregion, int widthGrid,
							  int numHistogramBins , double weightingSigmaFraction , double maxDescriptorElementValue )
	{
		this.widthSubregion = widthSubregion;
		this.widthGrid = widthGrid;
		this.numHistogramBins = numHistogramBins;
		this.maxDescriptorElementValue = maxDescriptorElementValue;

		this.histogramBinWidth = 2.0*Math.PI/numHistogramBins;

		// number of samples wide the descriptor window is
		int descriptorWindow = widthSubregion*widthGrid;
		double weightSigma = descriptorWindow*weightingSigmaFraction;
		gaussianWeight = createGaussianWeightKernel(weightSigma,descriptorWindow/2);
	}

	/**
	 * Adjusts the descriptor.  This adds lighting invariance and reduces the affects of none-affine changes
	 * in lighting.
	 *
	 * 1) Apply L2 normalization
	 * 2) Clip using max descriptor value
	 * 3) Apply L2 normalization again
	 */
	public static void normalizeDescriptor(TupleDesc_F64 descriptor , double maxDescriptorElementValue ) {
		// normalize descriptor to unit length
		UtilFeature.normalizeL2(descriptor);

		// clip the values
		for (int i = 0; i < descriptor.size(); i++) {
			double value = descriptor.value[i];
			if( value > maxDescriptorElementValue ) {
				descriptor.value[i] = maxDescriptorElementValue;
			}
		}

		// normalize again
		UtilFeature.normalizeL2(descriptor);
	}

	/**
	 * Creates a gaussian weighting kernel with an even number of elements along its width
	 */
	protected static float[] createGaussianWeightKernel( double sigma , int radius ) {
		Kernel2D_F32 ker = FactoryKernelGaussian.gaussian2D_F32(sigma,radius,false,false);
		float maxValue = KernelMath.maxAbs(ker.data,4*radius*radius);
		KernelMath.divide(ker,maxValue);
		return ker.data;
	}

	/**
	 * Applies trilinear interpolation across the descriptor
	 */
	protected void trilinearInterpolation( float weight , float sampleX , float sampleY , double angle , TupleDesc_F64 descriptor )
	{
		for (int i = 0; i < widthGrid; i++) {
			double weightGridY = 1.0 - Math.abs(sampleY-i);
			if( weightGridY <= 0) continue;
			for (int j = 0; j < widthGrid; j++) {
				double weightGridX = 1.0 - Math.abs(sampleX-j);
				if( weightGridX <= 0 ) continue;
				for (int k = 0; k < numHistogramBins; k++) {
					double angleBin = k*histogramBinWidth;
					double weightHistogram = 1.0 - UtilAngle.dist(angle,angleBin)/histogramBinWidth;
					if( weightHistogram <= 0 ) continue;

					int descriptorIndex = (i*widthGrid + j)*numHistogramBins + k;
					descriptor.value[descriptorIndex] += weight*weightGridX*weightGridY*weightHistogram;
				}
			}
		}
	}

	/**
	 * Number of elements in the descriptor.
	 */
	public int getDescriptorLength() {
		return widthGrid*widthGrid*numHistogramBins;
	}

	/**
	 * Radius of descriptor in pixels.  Width is radius*2
	 */
	public int getCanonicalRadius() {
		return widthGrid*widthSubregion/2;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy