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

io.github.mianalysis.mia.module.objects.convert.CreateDistanceMap 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.8
Show newest version
// TODO: Normalised distance from centre to edge.  Will need to calculate line between the two and assign points on that line

package io.github.mianalysis.mia.module.objects.convert;

import java.util.HashMap;

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

import ij.IJ;
import ij.ImagePlus;
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.module.images.process.ImageCalculator;
import io.github.mianalysis.mia.module.images.process.ImageMath;
import io.github.mianalysis.mia.module.images.process.InvertIntensity;
import io.github.mianalysis.mia.module.images.process.binary.BinaryOperations2D;
import io.github.mianalysis.mia.module.images.process.binary.DistanceMap;
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.Point;
import io.github.mianalysis.mia.object.image.Image;
import io.github.mianalysis.mia.object.image.ImageFactory;
import io.github.mianalysis.mia.object.parameters.BooleanP;
import io.github.mianalysis.mia.object.parameters.ChoiceP;
import io.github.mianalysis.mia.object.parameters.InputObjectsP;
import io.github.mianalysis.mia.object.parameters.OutputImageP;
import io.github.mianalysis.mia.object.parameters.Parameters;
import io.github.mianalysis.mia.object.parameters.SeparatorP;
import io.github.mianalysis.mia.object.parameters.choiceinterfaces.SpatialUnitsInterface;
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 io.github.mianalysis.mia.process.ColourFactory;


/**
* Creates a distance map for a selected object set.  Pixels in the output image are encoded with the distance to the nearest image edge or centroid (depending on setting).  A single distance map image is created for all objects in the specified set.  Uses the plugin "MorphoLibJ".
*/
@Plugin(type = Module.class, priority=Priority.LOW, visible=true)
public class CreateDistanceMap extends Module {

	/**
	* 
	*/
    public static final String INPUT_SEPARATOR = "Objects input / image output";

	/**
	* Objects from workspace for which distance map will be created.  A single distance map will be created for all objects.
	*/
    public static final String INPUT_OBJECTS = "Input objects";

	/**
	* Output distance map image which will be added to the workspace.  This will contain the distance map for each object.
	*/
    public static final String OUTPUT_IMAGE = "Output image";


	/**
	* 
	*/
    public static final String DISTANCE_MAP_SEPARATOR = "Distance map controls";

