Search in sources :

Example 1 with TranslationTile2D

use of mpicbg.trakem2.align.TranslationTile2D in project TrakEM2 by trakem2.

the class StitchingTEM method stitchTopLeft.

/**
 * Stitch array of patches with upper left rule
 *
 * @param patch
 * @param grid_width
 * @param default_bottom_top_overlap
 * @param default_left_right_overlap
 * @param optimize
 * @return
 */
private static Runnable stitchTopLeft(final Patch[] patch, final int grid_width, final double default_bottom_top_overlap, final double default_left_right_overlap, final boolean optimize, final PhaseCorrelationParam param) {
    return new Runnable() {

        @Override
        public void run() {
            try {
                final int LEFT = 0, TOP = 1;
                int prev_i = 0;
                int prev = LEFT;
                double[] R1 = null, R2 = null;
                // for minimization:
                final ArrayList<AbstractAffineTile2D<?>> al_tiles = new ArrayList<AbstractAffineTile2D<?>>();
                // first patch-tile:
                final TranslationModel2D first_tile_model = new TranslationModel2D();
                // first_tile_model.getAffine().setTransform( patch[ 0 ].getAffineTransform() );
                first_tile_model.set((float) patch[0].getAffineTransform().getTranslateX(), (float) patch[0].getAffineTransform().getTranslateY());
                al_tiles.add(new TranslationTile2D(first_tile_model, patch[0]));
                for (int i = 1; i < patch.length; i++) {
                    if (Thread.currentThread().isInterrupted()) {
                        return;
                    }
                    // boundary checks: don't allow displacements beyond these values
                    final double default_dx = default_left_right_overlap;
                    final double default_dy = default_bottom_top_overlap;
                    // for minimization:
                    AbstractAffineTile2D<?> tile_left = null;
                    AbstractAffineTile2D<?> tile_top = null;
                    final TranslationModel2D tile_model = new TranslationModel2D();
                    // tile_model.getAffine().setTransform( patch[ i ].getAffineTransform() );
                    tile_model.set((float) patch[i].getAffineTransform().getTranslateX(), (float) patch[i].getAffineTransform().getTranslateY());
                    final AbstractAffineTile2D<?> tile = new TranslationTile2D(tile_model, patch[i]);
                    al_tiles.add(tile);
                    // stitch with the one above if starting row
                    if (0 == i % grid_width) {
                        prev_i = i - grid_width;
                        prev = TOP;
                    } else {
                        prev_i = i - 1;
                        prev = LEFT;
                    }
                    if (TOP == prev) {
                        // compare with top only
                        R1 = correlate(patch[prev_i], patch[i], param.overlap, param.cc_scale, TOP_BOTTOM, default_dx, default_dy, param.min_R);
                        R2 = null;
                        tile_top = al_tiles.get(i - grid_width);
                    } else {
                        // the one on the left
                        R2 = correlate(patch[prev_i], patch[i], param.overlap, param.cc_scale, LEFT_RIGHT, default_dx, default_dy, param.min_R);
                        tile_left = al_tiles.get(i - 1);
                        // the one above
                        if (i - grid_width > -1) {
                            R1 = correlate(patch[i - grid_width], patch[i], param.overlap, param.cc_scale, TOP_BOTTOM, default_dx, default_dy, param.min_R);
                            tile_top = al_tiles.get(i - grid_width);
                        } else {
                            R1 = null;
                        }
                    }
                    // boundary limits: don't move by more than the small dimension of the stripe
                    // TODO: only the dx for left (and the dy for top) should be compared and found to be smaller or equal; the other dimension should be unbounded -for example, for manually acquired, grossly out-of-grid tiles.
                    final int max_abs_delta;
                    final Rectangle box = new Rectangle();
                    final Rectangle box2 = new Rectangle();
                    // check and apply: falls back to default overlaps when getting bad results
                    if (TOP == prev) {
                        if (SUCCESS == R1[2]) {
                            // trust top
                            if (optimize)
                                addMatches(tile_top, tile, R1[0], R1[1]);
                            else {
                                patch[i - grid_width].getBoundingBox(box);
                                patch[i].setLocation(box.x + R1[0], box.y + R1[1]);
                            }
                        } else {
                            final Rectangle b2 = patch[i - grid_width].getBoundingBox(null);
                            // don't move: use default overlap
                            if (optimize)
                                addMatches(tile_top, tile, 0, b2.height - default_bottom_top_overlap);
                            else {
                                patch[i - grid_width].getBoundingBox(box);
                                patch[i].setLocation(box.x, box.y + b2.height - default_bottom_top_overlap);
                            }
                        }
                    } else {
                        // the one on top, if any
                        if (i - grid_width > -1) {
                            if (SUCCESS == R1[2]) {
                                // top is good
                                if (SUCCESS == R2[2]) {
                                    // combine left and top
                                    if (optimize) {
                                        addMatches(tile_left, tile, R2[0], R2[1]);
                                        addMatches(tile_top, tile, R1[0], R1[1]);
                                    } else {
                                        patch[i - 1].getBoundingBox(box);
                                        patch[i - grid_width].getBoundingBox(box2);
                                        patch[i].setLocation((box.x + R1[0] + box2.x + R2[0]) / 2, (box.y + R1[1] + box2.y + R2[1]) / 2);
                                    }
                                } else {
                                    // use top alone
                                    if (optimize)
                                        addMatches(tile_top, tile, R1[0], R1[1]);
                                    else {
                                        patch[i - grid_width].getBoundingBox(box);
                                        patch[i].setLocation(box.x + R1[0], box.y + R1[1]);
                                    }
                                }
                            } else {
                                // ignore top
                                if (SUCCESS == R2[2]) {
                                    // use left alone
                                    if (optimize)
                                        addMatches(tile_left, tile, R2[0], R2[1]);
                                    else {
                                        patch[i - 1].getBoundingBox(box);
                                        patch[i].setLocation(box.x + R2[0], box.y + R2[1]);
                                    }
                                } else {
                                    patch[prev_i].getBoundingBox(box);
                                    patch[i - grid_width].getBoundingBox(box2);
                                    // left not trusted, top not trusted: use a combination of defaults for both
                                    if (optimize) {
                                        addMatches(tile_left, tile, box.width - default_left_right_overlap, 0);
                                        addMatches(tile_top, tile, 0, box2.height - default_bottom_top_overlap);
                                    } else {
                                        patch[i].setLocation(box.x + box.width - default_left_right_overlap, box2.y + box2.height - default_bottom_top_overlap);
                                    }
                                }
                            }
                        } else if (SUCCESS == R2[2]) {
                            // use left alone (top not applicable in top row)
                            if (optimize)
                                addMatches(tile_left, tile, R2[0], R2[1]);
                            else {
                                patch[i - 1].getBoundingBox(box);
                                patch[i].setLocation(box.x + R2[0], box.y + R2[1]);
                            }
                        } else {
                            patch[prev_i].getBoundingBox(box);
                            // left not trusted, and top not applicable: use default overlap with left tile
                            if (optimize)
                                addMatches(tile_left, tile, box.width - default_left_right_overlap, 0);
                            else {
                                patch[i].setLocation(box.x + box.width - default_left_right_overlap, box.y);
                            }
                        }
                    }
                    if (!optimize)
                        Display.repaint(patch[i].getLayer(), patch[i], null, 0, false);
                    Utils.log2(i + ": Done patch " + patch[i]);
                }
                if (optimize) {
                    final ArrayList<AbstractAffineTile2D<?>> al_fixed_tiles = new ArrayList<AbstractAffineTile2D<?>>();
                    // Add locked tiles as fixed tiles, if any:
                    for (int i = 0; i < patch.length; i++) {
                        if (patch[i].isLocked2())
                            al_fixed_tiles.add(al_tiles.get(i));
                    }
                    if (al_fixed_tiles.isEmpty()) {
                        // When none, add the first one as fixed
                        al_fixed_tiles.add(al_tiles.get(0));
                    }
                    // Optimize iteratively tile configuration by removing bad matches
                    optimizeTileConfiguration(al_tiles, al_fixed_tiles, param);
                    for (final AbstractAffineTile2D<?> t : al_tiles) t.getPatch().setAffineTransform(t.getModel().createAffine());
                }
                // Remove or hide disconnected tiles
                if (param.hide_disconnected || param.remove_disconnected) {
                    final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(al_tiles);
                    final List<AbstractAffineTile2D<?>> interestingTiles;
                    // find largest graph
                    Set<Tile<?>> largestGraph = null;
                    for (final Set<Tile<?>> graph : graphs) if (largestGraph == null || largestGraph.size() < graph.size())
                        largestGraph = graph;
                    Utils.log("Size of largest stitching graph = " + largestGraph.size());
                    interestingTiles = new ArrayList<AbstractAffineTile2D<?>>();
                    for (final Tile<?> t : largestGraph) interestingTiles.add((AbstractAffineTile2D<?>) t);
                    if (param.hide_disconnected)
                        for (final AbstractAffineTile2D<?> t : al_tiles) if (!interestingTiles.contains(t))
                            t.getPatch().setVisible(false);
                    if (param.remove_disconnected)
                        for (final AbstractAffineTile2D<?> t : al_tiles) if (!interestingTiles.contains(t))
                            t.getPatch().remove(false);
                }
                // all
                Display.repaint(patch[0].getLayer(), null, 0, true);
            // 
            } catch (final Exception e) {
                IJError.print(e);
            }
        }
    };
}
Also used : TranslationTile2D(mpicbg.trakem2.align.TranslationTile2D) Set(java.util.Set) AbstractAffineTile2D(mpicbg.trakem2.align.AbstractAffineTile2D) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) Tile(mpicbg.models.Tile) Point(mpicbg.models.Point) TranslationModel2D(mpicbg.models.TranslationModel2D)

Example 2 with TranslationTile2D

use of mpicbg.trakem2.align.TranslationTile2D in project TrakEM2 by trakem2.

the class StitchingTEM method montageWithPhaseCorrelation.

/**
 * Perform montage based on phase correlation
 * @param col collection of patches
 * @param param phase correlation parameters
 */
public static void montageWithPhaseCorrelation(final Collection<Patch> col, final PhaseCorrelationParam param) {
    if (null == col || col.size() < 1)
        return;
    final ArrayList<Patch> al = new ArrayList<Patch>(col);
    final ArrayList<AbstractAffineTile2D<?>> tiles = new ArrayList<AbstractAffineTile2D<?>>();
    final ArrayList<AbstractAffineTile2D<?>> fixed_tiles = new ArrayList<AbstractAffineTile2D<?>>();
    for (final Patch p : al) {
        // Pre-check: just a warning
        final int aff_type = p.getAffineTransform().getType();
        switch(p.getAffineTransform().getType()) {
            case AffineTransform.TYPE_IDENTITY:
            case AffineTransform.TYPE_TRANSLATION:
                // ok
                break;
            default:
                Utils.log2("WARNING: patch with a non-translation transform: " + p);
                break;
        }
        // create tiles
        final TranslationTile2D tile = new TranslationTile2D(new TranslationModel2D(), p);
        tiles.add(tile);
        if (p.isLocked2()) {
            Utils.log("Added fixed (locked) tile " + p);
            fixed_tiles.add(tile);
        }
    }
    // Get acceptable values
    double cc_scale = param.cc_scale;
    if (cc_scale < 0 || cc_scale > 1) {
        Utils.log("Unacceptable cc_scale of " + param.cc_scale + ". Using 1 instead.");
        cc_scale = 1;
    }
    float overlap = param.overlap;
    if (overlap < 0 || overlap > 1) {
        Utils.log("Unacceptable overlap of " + param.overlap + ". Using 1 instead.");
        overlap = 1;
    }
    for (int i = 0; i < al.size(); i++) {
        final Patch p1 = al.get(i);
        final Rectangle r1 = p1.getBoundingBox();
        // find overlapping, add as connections
        for (int j = i + 1; j < al.size(); j++) {
            if (Thread.currentThread().isInterrupted())
                return;
            final Patch p2 = al.get(j);
            final Rectangle r2 = p2.getBoundingBox();
            if (r1.intersects(r2)) {
                // Skip if it's a diagonal overlap
                final int dx = Math.abs(r1.x - r2.x);
                final int dy = Math.abs(r1.y - r2.y);
                if (dx > r1.width / 2 && dy > r1.height / 2) {
                    // skip diagonal match
                    Utils.log2("Skipping diagonal overlap between " + p1 + " and " + p2);
                    continue;
                }
                p1.getProject().getLoader().releaseToFit((long) (p1.getWidth() * p1.getHeight() * 25));
                final double[] R;
                if (1 == overlap) {
                    R = correlate(p1, p2, overlap, cc_scale, TOP_BOTTOM, 0, 0, param.min_R);
                    if (SUCCESS == R[2]) {
                        addMatches(tiles.get(i), tiles.get(j), R[0], R[1]);
                    }
                } else {
                    switch(getClosestOverlapLocation(p1, p2)) {
                        case // p1 overlaps p2 from the left
                        0:
                            R = correlate(p1, p2, overlap, cc_scale, LEFT_RIGHT, 0, 0, param.min_R);
                            if (SUCCESS == R[2]) {
                                addMatches(tiles.get(i), tiles.get(j), R[0], R[1]);
                            }
                            break;
                        case // p1 overlaps p2 from the top
                        1:
                            R = correlate(p1, p2, overlap, cc_scale, TOP_BOTTOM, 0, 0, param.min_R);
                            if (SUCCESS == R[2]) {
                                addMatches(tiles.get(i), tiles.get(j), R[0], R[1]);
                            }
                            break;
                        case // p1 overlaps p2 from the right
                        2:
                            R = correlate(p2, p1, overlap, cc_scale, LEFT_RIGHT, 0, 0, param.min_R);
                            if (SUCCESS == R[2]) {
                                addMatches(tiles.get(j), tiles.get(i), R[0], R[1]);
                            }
                            break;
                        case // p1 overlaps p2 from the bottom
                        3:
                            R = correlate(p2, p1, overlap, cc_scale, TOP_BOTTOM, 0, 0, param.min_R);
                            if (SUCCESS == R[2]) {
                                addMatches(tiles.get(j), tiles.get(i), R[0], R[1]);
                            }
                            break;
                        default:
                            Utils.log("Unknown overlap direction!");
                            continue;
                    }
                }
            }
        }
    }
    if (param.remove_disconnected || param.hide_disconnected) {
        for (final Iterator<AbstractAffineTile2D<?>> it = tiles.iterator(); it.hasNext(); ) {
            final AbstractAffineTile2D<?> t = it.next();
            if (null != t.getMatches() && t.getMatches().isEmpty()) {
                if (param.hide_disconnected)
                    t.getPatch().setVisible(false);
                else if (param.remove_disconnected)
                    t.getPatch().remove(false);
                it.remove();
            }
        }
    }
    // Optimize tile configuration by removing bad matches
    optimizeTileConfiguration(tiles, fixed_tiles, param);
    for (final AbstractAffineTile2D<?> t : tiles) t.getPatch().setAffineTransform(t.getModel().createAffine());
    try {
        Display.repaint(al.get(0).getLayer());
    } catch (final Exception e) {
    }
}
Also used : TranslationTile2D(mpicbg.trakem2.align.TranslationTile2D) AbstractAffineTile2D(mpicbg.trakem2.align.AbstractAffineTile2D) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) Point(mpicbg.models.Point) TranslationModel2D(mpicbg.models.TranslationModel2D) Patch(ini.trakem2.display.Patch)

Aggregations

Rectangle (java.awt.Rectangle)2 ArrayList (java.util.ArrayList)2 Point (mpicbg.models.Point)2 TranslationModel2D (mpicbg.models.TranslationModel2D)2 AbstractAffineTile2D (mpicbg.trakem2.align.AbstractAffineTile2D)2 TranslationTile2D (mpicbg.trakem2.align.TranslationTile2D)2 Patch (ini.trakem2.display.Patch)1 Set (java.util.Set)1 Tile (mpicbg.models.Tile)1