use of ini.trakem2.utils.Montage in project TrakEM2 by trakem2.
the class Loader method importGrid.
/**
* Import a grid of images and put them in the layer. If the directory (@param dir) is null, it'll be asked for.
*/
public Bureaucrat importGrid(final Layer layer, String dir) {
try {
String file = null;
if (null == dir) {
final String[] dn = Utils.selectFile("Select first image");
if (null == dn)
return null;
dir = dn[0];
file = dn[1];
}
// char digit digit
String convention = "cdd";
boolean chars_are_columns = true;
// examine file name
/*
if (file.matches("\\A[a-zA-Z]\\d\\d.*")) { // one letter, 2 numbers
//means:
// \A - beggining of input
// [a-zA-Z] - any letter upper or lower case
// \d\d - two consecutive digits
// .* - any row of chars
ini_grid_convention = true;
}
*/
// ask for chars->rows, numbers->columns or viceversa
final GenericDialog gd = new GenericDialog("Conventions");
gd.addStringField("file_name_contains:", "");
gd.addNumericField("base_x: ", 0, 3);
gd.addNumericField("base_y: ", 0, 3);
gd.addMessage("Use: x(any), c(haracter), d(igit)");
gd.addStringField("convention: ", convention);
final String[] cr = new String[] { "columns", "rows" };
gd.addChoice("characters are: ", cr, cr[0]);
gd.addMessage("[File extension ignored]");
// as asked by Joachim Walter
gd.addNumericField("bottom-top overlap: ", 0, 3);
gd.addNumericField("left-right overlap: ", 0, 3);
gd.addCheckbox("link_images", false);
gd.addCheckbox("montage with phase correlation", false);
gd.addCheckbox("homogenize_contrast", true);
final Component[] c = { (Component) gd.getSliders().get(gd.getSliders().size() - 2), (Component) gd.getNumericFields().get(gd.getNumericFields().size() - 2), (Component) gd.getSliders().get(gd.getSliders().size() - 1), (Component) gd.getNumericFields().get(gd.getNumericFields().size() - 1), (Component) gd.getChoices().get(gd.getChoices().size() - 1) };
// enable the checkbox to control the slider and its associated numeric field:
Utils.addEnablerListener((Checkbox) gd.getCheckboxes().get(gd.getCheckboxes().size() - 1), c, null);
// gd.addCheckbox("Apply non-linear deformation", false);
gd.showDialog();
if (gd.wasCanceled()) {
return null;
}
// collect data
// filter away files not containing this tag
final String regex = gd.getNextString();
// the base x,y of the whole grid
final double bx = gd.getNextNumber();
final double by = gd.getNextNumber();
// if (!ini_grid_convention) {
convention = gd.getNextString().toLowerCase();
// }
if (/*!ini_grid_convention && */
(null == convention || convention.equals("") || -1 == convention.indexOf('c') || -1 == convention.indexOf('d'))) {
// TODO check that the convention has only 'cdx' chars and also that there is an island of 'c's and of 'd's only.
Utils.showMessage("Convention '" + convention + "' needs both c(haracters) and d(igits), optionally 'x', and nothing else!");
return null;
}
chars_are_columns = (0 == gd.getNextChoiceIndex());
final double bt_overlap = gd.getNextNumber();
final double lr_overlap = gd.getNextNumber();
final boolean link_images = gd.getNextBoolean();
final boolean stitch_tiles = gd.getNextBoolean();
final boolean homogenize_contrast = gd.getNextBoolean();
// start magic
// get ImageJ-openable files that comply with the convention
final File images_dir = new File(dir);
if (!(images_dir.exists() && images_dir.isDirectory())) {
Utils.showMessage("Something went wrong:\n\tCan't find directory " + dir);
return null;
}
final String[] file_names = images_dir.list(new ImageFileFilter(regex, convention));
if (null == file && file_names.length > 0) {
// the 'selected' file
file = file_names[0];
}
Utils.showStatus("Adding " + file_names.length + " patches.", false);
if (0 == file_names.length) {
Utils.log("Zero files match the convention '" + convention + "'");
return null;
}
// How to: select all files, and order their names in a double array as they should be placed in the Display. Then place them, displacing by offset, and resizing if necessary.
// gather image files:
final Montage montage = new Montage(convention, chars_are_columns);
montage.addAll(file_names);
// an array of Object[] arrays, of unequal length maybe, each containing a column of image file names
final ArrayList<String[]> cols = montage.getCols();
// !@#$%^&*
final String dir_ = dir;
final double bt_overlap_ = bt_overlap;
final double lr_overlap_ = lr_overlap;
final String file_ = file;
return Bureaucrat.createAndStart(new Worker.Task("Insert grid", true) {
@Override
public void exec() {
StitchingTEM.PhaseCorrelationParam pc_param = null;
if (stitch_tiles) {
pc_param = new StitchingTEM.PhaseCorrelationParam();
pc_param.setup(layer);
}
insertGrid(layer, dir_, file_, file_names.length, cols, bx, by, bt_overlap_, lr_overlap_, link_images, stitch_tiles, homogenize_contrast, pc_param, this);
}
}, layer.getProject());
} catch (final Exception e) {
IJError.print(e);
}
return null;
}
use of ini.trakem2.utils.Montage 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) {
}
}
use of ini.trakem2.utils.Montage in project TrakEM2 by trakem2.
the class StitchingTEM method montageWithPhaseCorrelation.
/**
* @param layers
* @param worker Optional, the {@link Worker} running this task.
*/
public static void montageWithPhaseCorrelation(final List<Layer> layers, final Worker worker) {
final PhaseCorrelationParam param = new PhaseCorrelationParam();
final Collection<Displayable> col = layers.get(0).getDisplayables(Patch.class);
if (!param.setup(col.size() > 0 ? (Patch) col.iterator().next() : null)) {
return;
}
final int i = 1;
for (final Layer la : layers) {
if (Thread.currentThread().isInterrupted() || (null != worker && worker.hasQuitted()))
return;
if (null != worker)
worker.setTaskName("Montage layer " + i + "/" + layers.size());
final Collection<Patch> patches = (Collection<Patch>) (Collection) la.getDisplayables(Patch.class);
AlignTask.transformPatchesAndVectorData(patches, new Runnable() {
@Override
public void run() {
montageWithPhaseCorrelation(patches, param);
}
});
}
}
use of ini.trakem2.utils.Montage in project TrakEM2 by trakem2.
the class AlignTask method montageLayers.
@SuppressWarnings({ "rawtypes", "unchecked" })
public static final void montageLayers(final Align.ParamOptimize p, final List<Layer> layers, final boolean tilesAreInPlaceIn, final boolean largestGraphOnlyIn, final boolean hideDisconnectedTilesIn, final boolean deleteDisconnectedTilesIn, final boolean sloppyOverlapTest) {
int i = 0;
for (final Layer layer : layers) {
if (Thread.currentThread().isInterrupted())
return;
final Collection<Displayable> patches = layer.getDisplayables(Patch.class, true);
if (patches.isEmpty())
continue;
for (final Displayable patch : patches) {
if (patch.isLinked() && !patch.isOnlyLinkedTo(Patch.class)) {
Utils.log("Cannot montage layer " + layer + "\nReason: at least one Patch is linked to non-image data: " + patch);
continue;
}
}
Utils.log("====\nMontaging layer " + layer);
Utils.showProgress(((double) i) / layers.size());
i++;
alignPatches(p, new ArrayList<Patch>((Collection<Patch>) (Collection) patches), new ArrayList<Patch>(), tilesAreInPlaceIn, largestGraphOnlyIn, hideDisconnectedTilesIn, deleteDisconnectedTilesIn, sloppyOverlapTest);
Display.repaint(layer);
}
}
Aggregations