use of ini.trakem2.display.Displayable in project TrakEM2 by trakem2.
the class DistortionCorrectionTask method correctDistortionFromSelection.
public static final Bureaucrat correctDistortionFromSelection(final Selection selection) {
final List<Patch> patches = new ArrayList<Patch>();
for (final Displayable d : Display.getFront().getSelection().getSelected()) if (d instanceof Patch)
patches.add((Patch) d);
if (patches.size() < 2) {
Utils.log("No images in the selection.");
return null;
}
final Worker worker = new Worker("Lens correction") {
@Override
public final void run() {
try {
startedWorking();
if (!correctDistortionFromSelectionParam.setup(selection))
return;
DistortionCorrectionTask.run(correctDistortionFromSelectionParam.clone(), patches, selection.getActive(), selection.getLayer(), null);
Display.repaint();
} catch (final Exception e) {
IJError.print(e);
} finally {
finishedWorking();
}
}
};
return Bureaucrat.createAndStart(worker, selection.getProject());
}
use of ini.trakem2.display.Displayable in project TrakEM2 by trakem2.
the class ExportUnsignedShort method makeFlatImage.
public static final Pair<ShortProcessor, ByteProcessor> makeFlatImage(final List<Patch> patches, final Rectangle roi, final double backgroundValue, final double scale, final boolean makeAlphaMask) {
final ArrayList<PatchIntensityRange> patchIntensityRanges = new ArrayList<PatchIntensityRange>();
double min = Double.MAX_VALUE;
double max = -Double.MAX_VALUE;
for (final Displayable d : patches) {
final Patch patch = (Patch) d;
final PatchIntensityRange pir = new PatchIntensityRange(patch);
if (pir.min < min)
min = pir.min;
if (pir.max > max)
max = pir.max;
patchIntensityRanges.add(pir);
}
final double minI = -min * 65535.0 / (max - min);
final double maxI = (1.0 - min) * 65535.0 / (max - min);
final ShortProcessor sp;
if (Double.isNaN(scale)) {
sp = new ShortProcessor(roi.width, roi.height);
} else {
sp = new ShortProcessor((int) (roi.width * scale + 0.5), (int) (roi.height * scale + 0.5));
}
sp.setMinAndMax(minI, maxI);
if (0 != backgroundValue) {
sp.setValue(backgroundValue);
sp.setRoi(0, 0, roi.width, roi.height);
sp.fill();
}
final ByteProcessor alphaTarget = makeAlphaMask ? new ByteProcessor(sp.getWidth(), sp.getHeight()) : null;
for (final PatchIntensityRange pir : patchIntensityRanges) {
map(new PatchTransform(pir), roi.x, roi.y, scale, mapIntensities(pir, min, max), sp, alphaTarget);
}
return new Pair<ShortProcessor, ByteProcessor>(sp, alphaTarget);
}
use of ini.trakem2.display.Displayable in project TrakEM2 by trakem2.
the class AlignTask method alignMultiLayerMosaicTask.
@SuppressWarnings({ "unchecked", "rawtypes" })
public static final void alignMultiLayerMosaicTask(final List<Layer> layerRange, final Patch nail, final Align.Param cp, final Align.ParamOptimize p, final Align.ParamOptimize pcp, final boolean tilesAreInPlaceIn, final boolean largestGraphOnlyIn, final boolean hideDisconnectedTilesIn, final boolean deleteDisconnectedTilesIn, final boolean deformIn) {
/* register */
final List<AbstractAffineTile2D<?>> allTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> allFixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> previousLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final HashMap<Patch, PointMatch> tileCenterPoints = new HashMap<Patch, PointMatch>();
final Collection<Patch> fixedPatches = new HashSet<Patch>();
if (null != nail)
fixedPatches.add(nail);
Layer previousLayer = null;
for (final Layer layer : layerRange) {
/* align all tiles in the layer */
final List<Patch> patches = new ArrayList<Patch>();
for (// ignore hidden tiles
final Displayable a : // ignore hidden tiles
layer.getDisplayables(Patch.class, true)) if (a instanceof Patch)
patches.add((Patch) a);
final List<AbstractAffineTile2D<?>> currentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> fixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(p, patches, fixedPatches, currentLayerTiles, fixedTiles);
// Will consider graphs and hide/delete tiles when all cross-layer graphs are found.
alignTiles(p, currentLayerTiles, fixedTiles, tilesAreInPlaceIn, false, false, false);
if (Thread.currentThread().isInterrupted())
return;
/* connect to the previous layer */
/* generate tiles with the cross-section model from the current layer tiles */
/* ------------------------------------------------------------------------ */
/* TODO step back and make tiles bare containers for a patch and a model such that by changing the model the tile can be reused */
final HashMap<Patch, AbstractAffineTile2D<?>> currentLayerPatchTiles = new HashMap<Patch, AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : currentLayerTiles) currentLayerPatchTiles.put(t.getPatch(), t);
final List<AbstractAffineTile2D<?>> csCurrentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final Set<AbstractAffineTile2D<?>> csFixedTiles = new HashSet<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(cp, patches, fixedPatches, csCurrentLayerTiles, csFixedTiles);
final HashMap<Tile<?>, AbstractAffineTile2D<?>> tileTiles = new HashMap<Tile<?>, AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : csCurrentLayerTiles) tileTiles.put(currentLayerPatchTiles.get(t.getPatch()), t);
for (final AbstractAffineTile2D<?> t : currentLayerTiles) {
final AbstractAffineTile2D<?> csLayerTile = tileTiles.get(t);
csLayerTile.addMatches(t.getMatches());
for (final Tile<?> ct : t.getConnectedTiles()) csLayerTile.addConnectedTile(tileTiles.get(ct));
}
/* add a fixed tile only if there was a Patch selected */
allFixedTiles.addAll(csFixedTiles);
/* first, align connected graphs to each other */
/* graphs in the current layer */
final List<Set<Tile<?>>> currentLayerGraphs = AbstractAffineTile2D.identifyConnectedGraphs(csCurrentLayerTiles);
if (Thread.currentThread().isInterrupted())
return;
// /* TODO just for visualization */
// for ( final Set< Tile< ? > > graph : currentLayerGraphs )
// {
// Display.getFront().getSelection().clear();
// Display.getFront().setLayer( ( ( AbstractAffineTile2D< ? > )graph.iterator().next() ).getPatch().getLayer() );
//
// for ( final Tile< ? > tile : graph )
// {
// Display.getFront().getSelection().add( ( ( AbstractAffineTile2D< ? > )tile ).getPatch() );
// Display.repaint();
// }
// Utils.showMessage( "OK" );
// }
/* graphs from the whole system that are present in the previous layer */
final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(allTiles);
final HashMap<Set<Tile<?>>, Set<Tile<?>>> graphGraphs = new HashMap<Set<Tile<?>>, Set<Tile<?>>>();
for (final Set<Tile<?>> graph : graphs) {
if (Thread.currentThread().isInterrupted())
return;
final Set<Tile<?>> previousLayerGraph = new HashSet<Tile<?>>();
for (final Tile<?> tile : previousLayerTiles) {
if (graph.contains(tile)) {
graphGraphs.put(graph, previousLayerGraph);
previousLayerGraph.add(tile);
}
}
}
final Collection<Set<Tile<?>>> previousLayerGraphs = graphGraphs.values();
// /* TODO just for visualization */
// for ( final Set< Tile< ? > > graph : previousLayerGraphs )
// {
// Display.getFront().getSelection().clear();
// Display.getFront().setLayer( ( ( AbstractAffineTile2D< ? > )graph.iterator().next() ).getPatch().getLayer() );
//
// for ( final Tile< ? > tile : graph )
// {
// Display.getFront().getSelection().add( ( ( AbstractAffineTile2D< ? > )tile ).getPatch() );
// Display.repaint();
// }
// Utils.showMessage( "OK" );
// }
/* generate snapshots of the graphs and preregister them using the parameters defined in cp */
final List<AbstractAffineTile2D<?>[]> crossLayerTilePairs = new ArrayList<AbstractAffineTile2D<?>[]>();
for (final Set<Tile<?>> currentLayerGraph : currentLayerGraphs) {
for (final Set<Tile<?>> previousLayerGraph : previousLayerGraphs) {
if (Thread.currentThread().isInterrupted())
return;
alignGraphs(cp, layer, previousLayer, currentLayerGraph, previousLayerGraph);
/* TODO this is pointless data shuffling just for type incompatibility---fix this at the root */
final ArrayList<AbstractAffineTile2D<?>> previousLayerGraphTiles = new ArrayList<AbstractAffineTile2D<?>>();
previousLayerGraphTiles.addAll((Set) previousLayerGraph);
final ArrayList<AbstractAffineTile2D<?>> currentLayerGraphTiles = new ArrayList<AbstractAffineTile2D<?>>();
currentLayerGraphTiles.addAll((Set) currentLayerGraph);
AbstractAffineTile2D.pairOverlappingTiles(previousLayerGraphTiles, currentLayerGraphTiles, crossLayerTilePairs);
}
}
/* ------------------------------------------------------------------------ */
/* this is without the affine/rigid approximation per graph */
// AbstractAffineTile2D.pairTiles( previousLayerTiles, csCurrentLayerTiles, crossLayerTilePairs );
Align.connectTilePairs(cp, csCurrentLayerTiles, crossLayerTilePairs, Runtime.getRuntime().availableProcessors());
if (Thread.currentThread().isInterrupted())
return;
// for ( final AbstractAffineTile2D< ? >[] tilePair : crossLayerTilePairs )
// {
// Display.getFront().setLayer( tilePair[ 0 ].getPatch().getLayer() );
// Display.getFront().getSelection().clear();
// Display.getFront().getSelection().add( tilePair[ 0 ].getPatch() );
// Display.getFront().getSelection().add( tilePair[ 1 ].getPatch() );
//
// Utils.showMessage( "1: OK?" );
//
// Display.getFront().setLayer( tilePair[ 1 ].getPatch().getLayer() );
// Display.getFront().getSelection().clear();
// Display.getFront().getSelection().add( tilePair[ 0 ].getPatch() );
// Display.getFront().getSelection().add( tilePair[ 1 ].getPatch() );
//
// Utils.showMessage( "2: OK?" );
// }
/* prepare the next loop */
allTiles.addAll(csCurrentLayerTiles);
previousLayerTiles.clear();
previousLayerTiles.addAll(csCurrentLayerTiles);
/* optimize */
Align.optimizeTileConfiguration(pcp, allTiles, allFixedTiles);
if (Thread.currentThread().isInterrupted())
return;
for (final AbstractAffineTile2D<?> t : allTiles) t.getPatch().setAffineTransform(t.getModel().createAffine());
previousLayer = layer;
}
final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(allTiles);
final List<AbstractAffineTile2D<?>> interestingTiles = new ArrayList<AbstractAffineTile2D<?>>();
if (largestGraphOnlyIn && (hideDisconnectedTilesIn || deleteDisconnectedTilesIn)) {
if (Thread.currentThread().isInterrupted())
return;
/* find largest graph. */
Set<Tile<?>> largestGraph = null;
for (final Set<Tile<?>> graph : graphs) if (largestGraph == null || largestGraph.size() < graph.size())
largestGraph = graph;
final Set<AbstractAffineTile2D<?>> tiles_to_keep = new HashSet<AbstractAffineTile2D<?>>();
for (final Tile<?> t : largestGraph) tiles_to_keep.add((AbstractAffineTile2D<?>) t);
if (hideDisconnectedTilesIn)
for (final AbstractAffineTile2D<?> t : allTiles) if (!tiles_to_keep.contains(t))
t.getPatch().setVisible(false);
if (deleteDisconnectedTilesIn)
for (final AbstractAffineTile2D<?> t : allTiles) if (!tiles_to_keep.contains(t))
t.getPatch().remove(false);
interestingTiles.addAll(tiles_to_keep);
} else
interestingTiles.addAll(allTiles);
if (deformIn) {
/* ############################################ */
/* experimental: use the center points of all tiles to define a MLS deformation from the pure intra-layer registration to the globally optimal */
Utils.log("deforming...");
/* store the center location of each single tile for later deformation */
for (final AbstractAffineTile2D<?> t : interestingTiles) {
final double[] c = new double[] { t.getWidth() / 2.0, t.getHeight() / 2.0 };
t.getModel().applyInPlace(c);
final Point q = new Point(c);
tileCenterPoints.put(t.getPatch(), new PointMatch(q.clone(), q));
}
for (final Layer layer : layerRange) {
Utils.log("layer" + layer);
if (Thread.currentThread().isInterrupted())
return;
/* again, align all tiles in the layer */
final List<Patch> patches = new ArrayList<Patch>();
for (final Displayable a : layer.getDisplayables(Patch.class)) if (a instanceof Patch)
patches.add((Patch) a);
final List<AbstractAffineTile2D<?>> currentLayerTiles = new ArrayList<AbstractAffineTile2D<?>>();
final List<AbstractAffineTile2D<?>> fixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
Align.tilesFromPatches(p, patches, fixedPatches, currentLayerTiles, fixedTiles);
/* add a fixed tile only if there was a Patch selected */
allFixedTiles.addAll(fixedTiles);
// will consider graphs and hide/delete tiles when all cross-layer graphs are found
alignTiles(p, currentLayerTiles, fixedTiles, true, false, false, false);
/* for each independent graph do an independent transform */
final List<Set<Tile<?>>> currentLayerGraphs = AbstractAffineTile2D.identifyConnectedGraphs(currentLayerTiles);
for (final Set<Tile<?>> graph : currentLayerGraphs) {
/* update the tile-center pointmatches */
final Collection<PointMatch> matches = new ArrayList<PointMatch>();
final Collection<AbstractAffineTile2D<?>> toBeDeformedTiles = new ArrayList<AbstractAffineTile2D<?>>();
for (final AbstractAffineTile2D<?> t : (Collection<AbstractAffineTile2D<?>>) (Collection) graph) {
final PointMatch pm = tileCenterPoints.get(t.getPatch());
if (pm == null)
continue;
final double[] pl = pm.getP1().getL();
pl[0] = t.getWidth() / 2.0;
pl[1] = t.getHeight() / 2.0;
t.getModel().applyInPlace(pl);
matches.add(pm);
toBeDeformedTiles.add(t);
}
for (final AbstractAffineTile2D<?> t : toBeDeformedTiles) {
if (Thread.currentThread().isInterrupted())
return;
try {
final Patch patch = t.getPatch();
final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
final AffineTransform pat = new AffineTransform();
pat.translate(-pbox.x, -pbox.y);
pat.preConcatenate(patch.getAffineTransform());
final mpicbg.trakem2.transform.AffineModel2D toWorld = new mpicbg.trakem2.transform.AffineModel2D();
toWorld.set(pat);
final MovingLeastSquaresTransform2 mlst = Align.createMLST(matches, 1.0f);
final CoordinateTransformList<CoordinateTransform> ctl = new CoordinateTransformList<CoordinateTransform>();
ctl.add(toWorld);
ctl.add(mlst);
ctl.add(toWorld.createInverse());
patch.appendCoordinateTransform(ctl);
patch.getProject().getLoader().regenerateMipMaps(patch);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
}
}
layerRange.get(0).getParent().setMinimumDimensions();
IJ.log("Done: register multi-layer mosaic.");
return;
}
use of ini.trakem2.display.Displayable 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);
}
}
use of ini.trakem2.display.Displayable in project TrakEM2 by trakem2.
the class AlignTask method transformVectorData.
public static final void transformVectorData(final ReferenceData rd, /* The transformations of patches before alignment. */
final Collection<Displayable> vdata, /* The VectorData instances to transform along with images. */
final LayerSet target_layerset) /* The LayerSet in which the vdata and the transformed images exist. */
{
final ExecutorService exec = Utils.newFixedThreadPool("AlignTask-transformVectorData");
try {
final Collection<Future<?>> fus = new ArrayList<Future<?>>();
final HashMap<Long, Layer> lidm = new HashMap<Long, Layer>();
for (final Long lid : rd.src_layer_lids_used) {
final Layer la = target_layerset.getLayer(lid.longValue());
if (null == la) {
Utils.log("ERROR layer with id " + lid + " NOT FOUND in target layerset!");
continue;
}
lidm.put(lid, la);
}
for (final Map.Entry<Displayable, Map<Long, TreeMap<Integer, Long>>> ed : rd.underlying.entrySet()) {
// The VectorData instance to transform
final Displayable d = ed.getKey();
// Process Displayables concurrently:
fus.add(exec.submit(new Runnable() {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void run() {
for (final Map.Entry<Long, TreeMap<Integer, Long>> el : ed.getValue().entrySet()) {
// The entry has the id of the layer and the stack-index-ordered list of Patch that intersect VectorData d in that Layer
final Layer layer = lidm.get(el.getKey());
if (null == layer) {
Utils.log("ERROR layer with id " + el.getKey() + " NOT FOUND in target layerset!");
continue;
}
// Utils.log("Editing Displayable " + d + " at layer " + layer);
// list of Patch ids affecting VectorData/Displayable d
final ArrayList<Long> pids = new ArrayList<Long>(el.getValue().values());
// so now Patch ids are sorted from top to bottom
Collections.reverse(pids);
// The area already processed in the layer
final Area used_area = new Area();
// The map of areas vs transforms for each area to apply to the VectorData, to its data within the layer only
final VectorDataTransform vdt = new VectorDataTransform(layer);
// The list of transforms to apply to each VectorData
for (final long pid : pids) {
// Find the Patch with id 'pid' in Layer 'la' of the target LayerSet:
final DBObject ob = layer.findById(pid);
if (null == ob || !(ob instanceof Patch)) {
Utils.log("ERROR layer with id " + layer.getId() + " DOES NOT CONTAIN a Patch with id " + pid);
continue;
}
final Patch patch = (Patch) ob;
// no need to synch, read only from now on
final Patch.TransformProperties props = rd.tp.get(pid);
if (null == props) {
Utils.log("ERROR: could not find any Patch.TransformProperties for patch " + patch);
continue;
}
final Area a = new Area(props.area);
a.subtract(used_area);
if (M.isEmpty(a)) {
// skipping fully occluded Patch
continue;
}
// Accumulate:
used_area.add(props.area);
// For the remaining area within this Layer, define a transform
// Generate a CoordinateTransformList that includes:
// 1 - an inverted transform from Patch coords to world coords
// 2 - the CoordinateTransform of the Patch, if any
// 3 - the AffineTransform of the Patch
//
// The idea is to first send the data from world to pixel space of the Patch, using the old transfroms,
// and then from pixel space of the Patch to world, using the new transforms.
final CoordinateTransformList tlist = new CoordinateTransformList();
// 1. Inverse of the old affine: from world into the old patch mipmap
final mpicbg.models.AffineModel2D aff_inv = new mpicbg.models.AffineModel2D();
try {
aff_inv.set(props.at.createInverse());
} catch (final NoninvertibleTransformException nite) {
Utils.log("ERROR: could not invert the affine transform for Patch " + patch);
IJError.print(nite);
continue;
}
tlist.add(aff_inv);
// 2. Inverse of the old coordinate transform of the Patch: from old mipmap to pixels in original image
if (null != props.ct) {
// The props.ct is a CoordinateTransform, not necessarily an InvertibleCoordinateTransform
// So the mesh is necessary to ensure the invertibility
final mpicbg.trakem2.transform.TransformMesh mesh = new mpicbg.trakem2.transform.TransformMesh(props.ct, props.meshResolution, props.o_width, props.o_height);
/* // Apparently not needed; the inverse affine in step 1 took care of it.
* // (the affine of step 1 includes the mesh translation)
Rectangle box = mesh.getBoundingBox();
AffineModel2D aff = new AffineModel2D();
aff.set(new AffineTransform(1, 0, 0, 1, box.x, box.y));
tlist.add(aff);
*/
tlist.add(new InverseICT(mesh));
}
// 3. New coordinate transform of the Patch: from original image to new mipmap
final mpicbg.trakem2.transform.CoordinateTransform ct = patch.getCoordinateTransform();
if (null != ct) {
tlist.add(ct);
final mpicbg.trakem2.transform.TransformMesh mesh = new mpicbg.trakem2.transform.TransformMesh(ct, patch.getMeshResolution(), patch.getOWidth(), patch.getOHeight());
// correct for mesh bounds -- Necessary because it comes from the other side, and the removal of the translation here is re-added by the affine in step 4!
final Rectangle box = mesh.getBoundingBox();
final AffineModel2D aff = new AffineModel2D();
aff.set(new AffineTransform(1, 0, 0, 1, -box.x, -box.y));
tlist.add(aff);
}
// 4. New affine transform of the Patch: from mipmap to world
final mpicbg.models.AffineModel2D new_aff = new mpicbg.models.AffineModel2D();
new_aff.set(patch.getAffineTransform());
tlist.add(new_aff);
/*
// TODO Consider caching the tlist for each Patch, or for a few thousand of them maximum.
// But it could blow up memory astronomically.
// The old part:
final mpicbg.models.InvertibleCoordinateTransformList old = new mpicbg.models.InvertibleCoordinateTransformList();
if (null != props.ct) {
mpicbg.trakem2.transform.TransformMesh mesh = new mpicbg.trakem2.transform.TransformMesh(props.ct, props.meshResolution, props.o_width, props.o_height);
old.add(mesh);
}
final mpicbg.models.AffineModel2D old_aff = new mpicbg.models.AffineModel2D();
old_aff.set(props.at);
old.add(old_aff);
tlist.add(new InverseICT(old));
// The new part:
final mpicbg.models.AffineModel2D new_aff = new mpicbg.models.AffineModel2D();
new_aff.set(patch.getAffineTransform());
tlist.add(new_aff);
final mpicbg.trakem2.transform.CoordinateTransform ct = patch.getCoordinateTransform();
if (null != ct) tlist.add(ct);
*/
vdt.add(a, tlist);
}
// Apply the map of area vs tlist for the data section of d within the layer:
try {
((VectorData) d).apply(vdt);
} catch (final Exception t) {
Utils.log("ERROR transformation failed for " + d + " at layer " + layer);
IJError.print(t);
}
}
}
}));
}
Utils.wait(fus);
Display.repaint();
} finally {
exec.shutdown();
}
}
Aggregations