/**
*
* Copyright (C) 2008 Verena Kaynig.
*
* 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 (http://www.gnu.org/licenses/gpl.txt )
*
* 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.
*/
/* **************************************************************** *
* Representation of a non linear transform by explicit polynomial
* kernel expansion.
*
* TODO:
* - make different kernels available
* - inverse transform for visualization
* - improve image interpolation
* - apply and applyInPlace should use precalculated transform?
* (What about out of image range pixels?)
*
* Author: Verena Kaynig
* Kontakt: verena.kaynig@inf.ethz.ch
*
* **************************************************************** */
package mpicbg.trakem2.transform;
public class NonLinearCoordinateTransform implements mpicbg.trakem2.transform.CoordinateTransform
{
protected double[][] beta = null;
protected double[] normMean = null;
protected double[] normVar = null;
protected int dimension = 0;
protected int length = 0;
protected int width = 0;
protected int height = 0;
public NonLinearCoordinateTransform(){};
@Override
public void init( final String data ) throws NumberFormatException{
final String[] fields = data.split( " " );
int c = 0;
dimension = Integer.parseInt(fields[c]); c++;
length = Integer.parseInt(fields[c]); c++;
beta = new double[length][2];
normMean = new double[length];
normVar = new double[length];
if ( fields.length == 4 + 4*length )
{
for (int i=0; i < length; i++){
beta[i][0] = Double.parseDouble(fields[c]); c++;
beta[i][1] = Double.parseDouble(fields[c]); c++;
}
for (int i=0; i < length; i++){
normMean[i] = Double.parseDouble(fields[c]); c++;
}
for (int i=0; i < length; i++){
normVar[i] = Double.parseDouble(fields[c]); c++;
}
width = Integer.parseInt(fields[c]); c++;
height = Integer.parseInt(fields[c]); c++;
}
else throw new NumberFormatException( "Inappropriate parameters for " + this.getClass().getCanonicalName() );
}
@Override
public String toXML(final String indent){
return new StringBuilder(indent).append("<ict_transform class=\"").append(this.getClass().getCanonicalName()).append("\" data=\"").append(toDataString()).append("\"/>").toString();
}
@Override
public String toDataString(){
String data = "";
data += Integer.toString(dimension) + " ";
data += Integer.toString(length) + " ";
for (int i=0; i < length; i++){
data += Double.toString(beta[i][0]) + " ";
data += Double.toString(beta[i][1]) + " ";
}
for (int i=0; i < length; i++){
data += Double.toString(normMean[i]) + " ";
}
for (int i=0; i < length; i++){
data += Double.toString(normVar[i]) + " ";
}
data += Integer.toString(width) + " ";
data += Integer.toString(height) + " ";
return data;
}
@Override
public String toString(){ return toDataString(); }
@Override
public double[] apply(final double[] location) {
final double[] position = { (double) location[0], (double) location[1] };
final double[] featureVector = kernelExpand(position);
return multiply(beta, featureVector);
}
@Override
public void applyInPlace(final double[] location) {
final double[] position = { (double) location[0], (double) location[1] };
final double[] featureVector = kernelExpand(position);
final double[] newPosition = multiply(beta, featureVector);
location[0] = newPosition[0];
location[1] = newPosition[1];
}
static protected double[] multiply(final double beta[][], final double featureVector[]) {
final double[] result = { 0.0, 0.0 };
if (beta.length != featureVector.length)
{
return new double[2];
}
for (int i = 0; i < featureVector.length; ++i)
{
result[0] = result[0] + featureVector[i] * beta[i][0];
result[1] = result[1] + featureVector[i] * beta[i][1];
}
return result;
}
public double[] kernelExpand( final double position[] ) {
final double expanded[] = new double[length];
int counter = 0;
for (int i = 1; i <= dimension; i++) {
for (double j = i; j >= 0; j--) {
final double val = Math.pow(position[0], j)
* Math.pow(position[1], i - j);
expanded[counter] = val;
++counter;
}
}
for (int i = 0; i < length - 1; i++) {
expanded[i] = expanded[i] - normMean[i];
expanded[i] = expanded[i] / normVar[i];
}
expanded[length - 1] = 100;
return expanded;
}
/**
* TODO Make this more efficient
*/
@Override
public NonLinearCoordinateTransform copy()
{
final NonLinearCoordinateTransform t = new NonLinearCoordinateTransform();
t.init(toDataString());
return t;
}
}