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);
}
}
};
}
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) {
}
}
Aggregations