io.github.mianalysis.mia.module.images.transform.ProjectImage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mia-modules Show documentation
Show all versions of mia-modules Show documentation
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.
package io.github.mianalysis.mia.module.images.transform;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import org.scijava.Priority;
import org.scijava.plugin.Plugin;
import ij.IJ;
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.images.configure.SetLookupTable;
import io.github.mianalysis.mia.object.Workspace;
import io.github.mianalysis.mia.object.Workspaces;
import io.github.mianalysis.mia.object.image.Image;
import io.github.mianalysis.mia.object.image.ImageFactory;
import io.github.mianalysis.mia.object.image.ImageType;
import io.github.mianalysis.mia.object.image.ImgPlusImage;
import io.github.mianalysis.mia.object.parameters.ChoiceP;
import io.github.mianalysis.mia.object.parameters.InputImageP;
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.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 net.imagej.ImgPlus;
import net.imagej.axis.Axes;
import net.imagej.axis.AxisType;
import net.imagej.axis.CalibratedAxis;
import net.imagej.axis.DefaultLinearAxis;
import net.imagej.ops.OpService;
import net.imagej.ops.Ops;
import net.imagej.ops.special.computer.UnaryComputerOp;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.cache.img.DiskCachedCellImg;
import net.imglib2.cache.img.DiskCachedCellImgFactory;
import net.imglib2.cache.img.DiskCachedCellImgOptions;
import net.imglib2.loops.LoopBuilder;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.Views;
/**
* Created by sc13967 on 04/05/2017.
*/
/**
*
*/
@Plugin(type = Module.class, priority = Priority.LOW, visible = true)
public class ProjectImage & NativeType> extends Module {
/**
*
*/
public static final String INPUT_SEPARATOR = "Image input/output";
/**
*
*/
public static final String INPUT_IMAGE = "Input image";
/**
*
*/
public static final String OUTPUT_IMAGE = "Output image";
/**
*
*/
public static final String PROJECTION_SEPARATOR = "Image projection";
// public static final String AXIS_1 = "Axis 2";
// public static final String AXIS_2 = "Axis 1";
/**
*
*/
public static final String PROJECTION_AXIS = "Projection axis";
/**
*
*/
public static final String PROJECTION_MODE = "Projection mode";
public ProjectImage(Modules modules) {
super("Project image", modules);
}
// public static void main(String[] args) throws UnsupportedEncodingException {
// String inputPath = "/Users/sc13967/Documents/Programming/Java/mia/mia-modules/src/test/resources/images/noisygradient/NoisyGradient3D_8bit.zip";
// // Doing the main part of the test
// // Creating a new workspace
// Workspaces workspaces = new Workspaces();
// Workspace workspace = workspaces.getNewWorkspace(null, 1);
// // Loading the test image and adding to workspace
// // String inputPath = URLDecoder.decode(ProjectImage.class.getResource(inputName).getPath(), "UTF-8");
// ImagePlus ipl = IJ.openImage(inputPath);
// Image image = ImageFactory.createImage("Test_image", ipl, ImageType.IMAGEPLUS);
// workspace.addImage(image);
// Modules modules = new Modules();
// ProjectImage projectImage = new ProjectImage<>(modules);
// projectImage.updateParameterValue(ProjectImage.INPUT_IMAGE, "Test_image");
// modules.add(projectImage);
// for (int i =0;i<1000;i++) {
// projectImage.execute(workspace);
// }
// }
public interface AxisModes {
// String X = "X";
// String Y = "Y";
String Z = "Z";
String CHANNEL = "Channel";
String TIME = "Time";
// String[] ALL = new String[] { X, Y, Z, CHANNEL, TIME };
String[] ALL = new String[] { Z, CHANNEL, TIME };
}
public interface ProjectionModes {
String AVERAGE = "Average";
String MIN = "Minimum";
String MEDIAN = "Median";
String MAX = "Maximum";
String STDEV = "Standard deviation";
String SUM = "Sum";
String[] ALL = new String[] { AVERAGE, MIN, MEDIAN, MAX, STDEV, SUM };
}
static AxisType getAxis(String axisName) {
switch (axisName) {
// case AxisModes.X:
// return Axes.X;
// case AxisModes.Y:
// return Axes.Y;
case AxisModes.CHANNEL:
return Axes.CHANNEL;
case AxisModes.Z:
default:
return Axes.Z;
case AxisModes.TIME:
return Axes.TIME;
}
}
static String getProjection(String projectionMode) {
switch (projectionMode) {
case ProjectionModes.AVERAGE:
return Ops.Stats.Mean.NAME;
case ProjectionModes.MIN:
return Ops.Stats.Min.NAME;
case ProjectionModes.MEDIAN:
return Ops.Stats.Median.NAME;
case ProjectionModes.MAX:
default:
return Ops.Stats.Max.NAME;
case ProjectionModes.STDEV:
return Ops.Stats.StdDev.NAME;
case ProjectionModes.SUM:
return Ops.Stats.Sum.NAME;
}
}
public static & NativeType> Image projectImageInZ(Image inputImage,
String outputImageName, String projectionMode) {
// return project(inputImage, outputImageName, AxisModes.X, AxisModes.Y,
// AxisModes.Z, projectionMode);
return project(inputImage, outputImageName, AxisModes.Z, projectionMode);
}
// public static & NativeType> Image project(Image
// inputImage, String outputImageName,
// String outputXAxis, String outputYAxis, String projectionAxis, String
// projectionMode) {
public static & NativeType> Image project(Image inputImage, String outputImageName,
String projectionAxis, String projectionMode) {
ImgPlus img = inputImage.getImgPlus();
// Getting key axis indices
// AxisType xType = getAxis(outputXAxis);
// AxisType yType = getAxis(outputYAxis);
AxisType xType = Axes.X;
AxisType yType = Axes.Y;
AxisType projType = getAxis(projectionAxis);
// Check specified axes exist, else return null
if (!checkAxisExists(img, xType) | !checkAxisExists(img, yType))
return null;
// If the projection axis doesn't exist, permute to correct view, then output
if (img.dimensionIndex(projType) == -1)
return getNonProjectedImage(img, outputImageName, xType, yType, projectionMode);
HashMap axisAssignments = getAxisAssignments(img);
// Permute axes, so that display axes X and Y are at positions 0 and 1 and
// projection axis is at position 2
RandomAccessibleInterval perm = permute(img, axisAssignments, xType, 0);
perm = permute(perm, axisAssignments, yType, 1);
perm = permute(perm, axisAssignments, projType, 2);
perm = enforceCZT(perm, axisAssignments);
perm = Views.addDimension(perm, 0, 0);
// Determine output size
int idx = 0;
HashMap axes = new HashMap<>();
int[] projected_dimensions = new int[perm.numDimensions() - 1];
for (int d = 0; d < img.numDimensions(); ++d) {
if (d != 2) {
projected_dimensions[idx] = (int) perm.dimension(d);
axes.put(idx, img.axis(img.dimensionIndex(axisAssignments.get(d))));
idx++;
}
}
projected_dimensions[idx] = 1;
axes.put(idx, img.axis(img.dimensionIndex(axisAssignments.get(2))));
OpService ops = MIA.getOpService();
// Creating output image
T type;
switch (projectionMode) {
case ProjectionModes.AVERAGE:
case ProjectionModes.STDEV:
case ProjectionModes.SUM:
type = (T) new FloatType();
break;
default:
case ProjectionModes.MAX:
case ProjectionModes.MIN:
case ProjectionModes.MEDIAN:
type = (T) img.firstElement();
break;
}
DiskCachedCellImgOptions options = ImgPlusImage.getCellImgOptions();
DiskCachedCellImg dcImage = new DiskCachedCellImgFactory(type, options).create(projected_dimensions);
ImgPlus proj = new ImgPlus<>(dcImage);
// Apply transformation
UnaryComputerOp mean_op = (UnaryComputerOp) ops.op(getProjection(projectionMode), img);
ops.transform().project(proj, perm, mean_op, 2);
// Update axes
int dOut = 0;
for (int dIn = 0; dIn < img.numDimensions(); dIn++) {
if (dIn != img.dimensionIndex(projType)) {
CalibratedAxis axIn = img.axis(dIn);
CalibratedAxis axOut = new DefaultLinearAxis(axIn.type(), axIn.unit(), axIn.calibratedValue(1));
proj.setAxis(axOut, dOut++);
}
}
CalibratedAxis axIn = img.axis(img.dimensionIndex(projType));
CalibratedAxis axOut = new DefaultLinearAxis(axIn.type(), axIn.unit(), axIn.calibratedValue(1));
proj.setAxis(axOut, proj.numDimensions() - 1);
Image projectedImage = ImageFactory.createImage(outputImageName, proj);
SetLookupTable.copyLUTFromImage(projectedImage,inputImage);
dcImage.shutdown();
return projectedImage;
}
static & NativeType, R extends RealType & NativeType> Image getNonProjectedImage(ImgPlus img, String outputImageName,
AxisType xType, AxisType yType, String projectionMode) {
HashMap axisAssignments = getAxisAssignments(img);
// Permute axes, so that display axes X and Y are at positions 0 and 1 and
// projection axis is at position 2
RandomAccessibleInterval perm = permute(img, axisAssignments, xType, 0);
perm = permute(perm, axisAssignments, yType, 1);
int idx = 0;
HashMap axes = new HashMap<>();
int[] projected_dimensions = new int[perm.numDimensions()];
for (int d = 0; d < img.numDimensions(); ++d) {
projected_dimensions[idx] = (int) perm.dimension(d);
axes.put(idx, img.axis(img.dimensionIndex(axisAssignments.get(d))));
idx++;
}
T type;
switch (projectionMode) {
case ProjectionModes.AVERAGE:
case ProjectionModes.STDEV:
case ProjectionModes.SUM:
type = (T) new FloatType();
break;
default:
case ProjectionModes.MAX:
case ProjectionModes.MIN:
case ProjectionModes.MEDIAN:
type = (T) img.firstElement();
break;
}
DiskCachedCellImgFactory factory = new DiskCachedCellImgFactory(type);
DiskCachedCellImg dcImage = factory.create(projected_dimensions);
ImgPlus outImg = new ImgPlus<>(dcImage);
if (projectionMode.equals(ProjectionModes.STDEV))
LoopBuilder.setImages(outImg).forEachPixel(pixel -> pixel.setReal(0));
else
LoopBuilder.setImages(perm,outImg).forEachPixel((s,t) -> t.setReal(s.getRealDouble()));
// Update axes
for (int cD = 0; cD < outImg.numDimensions(); cD++)
outImg.setAxis(axes.get(cD), cD);
// ImagePlus outputImagePlus = ImageJFunctions.wrap(outImg,outputImageName);
// ImgPlusTools.applyAxes(outImg,outputImagePlus);
dcImage.shutdown();
return ImageFactory.createImage(outputImageName, outImg);
}
static & NativeType> RandomAccessibleInterval enforceCZT(
RandomAccessibleInterval imgPlus, HashMap axisAssignments) {
// This problem should only arise with 5 dimensions, as the first 3 are fixed by
// the projection configuration
if (imgPlus.numDimensions() == 5) {
if (axisAssignments.get(3) == Axes.Z && axisAssignments.get(4) == Axes.CHANNEL) {
return permute(imgPlus, axisAssignments, Axes.Z, 4);
} else if (axisAssignments.get(3) == Axes.TIME && axisAssignments.get(4) == Axes.CHANNEL) {
return permute(imgPlus, axisAssignments, Axes.TIME, 4);
} else if (axisAssignments.get(3) == Axes.TIME && axisAssignments.get(4) == Axes.Z) {
return permute(imgPlus, axisAssignments, Axes.TIME, 4);
}
}
return imgPlus;
}
static & NativeType> HashMap getAxisAssignments(ImgPlus img) {
// Create HashMap containing axis assignments
HashMap axisAssignments = new HashMap<>();
for (int d = 0; d < img.numDimensions(); ++d)
axisAssignments.put(d, img.axis(d).type());
return axisAssignments;
}
static boolean checkAxisExists(ImgPlus imgPlus, AxisType type) {
if (imgPlus.dimensionIndex(type) == -1) {
MIA.log.writeWarning("Specified axis (" + type.getLabel() + ") not present in image");
return false;
}
return true;
}
static & NativeType> RandomAccessibleInterval permute(
RandomAccessibleInterval interval, HashMap axisAssignments, AxisType fromType,
int toIdx) {
int fromIdx = getPosition(axisAssignments, fromType);
AxisType toType = axisAssignments.get(toIdx);
axisAssignments.put(toIdx, fromType);
axisAssignments.put(fromIdx, toType);
return Views.permute(interval, fromIdx, toIdx);
}
static int getPosition(HashMap axisAssignments, AxisType type) {
for (int position : axisAssignments.keySet()) {
if (axisAssignments.get(position) == type)
return position;
}
// If this axis isn't in the list, return -1
return -1;
}
@Override
public Category getCategory() {
return Categories.IMAGES_TRANSFORM;
}
@Override
public String getVersionNumber() {
return "1.0.0";
}
@Override
public String getDescription() {
return "";
}
@Override
public Status process(Workspace workspace) {
// Loading image into workspace
String inputImageName = parameters.getValue(INPUT_IMAGE, workspace);
Image inputImage = workspace.getImages().get(inputImageName);
// Getting parameters
String outputImageName = parameters.getValue(OUTPUT_IMAGE, workspace);
// String xAxis = parameters.getValue(AXIS_1);
// String yAxis = parameters.getValue(AXIS_2);
String projectionAxis = parameters.getValue(PROJECTION_AXIS, workspace);
String projectionMode = parameters.getValue(PROJECTION_MODE, workspace);
// Create max projection image
// Image outputImage = project(inputImage, outputImageName, xAxis, yAxis,
// projectionAxis, projectionMode);
Image outputImage = project(inputImage, outputImageName, projectionAxis, projectionMode);
// Adding projected image to workspace
workspace.addImage(outputImage);
if (showOutput)
outputImage.show();
return Status.PASS;
}
@Override
protected void initialiseParameters() {
parameters.add(new SeparatorP(INPUT_SEPARATOR, this));
parameters.add(new InputImageP(INPUT_IMAGE, this));
parameters.add(new OutputImageP(OUTPUT_IMAGE, this));
parameters.add(new SeparatorP(PROJECTION_SEPARATOR, this));
// parameters.add(new ChoiceP(AXIS_1, this, AxisModes.X, AxisModes.ALL));
// parameters.add(new ChoiceP(AXIS_2, this, AxisModes.Y, AxisModes.ALL));
parameters.add(new ChoiceP(PROJECTION_AXIS, this, AxisModes.Z, AxisModes.ALL));
parameters.add(new ChoiceP(PROJECTION_MODE, this, ProjectionModes.MAX, ProjectionModes.ALL));
}
@Override
public Parameters updateAndGetParameters() {
return parameters;
}
@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;
}
}