use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class ExportARGB method makeFlatImageARGBFromMipMaps.
/**
* Returns nonsense or throws an Exception if mipmaps are not available.
* Limited to 2GB arrays for the final image.
*
* @param patches
* @param roi
* @param backgroundValue
* @param scale
* @return
*/
public static final Pair<ColorProcessor, ByteProcessor> makeFlatImageARGBFromMipMaps(final List<Patch> patches, final Rectangle roi, final double backgroundValue, final double scale) {
final int width = (int) (roi.width * scale);
final int height = (int) (roi.height * scale);
// Process the three channels separately in order to use proper alpha composition
final ColorProcessor target = new ColorProcessor(width, height);
target.setInterpolationMethod(ImageProcessor.BILINEAR);
final ByteProcessor targetMask = new ByteProcessor(width, height);
targetMask.setInterpolationMethod(ImageProcessor.BILINEAR);
final Loader loader = patches.get(0).getProject().getLoader();
for (final Patch patch : patches) {
// MipMap image, already including any coordinate transforms and the alpha mask (if any), by definition.
final MipMapImage mipMap = loader.fetchImage(patch, scale);
// / DEBUG: is there an alpha channel at all?
// new ij.ImagePlus("alpha of " + patch.getTitle(), new ByteProcessor( mipMap.image.getWidth(null), mipMap.image.getHeight(null), new ColorProcessor( mipMap.image ).getChannel( 4 ))).show();
// Yes, there is, even though the mipmap images have the alpha pre-multiplied
// Work-around strange bug that makes mipmap-loaded images paint with 7-bit depth instead of 8-bit depth
final BufferedImage bi = new BufferedImage(mipMap.image.getWidth(null), mipMap.image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = bi.createGraphics();
g2d.drawImage(mipMap.image, 0, 0, null);
g2d.dispose();
final int[] pix = extractARGBIntArray(bi);
bi.flush();
// DEBUG: does the BufferedImage have the alpha channel?
// {
// final byte[] aa = new byte[pix.length];
// for (int i=0; i<aa.length; ++i) aa[i] = (byte)((pix[i] & 0xff000000) >> 24);
// new ij.ImagePlus("alpha of BI of " + patch.getTitle(), new ByteProcessor(bi.getWidth(), bi.getHeight(), aa)).show();
// }
// YES: the alpha, containing the outside too. All fine.
final ByteProcessor alpha;
final ColorProcessor rgb = new ColorProcessor(bi.getWidth(), bi.getHeight(), pix);
if (patch.hasAlphaChannel()) {
// The mipMap has the alpha channel in it, even if the alpha is pre-multiplied as well onto the images.
final byte[] a = new byte[pix.length];
for (int i = 0; i < a.length; ++i) {
a[i] = (byte) ((pix[i] & 0xff000000) >> 24);
}
alpha = new ByteProcessor(bi.getWidth(), bi.getHeight(), a);
} else {
alpha = new ByteProcessor(bi.getWidth(), bi.getHeight());
Arrays.fill((byte[]) alpha.getPixels(), (byte) 255);
}
// The affine to apply to the MipMap.image
final AffineTransform atc = new AffineTransform();
atc.scale(scale, scale);
atc.translate(-roi.x, -roi.y);
final AffineTransform at = new AffineTransform();
at.preConcatenate(atc);
at.concatenate(patch.getAffineTransform());
at.scale(mipMap.scaleX, mipMap.scaleY);
final AffineModel2D aff = new AffineModel2D();
aff.set(at);
final CoordinateTransformMesh mesh = new CoordinateTransformMesh(aff, patch.getMeshResolution(), bi.getWidth(), bi.getHeight());
final TransformMeshMappingWithMasks<CoordinateTransformMesh> mapping = new TransformMeshMappingWithMasks<CoordinateTransformMesh>(mesh);
// no interpolation
alpha.setInterpolationMethod(ImageProcessor.NEAREST_NEIGHBOR);
rgb.setInterpolationMethod(ImageProcessor.BILINEAR);
mapping.map(rgb, alpha, target, targetMask);
}
return new Pair<ColorProcessor, ByteProcessor>(target, targetMask);
}
use of ini.trakem2.display.Patch in project TrakEM2 by trakem2.
the class ExportUnsignedByte method makeFlatImage.
public static final Pair<ByteProcessor, ByteProcessor> makeFlatImage(final List<Patch> patches, final Rectangle roi, final double backgroundValue, final double scale, final ImageSource fetcher) {
final ByteProcessor target = new ByteProcessor((int) (roi.width * scale), (int) (roi.height * scale));
target.setInterpolationMethod(ImageProcessor.BILINEAR);
final ByteProcessor targetMask = new ByteProcessor(target.getWidth(), target.getHeight());
targetMask.setInterpolationMethod(ImageProcessor.NEAREST_NEIGHBOR);
for (final Patch patch : patches) {
final ImageData imgd = fetcher.fetch(patch, scale);
// The affine to apply to the MipMap.image
final AffineTransform atc = new AffineTransform();
atc.scale(scale, scale);
atc.translate(-roi.x, -roi.y);
final AffineTransform at = new AffineTransform();
at.preConcatenate(atc);
at.concatenate(patch.getAffineTransform());
at.scale(imgd.scaleX, imgd.scaleY);
final AffineModel2D aff = new AffineModel2D();
aff.set(at);
final CoordinateTransformMesh mesh = new CoordinateTransformMesh(aff, patch.getMeshResolution(), imgd.bp.getWidth(), imgd.bp.getHeight());
final TransformMeshMappingWithMasks<CoordinateTransformMesh> mapping = new TransformMeshMappingWithMasks<CoordinateTransformMesh>(mesh);
imgd.bp.setInterpolationMethod(ImageProcessor.BILINEAR);
// no interpolation
imgd.alpha.setInterpolationMethod(ImageProcessor.NEAREST_NEIGHBOR);
mapping.map(imgd.bp, imgd.alpha, target, targetMask);
}
return new Pair<ByteProcessor, ByteProcessor>(target, targetMask);
}
use of ini.trakem2.display.Patch 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.Patch 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.Patch 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