	/**
	* Controls where the distances are calculated from:
  • "Distance from object centroid" Each pixel is encoded with the distance from the centre of the respective object.
  • "Distance from object edge" Each pixel is encoded with the distance from the edge of the respective object.
*/ public static final String REFERENCE_MODE = "Reference mode"; /** * When selected (and "Reference mode" is set to "Distance from object edge"), the distance map will be inverted, such that the distances inside objects are also positive. If not selected, the distances inside objects will be negative. Distance values outside objects are always positive. */ public static final String INVERT_MAP_WITHIN_OBJECTS = "Invert map within objects"; /** * Controls which regions of the image are displayed:
  • "Inside and outside" Distances both inside and outside the objects are non-zero.
  • "Inside only" Distances are shown inside each object, but are set to zero for all pixels outside an object.
  • "Outside only" Distances are shown outside each object, but are set to zero for all pixels inside an object.
*/ public static final String MASKING_MODE = "Masking mode"; /** * When selected, the distance values inside each object are normalised to the range 0-1. Normalisation is performed on an object-by-object basis, so the absolute distance values cannot be directly compared between objects. */ public static final String NORMALISE_MAP_PER_OBJECT = "Normalise map per object"; /** * Controls whether spatial values are assumed to be specified in calibrated units (as defined by the "Input control" parameter "Spatial unit") or pixel units. */ public static final String SPATIAL_UNITS_MODE = "Spatial units mode"; public CreateDistanceMap(Modules modules) { super("Create distance map", modules); } public interface ReferenceModes { String DISTANCE_FROM_CENTROID = "Distance from object centroid"; String DISTANCE_FROM_EDGE = "Distance from object edge"; String[] ALL = new String[] { DISTANCE_FROM_CENTROID, DISTANCE_FROM_EDGE }; } public interface MaskingModes { String INSIDE_AND_OUTSIDE = "Inside and outside"; String INSIDE_ONLY = "Inside only"; String OUTSIDE_ONLY = "Outside only"; String[] ALL = new String[] { INSIDE_AND_OUTSIDE, INSIDE_ONLY, OUTSIDE_ONLY }; } public interface SpatialUnitsModes extends SpatialUnitsInterface { } public static Image getCentroidDistanceMap(Objs inputObjects, String outputImageName) { // Getting image parameters int width = inputObjects.getWidth(); int height = inputObjects.getHeight(); int nZ = inputObjects.getNSlices(); int nT = inputObjects.getNFrames(); // Creating a blank image (8-bit, so binary operations work) ImagePlus distanceMapIpl = IJ.createHyperStack(outputImageName, width, height, 1, nZ, nT, 8); Image distanceMap = ImageFactory.createImage(outputImageName, distanceMapIpl); inputObjects.applyCalibration(distanceMap); // Adding a spot to the centre of each object for (Obj inputObj : inputObjects.values()) { int x = (int) Math.round(inputObj.getXMean(true)); int y = (int) Math.round(inputObj.getYMean(true)); int z = (int) Math.round(inputObj.getZMean(true, false)); int t = inputObj.getT(); distanceMapIpl.setPosition(1, z + 1, t + 1); distanceMapIpl.getProcessor().set(x, y, 255); } // Calculating the distance map return DistanceMap.process(distanceMap, outputImageName, false, DistanceMap.WeightModes.WEIGHTS_3_4_5_7, true, false); } public static Image getEdgeDistanceMap(Objs inputObjects, String outputImageName, boolean invertInside) { // Creating an objects image HashMap hues = ColourFactory.getSingleColourValues(inputObjects, ColourFactory.SingleColours.WHITE); Image objImage = inputObjects.convertToImage(outputImageName, hues, 8, false); // Calculating the distance maps. The inside map is set to negative String weightMode = DistanceMap.WeightModes.WEIGHTS_3_4_5_7; Image outsideDistImage = DistanceMap.process(objImage, "DistanceOutside", false, weightMode, true, false); InvertIntensity.process(objImage); BinaryOperations2D.process(objImage, BinaryOperations2D.OperationModes.ERODE, 1, 1, false); Image insideDistImage = DistanceMap.process(objImage, "DistanceInside", false, weightMode, true, false); // If selected, inverting the inside of the object, so values here are negative if (invertInside) ImageMath.process(insideDistImage, ImageMath.CalculationModes.MULTIPLY, -1.0); // Compiling the distance map return ImageCalculator.process(insideDistImage, outsideDistImage, ImageCalculator.CalculationMethods.ADD, ImageCalculator.OverwriteModes.CREATE_NEW, outputImageName, true, true); } public static void applyMasking(Image inputImage, Objs inputObjects, String maskingMode) { // If the masking mode is set to INSIDE_AND_OUTSIDE skip this method if (maskingMode.equals(MaskingModes.INSIDE_AND_OUTSIDE)) return; ImagePlus inputIpl = inputImage.getImagePlus(); // Convert to image (and possibly invert), set to binary image (0 and 1) and // multiply as appropriate HashMap hues = ColourFactory.getSingleColourValues(inputObjects, ColourFactory.SingleColours.WHITE); ImagePlus objIpl = inputObjects.convertToImage("Objects", hues, 8, false).getImagePlus(); // For outside only masks invert the mask if (maskingMode.equals(MaskingModes.OUTSIDE_ONLY)) InvertIntensity.process(objIpl); // Normalising the mask ImageMath.process(objIpl, ImageMath.CalculationModes.DIVIDE, 255); // Applying the mask String calculationMode = ImageCalculator.CalculationMethods.MULTIPLY; String overwriteMode = ImageCalculator.OverwriteModes.OVERWRITE_IMAGE1; ImageCalculator.process(inputIpl, objIpl, calculationMode, overwriteMode, null, false, true); } public static void applyNormalisation(Image inputImage, Objs inputObjects) { // Iterating over each object, calculating the largest distance, then dividing // all pixels within that object by // this value for (Obj inputObject : inputObjects.values()) { double maxDistance = getMaximumDistance(inputImage, inputObject); applyNormalisation(inputImage, inputObject, maxDistance); } } static double getMaximumDistance(Image inputImage, Obj inputObject) { // Iterating over each point in the object, getting the largest distance double maxDistance = Double.MIN_VALUE; int t = inputObject.getT(); ImagePlus inputIpl = inputImage.getImagePlus(); for (Point point : inputObject.getCoordinateSet()) { int x = point.getX(); int y = point.getY(); int z = point.getZ(); inputIpl.setPosition(1, z + 1, t + 1); double currentValue = inputIpl.getProcessor().getPixelValue(x, y); maxDistance = Math.max(Math.abs(currentValue), maxDistance); } return maxDistance; } static void applyNormalisation(Image inputImage, Obj inputObject, double maxDistance) { int t = inputObject.getT(); ImagePlus inputIpl = inputImage.getImagePlus(); for (Point point : inputObject.getCoordinateSet()) { int x = point.getX(); int y = point.getY(); int z = point.getZ(); inputIpl.setPosition(1, z + 1, t + 1); double currentValue = inputIpl.getProcessor().getPixelValue(x, y); inputIpl.getProcessor().setf(x, y, (float) (currentValue / maxDistance)); } } @Override public Category getCategory() { return Categories.OBJECTS_CONVERT; } @Override public String getVersionNumber() { return "1.0.0"; } @Override public String getDescription() { return "Creates a distance map for a selected object set. Pixels in the output image are encoded with the distance to the nearest image edge or centroid (depending on setting). A single distance map image is created for all objects in the specified set. Uses the plugin \"MorphoLibJ\"."; } @Override public Status process(Workspace workspace) { // Getting input objects String inputObjectsName = parameters.getValue(INPUT_OBJECTS,workspace); Objs inputObjects = workspace.getObjects(inputObjectsName); // Getting other parameters String outputImageName = parameters.getValue(OUTPUT_IMAGE,workspace); String referenceMode = parameters.getValue(REFERENCE_MODE,workspace); boolean invertInside = parameters.getValue(INVERT_MAP_WITHIN_OBJECTS,workspace); String maskingMode = parameters.getValue(MASKING_MODE,workspace); boolean normaliseMap = parameters.getValue(NORMALISE_MAP_PER_OBJECT,workspace); String spatialUnits = parameters.getValue(SPATIAL_UNITS_MODE,workspace); // Initialising the distance map Image distanceMap = null; switch (referenceMode) { case ReferenceModes.DISTANCE_FROM_CENTROID: distanceMap = getCentroidDistanceMap(inputObjects, outputImageName); break; case ReferenceModes.DISTANCE_FROM_EDGE: distanceMap = getEdgeDistanceMap(inputObjects, outputImageName, invertInside); break; } if (distanceMap == null) return Status.PASS; // Applying masking applyMasking(distanceMap, inputObjects, maskingMode); // Performing normalisation (only when using inside-only masking) if (maskingMode.equals(MaskingModes.INSIDE_ONLY) && normaliseMap) applyNormalisation(distanceMap, inputObjects); // Applying spatial calibration (as long as we're not normalising the map) if (!(maskingMode.equals(MaskingModes.INSIDE_ONLY) && normaliseMap) && spatialUnits.equals(SpatialUnitsModes.CALIBRATED)) { double dppXY = inputObjects.getDppXY(); DistanceMap.applyCalibratedUnits(distanceMap, dppXY); } // Adding distance map to output and showing workspace.addImage(distanceMap); if (showOutput) distanceMap.show(); return Status.PASS; } @Override protected void initialiseParameters() { parameters.add(new SeparatorP(INPUT_SEPARATOR, this)); parameters.add(new InputObjectsP(INPUT_OBJECTS, this)); parameters.add(new OutputImageP(OUTPUT_IMAGE, this)); parameters.add(new SeparatorP(DISTANCE_MAP_SEPARATOR, this)); parameters.add(new ChoiceP(REFERENCE_MODE, this, ReferenceModes.DISTANCE_FROM_CENTROID, ReferenceModes.ALL)); parameters.add(new BooleanP(INVERT_MAP_WITHIN_OBJECTS, this, true)); parameters.add(new ChoiceP(MASKING_MODE, this, MaskingModes.INSIDE_AND_OUTSIDE, MaskingModes.ALL)); parameters.add(new BooleanP(NORMALISE_MAP_PER_OBJECT, this, false)); parameters.add(new ChoiceP(SPATIAL_UNITS_MODE, this, SpatialUnitsModes.PIXELS, SpatialUnitsModes.ALL)); 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_IMAGE)); returnedParameters.add(parameters.getParameter(DISTANCE_MAP_SEPARATOR)); returnedParameters.add(parameters.getParameter(REFERENCE_MODE)); switch ((String) parameters.getValue(REFERENCE_MODE,workspace)) { case ReferenceModes.DISTANCE_FROM_EDGE: returnedParameters.add(parameters.getParameter(INVERT_MAP_WITHIN_OBJECTS)); break; } returnedParameters.add(parameters.getParameter(MASKING_MODE)); switch ((String) parameters.getValue(MASKING_MODE,workspace)) { case MaskingModes.INSIDE_ONLY: returnedParameters.add(parameters.getParameter(NORMALISE_MAP_PER_OBJECT)); break; } // If we're not using the inside-only masking with normalisation, allow the // units to be specified. if (!(((String) parameters.getValue(MASKING_MODE,workspace)).equals(MaskingModes.INSIDE_ONLY) && (boolean) parameters.getValue(NORMALISE_MAP_PER_OBJECT,workspace))) { returnedParameters.add(parameters.getParameter(SPATIAL_UNITS_MODE)); } 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() { return null; } @Override public PartnerRefs updateAndGetPartnerRefs() { return null; } @Override public boolean verify() { return true; } void addParameterDescriptions() { parameters.get(INPUT_OBJECTS).setDescription( "Objects from workspace for which distance map will be created. A single distance map will be created for all objects."); parameters.get(OUTPUT_IMAGE).setDescription( "Output distance map image which will be added to the workspace. This will contain the distance map for each object."); parameters.get(REFERENCE_MODE).setDescription("Controls where the distances are calculated from:
    " + "
  • \"" + ReferenceModes.DISTANCE_FROM_CENTROID + "\" Each pixel is encoded with the distance from the centre of the respective object.
  • " + "
  • \"" + ReferenceModes.DISTANCE_FROM_EDGE + "\" Each pixel is encoded with the distance from the edge of the respective object.
"); parameters.get(INVERT_MAP_WITHIN_OBJECTS).setDescription("When selected (and \"" + REFERENCE_MODE + "\" is set to \"" + ReferenceModes.DISTANCE_FROM_EDGE + "\"), the distance map will be inverted, such that the distances inside objects are also positive. If not selected, the distances inside objects will be negative. Distance values outside objects are always positive."); parameters.get(MASKING_MODE).setDescription("Controls which regions of the image are displayed:
    " + "
  • \"" + MaskingModes.INSIDE_AND_OUTSIDE + "\" Distances both inside and outside the objects are non-zero.
  • " + "
  • \"" + MaskingModes.INSIDE_ONLY + "\" Distances are shown inside each object, but are set to zero for all pixels outside an object.
  • " + "
  • \"" + MaskingModes.OUTSIDE_ONLY + "\" Distances are shown outside each object, but are set to zero for all pixels inside an object.
"); parameters.get(NORMALISE_MAP_PER_OBJECT).setDescription( "When selected, the distance values inside each object are normalised to the range 0-1. Normalisation is performed on an object-by-object basis, so the absolute distance values cannot be directly compared between objects."); parameters.get(SPATIAL_UNITS_MODE).setDescription(SpatialUnitsInterface.getDescription()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy