use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.
the class AlignTask method alignPatches.
/**
* @param patches: the list of Patch instances to align, all belonging to the same Layer.
* @param fixedPatches: the list of Patch instances to keep locked in place, if any.
* @param m: {@link AlignTask#LINEAR_SIFT_CORRESPONDENCES}, {@link AlignTask#LINEAR_PHASE_CORRELATION} or {@link AlignTask#ELASTIC_BLOCK_CORRESPONDENCES}.
*/
public static final void alignPatches(final List<Patch> patches, final Set<Patch> fixedPatches, final int m) throws Exception {
if (patches.size() < 2) {
Utils.log("No images to align.");
return;
}
for (final Patch patch : fixedPatches) {
if (!patches.contains(patch)) {
Utils.log("The list of fixed patches contains at least one Patch not included in the list of patches to align!");
return;
}
}
if (ELASTIC_BLOCK_CORRESPONDENCES == m)
new ElasticMontage().exec(patches, fixedPatches);
else if (LINEAR_PHASE_CORRELATION == m) {
// Montage all given patches, fixedPatches is ignored!
if (!fixedPatches.isEmpty())
Utils.log("Ignoring " + fixedPatches.size() + " fixed patches.");
StitchingTEM.montageWithPhaseCorrelation(patches);
} else if (LINEAR_SIFT_CORRESPONDENCES == m) {
if (!Align.paramOptimize.setup("Montage Selection"))
return;
final GenericDialog gd = new GenericDialog("Montage Selection: Miscellaneous");
gd.addCheckbox("tiles are roughly in place", tilesAreInPlace);
gd.addCheckbox("sloppy overlap test (fast)", sloppyOverlapTest);
gd.addCheckbox("consider largest graph only", largestGraphOnly);
gd.addCheckbox("hide tiles from non-largest graph", hideDisconnectedTiles);
gd.addCheckbox("delete tiles from non-largest graph", deleteDisconnectedTiles);
gd.showDialog();
if (gd.wasCanceled())
return;
tilesAreInPlace = gd.getNextBoolean();
sloppyOverlapTest = gd.getNextBoolean();
largestGraphOnly = gd.getNextBoolean();
hideDisconnectedTiles = gd.getNextBoolean();
deleteDisconnectedTiles = gd.getNextBoolean();
final Align.ParamOptimize p = Align.paramOptimize.clone();
alignPatches(p, patches, fixedPatches, tilesAreInPlace, largestGraphOnly, hideDisconnectedTiles, deleteDisconnectedTiles, sloppyOverlapTest);
} else
Utils.log("Don't know how to align with mode " + m);
}
use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.
the class AlignTask method alignMultiLayerMosaic.
/**
* Align a multi-layer mosaic.
*
* @param l the current layer
*/
public static final void alignMultiLayerMosaic(final Layer l, final Patch nail) {
/* layer range and misc */
final List<Layer> layers = l.getParent().getLayers();
final String[] layerTitles = new String[layers.size()];
for (int i = 0; i < layers.size(); ++i) layerTitles[i] = l.getProject().findLayerThing(layers.get(i)).toString();
final GenericDialog gd1 = new GenericDialog("Align Multi-Layer Mosaic : Layer Range");
gd1.addMessage("Layer Range:");
final int sel = l.getParent().indexOf(l);
gd1.addChoice("first :", layerTitles, layerTitles[sel]);
gd1.addChoice("last :", layerTitles, layerTitles[sel]);
gd1.addMessage("Miscellaneous:");
gd1.addCheckbox("tiles are roughly in place", tilesAreInPlace);
gd1.addCheckbox("consider largest graph only", largestGraphOnly);
gd1.addCheckbox("hide tiles from non-largest graph", hideDisconnectedTiles);
gd1.addCheckbox("delete tiles from non-largest graph", deleteDisconnectedTiles);
gd1.addCheckbox("deform layers", deform);
gd1.showDialog();
if (gd1.wasCanceled())
return;
final int first = gd1.getNextChoiceIndex();
final int last = gd1.getNextChoiceIndex();
final int d = first < last ? 1 : -1;
tilesAreInPlace = gd1.getNextBoolean();
largestGraphOnly = gd1.getNextBoolean();
hideDisconnectedTiles = gd1.getNextBoolean();
deleteDisconnectedTiles = gd1.getNextBoolean();
deform = gd1.getNextBoolean();
if (!Align.paramOptimize.setup("Align Multi-Layer Mosaic : Intra-Layer"))
return;
final Align.ParamOptimize p = Align.paramOptimize.clone();
final Align.ParamOptimize pcp = p.clone();
if (!Align.param.setup("Align Multi-Layer Mosaic : Cross-Layer"))
return;
final Align.Param cp = Align.param.clone();
pcp.desiredModelIndex = cp.desiredModelIndex;
final List<Layer> layerRange = new ArrayList<Layer>();
for (int i = first; i != last + d; i += d) layerRange.add(layers.get(i));
alignMultiLayerMosaicTask(layerRange, nail, cp, p, pcp, tilesAreInPlace, largestGraphOnly, hideDisconnectedTiles, deleteDisconnectedTiles, deform);
}
use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.
the class AlignTask method createTransformPropertiesTable.
/**
* Creates a map only for visible patches that intersect vdata.
* @param src_vdata represents the VectorData instances in original form, of the original project and layer set.
* @param tgt_vdata if not null, it must have the same size as src_data and their elements correspond one-to-one (as in, tgt element a clone of src element at the same index).
* @param lids_to_operate The id of the layers on which any operation will be done
* tgt_data enables transformVectorData to apply the transforms to copies of the src_vdata in another project.
*/
public static final ReferenceData createTransformPropertiesTable(final List<Displayable> src_vdata, final List<Displayable> tgt_vdata, final Set<Long> lids_to_operate) {
if (src_vdata.isEmpty())
return null;
final Map<Long, Patch.TransformProperties> tp = new HashMap<Long, Patch.TransformProperties>();
// A map of Displayable vs a map of Layer id vs list of Patch ids in that Layer that lay under the Patch, sorted by stack index
final Map<Displayable, Map<Long, TreeMap<Integer, Long>>> underlying = new HashMap<Displayable, Map<Long, TreeMap<Integer, Long>>>();
// The set of layers used
final Set<Long> src_layer_lids_used = new HashSet<Long>();
// Parallelize! This operation can be insanely expensive
final int nproc = Runtime.getRuntime().availableProcessors();
final ExecutorService exec = Utils.newFixedThreadPool(nproc, "AlignTask-createTransformPropertiesTable");
final List<Future<?>> dtasks = new ArrayList<Future<?>>();
final List<Future<?>> ltasks = new ArrayList<Future<?>>();
final Thread current = Thread.currentThread();
try {
for (int i = src_vdata.size() - 1; i > -1; i--) {
final Displayable src_d = src_vdata.get(i);
// filter out
if (!(src_d instanceof VectorData))
continue;
// use src_d if tgt_vdata is null
final Displayable tgt_d = null == tgt_vdata ? src_d : tgt_vdata.get(i);
// Some checking
if (!(tgt_d instanceof VectorData)) {
Utils.log("WARNING ignoring provided tgt_vdata " + tgt_d + " which is NOT a VectorData instance!");
continue;
}
if (src_d.getClass() != tgt_d.getClass()) {
Utils.log("WARNING src_d and tgt_d are instances of different classes:\n src_d :: " + src_d + "\n tgt_d :: " + tgt_d);
}
dtasks.add(exec.submit(new Runnable() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void run() {
final Map<Long, TreeMap<Integer, Long>> under = new HashMap<Long, TreeMap<Integer, Long>>();
synchronized (underlying) {
underlying.put(tgt_d, under);
}
if (current.isInterrupted())
return;
// Iterate the layers in which this VectorData has any data AND which have to be transformed
for (final Long olid : src_d.getLayerIds()) {
final long lid = olid.longValue();
// layer with id 'lid' is not affected
if (!lids_to_operate.contains(lid))
continue;
final Layer la = src_d.getLayerSet().getLayer(lid);
final Area a = src_d.getAreaAt(la);
if (null == a || a.isEmpty()) {
// does not paint in the layer
continue;
}
// The list of patches that lay under VectorData d, sorted by their stack index in the layer
final TreeMap<Integer, Long> stacked_patch_ids = new TreeMap<Integer, Long>();
synchronized (under) {
under.put(lid, stacked_patch_ids);
}
final boolean[] layer_visited = new boolean[] { false };
// Iterate source patches
for (final Patch patch : (Collection<Patch>) (Collection) la.getDisplayables(Patch.class, a, true)) {
// pick visible patches only
if (current.isInterrupted())
return;
try {
ltasks.add(exec.submit(new Runnable() {
@Override
public void run() {
if (current.isInterrupted())
return;
synchronized (patch) {
Patch.TransformProperties props;
synchronized (tp) {
props = tp.get(patch.getId());
}
if (null == props) {
props = patch.getTransformPropertiesCopy();
// Cache the props
synchronized (tp) {
tp.put(patch.getId(), props);
}
}
// Cache this patch as under the VectorData d
synchronized (stacked_patch_ids) {
// sorted by stack index
stacked_patch_ids.put(la.indexOf(patch), patch.getId());
// Utils.log("Added patch for layer " + la + " with stack index " + la.indexOf(patch) + ", patch " + patch);
}
if (!layer_visited[0]) {
// synch may fail to avoid adding it twice
// but it's ok since it's a Set anyway
layer_visited[0] = true;
synchronized (src_layer_lids_used) {
src_layer_lids_used.add(la.getId());
}
}
}
}
}));
} catch (final Throwable t) {
IJError.print(t);
return;
}
}
}
}
}));
}
Utils.wait(dtasks);
Utils.wait(ltasks);
} catch (final Throwable t) {
IJError.print(t);
} finally {
exec.shutdownNow();
}
return new ReferenceData(tp, underlying, src_layer_lids_used);
}
use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.
the class AlignTask method transformPatchesAndVectorData.
/**
* For registering within the same project instance.
*/
public static final void transformPatchesAndVectorData(final Collection<Patch> patches, final Runnable alignment) {
if (patches.isEmpty()) {
Utils.log("No patches to align!");
return;
}
// 1 - Collect all VectorData to transform
final ArrayList<Displayable> vdata = new ArrayList<Displayable>();
final LayerSet ls = patches.iterator().next().getLayerSet();
for (final Layer layer : ls.getLayers()) {
vdata.addAll(layer.getDisplayables(VectorData.class, false, true));
}
vdata.addAll(ls.getZDisplayables(VectorData.class, true));
// Perhaps none:
if (vdata.isEmpty()) {
alignment.run();
return;
}
// 2 - Store current transformation of each Patch under any VectorData
final HashSet<Long> lids = new HashSet<Long>();
for (final Patch p : patches) lids.add(p.getLayer().getId());
final ReferenceData rd = createTransformPropertiesTable(vdata, null, lids);
// 3 - Align:
alignment.run();
// 4 - Transform VectorData instances to match the position of the Patch instances over which they were defined
if (null != rd && !vdata.isEmpty())
transformVectorData(rd, vdata, ls);
}
use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.
the class AlignTask method alignGraphs.
private static final boolean alignGraphs(final Align.Param p, final Layer layer1, final Layer layer2, final Iterable<Tile<?>> graph1, final Iterable<Tile<?>> graph2) {
final Align.Param cp = p.clone();
final Selection selection1 = new Selection(null);
for (final Tile<?> tile : graph1) selection1.add(((AbstractAffineTile2D<?>) tile).getPatch());
final Rectangle graph1Box = selection1.getBox();
final Selection selection2 = new Selection(null);
for (final Tile<?> tile : graph2) selection2.add(((AbstractAffineTile2D<?>) tile).getPatch());
final Rectangle graph2Box = selection2.getBox();
final int maxLength = Math.max(Math.max(Math.max(graph1Box.width, graph1Box.height), graph2Box.width), graph2Box.height);
// final double scale = ( double )cp.sift.maxOctaveSize / maxLength;
/* rather ad hoc but we cannot just scale this to maxOctaveSize */
cp.sift.maxOctaveSize = Math.min(maxLength, 2 * p.sift.maxOctaveSize);
/* make sure that, despite rounding issues from scale, it is >= image size */
final double scale = (double) (cp.sift.maxOctaveSize - 1) / maxLength;
// cp.maxEpsilon *= scale;
final FloatArray2DSIFT sift = new FloatArray2DSIFT(cp.sift);
final SIFT ijSIFT = new SIFT(sift);
final ArrayList<Feature> features1 = new ArrayList<Feature>();
final ArrayList<Feature> features2 = new ArrayList<Feature>();
final ArrayList<PointMatch> candidates = new ArrayList<PointMatch>();
final ArrayList<PointMatch> inliers = new ArrayList<PointMatch>();
long s = System.currentTimeMillis();
ijSIFT.extractFeatures(layer1.getProject().getLoader().getFlatImage(layer1, graph1Box, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, selection1.getSelected(Patch.class), false, Color.GRAY).getProcessor(), features1);
Utils.log(features1.size() + " features extracted for graphs in layer \"" + layer1.getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
ijSIFT.extractFeatures(layer2.getProject().getLoader().getFlatImage(layer2, graph2Box, scale, 0xffffffff, ImagePlus.GRAY8, Patch.class, selection2.getSelected(Patch.class), false, Color.GRAY).getProcessor(), features2);
Utils.log(features2.size() + " features extracted for graphs in layer \"" + layer1.getTitle() + "\" (took " + (System.currentTimeMillis() - s) + " ms).");
boolean modelFound = false;
if (features1.size() > 0 && features2.size() > 0) {
s = System.currentTimeMillis();
FeatureTransform.matchFeatures(features1, features2, candidates, cp.rod);
final AbstractAffineModel2D<?> model;
switch(cp.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 false;
}
boolean again = false;
try {
do {
again = false;
modelFound = model.filterRansac(candidates, inliers, 1000, cp.maxEpsilon, cp.minInlierRatio, cp.minNumInliers, 3);
if (modelFound && cp.rejectIdentity) {
final ArrayList<Point> points = new ArrayList<Point>();
PointMatch.sourcePoints(inliers, points);
if (Transforms.isIdentity(model, points, cp.identityTolerance)) {
IJ.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 graphs in layer \"" + layer1.getTitle() + "\" and \"" + layer2.getTitle() + "\":\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(graph2Box.x, graph2Box.y);
b.scale(1.0f / scale, 1.0f / scale);
b.concatenate(model.createAffine());
b.scale(scale, scale);
b.translate(-graph1Box.x, -graph1Box.y);
for (final Displayable d : selection1.getSelected(Patch.class)) d.getAffineTransform().preConcatenate(b);
/* assign patch affine transformation to the tile model */
for (final Tile<?> t : graph1) ((AbstractAffineTile2D<?>) t).initModel();
Display.repaint(layer1);
} else
IJ.log("No model found for graphs in layer \"" + layer1.getTitle() + "\" and \"" + layer2.getTitle() + "\":\n correspondence candidates " + candidates.size() + "\n took " + (System.currentTimeMillis() - s) + " ms");
}
return modelFound;
}
Aggregations