All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.mianalysis.mia.module.objects.process.FitConcaveHull2D Maven / Gradle / Ivy

Go to download

ModularImageAnalysis (MIA) is an ImageJ plugin which provides a modular framework for assembling image and object analysis workflows. Detected objects can be transformed, filtered, measured and related. Analysis workflows are batch-enabled by default, allowing easy processing of high-content datasets.

There is a newer version: 1.6.12
Show newest version
package io.github.mianalysis.mia.module.objects.process;

import java.awt.geom.Area;
import java.util.ArrayList;

import org.scijava.Priority;
import org.scijava.plugin.Plugin;

import ij.gui.Roi;
import io.github.mianalysis.mia.MIA;
import io.github.mianalysis.mia.module.Categories;
import io.github.mianalysis.mia.module.Category;
import io.github.mianalysis.mia.module.Module;
import io.github.mianalysis.mia.module.Modules;
import io.github.mianalysis.mia.object.Obj;
import io.github.mianalysis.mia.object.Objs;
import io.github.mianalysis.mia.object.Workspace;
import io.github.mianalysis.mia.object.coordinates.volume.PointOutOfRangeException;
import io.github.mianalysis.mia.object.coordinates.volume.VolumeType;
import io.github.mianalysis.mia.object.parameters.InputObjectsP;
import io.github.mianalysis.mia.object.parameters.Parameters;
import io.github.mianalysis.mia.object.parameters.SeparatorP;
import io.github.mianalysis.mia.object.parameters.objects.OutputObjectsP;
import io.github.mianalysis.mia.object.parameters.text.IntegerP;
import io.github.mianalysis.mia.object.refs.collections.ImageMeasurementRefs;
import io.github.mianalysis.mia.object.refs.collections.MetadataRefs;
import io.github.mianalysis.mia.object.refs.collections.ObjMeasurementRefs;
import io.github.mianalysis.mia.object.refs.collections.ObjMetadataRefs;
import io.github.mianalysis.mia.object.refs.collections.ParentChildRefs;
import io.github.mianalysis.mia.object.refs.collections.PartnerRefs;
import io.github.mianalysis.mia.object.system.Status;
import signalprocesser.voronoi.VPoint;
import signalprocesser.voronoi.VoronoiAlgorithm;
import signalprocesser.voronoi.representation.AbstractRepresentation;
import signalprocesser.voronoi.representation.RepresentationFactory;
import signalprocesser.voronoi.representation.triangulation.TriangulationRepresentation;
import signalprocesser.voronoi.representation.triangulation.TriangulationRepresentation.CalcCutOff;
import signalprocesser.voronoi.shapegeneration.ShapeGeneration;


/**
* Fits a 2D concave hull to all objects in a collection.  Each input object will be fit with a single concave hull.  Even for non-contiguous input objects, a single concave hull will be created.  The smoothness of the output hull is controlled by the "range" parameter, with smaller range values more closely following the surface of the object.  Larger range values should be used to overcome gaps in object edges.

Generated concave hulls are set as children of their respective input object. If objects are in 3D, a Z-projection of the object is used.

The implementation used in this module ("chi-shapes") is entirely from the "Concave hulls" library by Glenn Hudson and Matt Duckham (link). A paper with full details of the characteristic hulls algorithm is published in Pattern Recognition:

Duckham, M., Kulik, L., Worboys, M.F., Galton, A. (2008) "Efficient generation of simple polygons for characterizing the shape of a set of points in the plane", Pattern Recognition, 41, 3224-3236 (PDF, DOI). */ @Plugin(type = Module.class, priority = Priority.LOW, visible = true) public class FitConcaveHull2D extends Module { /** * */ public static final String INPUT_SEPARATOR = "Object input/output"; /** * Input objects to create 2D concave hulls for. Each convex hull will be a child of its respective input object. */ public static final String INPUT_OBJECTS = "Input objects"; /** * Output concave hull objects will be stored in the workspace with this name. Each concave hull object will be a child of the input object it was created from. */ public static final String OUTPUT_OBJECTS = "Output objects"; /** * */ public static final String HULL_SEPARATOR = "Concave hull controls"; public static final String RANGE_PX = "Range (px)"; public Obj processObject(Obj inputObject, Objs outputObjects, int range) { // We have to explicitly define this, as the number of slices is 1 (potentially // unlike the input object) Obj outputObject = outputObjects.createAndAddNewObject(VolumeType.QUADTREE); outputObject.setT(inputObject.getT()); outputObject.addParent(inputObject); inputObject.addChild(outputObject); // Process slice-by-slice for (int z = 0; z < inputObject.getNSlices(); z++) { Roi roi = inputObject.getRoi(z); if (roi == null) continue; ArrayList points = new ArrayList(); points = RepresentationFactory.convertPointsToSimpleTriangulationPoints(points); AbstractRepresentation representation = RepresentationFactory.createTriangulationRepresentation(); TriangulationRepresentation triangularrep = (TriangulationRepresentation) representation; triangularrep.beginAlgorithm(points); CalcCutOff calccutoff = new CalcCutOff() { public int calculateCutOff(TriangulationRepresentation rep) { return range; } }; triangularrep.setCalcCutOff(calccutoff); for (java.awt.Point point : roi.getContainedPoints()) points.add(representation.createPoint((int) Math.round(point.x), (int) Math.round(point.y))); VoronoiAlgorithm.generateVoronoi(triangularrep, points); ArrayList outterpoints = triangularrep.getPointsFormingOutterBoundary(); Area shape; try { shape = ShapeGeneration.createArea(outterpoints); } catch (NullPointerException e) { // Occasionally, the concave hull fitter will give a NullPointerException. When // this happens, a warning is shown and the raw ROI points used. outputObject.addPointsFromRoi(roi, z); MIA.log.writeWarning( "Concave hull fitting failed for object " + inputObject.getID() + " slice " + (z + 1)); continue; } try { outputObject.addPointsFromShape(shape, z); } catch (PointOutOfRangeException e) { } } return outputObject; } public FitConcaveHull2D(Modules modules) { super("Fit concave hull 2D", modules); } @Override public String getVersionNumber() { return "1.0.0"; } @Override public String getDescription() { return "Fits a 2D concave hull to all objects in a collection. Each input object will be fit with a single concave hull. Even for non-contiguous input objects, a single concave hull will be created. The smoothness of the output hull is controlled by the \"range\" parameter, with smaller range values more closely following the surface of the object. Larger range values should be used to overcome gaps in object edges.

" + "Generated concave hulls are set as children of their respective input object. If objects are in 3D, a Z-projection of the object is used.

" + "The implementation used in this module (\"chi-shapes\") is entirely from the \"Concave hulls\" library by Glenn Hudson and Matt Duckham (link). A paper with full details of the characteristic hulls algorithm is published in Pattern Recognition:

" + "Duckham, M., Kulik, L., Worboys, M.F., Galton, A. (2008) \"Efficient generation of simple polygons for characterizing the shape of a set of points in the plane\", Pattern Recognition, 41, 3224-3236 (PDF, DOI)."; } @Override public Category getCategory() { return Categories.OBJECTS_PROCESS; } @Override protected Status process(Workspace workspace) { // Getting input objects String inputObjectsName = parameters.getValue(INPUT_OBJECTS,workspace); Objs inputObjects = workspace.getObjects(inputObjectsName); // Getting parameters String outputObjectsName = parameters.getValue(OUTPUT_OBJECTS,workspace); int range = parameters.getValue(RANGE_PX,workspace); // If necessary, creating a new Objs and adding it to the Workspace Objs outputObjects = new Objs(outputObjectsName, inputObjects); workspace.addObjects(outputObjects); int count = 0; int total = inputObjects.size(); for (Obj inputObject : inputObjects.values()) { processObject(inputObject, outputObjects, range); writeProgressStatus(++count, total, "objects"); } if (showOutput) outputObjects.convertToImageIDColours().show(false); return Status.PASS; } @Override protected void initialiseParameters() { parameters.add(new SeparatorP(INPUT_SEPARATOR, this)); parameters.add(new InputObjectsP(INPUT_OBJECTS, this)); parameters.add(new OutputObjectsP(OUTPUT_OBJECTS, this)); parameters.add(new SeparatorP(HULL_SEPARATOR, this)); parameters.add(new IntegerP(RANGE_PX, this, 50)); addParameterDescriptions(); } @Override public Parameters updateAndGetParameters() { Workspace workspace = null; Parameters returnedParameters = new Parameters(); returnedParameters.add(parameters.getParameter(INPUT_SEPARATOR)); returnedParameters.add(parameters.getParameter(INPUT_OBJECTS)); returnedParameters.add(parameters.getParameter(OUTPUT_OBJECTS)); returnedParameters.add(parameters.getParameter(HULL_SEPARATOR)); returnedParameters.add(parameters.getParameter(RANGE_PX)); return returnedParameters; } @Override public ImageMeasurementRefs updateAndGetImageMeasurementRefs() { return null; } @Override public ObjMeasurementRefs updateAndGetObjectMeasurementRefs() { return null; } @Override public ObjMetadataRefs updateAndGetObjectMetadataRefs() { return null; } @Override public MetadataRefs updateAndGetMetadataReferences() { return null; } @Override public ParentChildRefs updateAndGetParentChildRefs() { Workspace workspace = null; ParentChildRefs returnedRelationships = new ParentChildRefs(); String inputObjectsName = parameters.getValue(INPUT_OBJECTS,workspace); String outputObjectsName = parameters.getValue(OUTPUT_OBJECTS,workspace); returnedRelationships.add(parentChildRefs.getOrPut(inputObjectsName, outputObjectsName)); return returnedRelationships; } @Override public PartnerRefs updateAndGetPartnerRefs() { return null; } @Override public boolean verify() { return true; } void addParameterDescriptions() { parameters.get(INPUT_OBJECTS).setDescription( "Input objects to create 2D concave hulls for. Each convex hull will be a child of its respective input object."); parameters.get(OUTPUT_OBJECTS).setDescription( "Output concave hull objects will be stored in the workspace with this name. Each concave hull object will be a child of the input object it was created from."); parameters.get(RANGE_PX).setDescription( "The maximum gap in the surface (edge) of an object that the hull can smooth over. For gaps larger than this the hull will follow the discontinuity."); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy