Skip to content

Commit

Permalink
Add ModelFitter to directly fit mpicbg AffineModels
Browse files Browse the repository at this point in the history
  • Loading branch information
imagejan committed Mar 19, 2019
1 parent ee71d65 commit 68495c2
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 0 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,11 @@
<groupId>gov.nist.math</groupId>
<artifactId>jama</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
167 changes: 167 additions & 0 deletions src/main/java/ch/fmi/ModelFitter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@

package ch.fmi;

import java.util.ArrayList;
import mpicbg.models.Affine2D;
import mpicbg.models.Affine3D;
import mpicbg.models.AffineModel2D;
import mpicbg.models.AffineModel3D;
import mpicbg.models.IllDefinedDataPointsException;
import mpicbg.models.Model;
import mpicbg.models.NotEnoughDataPointsException;
import mpicbg.models.Point;
import mpicbg.models.PointMatch;
import mpicbg.models.RigidModel2D;
import mpicbg.models.RigidModel3D;
import mpicbg.models.SimilarityModel2D;
import mpicbg.models.SimilarityModel3D;
import mpicbg.models.TranslationModel2D;
import mpicbg.models.TranslationModel3D;
import org.scijava.ItemIO;
import org.scijava.command.Command;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;

@Plugin(type = Command.class, headless = true,
menuPath = "FMI>Fit Transformation Model to Paired Point Sets")
public class ModelFitter implements Command {

final static protected String TRANSLATION = "Translation";
final static protected String RIGID = "Rigid";
final static protected String SIMILARITY = "Similarity";
final static protected String AFFINE = "Affine";
final static protected String DIM2D = "2D";
final static protected String DIM3D = "3D";

@Parameter(label = "Type of Transformation", choices = { TRANSLATION, RIGID,
SIMILARITY, AFFINE })
private String transformType;

@Parameter(label = "Dimensionality", choices = { DIM2D, DIM3D })
private String dim;

@Parameter(label = "Set 1 - X Coordinates")
private double[] x1;

@Parameter(label = "Set 1 - Y Coordinates")
private double[] y1;

@Parameter(label = "Set 1 - Z Coordinates", required = false)
private double[] z1 = null;

@Parameter(label = "Set 2 - X Coordinates")
private double[] x2;

@Parameter(label = "Set 2 - Y Coordinates")
private double[] y2;

@Parameter(label = "Set 2 - Z Coordinates", required = false)
private double[] z2 = null;

@Parameter(type = ItemIO.OUTPUT)
private double[] affine;

private Model<?> model;

@Override
public void run() {
// Choose model
switch (transformType) {
case TRANSLATION:
model = dim.equals(DIM2D) ? //
new TranslationModel2D() : new TranslationModel3D();
break;
case RIGID:
model = dim.equals(DIM2D) ? //
new RigidModel2D() : new RigidModel3D();
break;
case SIMILARITY:
model = dim.equals(DIM2D) ? //
new SimilarityModel2D() : new SimilarityModel3D();
break;
case AFFINE:
model = dim.equals(DIM2D) ? //
new AffineModel2D() : new AffineModel3D();
break;
}

// Prepare point correspondences (assuming positional correspondence)
assert x1.length == y1.length : "X and Y vectors for first point set need to be equal length";
assert x2.length == y2.length : "X and Y vectors for second point set need to be equal length";
assert x1.length == x2.length : "Both point sets need to have equal length";

ArrayList<PointMatch> correspondences = new ArrayList<>();
for (int i = 0; i < x1.length; i++) {
double[] pos1 = new double[dim.equals(DIM2D) ? 2 : 3];
pos1[0] = x1[i];
pos1[1] = y1[i];
if (dim.equals(DIM3D)) pos1[2] = z1[i];
Point p1 = new Point(pos1);
double[] pos2 = new double[dim.equals(DIM2D) ? 2 : 3];
pos2[0] = x2[i];
pos2[1] = y2[i];
if (dim.equals(DIM3D)) pos2[2] = z2[i];
Point p2 = new Point(pos2);
correspondences.add(new PointMatch(p1, p2));
}

// Fit the model
try {
model.fit(correspondences);
}
catch (NotEnoughDataPointsException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
}
catch (IllDefinedDataPointsException exc) {
// TODO Auto-generated catch block
exc.printStackTrace();
}

// Retrieve results
affine = new double[12];
double[] temp;
switch (dim) {
case DIM2D:
temp = new double[6];
((Affine2D<?>) model).toArray(temp);
map2DColsTo3DRows(temp, affine);
break;
case DIM3D:
temp = new double[12];
((Affine3D<?>) model).toArray(temp);
mapColsToRows(temp, affine);
break;
}
}

private void map2DColsTo3DRows(double[] temp, double[] mat) {
mat[0] = temp[0];
mat[1] = temp[2];
mat[2] = 0; //
mat[3] = temp[4];
mat[4] = temp[1];
mat[5] = temp[3];
mat[6] = 0; //
mat[7] = temp[5];
mat[8] = 0; //
mat[9] = 0; //
mat[10] = 1; //
mat[11] = 0; //
}

private void mapColsToRows(double[] temp, double[] mat) {
mat[0] = temp[0];
mat[1] = temp[3];
mat[2] = temp[6];
mat[3] = temp[9];
mat[4] = temp[1];
mat[5] = temp[4];
mat[6] = temp[7];
mat[7] = temp[10];
mat[8] = temp[2];
mat[9] = temp[5];
mat[10] = temp[8];
mat[11] = temp[11];
}
}
148 changes: 148 additions & 0 deletions src/test/java/ch/fmi/ModelFitterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@

