use of mpicbg.trakem2.transform.TransformMesh in project TrakEM2 by trakem2.
the class Blending method blendPatches.
public static final void blendPatches(final Set<Patch> patches, final boolean respect_current_mask) {
ExecutorService exe = null;
try {
if (null == patches || patches.size() < 2)
return;
final Layer layer = patches.iterator().next().getLayer();
for (final Patch p : patches) {
if (null != p.getCoordinateTransform()) {
Utils.log("CANNOT blend: at least one image has a coordinate transform.\nBlending of coordinate-transformed images will be enabled in the near future.");
return;
}
if (p.getLayer() != layer) {
Utils.log("CANNOT blend: all images must belong to the same layer!\n Otherwise the overlap cannot be computed.");
return;
}
}
final HashMap<Patch, TransformMesh> meshes = new HashMap<Patch, TransformMesh>();
for (final Patch p : patches) {
meshes.put(p, null == p.getCoordinateTransform() ? null : new TransformMesh(p.getCoordinateTransform(), p.getMeshResolution(), p.getOWidth(), p.getOHeight()));
}
exe = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
final List<Future<?>> futures = Collections.synchronizedList(new ArrayList<Future<?>>());
final List<Future<?>> futures2 = Collections.synchronizedList(new ArrayList<Future<?>>());
// Cache the indices that determine overlap order within the layer
final HashMap<Patch, Integer> indices = new HashMap<Patch, Integer>();
int i = 0;
for (final Displayable d : layer.getDisplayables()) {
if (d.getClass() == Patch.class && patches.contains((Patch) d)) {
indices.put((Patch) d, i);
}
i += 1;
}
for (final Patch p : patches) {
if (Thread.currentThread().isInterrupted())
break;
futures.add(exe.submit(new Runnable() {
@Override
public void run() {
final int pLayerIndex = indices.get(p);
final Set<Patch> overlapping = new HashSet<Patch>();
for (final Patch op : patches) {
if (indices.get(op) < pLayerIndex)
overlapping.add(op);
}
if (setBlendingMask(p, overlapping, meshes, respect_current_mask)) {
futures2.add(p.updateMipMaps());
}
}
}, null));
}
// join all:
Utils.waitIfAlive(futures, false);
Utils.waitIfAlive(futures2, false);
} catch (final Exception e) {
IJError.print(e);
} finally {
if (null != exe)
exe.shutdown();
Display.repaint();
}
}
use of mpicbg.trakem2.transform.TransformMesh in project TrakEM2 by trakem2.
the class NonLinearTransformMode method mousePressed.
@Override
public void mousePressed(final MouseEvent me, final int x_p, final int y_p, final double magnification) {
/* find if clicked on a point */
p_clicked = null;
double min = Double.MAX_VALUE;
final Point mouse = new Point(new double[] { x_p, y_p });
final double a = 64.0 / magnification / magnification;
for (final Point p : points) {
final double sd = Point.squareDistance(p, mouse);
if (sd < min && sd < a) {
p_clicked = p;
min = sd;
}
}
if (me.isShiftDown()) {
if (null == p_clicked) {
/* add one */
try {
if (points.size() > 0) {
/*
* Create a pseudo-invertible (TransformMesh) for the screen.
*/
final CoordinateTransform mlst = createCT();
final SimilarityModel2D toWorld = new SimilarityModel2D();
toWorld.set(1.0 / magnification, 0, srcRect.x, srcRect.y);
final SimilarityModel2D toScreen = toWorld.createInverse();
final mpicbg.models.CoordinateTransformList<mpicbg.models.CoordinateTransform> ctl = new mpicbg.models.CoordinateTransformList<mpicbg.models.CoordinateTransform>();
ctl.add(toWorld);
ctl.add(mlst);
ctl.add(toScreen);
final CoordinateTransformMesh ctm = new CoordinateTransformMesh(ctl, 32, (int) Math.ceil(srcRect.width * magnification), (int) Math.ceil(srcRect.height * magnification));
final double[] l = mouse.getL();
toScreen.applyInPlace(l);
ctm.applyInverseInPlace(l);
toWorld.applyInPlace(l);
}
points.add(mouse);
p_clicked = mouse;
} catch (final Exception e) {
Utils.log("Could not add point");
e.printStackTrace();
}
} else if (Utils.isControlDown(me)) {
// remove it
points.remove(p_clicked);
p_clicked = null;
}
}
}
use of mpicbg.trakem2.transform.TransformMesh in project TrakEM2 by trakem2.
the class Patch method keyPressed.
@Override
public void keyPressed(final KeyEvent ke) {
final Object source = ke.getSource();
if (!(source instanceof DisplayCanvas))
return;
final DisplayCanvas dc = (DisplayCanvas) source;
final Roi roi = dc.getFakeImagePlus().getRoi();
final int mod = ke.getModifiers();
switch(ke.getKeyCode()) {
case KeyEvent.VK_C:
// Ignoring masks: outside is already black, and ImageJ cannot handle alpha masks.
if (0 == (mod ^ (Event.SHIFT_MASK | Event.ALT_MASK))) {
// Place the source image, untransformed, into clipboard:
final ImagePlus imp = getImagePlus();
if (null != imp)
imp.copy(false);
} else if (0 == mod || (0 == (mod ^ Event.SHIFT_MASK))) {
CoordinateTransformList<CoordinateTransform> list = null;
if (hasCoordinateTransform()) {
list = new CoordinateTransformList<CoordinateTransform>();
list.add(getCoordinateTransform());
}
if (0 == mod) {
// SHIFT is not down
final AffineModel2D am = new AffineModel2D();
am.set(this.at);
if (null == list)
list = new CoordinateTransformList<CoordinateTransform>();
list.add(am);
}
ImageProcessor ip;
if (null != list) {
final TransformMesh mesh = new TransformMesh(list, meshResolution, o_width, o_height);
final TransformMeshMapping mapping = new TransformMeshMapping(mesh);
ip = mapping.createMappedImageInterpolated(getImageProcessor());
} else {
ip = getImageProcessor();
}
new ImagePlus(this.title, ip).copy(false);
}
ke.consume();
break;
case KeyEvent.VK_F:
// fill mask with current ROI using
if (null != roi && M.isAreaROI(roi)) {
Bureaucrat.createAndStart(new Worker.Task("Filling image mask") {
@Override
public void exec() {
getLayerSet().addDataEditStep(Patch.this);
if (0 == mod) {
addAlphaMask(roi, ProjectToolbar.getForegroundColorValue());
} else if (0 == (mod ^ Event.SHIFT_MASK)) {
// shift is down: fill outside
try {
final Area localRoi = M.areaInInts(M.getArea(roi)).createTransformedArea(at.createInverse());
final Area invLocalRoi = new Area(new Rectangle(0, 0, getOWidth(), getOHeight()));
invLocalRoi.subtract(localRoi);
addAlphaMaskLocal(invLocalRoi, ProjectToolbar.getForegroundColorValue());
} catch (final NoninvertibleTransformException e) {
IJError.print(e);
return;
}
}
getLayerSet().addDataEditStep(Patch.this);
// wait
try {
updateMipMaps().get();
} catch (final Throwable t) {
IJError.print(t);
}
Display.repaint();
}
}, project);
}
// capturing:
ke.consume();
break;
default:
super.keyPressed(ke);
break;
}
}
use of mpicbg.trakem2.transform.TransformMesh in project TrakEM2 by trakem2.
the class Patch method getArea.
/**
* Returns an Area in world coords representing the inside of this Patch. The fully alpha pixels are considered outside.
*/
@Override
public Area getArea() {
CoordinateTransform ct = null;
if (hasAlphaMask()) {
// Read the mask as a ROI for the 0 pixels only and apply the AffineTransform to it:
ImageProcessor alpha_mask = getAlphaMask();
if (null == alpha_mask) {
Utils.log2("Could not retrieve alpha mask for " + this);
} else {
if (hasCoordinateTransform()) {
// must transform it
ct = getCoordinateTransform();
final TransformMesh mesh = new TransformMesh(ct, meshResolution, o_width, o_height);
final TransformMeshMapping mapping = new TransformMeshMapping(mesh);
// Without interpolation
alpha_mask = mapping.createMappedImage(alpha_mask);
// Keep in mind the affine of the Patch already contains the translation specified by the mesh bounds.
}
// Threshold all non-zero areas of the mask:
alpha_mask.setThreshold(1, 255, ImageProcessor.NO_LUT_UPDATE);
final ImagePlus imp = new ImagePlus("", alpha_mask);
// TODO replace by our much faster method that scans by line, in AmiraImporter
final ThresholdToSelection tts = new ThresholdToSelection();
tts.setup("", imp);
tts.run(alpha_mask);
final Roi roi = imp.getRoi();
if (null == roi) {
// All pixels in the alpha mask have a value of zero
return new Area();
}
return M.getArea(roi).createTransformedArea(this.at);
}
}
// No alpha mask, or error in retrieving it:
final int[] x = new int[o_width + o_width + o_height + o_height];
final int[] y = new int[x.length];
int next = 0;
// Top edge:
for (int i = 0; i <= o_width; i++, next++) {
// len: o_width + 1
x[next] = i;
y[next] = 0;
}
// Right edge:
for (int i = 1; i <= o_height; i++, next++) {
// len: o_height
x[next] = o_width;
y[next] = i;
}
// bottom edge:
for (int i = o_width - 1; i > -1; i--, next++) {
// len: o_width
x[next] = i;
y[next] = o_height;
}
// left edge:
for (int i = o_height - 1; i > 0; i--, next++) {
// len: o_height -1
x[next] = 0;
y[next] = i;
}
if (hasCoordinateTransform() && null == ct)
ct = getCoordinateTransform();
if (null != ct) {
final CoordinateTransformList<CoordinateTransform> t = new CoordinateTransformList<CoordinateTransform>();
t.add(ct);
final TransformMesh mesh = new TransformMesh(ct, meshResolution, o_width, o_height);
final Rectangle box = mesh.getBoundingBox();
final AffineTransform aff = new AffineTransform(this.at);
// Must correct for the inverse of the mesh translation, because the affine also includes the translation.
aff.translate(-box.x, -box.y);
final AffineModel2D affm = new AffineModel2D();
affm.set(aff);
t.add(affm);
/*
* WORKS FINE, but for points that fall outside the mesh, they don't get transformed!
// Do it like Patch does it to generate the mipmap, with a mesh (and all the imprecisions of a mesh):
final CoordinateTransformList t = new CoordinateTransformList();
final TransformMesh mesh = new TransformMesh(this.ct, meshResolution, o_width, o_height);
final AffineTransform aff = new AffineTransform(this.at);
t.add(mesh);
final AffineModel2D affm = new AffineModel2D();
affm.set(aff);
t.add(affm);
*/
final double[] f = new double[] { x[0], y[0] };
t.applyInPlace(f);
final Path2D.Float path = new Path2D.Float(Path2D.Float.WIND_EVEN_ODD, x.length + 1);
path.moveTo(f[0], f[1]);
for (int i = 1; i < x.length; i++) {
f[0] = x[i];
f[1] = y[i];
t.applyInPlace(f);
path.lineTo(f[0], f[1]);
}
// line to last call to moveTo
path.closePath();
return new Area(path);
} else {
return new Area(new Polygon(x, y, x.length)).createTransformedArea(this.at);
}
}
use of mpicbg.trakem2.transform.TransformMesh in project TrakEM2 by trakem2.
the class Patch method toPixelCoordinate.
/**
* @see Patch#toPixelCoordinate(double, double)
* @param world_x The X of the world coordinate (in pixels, uncalibrated)
* @param world_y The Y of the world coordinate (in pixels, uncalibrated)
* @param aff The {@link AffineTransform} of the {@link Patch}.
* @param ct The {@link CoordinateTransform} of the {@link Patch}, if any (can be null).
* @param meshResolution The precision demanded for approximating a transform with a {@link TransformMesh}.
* @param o_width The width of the image underlying the {@link Patch}.
* @param o_height The height of the image underlying the {@link Patch}.
* @return A {@code double[]} array with the x,y values.
* @throws NoninvertibleTransformException
*/
public static final double[] toPixelCoordinate(final double world_x, final double world_y, final AffineTransform aff, final CoordinateTransform ct, final int meshResolution, final int o_width, final int o_height) throws NoninvertibleTransformException {
// Inverse the affine
final double[] d = new double[] { world_x, world_y };
aff.inverseTransform(d, 0, d, 0, 1);
// Inverse the coordinate transform
if (null != ct) {
final double[] f = new double[] { d[0], d[1] };
final mpicbg.models.InvertibleCoordinateTransform t = mpicbg.models.InvertibleCoordinateTransform.class.isAssignableFrom(ct.getClass()) ? (mpicbg.models.InvertibleCoordinateTransform) ct : new mpicbg.trakem2.transform.TransformMesh(ct, meshResolution, o_width, o_height);
try {
t.applyInverseInPlace(f);
} catch (final NoninvertibleModelException e) {
}
d[0] = f[0];
d[1] = f[1];
}
return d;
}
Aggregations