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

io.github.mianalysis.mia.module.objects.measure.intensity.MeasureIntensityAlongPath 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.measure.intensity;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.scijava.Priority;
import org.scijava.plugin.Plugin;

import ij.ImagePlus;
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.module.inputoutput.abstrakt.AbstractSaver;
import io.github.mianalysis.mia.module.objects.process.CreateSkeleton;
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.coordinates.volume.PointOutOfRangeException;
import io.github.mianalysis.mia.object.coordinates.volume.SpatCal;
import io.github.mianalysis.mia.object.coordinates.volume.Volume;
import io.github.mianalysis.mia.object.coordinates.volume.VolumeType;
import io.github.mianalysis.mia.object.image.Image;
import io.github.mianalysis.mia.object.parameters.BooleanP;
import io.github.mianalysis.mia.object.parameters.InputImageP;
import io.github.mianalysis.mia.object.parameters.InputObjectsP;
import io.github.mianalysis.mia.object.parameters.ParameterGroup;
import io.github.mianalysis.mia.object.parameters.Parameters;
import io.github.mianalysis.mia.object.parameters.SeparatorP;
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;

/**
 * Created by sc13967 on 22/06/2017.
 */

/**
* Measures the intensity profile along the pixel-wide backbone of an object and outputs this profile to .xlsx file.  Input objects are skeletonised to single pixel-wide representations prior to measurement; however, pre-skeletonised objects can also be processed.

Output results are stored in a multi-sheet .xlsx file, where each sheet includes the profile for a specific input image. Each row of a sheet contains the profile for a single object. Profiles are linearly-interpolated such that each measured position along a profile is 1px from the previous.

Note: Objects must either form a single line (i.e. not contain multiple branches) or reduce to a single line during skeletonisation. No profile will be recorded for any objects which fail this requirement. */ @Plugin(type = Module.class, priority = Priority.LOW, visible = true) public class MeasureIntensityAlongPath extends AbstractSaver { /** * */ public static final String INPUT_SEPARATOR = "Object input"; /** * Objects for which intensity profiles will be generated. */ public static final String INPUT_OBJECTS = "Input objects"; /** * */ public static final String IMAGE_SEPARATOR = "Image input"; public static final String INPUT_IMAGE = "Input image"; /** * Include another image from the workspace to be measured. Each separate image will be measured at the same spatial points and be saved to a separate sheet of the .xlsx file. */ public static final String MEASURE_ANOTHER_IMAGE = "Measure another image"; /** * */ public static final String OUTPUT_SEPARATOR = "Data output"; /** * Include columns recording the XYZ object centroid in pixel (or slice) units. */ public static final String INCLUDE_CENTROIDS = "Include centroids"; /** * Include a column recording the timepoint that the objects were present in. */ public static final String INCLUDE_TIMEPOINTS = "Include timepoints"; public MeasureIntensityAlongPath(Modules modules) { super("Measure intensity along path", modules); } public static SXSSFWorkbook process(Objs objects, Image[] images, boolean includeCentroids, boolean includeTimepoints) { // Creating workbook SXSSFWorkbook workbook = new SXSSFWorkbook(); Sheet[] sheets = new Sheet[images.length]; for (int i = 0; i < images.length; i++) { sheets[i] = workbook.createSheet(images[i].getName()); addHeaderToSheet(sheets[i], includeCentroids, includeTimepoints); } for (Obj object : objects.values()) for (int i = 0; i < images.length; i++) process(object, images[i], sheets[i], includeCentroids, includeTimepoints); return workbook; } public static void process(Obj object, Image image, Sheet sheet, boolean includeCentroids, boolean includeTimepoints) { if (object.size() < 2) return; // Ordering points LinkedHashSet> orderedPoints = new LinkedHashSet<>( CreateSkeleton.getLargestShortestPath(object)); LinkedHashMap rawIntensities = measureIntensityProfile(orderedPoints, image, object.getT(), object.getSpatialCalibration()); LinkedHashMap spacedIntensities = interpolateProfile(rawIntensities); addProfileToSheet(sheet, object, spacedIntensities, includeCentroids, includeTimepoints); } public static LinkedHashMap measureIntensityProfile(Collection> points, Image image, int t, SpatCal spatCal) { ImagePlus ipl = image.getImagePlus(); LinkedHashMap profile = new LinkedHashMap<>(); Point prevPoint = null; double distance = 0; for (Point point : points) { int x = point.getX(); int y = point.getY(); int z = point.getZ(); ipl.setPosition(1, z + 1, t + 1); double intensity = ipl.getProcessor().getPixel(x, y); if (prevPoint != null) { Volume volume1 = new Volume(VolumeType.POINTLIST, spatCal.duplicate()); Volume volume2 = new Volume(VolumeType.POINTLIST, spatCal.duplicate()); try { volume1.add(prevPoint.getX(), prevPoint.getY(), prevPoint.getZ()); volume2.add(point.getX(), point.getY(), point.getZ()); } catch (PointOutOfRangeException e) { MIA.log.writeError(e); } distance += volume1.getCentroidSeparation(volume2, true); } profile.put(distance, intensity); prevPoint = point; } return profile; } public static LinkedHashMap interpolateProfile(LinkedHashMap profile) { LinkedHashMap interpolated = new LinkedHashMap<>(); if (profile.size() == 0) { return interpolated; } else if (profile.size() == 1) { interpolated.put(0, profile.values().iterator().next()); return interpolated; } // Converting to double arrays (here, x is parametric location along the profile // and y is the raw intensity at that point) double[] x = profile.keySet().stream().mapToDouble(Double::doubleValue).toArray(); double[] y = profile.values().stream().mapToDouble(Double::doubleValue).toArray(); PolynomialSplineFunction spline = new LinearInterpolator().interpolate(x, y); int max = (int) Math.floor(x[x.length - 1]); for (int i = 0; i <= max; i++) interpolated.put(i, spline.value(i)); return interpolated; } public static void addHeaderToSheet(Sheet sheet, boolean includeCentroids, boolean includeTimepoints) { int colCount = 0; Row row = sheet.createRow(0); Workbook workbook = sheet.getWorkbook(); // Header cells will be bold CellStyle cellStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); cellStyle.setFont(font); Cell cell = row.createCell(colCount++); cell.setCellValue("OBJECT_ID"); cell.setCellStyle(cellStyle); if (includeCentroids) { cell = row.createCell(colCount++); cell.setCellValue("X_CENTROID_(PX)"); cell.setCellStyle(cellStyle); cell = row.createCell(colCount++); cell.setCellValue("Y_CENTROID_(PX)"); cell.setCellStyle(cellStyle); cell = row.createCell(colCount++); cell.setCellValue("Z_CENTROID_(SLICE)"); cell.setCellStyle(cellStyle); } if (includeTimepoints) { cell = row.createCell(colCount++); cell.setCellValue("TIMEPOINT"); cell.setCellStyle(cellStyle); } cell = row.createCell(colCount++); cell.setCellValue("PROFILE -->"); cell.setCellStyle(cellStyle); } public static void addProfileToSheet(Sheet sheet, Obj object, LinkedHashMap profile, boolean includeCentroids, boolean includeTimepoints) { int colCount = 0; int rowCount = sheet.getLastRowNum() + 1; Row row = sheet.createRow(rowCount++); row.createCell(colCount++).setCellValue(object.getID()); if (includeCentroids) { row.createCell(colCount++).setCellValue(object.getXMean(true)); row.createCell(colCount++).setCellValue(object.getYMean(true)); row.createCell(colCount++).setCellValue(object.getZMean(true, false)); } if (includeTimepoints) row.createCell(colCount++).setCellValue(object.getID()); for (double value : profile.values()) row.createCell(colCount++).setCellValue(value); } public static void writeDistancesFile(SXSSFWorkbook workbook, String path) { // Writing the workbook to file try { FileOutputStream outputStream = new FileOutputStream(path); workbook.write(outputStream); workbook.close(); outputStream.close(); } catch (FileNotFoundException e) { try { String dateTime = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date()); String rootPath = FilenameUtils.removeExtension(path); String newOutPath = rootPath + "_(" + dateTime + ").xlsx"; FileOutputStream outputStream = null; outputStream = new FileOutputStream(newOutPath); workbook.write(outputStream); workbook.close(); outputStream.close(); MIA.log.writeWarning("Target file (" + new File(path).getName() + ") inaccessible"); MIA.log.writeWarning("Saved to alternative file (" + new File(newOutPath).getName() + ")"); } catch (IOException e1) { MIA.log.writeError(e); } } catch (IOException e) { MIA.log.writeError(e); } } @Override public Category getCategory() { return Categories.OBJECTS_MEASURE_INTENSITY; } @Override public String getVersionNumber() { return "1.0.0"; } @Override public String getDescription() { return "Measures the intensity profile along the pixel-wide backbone of an object and outputs this profile to .xlsx file. Input objects are skeletonised to single pixel-wide representations prior to measurement; however, pre-skeletonised objects can also be processed.

" + "Output results are stored in a multi-sheet .xlsx file, where each sheet includes the profile for a specific input image. Each row of a sheet contains the profile for a single object. Profiles are linearly-interpolated such that each measured position along a profile is 1px from the previous.

" + "Note: Objects must either form a single line (i.e. not contain multiple branches) or reduce to a single line during skeletonisation. No profile will be recorded for any objects which fail this requirement."; } @Override public Status process(Workspace workspace) { // Getting objects to measure String inputObjectsName = parameters.getValue(INPUT_OBJECTS, workspace); Objs inputObjects = workspace.getObjects().get(inputObjectsName); // Getting parameters ParameterGroup inputImages = parameters.getParameter(MEASURE_ANOTHER_IMAGE); LinkedHashMap imageCollections = inputImages.getCollections(true); boolean includeCentroids = parameters.getValue(INCLUDE_CENTROIDS, workspace); boolean includeTimepoints = parameters.getValue(INCLUDE_TIMEPOINTS, workspace); String appendSeriesMode = parameters.getValue(APPEND_SERIES_MODE, workspace); String appendDateTimeMode = parameters.getValue(APPEND_DATETIME_MODE, workspace); String suffix = parameters.getValue(SAVE_SUFFIX, workspace); // If there are no input objects skip the module if (inputObjects == null) return Status.PASS; Obj firstObj = inputObjects.getFirst(); if (firstObj == null) return Status.PASS; Image[] images = new Image[imageCollections.size()]; int i = 0; for (Parameters imageCollection : imageCollections.values()) { String imageName = imageCollection.getValue(INPUT_IMAGE, workspace); images[i++] = workspace.getImage(imageName); } SXSSFWorkbook workbook = process(inputObjects, images, includeCentroids, includeTimepoints); String outputPath = getOutputPath(modules, workspace); String outputName = getOutputName(modules, workspace); // Adding last bits to name outputPath = outputPath + outputName; outputPath = appendSeries(outputPath, workspace, appendSeriesMode); outputPath = appendDateTime(outputPath, appendDateTimeMode); outputPath = outputPath + suffix + ".xlsx"; writeDistancesFile(workbook, outputPath); return Status.PASS; } @Override protected void initialiseParameters() { super.initialiseParameters(); parameters.add(new SeparatorP(INPUT_SEPARATOR, this)); parameters.add(new InputObjectsP(INPUT_OBJECTS, this)); parameters.add(new SeparatorP(IMAGE_SEPARATOR, this)); Parameters collection = new Parameters(); collection.add(new InputImageP(INPUT_IMAGE, this)); parameters.add(new ParameterGroup(MEASURE_ANOTHER_IMAGE, this, collection, 1)); parameters.add(new SeparatorP(OUTPUT_SEPARATOR, this)); parameters.add(new BooleanP(INCLUDE_CENTROIDS, this, false)); parameters.add(new BooleanP(INCLUDE_TIMEPOINTS, this, false)); addParameterDescriptions(); } @Override public Parameters updateAndGetParameters() { Parameters returnedParameters = new Parameters(); returnedParameters.add(parameters.getParameter(INPUT_SEPARATOR)); returnedParameters.add(parameters.getParameter(INPUT_OBJECTS)); returnedParameters.add(parameters.getParameter(IMAGE_SEPARATOR)); returnedParameters.add(parameters.getParameter(MEASURE_ANOTHER_IMAGE)); returnedParameters.add(parameters.getParameter(OUTPUT_SEPARATOR)); returnedParameters.add(parameters.getParameter(INCLUDE_CENTROIDS)); returnedParameters.add(parameters.getParameter(INCLUDE_TIMEPOINTS)); returnedParameters.addAll(super.updateAndGetParameters()); 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; } protected void addParameterDescriptions() { super.addParameterDescriptions(); parameters.get(INPUT_OBJECTS).setDescription("Objects for which intensity profiles will be generated."); Parameters collection = ((ParameterGroup) parameters.get(MEASURE_ANOTHER_IMAGE)).getTemplateParameters(); collection.get(INPUT_IMAGE).setDescription( "Image for which the intensity profile will be measured. Results from this profile will be added to a separate sheet in the .xlsx file."); parameters.get(MEASURE_ANOTHER_IMAGE).setDescription( "Include another image from the workspace to be measured. Each separate image will be measured at the same spatial points and be saved to a separate sheet of the .xlsx file."); parameters.get(INCLUDE_CENTROIDS) .setDescription("Include columns recording the XYZ object centroid in pixel (or slice) units."); parameters.get(INCLUDE_TIMEPOINTS) .setDescription("Include a column recording the timepoint that the objects were present in."); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy