/*-
* #%L
* Fiji distribution of ImageJ for the life sciences.
* %%
* Copyright (C) 2007 - 2017 Fiji developers.
* %%
* This program 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 2 of the
* License, or (at your option) any later version.
*
* This program 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 this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package mpicbg.pointdescriptor;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
import net.imglib2.util.Util;
import mpicbg.models.Point;
import mpicbg.models.PointMatch;
import mpicbg.pointdescriptor.exception.NoSuitablePointsException;
import mpicbg.pointdescriptor.matcher.Matcher;
import mpicbg.pointdescriptor.similarity.SimilarityMeasure;
public abstract class AbstractPointDescriptor< P extends Point, F extends AbstractPointDescriptor<P, F> >
{
/* The basis point for the point descriptor */
final P basisPoint;
/* The links to the point instances, they do not need to be duplicated */
final ArrayList< P > neighbors;
/* Point instances storing the relative coordinates to the basis point */
final ArrayList< LinkedPoint< P > > descriptorPoints;
/* Computes the similarity between two aligned AbstracPointDescriptors */
SimilarityMeasure similarityMeasure;
/* Creates different sets of PointMatches which are tested and computes a normalization factor for the score */
final Matcher matcher;
/* The best match combination from the last comparison */
ArrayList<PointMatch> bestPointMatchSet = null;
final int numDimensions;
final long id;
/* Used to assign a unique id to each PointDescriptor, if two points have the same distance we sort by id */
protected final static AtomicLong index = new AtomicLong( 0 );
public AbstractPointDescriptor( final P basisPoint, final ArrayList< P > orderedNearestNeighboringPoints,
final SimilarityMeasure similarityMeasure, final Matcher matcher ) throws NoSuitablePointsException
{
this.basisPoint = basisPoint;
this.numDimensions = basisPoint.getL().length;
this.similarityMeasure = similarityMeasure;
this.matcher = matcher;
this.id = index.getAndIncrement();
this.neighbors = orderedNearestNeighboringPoints;
for ( final P point : orderedNearestNeighboringPoints )
if ( point.getL().length != numDimensions )
throw new NoSuitablePointsException( "At least one of the Points<P> given as ArrayList< P > orderedNearestNeighboringPoints have a different dimensionality than the basis point." );
/* Set up the Descriptor with relative distances */
this.descriptorPoints = new ArrayList< LinkedPoint< P > >( neighbors.size() );
final double[] basis;
if ( useWorldCoordinatesForDescriptorBuildUp() )
basis = basisPoint.getW().clone();
else
basis = basisPoint.getL().clone();
for ( final P absolute : orderedNearestNeighboringPoints )
{
final double[] localCoordinates;
if ( useWorldCoordinatesForDescriptorBuildUp() )
localCoordinates = absolute.getW().clone();
else
localCoordinates = absolute.getL().clone();
for ( int d = 0; d < numDimensions; ++d )
localCoordinates[ d ] -= basis[ d ];
descriptorPoints.add( new LinkedPoint< P >( localCoordinates, absolute ) );
}
}
/**
* Matches two {@link AbstractPointDescriptor}s of the same kind yielding a similarity value, the lower the better (0 means identical)
* @param pointDescriptor - the {@link AbstractPointDescriptor} to match
* @return similarity
*/
public double descriptorDistance( final F pointDescriptor )
{
/* init the point matches */
final ArrayList<ArrayList<PointMatch>> matchesList = matcher.createCandidates( this, pointDescriptor );
double bestSimilarity = Double.MAX_VALUE;
bestPointMatchSet = null;
for ( final ArrayList<PointMatch> matches : matchesList )
{
/* fit the model and apply to local point descriptor */
final Object fitResult = fitMatches( matches );
/* compute the similarity */
final double similarity = similarityMeasure.getSimilarity( matches ) * matcher.getNormalizationFactor( matches, fitResult );
if ( similarity < bestSimilarity )
{
bestSimilarity = similarity;
bestPointMatchSet = matches;
}
}
/* Reset the world coordinates of this descriptor */
if ( resetWorldCoordinatesAfterMatching() )
resetWorldCoordinates();
return bestSimilarity;
}
/**
* The combination of descriptorpoints that yielded the best similarity in the last comparison
*
* @return - List of {@link PointMatch}es containing the original datasets
*/
public ArrayList<PointMatch> getBestPointMatchSet() { return bestPointMatchSet; }
/**
* Resets the world coordinates of the descriptorPoints
*/
protected void resetWorldCoordinates()
{
for ( final Point point : descriptorPoints )
{
final double[] l = point.getL();
final double[] w = point.getW();
for ( int d = 0; d < l.length; ++d )
w[ d ] = l[ d ];
}
}
/**
* Computes a fit between this these {@link PointMatch}es, this method is called by the {@link Matcher}
*
* @param matches - The {@link Point}s to match
*
*/
// public abstract CoordinateTransform fit( final ArrayList<PointMatch> pointMatch );
public abstract Object fitMatches( final ArrayList<PointMatch> matches );
/**
* Tells if the descriptormatching should reset the world coordinates after the matching
* @return
*/
public abstract boolean resetWorldCoordinatesAfterMatching();
/**
* Tells if the build up of the descriptor should use the world coordinates or rather the local coordinates
* @return - true (world), false(local)
*/
public abstract boolean useWorldCoordinatesForDescriptorBuildUp();
/**
* @return The {@link SimilarityMeasure} object that is used to compute how similar two descriptors are
*/
public SimilarityMeasure getSimilarityMeasure() { return similarityMeasure; }
/**
* Sets the {@link SimilarityMeasure} object that is used to compute how similar two descriptors are
* @param similarityMeasure
*/
public void setSimilarityMeasure( final SimilarityMeasure similarityMeasure) { this.similarityMeasure = similarityMeasure; }
/**
* Return the unique id of this descriptor
* @return long id
*/
public long getId() { return id; }
/**
* The basis point for this descriptor
* @return the {@link Point} instance
*/
public P getBasisPoint() { return basisPoint; }
/**
* Returns a certain nearest neighbor (relative to the basis point) of this {@link AbstractPointDescriptor}
*
* @param index - the index (0 means first nearest neighbor)
* @return the {@link Point} instance
*/
public Point getDescriptorPoint( final int index ) { return descriptorPoints.get( index ); }
/**
* The points forming the {@link AbstractPointDescriptor} relative to the basis point
*/
// * @return - Arraylist< P >
public ArrayList< P > getOrderedNearestNeighboringPoints(){ return neighbors; }
/**
* The number of neighbors used for this descriptor
* @return - int number
*/
public int numNeighbors() { return neighbors.size(); }
/**
* Dimensionality of the {@link Point}s of this {@link AbstractPointDescriptor}
* @return - int dimensions
*/
public int numDimensions() { return numDimensions; }
// /**
// * Computes the difference between two PointDescriptors by summing up all individual differences.
// *
// * @param genericPointDescriptor - the PointDescriptor to compare to
// * @return The differences between two PointDescriptors
// *
// */
// public double getDifference( final AbstractPointDescriptor<P,?> genericPointDescriptor )
// {
// double difference = 0;
//
// for ( int d = 0; d < numDimensions; ++d )
// difference += Point.distance( getNeighborPoint( d ), genericPointDescriptor.getNeighborPoint( d ) );
//
// return difference;
// }
//
// /**
// * Computes the squared difference between two PointDescriptors by summing up all individual squared differences.
// *
// * @param genericPointDescriptor - the PointDescriptor to compare to
// * @return The differences between two PointDescriptors
// */
// public double getSquaredDifference( final AbstractPointDescriptor<P,?> genericPointDescriptor )
// {
// double difference = 0;
//
// for ( int d = 0; d < numDimensions; ++d )
// difference += Point.squareDistance( getNeighborPoint( d ), genericPointDescriptor.getNeighborPoint( d ) );
//
// return difference;
// }
/**
* Overwrites the toString method
*/
public String toString()
{
String out = this.getClass().getName() + "[" +id + "] has position: " + Util.printCoordinates( basisPoint.getW() );
for (int i = 0; i < neighbors.size(); i++)
out += "\nneighbor " + (i+1) + " vector: " + Util.printCoordinates( basisPoint.getW() );
return out;
}
}