use of mpicbg.models.AbstractAffineModel2D in project TrakEM2 by trakem2.
the class ManualAlignMode method apply.
@Override
public boolean apply() {
// Check there's more than one layer
if (m.size() < 2) {
Utils.showMessage("Need more than one layer to align!");
return false;
}
// Check that the current layer is one of the layers with landmarks.
// Will be used as reference
final Layer ref_layer = display.getLayer();
if (null == m.get(ref_layer)) {
Utils.showMessage("Please scroll to a layer with landmarks,\nto be used as reference.");
return false;
}
// Check that all layers have the same number of landmarks
int n_landmarks = -1;
for (final Map.Entry<Layer, Landmarks> e : m.entrySet()) {
final Landmarks lm = e.getValue();
if (-1 == n_landmarks) {
n_landmarks = lm.points.size();
continue;
}
if (n_landmarks != lm.points.size()) {
Utils.showMessage("Can't apply: there are different amounts of landmarks per layer.\nSee the log window.");
for (final Map.Entry<Layer, Landmarks> ee : m.entrySet()) {
Utils.log(ee.getValue().points.size() + " landmarks in layer " + ee.getKey());
}
return false;
}
}
// Sort Layers by Z
final TreeMap<Layer, Landmarks> sorted = new TreeMap<Layer, Landmarks>(new Comparator<Layer>() {
@Override
public boolean equals(final Object ob) {
return this == ob;
}
@Override
public int compare(final Layer l1, final Layer l2) {
// Ascending order
final double dz = l1.getZ() - l2.getZ();
if (dz < 0)
return -1;
else if (dz > 0)
return 1;
else
return 0;
}
});
sorted.putAll(m);
int iref = 0;
for (final Layer la : sorted.keySet()) {
if (la != ref_layer)
iref++;
else
break;
}
// Ok, now ask for a model
final GenericDialog gd = new GenericDialog("Model");
gd.addChoice("Model:", Align.Param.modelStrings, Align.Param.modelStrings[1]);
gd.addCheckbox("Propagate to first layer", 0 != iref);
((Component) gd.getCheckboxes().get(0)).setEnabled(0 != iref);
gd.addCheckbox("Propagate to last layer", sorted.size() - 1 != iref);
((Component) gd.getCheckboxes().get(1)).setEnabled(sorted.size() - 1 != iref);
gd.showDialog();
if (gd.wasCanceled())
return false;
final int model_index = gd.getNextChoiceIndex();
final boolean propagate_to_first = gd.getNextBoolean();
final boolean propagate_to_last = gd.getNextBoolean();
int min;
// Create a model as desired
final AbstractAffineModel2D<?> model;
switch(model_index) {
case 0:
min = 1;
model = new TranslationModel2D();
break;
case 1:
min = 2;
model = new RigidModel2D();
break;
case 2:
min = 2;
model = new SimilarityModel2D();
break;
case 3:
min = 3;
model = new AffineModel2D();
break;
default:
Utils.log("Unknown model index!");
return false;
}
if (n_landmarks < min) {
Utils.showMessage("Need at least " + min + " landmarks for a " + Align.Param.modelStrings[model_index] + " model");
return false;
}
Bureaucrat.createAndStart(new Worker.Task("Aligning layers with landmarks") {
@Override
public void exec() {
// Find layers with landmarks, in increasing Z.
// Match in pairs.
// So, get two submaps: from ref_layer to first, and from ref_layer to last
// strictly lower Z than ref_layer
final SortedMap<Layer, Landmarks> first_chunk_ = new TreeMap<Layer, Landmarks>(sorted.headMap(ref_layer));
// .. so add ref_layer
first_chunk_.put(ref_layer, m.get(ref_layer));
// equal or larger Z than ref_layer
final SortedMap<Layer, Landmarks> second_chunk = sorted.tailMap(ref_layer);
final SortedMap<Layer, Landmarks> first_chunk;
// Reverse order of first_chunk
if (first_chunk_.size() > 1) {
final SortedMap<Layer, Landmarks> fc = new TreeMap<Layer, Landmarks>(new Comparator<Layer>() {
@Override
public boolean equals(final Object ob) {
return this == ob;
}
@Override
public int compare(final Layer l1, final Layer l2) {
// Descending order
final double dz = l2.getZ() - l1.getZ();
if (dz < 0)
return -1;
else if (dz > 0)
return 1;
else
return 0;
}
});
fc.putAll(first_chunk_);
first_chunk = fc;
} else {
first_chunk = first_chunk_;
}
final LayerSet ls = ref_layer.getParent();
final Collection<Layer> affected_layers = new HashSet<Layer>(m.keySet());
// Gather all Patch instances that will be affected
final ArrayList<Patch> patches = new ArrayList<Patch>();
for (final Layer la : m.keySet()) patches.addAll(la.getAll(Patch.class));
if (propagate_to_first && first_chunk.size() > 1) {
final Collection<Layer> affected = ls.getLayers().subList(0, ls.indexOf(first_chunk.lastKey()));
for (final Layer la : affected) {
patches.addAll(la.getAll(Patch.class));
}
affected_layers.addAll(affected);
}
if (propagate_to_last && second_chunk.size() > 1) {
final Collection<Layer> affected = ls.getLayers().subList(ls.indexOf(second_chunk.lastKey()) + 1, ls.size());
for (final Layer la : affected) {
patches.addAll(la.getAll(Patch.class));
}
}
// Transform segmentations along with patches
AlignTask.transformPatchesAndVectorData(patches, new Runnable() {
@Override
public void run() {
// Apply!
// TODO: when adding non-linear transforms, use this single line for undo instead of all below:
// (these transforms may be non-linear as well, which alter mipmaps.)
// ls.addTransformStepWithData(affected_layers);
// Setup undo:
// Find all images in the range of affected layers,
// plus all Displayable of those layers (but Patch instances in a separate DoTransforms step,
// to avoid adding a "data" undo for them, which would recreate mipmaps when undone).
// plus all ZDisplayable that paint in those layers
final HashSet<Displayable> ds = new HashSet<Displayable>();
final ArrayList<Displayable> patches = new ArrayList<Displayable>();
for (final Layer layer : affected_layers) {
for (final Displayable d : layer.getDisplayables()) {
if (d.getClass() == Patch.class) {
patches.add(d);
} else {
ds.add(d);
}
}
}
for (final ZDisplayable zd : ls.getZDisplayables()) {
for (final Layer layer : affected_layers) {
if (zd.paintsAt(layer)) {
ds.add((Displayable) zd);
break;
}
}
}
if (ds.size() > 0) {
final Displayable.DoEdits step = ls.addTransformStepWithData(ds);
if (patches.size() > 0) {
final ArrayList<DoStep> a = new ArrayList<DoStep>();
a.add(new Displayable.DoTransforms().addAll(patches));
step.addDependents(a);
}
}
if (first_chunk.size() > 1) {
final AffineTransform aff = align(first_chunk, model);
if (propagate_to_first) {
for (final Layer la : ls.getLayers().subList(0, ls.indexOf(first_chunk.lastKey()))) {
// exclusive last
la.apply(Patch.class, aff);
}
}
}
if (second_chunk.size() > 1) {
final AffineTransform aff = align(second_chunk, model);
if (propagate_to_last) {
for (final Layer la : ls.getLayers().subList(ls.indexOf(second_chunk.lastKey()) + 1, ls.size())) {
// exclusive last
la.apply(Patch.class, aff);
}
}
}
Display.repaint();
// Store current state
if (ds.size() > 0) {
final Displayable.DoEdits step2 = ls.addTransformStepWithData(ds);
if (patches.size() > 0) {
final ArrayList<DoStep> a2 = new ArrayList<DoStep>();
a2.add(new Displayable.DoTransforms().addAll(patches));
step2.addDependents(a2);
}
}
}
});
}
}, display.getProject());
return true;
}
use of mpicbg.models.AbstractAffineModel2D in project TrakEM2 by trakem2.
the class NonLinearTransformMode method createCT.
private CoordinateTransform createCT() throws Exception {
final Collection<PointMatch> pm = new ArrayList<PointMatch>();
for (final Point p : points) {
pm.add(new PointMatch(new Point(p.getL()), new Point(p.getW())));
}
/*
* TODO replace this with the desired parameters of the transformation
*/
final MovingLeastSquaresTransform2 mlst = new MovingLeastSquaresTransform2();
mlst.setAlpha(1.0f);
Class<? extends AbstractAffineModel2D<?>> c = AffineModel2D.class;
switch(points.size()) {
case 1:
c = TranslationModel2D.class;
break;
case 2:
c = SimilarityModel2D.class;
break;
default:
break;
}
mlst.setModel(c);
mlst.setMatches(pm);
return mlst;
}
use of mpicbg.models.AbstractAffineModel2D in project TrakEM2 by trakem2.
the class Distortion_Correction method extractSIFTPointsThreaded.
protected static void extractSIFTPointsThreaded(final int index, final List<Feature>[] siftFeatures, final List<PointMatch>[] inliers, final AbstractAffineModel2D<?>[] models) {
// save all matching candidates
final List<PointMatch>[] candidates = new List[siftFeatures.length - 1];
final Thread[] threads = MultiThreading.newThreads();
// start at second
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ++ithread) {
threads[ithread] = new Thread() {
@Override
public void run() {
setPriority(Thread.NORM_PRIORITY);
for (int j = ai.getAndIncrement(); j < candidates.length; j = ai.getAndIncrement()) {
final int i = (j < index ? j : j + 1);
candidates[j] = FloatArray2DSIFT.createMatches(siftFeatures[index], siftFeatures[i], 1.5f, null, Float.MAX_VALUE, 0.5f);
}
}
};
}
MultiThreading.startAndJoin(threads);
// get rid of the outliers and save the rigid transformations to match
// the inliers
final AtomicInteger ai2 = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ++ithread) {
threads[ithread] = new Thread() {
@Override
public void run() {
setPriority(Thread.NORM_PRIORITY);
for (int i = ai2.getAndIncrement(); i < candidates.length; i = ai2.getAndIncrement()) {
final List<PointMatch> tmpInliers = new ArrayList<PointMatch>();
// RigidModel2D m =
// RigidModel2D.estimateBestModel(candidates.get(i),
// tmpInliers, sp.min_epsilon, sp.max_epsilon,
// sp.min_inlier_ratio);
final AbstractAffineModel2D<?> m;
switch(sp.expectedModelIndex) {
case 0:
m = new TranslationModel2D();
break;
case 1:
m = new RigidModel2D();
break;
case 2:
m = new SimilarityModel2D();
break;
case 3:
m = new AffineModel2D();
break;
default:
return;
}
boolean modelFound = false;
try {
modelFound = m.filterRansac(candidates[i], tmpInliers, 1000, sp.maxEpsilon, sp.minInlierRatio, 10);
} catch (final NotEnoughDataPointsException e) {
modelFound = false;
}
if (modelFound)
IJ.log("Model found:\n " + candidates[i].size() + " candidates\n " + tmpInliers.size() + " inliers\n " + String.format("%.2f", m.getCost()) + "px average displacement");
else
IJ.log("No Model found.");
inliers[index * (sp.numberOfImages - 1) + i] = tmpInliers;
models[index * (sp.numberOfImages - 1) + i] = m;
// System.out.println("**** MODEL ADDED: " +
// (index*(sp.numberOfImages-1)+i));
}
}
};
}
MultiThreading.startAndJoin(threads);
}
use of mpicbg.models.AbstractAffineModel2D in project TrakEM2 by trakem2.
the class Distortion_Correction method extractSIFTPoints.
protected void extractSIFTPoints(final int index, final List<Feature>[] siftFeatures, final List<List<PointMatch>> inliers, final List<AbstractAffineModel2D<?>> models) {
// save all matching candidates
final List<List<PointMatch>> candidates = new ArrayList<List<PointMatch>>();
for (int j = 0; j < siftFeatures.length; j++) {
if (index == j)
continue;
candidates.add(FloatArray2DSIFT.createMatches(siftFeatures[index], siftFeatures[j], 1.5f, null, Float.MAX_VALUE, 0.5f));
}
// get rid of the outliers and save the transformations to match the inliers
for (int i = 0; i < candidates.size(); ++i) {
final List<PointMatch> tmpInliers = new ArrayList<PointMatch>();
final AbstractAffineModel2D<?> m;
switch(sp.expectedModelIndex) {
case 0:
m = new TranslationModel2D();
break;
case 1:
m = new RigidModel2D();
break;
case 2:
m = new SimilarityModel2D();
break;
case 3:
m = new AffineModel2D();
break;
default:
return;
}
try {
m.filterRansac(candidates.get(i), tmpInliers, 1000, sp.maxEpsilon, sp.minInlierRatio, 10);
} catch (final NotEnoughDataPointsException e) {
e.printStackTrace();
}
inliers.add(tmpInliers);
models.add(m);
}
}
use of mpicbg.models.AbstractAffineModel2D 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