/*
* This file is part of the JFeatureLib project: https://github.com/locked-fg/JFeatureLib
* JFeatureLib is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* JFeatureLib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JFeatureLib; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* You are kindly asked to refer to the papers of the according authors which
* should be mentioned in the Javadocs of the respective classes as well as the
* JFeatureLib project itself.
*
* Hints how to cite the projects can be found at
* https://github.com/locked-fg/JFeatureLib/wiki/Citation
*/
package de.lmu.ifi.dbs.jfeaturelib.features.surf;
import ij.gui.GenericDialog;
import java.awt.Color;
//TODO move IJ-related code into IJFacade
// TODO JavaDoc for getter/setter
/**
* Parameter for SURF Detector, SURF Descriptor and for displaying the results.
*/
public class Params {
public final static String programVersion = "ImageJ SURF v2009-12-01";
public Params() { // TODO: move initializing here ?
}
public Params(int octaves, int layers, float threshold, int initStep, boolean upright, boolean displayOrientationVectors, boolean displayDescriptorWindows, int lineWidth, boolean displayStatistics) {
this.octaves = validate(octaves, 3, 4);
this.layers = validate(layers, 3, 4);
this.threshold = validate(threshold, 0, 1);
this.initStep = validate(initStep, 1, 6);
this.upright = upright;
this.displayOrientationVectors = displayOrientationVectors;
this.displayDescriptorWindows = displayDescriptorWindows;
this.lineWidth = validate(lineWidth, 1, 5);
this.displayStatistics = displayStatistics;
}
public Params(Params p) {
this.octaves = p.octaves;
this.layers = p.layers;
this.threshold = p.threshold;
this.initStep = p.initStep;
this.upright = p.upright;
this.displayOrientationVectors = p.displayOrientationVectors;
this.displayDescriptorWindows = p.displayDescriptorWindows;
this.lineWidth = p.lineWidth;
this.displayStatistics = p.displayStatistics;
}
private float validate(float val, float lowerBound, float upperBound) {
if (val < lowerBound) {
return lowerBound;
}
if (val > upperBound) {
return upperBound;
}
return val;
}
/**
* Validates parameter values for the constructor.
*
* @param lowerBound The lowest valid value.
* @param upperBound The highest valid value.
* @param val The value to validate.
*/
int validate(int val, int lowerBound, int upperBound) {
// return (val < lowerBound) ? lowerBound : (val > upperBound ? upperBound : val); // this is the same as below
if (val < lowerBound) {
return lowerBound;
}
if (val > upperBound) {
return upperBound;
}
return val;
}
/////////////////////////////////////////////////////////////////////////7
// Detector params
/**
* Defines octaves and layers (filter sizes). The actual implementation
* assumes the equal number of layers in each octave. Default is 4 octaves
* with 4 layers per octave and values as proposed in the SURF paper
* (2008):<br>
* <code>{{9,15,21,27},{15,27,39,51},{27,51,75,99},{51,99,147,195}}</code>.
*/
private int[][] filterSizes = {{9, 15, 21, 27}, {15, 27, 39, 51}, {27, 51, 75, 99}, {51, 99, 147, 195}};
private int[] maxFilterSizes = {27, 51, 99, 195};
//TODO: add alternate filter size (to apply after image scaling)
/**
* Number of analysed octaves. Default is 4.
*/
private int octaves = 4; // 3 .. 4 (filterSizes.length) OpenSURF (C++): 3 orig.SURF: 4
private int layers = 4; // 3 .. 4 (filterSizes[0].length)
public int getOctaves() {
return octaves;
}
public int getLayers() {
return layers;
}
public int getFilterSize(int octave, int layer) {
return filterSizes[octave][layer];
}
/**
* Returns the biggest filter size in the octave.
*/
public int getMaxFilterSize(int octave) {
return maxFilterSizes[octave];
}
// Set this flag "true" to double the image size
// boolean doubleImageSize = false;
// TODO: rename to "upscale image first"? + implementation (siehe ImageJ_SIFT e.g. as parameter to intImg constr.) + update constructor/getter/setter
/**
* The responses are thresholded such that all values below the
* <code>threshold</code> are removed. Increasing the threshold lowers the
* number of detected interest points, and vice versa. Must be >= 0 and <=
* 1.
*/
// private float threshold = 600;
// NB: in C++ version 0.0004f (sometimes 0.0006f); in C# version 0.002f
// orig.SURF: double thres = 4.0; "Blob response treshold"; cvsurf: "hessianThreshold"
// private float threshold = 0.0002f;
private float threshold = 0.001f;
public float getThreshold() {
return threshold;
}
/**
* The initial sampling step (1..6). Default is 2. <br> Will be doubled for
* each next octave (see stepIncFactor).
*/
private int initStep = 2; // orig.SURF: 2 (2 gives less IPs than 1 but much faster)
public int getInitStep() {
return initStep;
}
private int stepIncFactor = 2;
public int getStepIncFactor() {
return stepIncFactor;
}
// private int interp_steps = 5; // from C# version of OpenSURF (not exists in C++ version) TODO: purpose?
/////////////////////////////////////////////////////////////////////////7
// Descriptor params
/**
* Extract upright (i.e. not rotation invariant) descriptors. Default is
* <code>false</code>.
*/
private boolean upright;
public boolean isUpright() {
return upright;
}
private int descSize = 64;
public int getDescSize() {
return descSize;
}
/////////////////////////////////////////////////////////////////////////7
// Display params
private boolean displayOrientationVectors = true;
public boolean isDisplayOrientationVectors() {
return displayOrientationVectors;
}
private boolean displayDescriptorWindows = false;
public boolean isDisplayDescriptorWindows() {
return displayDescriptorWindows;
}
private int lineWidth = 1; // 1..5
public int getLineWidth() {
return lineWidth;
}
boolean displayStatistics = false;
public boolean isDisplayStatistics() {
return displayStatistics;
}
private Statistics stat = new Statistics();
public Statistics getStatistics() {
return stat;
}
/**
* Adds SURF parameter to the ImageJ GenericDialog
*
* The order and types of fields do affect
* the method Params#getSurfParamsFromDialog(GenericDialog). Bounds
* description depends on bounds check in constructor {@link Params#Params(int, int, float, int, boolean, boolean, boolean, int, boolean)}.
*/
public static void addSurfParamsToDialog(GenericDialog gd) {
Params p = new Params();
gd.addMessage("=================== DETECTOR ===================");
gd.addNumericField("Octaves (3..4) :", p.getOctaves(), 0);
gd.addNumericField("Layers per Octave (3..4) :", p.getLayers(), 0);
gd.addNumericField("Hessian Threshold (0..1) :", p.getThreshold(), 5);
gd.addNumericField("Initial Sampling Step (1..6) :", p.getInitStep(), 0);
gd.addMessage("================== DESCRIPTOR ==================");
gd.addCheckbox("Upright SURF (slightly faster, but not rotation invariant)", p.isUpright());
gd.addMessage("=============== DISPLAYING RESULTS ==============");
//TODO: add Matcher params: best/secondBest value, boolean reverseCheck, float tolerance for comparison with homography
// threshold for distance-between-matching-points (outlier/Ausrei�er)
gd.addCheckbox("Orientation Vectors (Length shows the strength of I.P.) ", p.isDisplayOrientationVectors());
gd.addCheckbox("Descriptor Windows", p.isDisplayDescriptorWindows());
gd.addNumericField("Line Width (1..5) :", p.getLineWidth(), 0);
gd.addCheckbox("Statistics", p.isDisplayStatistics());
}
/**
* Reads SURF parameter from the ImageJ
* <code>GenericDialog</code> and returns a
* <code>SurfParams</code> object. Depends on the order and types of fields
* in the method {@link Params#addSurfParamsToDialog(GenericDialog)}.
*/
public static Params getSurfParamsFromDialog(GenericDialog gd) {
int octaves = (int) gd.getNextNumber();
int layers = (int) gd.getNextNumber();
float threshold = (float) gd.getNextNumber();
int initStep = (int) gd.getNextNumber();
boolean upright = gd.getNextBoolean();
boolean displayOrientationVectors = gd.getNextBoolean();
boolean displayDescriptorWindows = gd.getNextBoolean();
int lineWidth = (int) gd.getNextNumber();
boolean displayStatistics = gd.getNextBoolean();
return new Params(octaves, layers, threshold, initStep, upright, displayOrientationVectors, displayDescriptorWindows, lineWidth, displayStatistics);
}
public Color getDescriptorWindowColor() {
return Color.PINK;
}
public Color getOrientationVectorColor() {
return Color.YELLOW;
}
/**
* Drawing color for dark blobs on light background
*/
public Color getDarkPointColor() {
return Color.BLUE;
}
/**
* Drawing color for light blobs on dark background
*/
public Color getLightPointColor() {
return Color.RED;
}
}