package ch.fmi;

import static org.junit.Assert.assertArrayEquals;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.scijava.Context;
import org.scijava.command.CommandModule;
import org.scijava.command.CommandService;

public class ModelFitterTest {

private Context context;

@Before
public void initialize() {
context = new Context();
}

@After
public void disposeContext() {
if (context != null) {
context.dispose();
context = null;
}
}

@Test
public void test3DSimilarityModel() throws InterruptedException,
ExecutionException
{
// Create Point Arrays
double[] x1 = { 0, 1, 1, 1 };
double[] y1 = { 0, 0, 1, 1 };
double[] z1 = { 0, 0, 0, 1 };
double[] x2 = { 2, 2, 4, 4 };
double[] y2 = { 0, -2, -2, -2 };
double[] z2 = { 0, 0, 0, 2 };

Map<String, Object> inputMap = new HashMap<>();
inputMap.put("transformType", ModelFitter.SIMILARITY);
inputMap.put("dim", ModelFitter.DIM3D);
inputMap.put("x1", x1);
inputMap.put("y1", y1);
inputMap.put("z1", z1);
inputMap.put("x2", x2);
inputMap.put("y2", y2);
inputMap.put("z2", z2);

// Fit Model
CommandService commandService = context.getService(CommandService.class);
CommandModule module = commandService.run(ModelFitter.class, true, inputMap)
.get();

// Compare
double[] expected = { //
0, 2, 0, 2, //
-2, 0, 0, 0, //
0, 0, 2, 0 //
};
double[] result = (double[]) module.getOutput("affine");
System.out.println(Arrays.toString(result));
assertArrayEquals("Affine matrix", expected, result, 0.01);
}

@Test
public void test3DAffineModel() throws InterruptedException,
ExecutionException
{
// Create Point Arrays
double[] x1 = { 0, 1, 1, 1 };
double[] y1 = { 0, 0, 1, 1 };
double[] z1 = { 0, 0, 0, 1 };
double[] x2 = { 2, 2, 4, 4 };
double[] y2 = { 0, -2, -3, -3 };
double[] z2 = { 0, 0, 0, 2 };

Map<String, Object> inputMap = new HashMap<>();
inputMap.put("transformType", ModelFitter.AFFINE);
inputMap.put("dim", ModelFitter.DIM3D);
inputMap.put("x1", x1);
inputMap.put("y1", y1);
inputMap.put("z1", z1);
inputMap.put("x2", x2);
inputMap.put("y2", y2);
inputMap.put("z2", z2);

// Fit Model
CommandService commandService = context.getService(CommandService.class);
CommandModule module = commandService.run(ModelFitter.class, true, inputMap)
.get();

// Compare
double[] expected = { //
0, 2, 0, 2, //
-2, -1, 0, 0, //
0, 0, 2, 0 //
};
double[] result = (double[]) module.getOutput("affine");
System.out.println(Arrays.toString(result));
assertArrayEquals("Affine matrix", expected, result, 0.01);
}

@Test
public void test2DAffineModel() throws InterruptedException,
ExecutionException
{
// Create Point Arrays
double[] x1 = { 0, 1, 1, 0 };
double[] y1 = { 0, 0, 1, 1 };
double[] x2 = { 2, 2, 4, 4 };
double[] y2 = { 0, -2, -3, -1 };

Map<String, Object> inputMap = new HashMap<>();
inputMap.put("transformType", ModelFitter.AFFINE);
inputMap.put("dim", ModelFitter.DIM2D);
inputMap.put("x1", x1);
inputMap.put("y1", y1);
inputMap.put("x2", x2);
inputMap.put("y2", y2);

inputMap.put("z1", x1); // dummy input
inputMap.put("z2", x1); // dummy input

// Fit Model
CommandService commandService = context.getService(CommandService.class);
CommandModule module = commandService.run(ModelFitter.class, true, inputMap)
.get();

// Compare
double[] expected = { //
0, 2, 0, 2, //
-2, -1, 0, 0, //
0, 0, 1, 0 //
};

double[] result = (double[]) module.getOutput("affine");
System.out.println(Arrays.toString(result));

assertArrayEquals("Affine matrix", expected, result, 0.01);
}
}

0 comments on commit 68495c2

Please sign in to comment.