Search in sources :

Example 11 with Layer

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);
}
Also used : GenericDialog(ij.gui.GenericDialog) Patch(ini.trakem2.display.Patch)

Example 12 with Layer

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);
}
Also used : GenericDialog(ij.gui.GenericDialog) ArrayList(java.util.ArrayList) Layer(ini.trakem2.display.Layer) Point(mpicbg.models.Point)

Example 13 with Layer

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);
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) VectorData(ini.trakem2.display.VectorData) HashSet(java.util.HashSet) Displayable(ini.trakem2.display.Displayable) TreeMap(java.util.TreeMap) Layer(ini.trakem2.display.Layer) Point(mpicbg.models.Point) Area(java.awt.geom.Area) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) Collection(java.util.Collection) Patch(ini.trakem2.display.Patch) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Example 14 with Layer

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);
}
Also used : Displayable(ini.trakem2.display.Displayable) LayerSet(ini.trakem2.display.LayerSet) ArrayList(java.util.ArrayList) Layer(ini.trakem2.display.Layer) Patch(ini.trakem2.display.Patch) VectorData(ini.trakem2.display.VectorData) HashSet(java.util.HashSet)

Example 15 with Layer

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;
}
Also used : NotEnoughDataPointsException(mpicbg.models.NotEnoughDataPointsException) SIFT(mpicbg.ij.SIFT) FloatArray2DSIFT(mpicbg.imagefeatures.FloatArray2DSIFT) Selection(ini.trakem2.display.Selection) Rectangle(java.awt.Rectangle) ArrayList(java.util.ArrayList) Feature(mpicbg.imagefeatures.Feature) RigidModel2D(mpicbg.trakem2.transform.RigidModel2D) AbstractAffineModel2D(mpicbg.models.AbstractAffineModel2D) AffineModel2D(mpicbg.models.AffineModel2D) SimilarityModel2D(mpicbg.models.SimilarityModel2D) Displayable(ini.trakem2.display.Displayable) Point(mpicbg.models.Point) Point(mpicbg.models.Point) FloatArray2DSIFT(mpicbg.imagefeatures.FloatArray2DSIFT) PointMatch(mpicbg.models.PointMatch) AffineTransform(java.awt.geom.AffineTransform) TranslationModel2D(mpicbg.trakem2.transform.TranslationModel2D) Patch(ini.trakem2.display.Patch)

Aggregations

Layer (ini.trakem2.display.Layer)61 ArrayList (java.util.ArrayList)52 Patch (ini.trakem2.display.Patch)43 Rectangle (java.awt.Rectangle)34 HashSet (java.util.HashSet)27 ImagePlus (ij.ImagePlus)26 Displayable (ini.trakem2.display.Displayable)25 AffineTransform (java.awt.geom.AffineTransform)23 GenericDialog (ij.gui.GenericDialog)22 Worker (ini.trakem2.utils.Worker)22 Point (mpicbg.models.Point)22 HashMap (java.util.HashMap)21 Point (java.awt.Point)19 Area (java.awt.geom.Area)19 NoninvertibleTransformException (java.awt.geom.NoninvertibleTransformException)18 File (java.io.File)16 Future (java.util.concurrent.Future)16 LayerSet (ini.trakem2.display.LayerSet)15 ExecutorService (java.util.concurrent.ExecutorService)14 Collection (java.util.Collection)13