use of mpicbg.models.Point in project TrakEM2 by trakem2.
the class ManualAlignMode method exportLandmarks.
/**
* Export landmarks into XML file, in patch coordinates.
*/
public boolean exportLandmarks() {
if (m.isEmpty()) {
Utils.log("No landmarks to export!");
return false;
}
final StringBuilder sb = new StringBuilder("<landmarks>\n");
for (final Map.Entry<Layer, Landmarks> e : new TreeMap<Layer, Landmarks>(m).entrySet()) {
// sorted by Layer z
sb.append(" <layer id=\"").append(e.getKey().getId()).append("\">\n");
for (final Point p : e.getValue().points) {
final double[] w = p.getW();
double x = w[0], y = w[1];
// Find the point in a patch, and inverseTransform it into the patch local coords
final Collection<Displayable> under = e.getKey().find(Patch.class, (int) x, (int) y, true);
if (!under.isEmpty()) {
final Patch patch = (Patch) under.iterator().next();
final Point2D.Double po = patch.inverseTransformPoint(x, y);
x = po.x;
y = po.y;
sb.append(" <point patch_id=\"").append(patch.getId()).append('\"');
} else {
// Store the point as absolute
sb.append(" <point ");
}
sb.append(" x=\"").append(x).append("\" y=\"").append(y).append("\" />\n");
}
sb.append(" </layer>\n");
}
sb.append("</landmarks>");
final File f = Utils.chooseFile(null, "landmarks", ".xml");
if (null != f && Utils.saveToFile(f, sb.toString())) {
return true;
}
return false;
}
use of mpicbg.models.Point in project TrakEM2 by trakem2.
the class MovingLeastSquaresTransform method init.
@Override
public final void init(final String data) throws NumberFormatException {
matches.clear();
final String[] fields = data.split("\\s+");
if (fields.length > 3) {
final int d = Integer.parseInt(fields[1]);
if ((fields.length - 3) % (2 * d + 1) == 0) {
if (d == 2) {
if (fields[0].equals("translation"))
model = new TranslationModel2D();
else if (fields[0].equals("rigid"))
model = new RigidModel2D();
else if (fields[0].equals("similarity"))
model = new SimilarityModel2D();
else if (fields[0].equals("affine"))
model = new AffineModel2D();
else
throw new NumberFormatException("Inappropriate parameters for " + this.getClass().getCanonicalName());
} else if (d == 3) {
if (fields[0].equals("affine"))
model = new AffineModel3D();
else
throw new NumberFormatException("Inappropriate parameters for " + this.getClass().getCanonicalName());
} else
throw new NumberFormatException("Inappropriate parameters for " + this.getClass().getCanonicalName());
alpha = Double.parseDouble(fields[2]);
int i = 2;
while (i < fields.length - 1) {
final double[] p1 = new double[d];
for (int k = 0; k < d; ++k) p1[k] = Double.parseDouble(fields[++i]);
final double[] p2 = new double[d];
for (int k = 0; k < d; ++k) p2[k] = Double.parseDouble(fields[++i]);
final double weight = Double.parseDouble(fields[++i]);
final PointMatch m = new PointMatch(new Point(p1), new Point(p2), weight);
matches.add(m);
}
} else
throw new NumberFormatException("Inappropriate parameters for " + this.getClass().getCanonicalName());
} else
throw new NumberFormatException("Inappropriate parameters for " + this.getClass().getCanonicalName());
}
use of mpicbg.models.Point in project TrakEM2 by trakem2.
the class StitchingTEM method addMatches.
/**
* dx, dy is the position of t2 relative to the 0,0 of t1.
*/
private static final void addMatches(final AbstractAffineTile2D<?> t1, final AbstractAffineTile2D<?> t2, final double dx, final double dy) {
final Point p1 = new Point(new double[] { 0, 0 });
final Point p2 = new Point(new double[] { dx, dy });
t1.addMatch(new PointMatch(p2, p1, 1.0f));
t2.addMatch(new PointMatch(p1, p2, 1.0f));
t1.addConnectedTile(t2);
t2.addConnectedTile(t1);
}
use of mpicbg.models.Point in project TrakEM2 by trakem2.
the class AlignTask method alignMultiLayerMosaicTask.
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final void alignMultiLayerMosaicTask(final List<Layer> layerRange, final Patch nail, final Align.Param cp, final Align.ParamOptimize p, final Align.ParamOptimize pcp, final boolean tilesAreInPlaceIn, final boolean largestGraphOnlyIn, final boolean hideDisconnectedTilesIn, final boolean deleteDisconnectedTilesIn, final boolean deformIn) {
/* register */
final List<AbstractAffineTile2D<?>> allTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> allFixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> previousLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final HashMap<Patch, PointMatch> tileCenterPoints = new HashMap<Patch, PointMatch>();
final Collection<Patch> fixedPatches = new HashSet<Patch>();
if (null != nail)
fixedPatches.add(nail);
Layer previousLayer = null;
for (final Layer layer : layerRange) {
/* align all tiles in the layer */
final List<Patch> patches = new ArrayList<Patch>();
for (// ignore hidden tiles
final Displayable a : // ignore hidden tiles
layer.getDisplayables(Patch.class, true)) if (a instanceof Patch)
patches.add((Patch) a);
final List<AbstractAffineTile2D<?>> currentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> fixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(p, patches, fixedPatches, currentLayerTiles, fixedTiles);
// Will consider graphs and hide/delete tiles when all cross-layer graphs are found.
alignTiles(p, currentLayerTiles, fixedTiles, tilesAreInPlaceIn, false, false, false);
if (Thread.currentThread().isInterrupted())
return;
/* connect to the previous layer */
/* generate tiles with the cross-section model from the current layer tiles */
/* ------------------------------------------------------------------------ */
/* TODO step back and make tiles bare containers for a patch and a model such that by changing the model the tile can be reused */
final HashMap<Patch, AbstractAffineTile2D<?>> currentLayerPatchTiles = new HashMap<Patch, AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : currentLayerTiles) currentLayerPatchTiles.put(t.getPatch(), t);
final List<AbstractAffineTile2D<?>> csCurrentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final Set<AbstractAffineTile2D<?>> csFixedTiles = new HashSet<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(cp, patches, fixedPatches, csCurrentLayerTiles, csFixedTiles);
final HashMap<Tile<?>, AbstractAffineTile2D<?>> tileTiles = new HashMap<Tile<?>, AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : csCurrentLayerTiles) tileTiles.put(currentLayerPatchTiles.get(t.getPatch()), t);
for (final AbstractAffineTile2D<?> t : currentLayerTiles) {
final AbstractAffineTile2D<?> csLayerTile = tileTiles.get(t);
csLayerTile.addMatches(t.getMatches());
for (final Tile<?> ct : t.getConnectedTiles()) csLayerTile.addConnectedTile(tileTiles.get(ct));
}
/* add a fixed tile only if there was a Patch selected */
allFixedTiles.addAll(csFixedTiles);
/* first, align connected graphs to each other */
/* graphs in the current layer */
final List<Set<Tile<?>>> currentLayerGraphs = AbstractAffineTile2D.identifyConnectedGraphs(csCurrentLayerTiles);
if (Thread.currentThread().isInterrupted())
return;
// /* TODO just for visualization */
// for ( final Set< Tile< ? > > graph : currentLayerGraphs )
// {
// Display.getFront().getSelection().clear();
// Display.getFront().setLayer( ( ( AbstractAffineTile2D< ? > )graph.iterator().next() ).getPatch().getLayer() );
//
// for ( final Tile< ? > tile : graph )
// {
// Display.getFront().getSelection().add( ( ( AbstractAffineTile2D< ? > )tile ).getPatch() );
// Display.repaint();
// }
// Utils.showMessage( "OK" );
// }
/* graphs from the whole system that are present in the previous layer */
final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(allTiles);
final HashMap<Set<Tile<?>>, Set<Tile<?>>> graphGraphs = new HashMap<Set<Tile<?>>, Set<Tile<?>>>();
for (final Set<Tile<?>> graph : graphs) {
if (Thread.currentThread().isInterrupted())
return;
final Set<Tile<?>> previousLayerGraph = new HashSet<Tile<?>>();
for (final Tile<?> tile : previousLayerTiles) {
if (graph.contains(tile)) {
graphGraphs.put(graph, previousLayerGraph);
previousLayerGraph.add(tile);
}
}
}
final Collection<Set<Tile<?>>> previousLayerGraphs = graphGraphs.values();
// /* TODO just for visualization */
// for ( final Set< Tile< ? > > graph : previousLayerGraphs )
// {
// Display.getFront().getSelection().clear();
// Display.getFront().setLayer( ( ( AbstractAffineTile2D< ? > )graph.iterator().next() ).getPatch().getLayer() );
//
// for ( final Tile< ? > tile : graph )
// {
// Display.getFront().getSelection().add( ( ( AbstractAffineTile2D< ? > )tile ).getPatch() );
// Display.repaint();
// }
// Utils.showMessage( "OK" );
// }
/* generate snapshots of the graphs and preregister them using the parameters defined in cp */
final List<AbstractAffineTile2D<?>[]> crossLayerTilePairs = new ArrayList<AbstractAffineTile2D<?>[]>();
for (final Set<Tile<?>> currentLayerGraph : currentLayerGraphs) {
for (final Set<Tile<?>> previousLayerGraph : previousLayerGraphs) {
if (Thread.currentThread().isInterrupted())
return;
alignGraphs(cp, layer, previousLayer, currentLayerGraph, previousLayerGraph);
/* TODO this is pointless data shuffling just for type incompatibility---fix this at the root */
final ArrayList<AbstractAffineTile2D<?>> previousLayerGraphTiles = new ArrayList<AbstractAffineTile2D<?>>();
previousLayerGraphTiles.addAll((Set) previousLayerGraph);
final ArrayList<AbstractAffineTile2D<?>> currentLayerGraphTiles = new ArrayList<AbstractAffineTile2D<?>>();
currentLayerGraphTiles.addAll((Set) currentLayerGraph);
AbstractAffineTile2D.pairOverlappingTiles(previousLayerGraphTiles, currentLayerGraphTiles, crossLayerTilePairs);
}
}
/* ------------------------------------------------------------------------ */
/* this is without the affine/rigid approximation per graph */
// AbstractAffineTile2D.pairTiles( previousLayerTiles, csCurrentLayerTiles, crossLayerTilePairs );
Align.connectTilePairs(cp, csCurrentLayerTiles, crossLayerTilePairs, Runtime.getRuntime().availableProcessors());
if (Thread.currentThread().isInterrupted())
return;
// for ( final AbstractAffineTile2D< ? >[] tilePair : crossLayerTilePairs )
// {
// Display.getFront().setLayer( tilePair[ 0 ].getPatch().getLayer() );
// Display.getFront().getSelection().clear();
// Display.getFront().getSelection().add( tilePair[ 0 ].getPatch() );
// Display.getFront().getSelection().add( tilePair[ 1 ].getPatch() );
//
// Utils.showMessage( "1: OK?" );
//
// Display.getFront().setLayer( tilePair[ 1 ].getPatch().getLayer() );
// Display.getFront().getSelection().clear();
// Display.getFront().getSelection().add( tilePair[ 0 ].getPatch() );
// Display.getFront().getSelection().add( tilePair[ 1 ].getPatch() );
//
// Utils.showMessage( "2: OK?" );
// }
/* prepare the next loop */
allTiles.addAll(csCurrentLayerTiles);
previousLayerTiles.clear();
previousLayerTiles.addAll(csCurrentLayerTiles);
/* optimize */
Align.optimizeTileConfiguration(pcp, allTiles, allFixedTiles);
if (Thread.currentThread().isInterrupted())
return;
for (final AbstractAffineTile2D<?> t : allTiles) t.getPatch().setAffineTransform(t.getModel().createAffine());
previousLayer = layer;
}
final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(allTiles);
final List<AbstractAffineTile2D<?>> interestingTiles = new ArrayList<AbstractAffineTile2D<?>>();
if (largestGraphOnlyIn && (hideDisconnectedTilesIn || deleteDisconnectedTilesIn)) {
if (Thread.currentThread().isInterrupted())
return;
/* find largest graph. */
Set<Tile<?>> largestGraph = null;
for (final Set<Tile<?>> graph : graphs) if (largestGraph == null || largestGraph.size() < graph.size())
largestGraph = graph;
final Set<AbstractAffineTile2D<?>> tiles_to_keep = new HashSet<AbstractAffineTile2D<?>>();
for (final Tile<?> t : largestGraph) tiles_to_keep.add((AbstractAffineTile2D<?>) t);
if (hideDisconnectedTilesIn)
for (final AbstractAffineTile2D<?> t : allTiles) if (!tiles_to_keep.contains(t))
t.getPatch().setVisible(false);
if (deleteDisconnectedTilesIn)
for (final AbstractAffineTile2D<?> t : allTiles) if (!tiles_to_keep.contains(t))
t.getPatch().remove(false);
interestingTiles.addAll(tiles_to_keep);
} else
interestingTiles.addAll(allTiles);
if (deformIn) {
/* ############################################ */
/* experimental: use the center points of all tiles to define a MLS deformation from the pure intra-layer registration to the globally optimal */
Utils.log("deforming...");
/* store the center location of each single tile for later deformation */
for (final AbstractAffineTile2D<?> t : interestingTiles) {
final double[] c = new double[] { t.getWidth() / 2.0, t.getHeight() / 2.0 };
t.getModel().applyInPlace(c);
final Point q = new Point(c);
tileCenterPoints.put(t.getPatch(), new PointMatch(q.clone(), q));
}
for (final Layer layer : layerRange) {
Utils.log("layer" + layer);
if (Thread.currentThread().isInterrupted())
return;
/* again, align all tiles in the layer */
final List<Patch> patches = new ArrayList<Patch>();
for (final Displayable a : layer.getDisplayables(Patch.class)) if (a instanceof Patch)
patches.add((Patch) a);
final List<AbstractAffineTile2D<?>> currentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> fixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(p, patches, fixedPatches, currentLayerTiles, fixedTiles);
/* add a fixed tile only if there was a Patch selected */
allFixedTiles.addAll(fixedTiles);
// will consider graphs and hide/delete tiles when all cross-layer graphs are found
alignTiles(p, currentLayerTiles, fixedTiles, true, false, false, false);
/* for each independent graph do an independent transform */
final List<Set<Tile<?>>> currentLayerGraphs = AbstractAffineTile2D.identifyConnectedGraphs(currentLayerTiles);
for (final Set<Tile<?>> graph : currentLayerGraphs) {
/* update the tile-center pointmatches */
final Collection<PointMatch> matches = new ArrayList<PointMatch>();
final Collection<AbstractAffineTile2D<?>> toBeDeformedTiles = new ArrayList<AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : (Collection<AbstractAffineTile2D<?>>) (Collection) graph) {
final PointMatch pm = tileCenterPoints.get(t.getPatch());
if (pm == null)
continue;
final double[] pl = pm.getP1().getL();
pl[0] = t.getWidth() / 2.0;
pl[1] = t.getHeight() / 2.0;
t.getModel().applyInPlace(pl);
matches.add(pm);
toBeDeformedTiles.add(t);
}
for (final AbstractAffineTile2D<?> t : toBeDeformedTiles) {
if (Thread.currentThread().isInterrupted())
return;
try {
final Patch patch = t.getPatch();
final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
final AffineTransform pat = new AffineTransform();
pat.translate(-pbox.x, -pbox.y);
pat.preConcatenate(patch.getAffineTransform());
final mpicbg.trakem2.transform.AffineModel2D toWorld = new mpicbg.trakem2.transform.AffineModel2D();
toWorld.set(pat);
final MovingLeastSquaresTransform2 mlst = Align.createMLST(matches, 1.0f);
final CoordinateTransformList<CoordinateTransform> ctl = new CoordinateTransformList<CoordinateTransform>();
ctl.add(toWorld);
ctl.add(mlst);
ctl.add(toWorld.createInverse());
patch.appendCoordinateTransform(ctl);
patch.getProject().getLoader().regenerateMipMaps(patch);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
}
}
layerRange.get(0).getParent().setMinimumDimensions();
IJ.log("Done: register multi-layer mosaic.");
return;
}
use of mpicbg.models.Point in project TrakEM2 by trakem2.
the class Align method alignLayersLinearly.
/**
* Align a range of layers by accumulating pairwise alignments of contiguous layers.
*
* @param layers The range of layers to align pairwise.
* @param numThreads The number of threads to use.
* @param filter The {@link Filter} to decide which {@link Patch} instances to use in each {@link Layer}. Can be null.
*/
public static final void alignLayersLinearly(final List<Layer> layers, final int numThreads, final Filter<Patch> filter) {
param.sift.maxOctaveSize = 1600;
if (!param.setup("Align layers linearly"))
return;
final Rectangle box = layers.get(0).getParent().getMinimalBoundingBox(Patch.class);
final double scale = Math.min(1.0, Math.min((double) param.sift.maxOctaveSize / box.width, (double) param.sift.maxOctaveSize / box.height));
final Param p = param.clone();
p.maxEpsilon *= scale;
final FloatArray2DSIFT sift = new FloatArray2DSIFT(p.sift);
final SIFT ijSIFT = new SIFT(sift);
Rectangle box1 = null;
Rectangle box2 = null;
final Collection<Feature> features1 = new ArrayList<Feature>();
final Collection<Feature> features2 = new ArrayList<Feature>();
final List<PointMatch> candidates = new ArrayList<PointMatch>();
final List<PointMatch> inliers = new ArrayList<PointMatch>();
final AffineTransform a = new AffineTransform();
int i = 0;
for (final Layer l : layers) {
long s = System.currentTimeMillis();
features1.clear();
features1.addAll(features2);
features2.clear();
final Rectangle box3 = l.getMinimalBoundingBox(Patch.class);
if (box3 == null)
continue;
box1 = box2;
box2 = box3;
final List<Patch> patches = l.getAll(Patch.class);
if (null != filter) {
for (final Iterator<Patch> it = patches.iterator(); it.hasNext(); ) {
if (!filter.accept(it.next()))
it.remove();
}
}
ijSIFT.extractFeatures(l.getProject().getLoader().getFlatImage(l, box2, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, patches, true).getProcessor(), features2);
Utils.log(features2.size() + " features extracted in layer \"" + l.getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
if (features1.size() > 0) {
s = System.currentTimeMillis();
candidates.clear();
FeatureTransform.matchFeatures(features2, features1, candidates, p.rod);
final AbstractAffineModel2D<?> model;
switch(p.expectedModelIndex) {
case 0:
model = new TranslationModel2D();
break;
case 1:
model = new RigidModel2D();
break;
case 2:
model = new SimilarityModel2D();
break;
case 3:
model = new AffineModel2D();
break;
default:
return;
}
boolean modelFound;
boolean again = false;
try {
do {
again = false;
modelFound = model.filterRansac(candidates, inliers, 1000, p.maxEpsilon, p.minInlierRatio, p.minNumInliers, 3);
if (modelFound && p.rejectIdentity) {
final ArrayList<Point> points = new ArrayList<Point>();
PointMatch.sourcePoints(inliers, points);
if (Transforms.isIdentity(model, points, p.identityTolerance)) {
Utils.log("Identity transform for " + inliers.size() + " matches rejected.");
candidates.removeAll(inliers);
inliers.clear();
again = true;
}
}
} while (again);
} catch (final NotEnoughDataPointsException e) {
modelFound = false;
}
if (modelFound) {
Utils.log("Model found for layer \"" + l.getTitle() + "\" and its predecessor:\n correspondences " + inliers.size() + " of " + candidates.size() + "\n average residual error " + (model.getCost() / scale) + " px\n took " + (System.currentTimeMillis() - s) + " ms");
final AffineTransform b = new AffineTransform();
b.translate(box1.x, box1.y);
b.scale(1.0f / scale, 1.0f / scale);
b.concatenate(model.createAffine());
b.scale(scale, scale);
b.translate(-box2.x, -box2.y);
a.concatenate(b);
l.apply(Displayable.class, a);
Display.repaint(l);
} else {
Utils.log("No model found for layer \"" + l.getTitle() + "\" and its predecessor:\n correspondence candidates " + candidates.size() + "\n took " + (System.currentTimeMillis() - s) + " ms");
a.setToIdentity();
}
}
IJ.showProgress(++i, layers.size());
}
}
Aggregations