Search in sources :

Example 11 with AffineModel2D

use of mpicbg.trakem2.transform.AffineModel2D 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);
    }
}
Also used : TransformMeshMapping(mpicbg.trakem2.transform.TransformMeshMapping) CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) Path2D(java.awt.geom.Path2D) Rectangle(java.awt.Rectangle) ImagePlus(ij.ImagePlus) ShapeRoi(ij.gui.ShapeRoi) Roi(ij.gui.Roi) ImageProcessor(ij.process.ImageProcessor) Area(java.awt.geom.Area) AffineModel2D(mpicbg.trakem2.transform.AffineModel2D) AffineTransform(java.awt.geom.AffineTransform) ThresholdToSelection(ij.plugin.filter.ThresholdToSelection) Polygon(java.awt.Polygon) CoordinateTransform(mpicbg.trakem2.transform.CoordinateTransform) TransformMesh(mpicbg.trakem2.transform.TransformMesh) CoordinateTransformMesh(mpicbg.models.CoordinateTransformMesh)

Example 12 with AffineModel2D

use of mpicbg.trakem2.transform.AffineModel2D in project TrakEM2 by trakem2.

the class Patch method getFullCoordinateTransform.

/**
 * Create a {@link CoordinateTransform} that incorporates both the
 * {@link CoordinateTransform} of this {@link Patch} (if present) and its
 * {@link AffineTransform}.  The returned {@link CoordinateTransform} directly
 * transfers the {@link Patch} into world coordinates.  An image can be rendered
 * e.g. using {@link mpicbg.ij.TransformMeshMapping} with an
 * {@link mpicbg.models.TransformMesh}.  Note that you may prefer to use
 * {@link mpicbg.models.TransformMesh} which does not perform auto-boxing as
 * opposed to {@link TransformMesh} in the mpicbg.trakem2 package.
 *
 * @return
 */
public final CoordinateTransform getFullCoordinateTransform() {
    final CoordinateTransform ctp = getCoordinateTransform();
    if (ctp == null) {
        final AffineModel2D affine = new AffineModel2D();
        affine.set(at);
        return affine;
    } else {
        final Rectangle box = getCoordinateTransformBoundingBox();
        final AffineTransform at2 = new AffineTransform(at);
        at2.translate(-box.x, -box.y);
        final AffineModel2D affine = new AffineModel2D();
        affine.set(at2);
        final CoordinateTransformList<CoordinateTransform> ctl = new CoordinateTransformList<CoordinateTransform>();
        ctl.add(ctp);
        ctl.add(affine);
        return ctl;
    }
}
Also used : CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) AffineModel2D(mpicbg.trakem2.transform.AffineModel2D) Rectangle(java.awt.Rectangle) AffineTransform(java.awt.geom.AffineTransform) CoordinateTransform(mpicbg.trakem2.transform.CoordinateTransform)

Example 13 with AffineModel2D

use of mpicbg.trakem2.transform.AffineModel2D 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);
}
Also used : ByteProcessor(ij.process.ByteProcessor) Loader(ini.trakem2.persistence.Loader) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D) ColorProcessor(ij.process.ColorProcessor) MipMapImage(ini.trakem2.display.MipMapImage) CoordinateTransformMesh(mpicbg.models.CoordinateTransformMesh) AffineTransform(java.awt.geom.AffineTransform) Patch(ini.trakem2.display.Patch) Pair(mpicbg.trakem2.util.Pair)

Example 14 with AffineModel2D

use of mpicbg.trakem2.transform.AffineModel2D 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);
}
Also used : ByteProcessor(ij.process.ByteProcessor) CoordinateTransformMesh(mpicbg.models.CoordinateTransformMesh) AffineTransform(java.awt.geom.AffineTransform) Patch(ini.trakem2.display.Patch) Pair(mpicbg.trakem2.util.Pair)

Example 15 with AffineModel2D

use of mpicbg.trakem2.transform.AffineModel2D 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();
    }
}
Also used : VectorDataTransform(ini.trakem2.display.VectorDataTransform) HashMap(java.util.HashMap) CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) DBObject(ini.trakem2.persistence.DBObject) VectorData(ini.trakem2.display.VectorData) AffineModel2D(mpicbg.models.AffineModel2D) CoordinateTransform(mpicbg.trakem2.transform.CoordinateTransform) AbstractAffineModel2D(mpicbg.models.AbstractAffineModel2D) AffineModel2D(mpicbg.models.AffineModel2D) Displayable(ini.trakem2.display.Displayable) TreeMap(java.util.TreeMap) Layer(ini.trakem2.display.Layer) NotEnoughDataPointsException(mpicbg.models.NotEnoughDataPointsException) NoninvertibleModelException(mpicbg.models.NoninvertibleModelException) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Area(java.awt.geom.Area) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) AffineTransform(java.awt.geom.AffineTransform) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) Patch(ini.trakem2.display.Patch)

Aggregations

AffineTransform (java.awt.geom.AffineTransform)14 Rectangle (java.awt.Rectangle)13 Patch (ini.trakem2.display.Patch)11 AffineModel2D (mpicbg.models.AffineModel2D)11 ArrayList (java.util.ArrayList)10 AbstractAffineModel2D (mpicbg.models.AbstractAffineModel2D)9 NotEnoughDataPointsException (mpicbg.models.NotEnoughDataPointsException)9 Point (mpicbg.models.Point)9 PointMatch (mpicbg.models.PointMatch)9 SimilarityModel2D (mpicbg.models.SimilarityModel2D)9 CoordinateTransform (mpicbg.trakem2.transform.CoordinateTransform)9 CoordinateTransformList (mpicbg.trakem2.transform.CoordinateTransformList)8 Layer (ini.trakem2.display.Layer)7 CoordinateTransformMesh (mpicbg.models.CoordinateTransformMesh)7 ByteProcessor (ij.process.ByteProcessor)6 ImageProcessor (ij.process.ImageProcessor)6 AffineModel2D (mpicbg.trakem2.transform.AffineModel2D)6 RigidModel2D (mpicbg.trakem2.transform.RigidModel2D)6 TranslationModel2D (mpicbg.trakem2.transform.TranslationModel2D)6 SIFT (mpicbg.ij.SIFT)5