/**
* License: GPL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License 2
* as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package bunwarpj.trakem2.transform;
import java.util.Collection;
import java.util.Stack;
import mpicbg.models.AbstractModel;
import mpicbg.models.IllDefinedDataPointsException;
import mpicbg.models.NotEnoughDataPointsException;
import mpicbg.models.PointMatch;
import mpicbg.trakem2.transform.CoordinateTransform;
import bunwarpj.BSplineModel;
import bunwarpj.Param;
import bunwarpj.Transformation;
import bunwarpj.bUnwarpJ_;
/**
* Class to implement elastic transforms based on cubic B-splines.
*
* @author Ignacio Arganda-Carreras (ignacio.arganda@gmail.com)
*/
public class CubicBSplineTransform extends AbstractModel< CubicBSplineTransform > implements CoordinateTransform
{
/** grid of B-spline coefficients for x- transformation */
private BSplineModel swx = null;
/** grid of B-spline coefficients for y- transformation */
private BSplineModel swy = null;
/** number of intervals between B-spline coefficients */
private int intervals = 0;
/** width of the image to be transformed */
private int width = 0;
/** height of the image to be transformed */
private int height = 0;
/** width of the source image (necessary for the fit method) */
private int sourceWidth = 0;
/** height of the source image (necessary for the fit method) */
private int sourceHeight = 0;
/** bUnwarpJ parameters (necessary for the fit method) */
private Param parameter = new Param(2, 0, 0, 2, 0.1, 0.1, 1.0, 0.0, 0.0, 0.01);
// -------------------------------------------------------------------
/**
* Empty constructor
*/
public CubicBSplineTransform(){}
// -------------------------------------------------------------------
/**
* Cubic B-spline transform constructor
*
* @param intervals intervals between B-spline coefficients
* @param cx B-spline coefficients for transformation in the x axis
* @param cy B-spline coefficients for transformation in the y axis
* @param width width of the target image
* @param height height of the target image
*/
public CubicBSplineTransform(final int intervals,
final double[][]cx,
final double[][]cy,
final int width,
final int height)
{
this.intervals = intervals;
this.swx = new BSplineModel(cx);
this.swy = new BSplineModel(cy);
this.width = width;
this.height = height;
}
// -------------------------------------------------------------------
/**
* Cubic B-spline transform constructor
*
* @param intervals intervals between B-spline coefficients
* @param swx B-spline model for transformation in the x axis
* @param swy B-spline model for transformation in the y axis
* @param width width of the target image
* @param height height of the target image
*/
public CubicBSplineTransform(final int intervals,
final BSplineModel swx,
final BSplineModel swy,
final int width,
final int height)
{
this.intervals = intervals;
this.swx = swx;
this.swy = swy;
this.width = width;
this.height = height;
}
// -------------------------------------------------------------------
/**
* Set cubic B-spline transform values
*
* @param intervals intervals between B-spline coefficients
* @param swx B-spline model for transformation in the x axis
* @param swy B-spline model for transformation in the y axis
* @param width width of the target image
* @param height height of the target image
*/
public void set(final int intervals,
final BSplineModel swx,
final BSplineModel swy,
final int width,
final int height)
{
this.intervals = intervals;
this.swx = swx;
this.swy = swy;
this.width = width;
this.height = height;
}
// -------------------------------------------------------------------
/**
* Set cubic B-spline transform constructor
*
* @param intervals intervals between B-spline coefficients
* @param cx B-spline coefficients for transformation in the x axis
* @param cy B-spline coefficients for transformation in the y axis
* @param width width of the target image
* @param height height of the target image
*/
public void set(final int intervals,
final double[][]cx,
final double[][]cy,
final int width,
final int height)
{
this.intervals = intervals;
this.swx = new BSplineModel(cx);
this.swy = new BSplineModel(cy);
this.width = width;
this.height = height;
}
// -------------------------------------------------------------------
/* (non-Javadoc)
* @see mpicbg.models.CoordinateTransform#apply(double[])
*/
@Override
public double[] apply(final double[] l)
{
final double[] w = l.clone();
applyInPlace(w);
return w;
}
// -------------------------------------------------------------------
/* (non-Javadoc)
* @see mpicbg.models.CoordinateTransform#applyInPlace(double[])
*/
@Override
public void applyInPlace(final double[] l)
{
// Compute the transformation mapping
final double tv = (double)(l[1] * intervals) / (double)(this.height - 1) + 1.0F;
final double tu = (double)(l[0] * intervals) / (double)(this.width - 1) + 1.0F;
l[0] = swx.prepareForInterpolationAndInterpolateI(tu, tv, false, false);
l[1] = swy.prepareForInterpolationAndInterpolateI(tu, tv, false, false);
}
// -------------------------------------------------------------------
/**
* Initialize cubic B-spline transform from the parameters of a string
*
* @param dataString basic cubic B-spline transform parameters
*/
@Override
public void init(final String dataString) throws NumberFormatException
{
// Read parameter between spaces
final String[] fields = dataString.split( "\\s+" );
int j = 0;
this.width = Integer.parseInt(fields[j++]);
this.height = Integer.parseInt(fields[j++]);
this.intervals = Integer.parseInt(fields[j++]);
final int size = (this.intervals + 3);
final int size2 = size * size;
//IJ.log("width = " + this.width + " height = " + this.height + " intervals = " + this.intervals);
if (fields.length < (2*size2 + 3))
throw new NumberFormatException( "Inappropriate parameters for " + this.getClass().getCanonicalName() );
else
{
final double[] cx = new double[size2];
for(int i = 0; i < size2; i++)
cx[i] = Double.parseDouble(fields[j++]);
final double[] cy = new double[size2];
for(int i = 0; i < size2; i++)
cy[i] = Double.parseDouble(fields[j++]);
this.swx = new BSplineModel(cx, size, size, 0);
this.swy = new BSplineModel(cy, size, size, 0);
}
}
// -------------------------------------------------------------------
/**
* Save cubic B-spline transform information into String
*/
@Override
public String toDataString()
{
final StringBuffer text = new StringBuffer(this.width + " " + this.height + " " + intervals);
final int size = (intervals + 3) * (intervals + 3);
final double[] cx = this.swx.getCoefficients();
for(int i = 0; i < size; i ++)
text.append( " " + cx[i] );
final double[] cy = this.swy.getCoefficients();
for(int i = 0; i < size; i ++)
text.append( " " + cy[i] );
return text.toString();
}
// -------------------------------------------------------------------
//@Override
@Override
final public String toXML( final String indent )
{
return indent + "<ict_transform class=\"" + this.getClass().getCanonicalName() + "\" data=\"" + toDataString() + "\"/>";
}
// -------------------------------------------------------------------
/**
* Clone method
*/
@Override
final public CubicBSplineTransform copy()
{
final CubicBSplineTransform transf = new CubicBSplineTransform();
transf.init( toDataString() );
return transf;
}
//@Override
@Override
public < P extends PointMatch >void fit(final Collection< P > matches)
throws NotEnoughDataPointsException, IllDefinedDataPointsException
{
final Stack< java.awt.Point > sourcePoints = new Stack<java.awt.Point>();
final Stack< java.awt.Point > targetPoints = new Stack<java.awt.Point>();
for ( final P pm : matches )
{
final double[] p1 = pm.getP1().getL();
final double[] p2 = pm.getP2().getL();
targetPoints.add( new java.awt.Point( ( int )Math.round( p1[ 0 ] ), ( int )Math.round( p1[ 1 ] ) ) );
sourcePoints.add( new java.awt.Point( ( int )Math.round( p2[ 0 ] ), ( int )Math.round( p2[ 1 ] ) ) );
}
final Transformation transf = bUnwarpJ_.computeTransformationBatch(sourceWidth,
sourceHeight, width, height, sourcePoints, targetPoints, parameter);
this.set(transf.getIntervals(), transf.getDirectDeformationCoefficientsX(),
transf.getDirectDeformationCoefficientsY(), width, height);
}
public void scale(final double xScale, final double yScale)
{
// Adapt transformation to scale
final double[] cx = swx.getCoefficients();
final double[] cy = swy.getCoefficients();
final double xScaleFactor = 1.0/xScale;
final double yScaleFactor = 1.0/yScale;
for(int i = 0; i < cx.length; i++)
{
cx[i] *= xScaleFactor;
cy[i] *= yScaleFactor;
}
}
public void set(final Param p, final int sourceWidth, final int sourceHeight, final int targetWidth, final int targetHeight)
{
this.parameter = p;
this.sourceHeight = sourceHeight;
this.sourceWidth = sourceWidth;
this.width = targetWidth;
this.height = targetHeight;
}
//@Override
@Override
public int getMinNumMatches() {
// TODO Auto-generated method stub
return 0;
}
//@Override
@Override
public void set(final CubicBSplineTransform m) {
init( m.toDataString() );
}
// @Override
// public void shake(float amount) {
// // TODO If you really need, implement it ...
//
// }
//@Override
@Override
public String toString() {
return toDataString();
}
}// end class CubicBSplineTransform