boofcv.alg.feature.describe.DescribeSiftCommon Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feature Show documentation
Show all versions of feature Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
/*
* 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;
}
}