/*-
* #%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.model;
import java.util.Collection;
import mpicbg.models.NotEnoughDataPointsException;
import mpicbg.models.PointMatch;
/**
* 2d-rigid transformation models to be applied to points in 2d-space.
* This model includes the closed form weighted least squares solution as
* described by \citet{SchaeferAl06} and implemented by Johannes Schindelin.
*
* BibTeX:
* <pre>
* @article{SchaeferAl06,
* author = {Scott Schaefer and Travis McPhail and Joe Warren},
* title = {Image deformation using moving least squares},
* journal = {ACM Transactions on Graphics},
* volume = {25},
* number = {3},
* year = {2006},
* pages = {533--540},
* publisher = {ACM},
* address = {New York, NY, USA},
* url = {http://faculty.cs.tamu.edu/schaefer/research/mls.pdf},
* }
* </pre>
*
* @author Stephan Saalfeld (saalfeld@mpi-cbg.de)
* @version 0.1b
*
*/
public class TranslationInvariantRigidModel2D extends TranslationInvariantModel<TranslationInvariantRigidModel2D>
{
static final protected int MIN_NUM_MATCHES = 2;
protected double cos = 1.0, sin = 0.0, tx = 0.0, ty = 0.0;
protected double itx = 0.0, ity = 0.0;
@Override
public boolean canDoNumDimension( final int numDimensions ) { return numDimensions == 2; }
@Override
final public int getMinNumMatches(){ return MIN_NUM_MATCHES; }
@Override
final public double[] apply( final double[] l )
{
assert l.length == 2 : "2d rigid transformations can be applied to 2d points only.";
final double[] transformed = l.clone();
applyInPlace( transformed );
return transformed;
}
@Override
final public void applyInPlace( final double[] l )
{
assert l.length == 2 : "2d rigid transformations can be applied to 2d points only.";
final double l0 = l[ 0 ];
l[ 0 ] = cos * l0 - sin * l[ 1 ] + tx;
l[ 1 ] = sin * l0 + cos * l[ 1 ] + ty;
}
@Override
public TranslationInvariantRigidModel2D copy()
{
final TranslationInvariantRigidModel2D m = new TranslationInvariantRigidModel2D();
m.cos = cos;
m.sin = sin;
m.tx = tx;
m.ty = ty;
m.itx = itx;
m.ity = ity;
m.cost = cost;
return m;
}
@Override
final public void set( final TranslationInvariantRigidModel2D m )
{
cos = m.cos;
sin = m.sin;
tx = m.tx;
ty = m.ty;
itx = m.itx;
ity = m.ity;
cost = m.cost;
}
/**
* Closed form weighted least squares solution as described by
* \citet{SchaeferAl06} and implemented by Johannes Schindelin.
*/
@Override
final public < P extends PointMatch >void fit( final Collection< P > matches )
throws NotEnoughDataPointsException
{
if ( matches.size() < MIN_NUM_MATCHES ) throw new NotEnoughDataPointsException( matches.size() + " data points are not enough to estimate a 2d rigid model, at least " + MIN_NUM_MATCHES + " data points required." );
cos = 0;
sin = 0;
for ( final P m : matches )
{
final double[] p = m.getP1().getL();
final double[] q = m.getP2().getW();
final double w = m.getWeight();
final double x1 = p[ 0 ];
final double y1 = p[ 1 ]; // x2
final double x2 = q[ 0 ]; // y1
final double y2 = q[ 1 ]; // y2
sin += w * ( x1 * y2 - y1 * x2 ); // x1 * y2 - x2 * y1 // assuming p1 is x1,x2 and p2 is y1,y2
cos += w * ( x1 * x2 + y1 * y2 ); // x1 * y1 + x2 * y2
}
final double norm = Math.sqrt( cos * cos + sin * sin );
cos /= norm;
sin /= norm;
}
}