/*- * #%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.IllDefinedDataPointsException; import mpicbg.models.NotEnoughDataPointsException; import mpicbg.models.PointMatch; /** * 3d-affine transformation models to be applied to points in 3d-space. * This model includes the closed form weighted least squares solution as * described by \citet{SchaeferAl06} transferred to 3d * * Changed by Stephan Preibisch to be translation invariant for descriptor matching * * 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< and Johannes Schindelin and Stephan Preibisch * @version 0.1b * */ public class TranslationInvariantAffineModel3D extends TranslationInvariantModel<TranslationInvariantAffineModel3D> { static final protected int MIN_NUM_MATCHES = 4; protected double m00 = 1.0, m01 = 0.0, m02 = 0.0, m10 = 0.0, m11 = 1.0, m12 = 0.0, m20 = 0.0, m21 = 0.0, m22 = 1.0; @Override public boolean canDoNumDimension( final int numDimensions ) { return numDimensions == 3; } @Override final public <P extends PointMatch> void fit( final Collection< P > matches ) throws NotEnoughDataPointsException, IllDefinedDataPointsException { if ( matches.size() < MIN_NUM_MATCHES ) throw new NotEnoughDataPointsException( matches.size() + " data points are not enough to estimate a 3d affine model, at least " + MIN_NUM_MATCHES + " data points required." ); double a00, a01, a02, a11, a12, a22; double b00, b01, b02, b10, b11, b12, b20, b21, b22; a00 = a01 = a02 = a11 = a12 = a22 = b00 = b01 = b02 = b10 = b11 = b12 = b20 = b21 = b22 = 0; for ( final PointMatch m : matches ) { final double[] p = m.getP1().getL(); final double[] q = m.getP2().getW(); final double w = m.getWeight(); final double px = p[ 0 ], py = p[ 1 ], pz = p[ 2 ]; final double qx = q[ 0 ], qy = q[ 1 ], qz = q[ 2 ]; a00 += w * px * px; a01 += w * px * py; a02 += w * px * pz; a11 += w * py * py; a12 += w * py * pz; a22 += w * pz * pz; b00 += w * px * qx; b01 += w * px * qy; b02 += w * px * qz; b10 += w * py * qx; b11 += w * py * qy; b12 += w * py * qz; b20 += w * pz * qx; b21 += w * pz * qy; b22 += w * pz * qz; } final double det = a00 * a11 * a22 + a01 * a12 * a02 + a02 * a01 * a12 - a02 * a11 * a02 - a12 * a12 * a00 - a22 * a01 * a01; if ( det == 0 ) throw new IllDefinedDataPointsException(); final double idet = 1f / det; final double ai00 = ( a11 * a22 - a12 * a12 ) * idet; final double ai01 = ( a02 * a12 - a01 * a22 ) * idet; final double ai02 = ( a01 * a12 - a02 * a11 ) * idet; final double ai11 = ( a00 * a22 - a02 * a02 ) * idet; final double ai12 = ( a02 * a01 - a00 * a12 ) * idet; final double ai22 = ( a00 * a11 - a01 * a01 ) * idet; m00 = ai00 * b00 + ai01 * b10 + ai02 * b20; m01 = ai01 * b00 + ai11 * b10 + ai12 * b20; m02 = ai02 * b00 + ai12 * b10 + ai22 * b20; m10 = ai00 * b01 + ai01 * b11 + ai02 * b21; m11 = ai01 * b01 + ai11 * b11 + ai12 * b21; m12 = ai02 * b01 + ai12 * b11 + ai22 * b21; m20 = ai00 * b02 + ai01 * b12 + ai02 * b22; m21 = ai01 * b02 + ai11 * b12 + ai12 * b22; m22 = ai02 * b02 + ai12 * b12 + ai22 * b22; } @Override final public void set( final TranslationInvariantAffineModel3D m ) { m00 = m.m00; m10 = m.m10; m20 = m.m20; m01 = m.m01; m11 = m.m11; m21 = m.m21; m02 = m.m02; m12 = m.m12; m22 = m.m22; cost = m.cost; } @Override public TranslationInvariantAffineModel3D copy() { final TranslationInvariantAffineModel3D m = new TranslationInvariantAffineModel3D(); m.m00 = m00; m.m10 = m10; m.m20 = m20; m.m01 = m01; m.m11 = m11; m.m21 = m21; m.m02 = m02; m.m12 = m12; m.m22 = m22; m.cost = cost; return m; } @Override final public int getMinNumMatches(){ return MIN_NUM_MATCHES; } @Override final public double[] apply( final double[] l ) { final double[] transformed = l.clone(); applyInPlace( transformed ); return transformed; } @Override final public void applyInPlace( final double[] l ) { assert l.length == 3 : "3d affine transformations can be applied to 3d points only."; final double l0 = l[ 0 ]; final double l1 = l[ 1 ]; l[ 0 ] = l0 * m00 + l1 * m01 + l[ 2 ] * m02; l[ 1 ] = l0 * m10 + l1 * m11 + l[ 2 ] * m12; l[ 2 ] = l0 * m20 + l1 * m21 + l[ 2 ] * m22; } @Override final public String toString() { return "3d-affine: (" + m00 + ", " + m01 + ", " + m02 + ", " + m10 + ", " + m11 + ", " + m12 + ", " + m20 + ", " + m21 + ", " + m22 + ")"; } }