use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class FindFociOptimiser_PlugIn method showResult.
@Nullable
private static AssignedPoint[] showResult(ImagePlus imp, ImagePlus mask, Parameters parameters, boolean showScoreImages, int matchSearchMethod, double matchSearchDistance) {
if (imp == null) {
return null;
}
// Clone the input image to allow display of the peaks on the original
final ImagePlus clone = cloneImage(imp, imp.getTitle() + " clone");
clone.show();
final FindFociProcessorOptions processorOptions = parameters.processorOptions.copy();
processorOptions.setMaskMethod(MaskMethod.PEAKS_ABOVE_SADDLE);
final FindFociOptions options = new FindFociOptions(true);
options.setOption(OutputOption.ROI_SELECTION, true);
options.setOption(OutputOption.MASK_ROI_SELECTION, true);
final FindFoci_PlugIn ff = new FindFoci_PlugIn();
ff.exec(clone, mask, processorOptions, options, true);
// Add 3D support here by getting the results from the results table
final List<FindFociResult> results = FindFoci_PlugIn.getLastResults();
final AssignedPoint[] predictedPoints = extractedPredictedPoints(results);
maskImage(clone, mask);
if (showScoreImages) {
final AssignedPoint[] actualPoints = extractRoiPoints(imp, mask);
final List<Coordinate> truePositives = new LinkedList<>();
final List<Coordinate> falsePositives = new LinkedList<>();
final List<Coordinate> falseNegatives = new LinkedList<>();
final double distanceThreshold = getDistanceThreshold(imp, matchSearchMethod, matchSearchDistance);
final ToDoubleBiFunction<Coordinate, Coordinate> distanceFunction = CoordinateUtils.getSquaredDistanceFunction(imp.getCalibration(), is3D(actualPoints));
MatchCalculator.analyseResultsCoordinates(actualPoints, predictedPoints, distanceThreshold, truePositives, falsePositives, falseNegatives, null, distanceFunction);
// Show image with TP, FP and FN. Use an overlay to support 3D images
final ImagePlus tpImp = cloneImage(imp, mask, imp.getTitle() + " TP");
tpImp.setOverlay(createOverlay(truePositives, imp));
tpImp.show();
final ImagePlus fpImp = cloneImage(imp, mask, imp.getTitle() + " FP");
fpImp.setOverlay(createOverlay(falsePositives, imp));
fpImp.show();
final ImagePlus fnImp = cloneImage(imp, mask, imp.getTitle() + " FN");
fnImp.setOverlay(createOverlay(falseNegatives, imp));
fnImp.show();
} else {
// Leaving old results would be confusing so close them
closeImage(imp.getTitle() + " TP");
closeImage(imp.getTitle() + " FP");
closeImage(imp.getTitle() + " FN");
}
return predictedPoints;
}
use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class FindFociBaseProcessor method extractMask.
/**
* Extract the mask image.
*
* @param mask the mask
* @return The mask image array
*/
@Nullable
private int[] extractMask(ImagePlus mask) {
if (mask == null) {
return null;
}
// Check sizes in X & Y
if (mask.getWidth() != maxx || mask.getHeight() != maxy || (mask.getNSlices() != maxz && mask.getNSlices() != 1)) {
return null;
}
final int maxx_maxy = maxx * maxy;
final int[] image;
if (mask.getNSlices() == 1) {
// Extract a single plane
final ImageProcessor ipMask = mask.getProcessor();
image = new int[maxx_maxy];
for (int i = maxx_maxy; i-- > 0; ) {
image[i] = ipMask.get(i);
}
} else {
final int maxx_maxy_maxz = maxx * maxy * maxz;
// If the same stack size then process through the image
final ImageStack stack = mask.getStack();
final int c = mask.getChannel();
final int f = mask.getFrame();
image = new int[maxx_maxy_maxz];
for (int slice = 1; slice <= mask.getNSlices(); slice++) {
final int stackIndex = mask.getStackIndex(c, slice, f);
final ImageProcessor ipMask = stack.getProcessor(stackIndex);
int index = maxx_maxy * slice;
for (int i = maxx_maxy; i-- > 0; ) {
index--;
image[index] = ipMask.get(i);
}
}
}
return image;
}
use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class AutoThreshold_PlugIn method exec.
/**
* Execute the plugin functionality.
*
* @param imp the image
* @param method the method
* @param noWhite the no white
* @param noBlack the no black
* @param doIwhite flag to set the foreground as white
* @param doIset flag to set the threshold on the image
* @param doIlog flag to log the threshold
* @param doIstackHistogram flag to use the stack histogram
* @return an Object[] array with the threshold and the ImagePlus. Does NOT show the new, image;
* just returns it.
*/
@Nullable
public Object[] exec(ImagePlus imp, AutoThreshold.Method method, boolean noWhite, boolean noBlack, boolean doIwhite, boolean doIset, boolean doIlog, boolean doIstackHistogram) {
// 0 - Check validity of parameters
if (null == imp) {
return null;
}
final int currentSlice = imp.getCurrentSlice();
ImageProcessor ip = imp.getProcessor();
final int xe = ip.getWidth();
final int ye = ip.getHeight();
int foreColour = 0;
int backColour = imp.getBitDepth() == 8 ? 255 : 65535;
if (doIwhite) {
foreColour = backColour;
backColour = 0;
}
final int[] data = ip.getHistogram();
IJ.showStatus("Thresholding...");
// 1 Do it
if (imp.getStackSize() == 1) {
ip.snapshot();
Undo.setup(Undo.FILTER, imp);
} else if (doIstackHistogram) {
// get the stack histogram into the data[] array
for (int i = 1; i <= imp.getStackSize(); i++) {
// Ignore the slice that has already been included
if (i == currentSlice) {
continue;
}
imp.setSliceWithoutUpdate(i);
ip = imp.getProcessor();
final int[] temp = ip.getHistogram();
for (int j = 0; j < data.length; j++) {
data[j] += temp[j];
}
}
imp.setSliceWithoutUpdate(currentSlice);
}
if (noBlack) {
data[0] = 0;
}
if (noWhite) {
data[data.length - 1] = 0;
}
final int threshold = AutoThreshold.getThreshold(method, data);
// show threshold in log window if required
if (doIlog) {
IJ.log(method + ": " + threshold);
}
if (threshold > -1) {
// threshold it
if (doIset) {
if (doIwhite) {
imp.getProcessor().setThreshold(threshold + 1.0, data.length - 1.0, ImageProcessor.RED_LUT);
} else {
imp.getProcessor().setThreshold(0, threshold, ImageProcessor.RED_LUT);
}
} else {
// Reset display range otherwise we can never set the threshold
imp.setDisplayRange(0, Math.max(backColour, foreColour));
if (doIstackHistogram) {
for (int j = 1; j <= imp.getStackSize(); j++) {
imp.setSliceWithoutUpdate(j);
ip = imp.getProcessor();
for (int y = 0; y < ye; y++) {
for (int x = 0; x < xe; x++) {
if (ip.getPixel(x, y) > threshold) {
ip.putPixel(x, y, foreColour);
} else {
ip.putPixel(x, y, backColour);
}
}
}
}
// threshold all of them
imp.setSliceWithoutUpdate(currentSlice);
} else {
for (int y = 0; y < ye; y++) {
for (int x = 0; x < xe; x++) {
if (ip.getPixel(x, y) > threshold) {
ip.putPixel(x, y, foreColour);
} else {
ip.putPixel(x, y, backColour);
}
}
}
}
imp.getProcessor().setThreshold(data.length - 1.0, data.length - 1.0, ImageProcessor.NO_LUT_UPDATE);
}
}
imp.updateAndDraw();
// 2 - Return the threshold and the image
return new Object[] { threshold, imp };
}
use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class CellOutliner_PlugIn method findCells.
@Nullable
private PolygonRoi[] findCells(ImageProcessor inputProcessor) {
// Limit processing to where it is needed
final Rectangle bounds = createBounds(inputProcessor, xpoints, ypoints, getCellRange());
ImageProcessor ip = inputProcessor.duplicate();
ip.setRoi(bounds);
ip = ip.crop();
if (kernels == null) {
kernels = createKernels();
convolved = null;
}
if (convolved == null) {
convolved = convolveImage(ip, kernels);
// showConvolvedImages(convolved)
}
if (ImageJUtils.isInterrupted()) {
return null;
}
FloatProcessor combinedIp = null;
Blitter blitter = null;
ImagePlus combinedImp = null;
if (debug) {
combinedIp = new FloatProcessor(ip.getWidth(), ip.getHeight());
blitter = new FloatBlitter(combinedIp);
combinedImp = displayImage(combinedIp, "Combined edge projection");
}
final PolygonRoi[] cells = new PolygonRoi[xpoints.length];
if (!this.buildMaskOutput) {
IJ.showStatus("Finding cells ...");
}
// Process each point
for (int n = 0; n < xpoints.length; n++) {
if (!this.buildMaskOutput) {
IJ.showProgress(n, xpoints.length);
}
final int cx = xpoints[n] - bounds.x;
final int cy = ypoints[n] - bounds.y;
// Restrict bounds using the cell radius and tolerance
final Rectangle pointBounds = createBounds(ip, cx, cy, cx, cy, getCellRange());
// Calculate the angle
final FloatProcessor angle = createAngleProcessor(cx, cy, pointBounds);
if (ImageJUtils.isInterrupted()) {
return null;
}
final FloatProcessor edgeProjection = computeEdgeProjection(convolved, pointBounds, angle);
// Initialise the edge as a circle.
PolygonRoi cell = null;
double[] params = { cx - pointBounds.x, cy - pointBounds.y, settings.cellRadius, settings.cellRadius, settings.cellRadius, 0 };
final double range = settings.cellRadius * 0.9;
// Iterate to find the best cell outline
boolean returnEllipticalFit = settings.ellipticalFit;
for (int iter = 0; iter < settings.iterations; iter++) {
// Use the current elliptical edge to define the weights for the edge projection
final FloatProcessor weights = createWeightMap(pointBounds, params, range);
if (ImageJUtils.isInterrupted() || weights == null) {
return null;
}
if (debug) {
displayImage(weights, "Weight map");
}
final FloatProcessor weightedEdgeProjection = applyWeights(edgeProjection, weights);
if (debug) {
blitter.copyBits(weightedEdgeProjection, pointBounds.x, pointBounds.y, Blitter.ADD);
combinedIp.resetMinAndMax();
combinedImp.updateAndDraw();
displayImage(weightedEdgeProjection, "Weighted edge projection");
}
cell = findPolygonalCell((int) Math.round(params[0]), (int) Math.round(params[1]), weightedEdgeProjection, angle);
// weights
final FloatProcessor weightMap = weightedEdgeProjection;
final double[] newParams = fitPolygonalCell(cell, params, weightMap);
if (newParams == null) {
returnEllipticalFit = false;
break;
}
// Set the parameters for the weight map
params = newParams;
}
assert cell != null : "No cell";
// Return either the fitted elliptical cell or the last polygon outline
if (returnEllipticalFit) {
final EllipticalCell e = new EllipticalCell();
final FloatPolygon ellipse = e.drawEllipse(params);
cell = new PolygonRoi(ellipse.xpoints, ellipse.ypoints, ellipse.npoints, Roi.POLYGON);
}
PolygonRoi finalCell = cell;
if (settings.dilate > 0) {
// Dilate the cell and then trace the new outline
final ByteProcessor bp = new ByteProcessor(pointBounds.width, pointBounds.height);
bp.setColor(CELL & 0xff);
bp.draw(cell);
for (int i = 0; i < settings.dilate; i++) {
dilate(bp);
}
cell = traceOutline(bp);
if (cell != null) {
finalCell = cell;
}
}
final Rectangle pos = finalCell.getBounds();
// Does not work in IJ 1.46+
// finalCell.setLocation(pos.x + bounds.x + pointBounds.x, pos.y + bounds.y + pointBounds.y)
// Create a new Polygon with the correct coordinates. This is required since IJ 1.46
// since setting the location is not passed through when drawing an overlay
final int[] xCoords = finalCell.getXCoordinates();
final int[] yCoords = finalCell.getYCoordinates();
final int npoints = finalCell.getNCoordinates();
for (int i = 0; i < npoints; i++) {
xCoords[i] += pos.x + bounds.x + pointBounds.x;
yCoords[i] += pos.y + bounds.y + pointBounds.y;
}
finalCell = new PolygonRoi(xCoords, yCoords, npoints, Roi.POLYGON);
cells[n] = finalCell;
}
return cells;
}
use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class CellOutliner_PlugIn method fitPolygonalCell.
/**
* Find an ellipse that optimises the fit to the polygon detected edges.
*
* @param roi the roi
* @param params the params
* @param weightMap the weight map
* @return the ellipse parameters
*/
@Nullable
private double[] fitPolygonalCell(PolygonRoi roi, double[] params, FloatProcessor weightMap) {
// Get an estimate of the starting parameters using the current polygon
final double[] startPoint = estimateStartPoint(roi, weightMap.getWidth(), weightMap.getHeight());
final int maxEval = 2000;
final DifferentiableEllipticalFitFunction func = new DifferentiableEllipticalFitFunction(roi, weightMap);
final double relativeThreshold = 100 * Precision.EPSILON;
final double absoluteThreshold = 100 * Precision.SAFE_MIN;
final ConvergenceChecker<PointVectorValuePair> checker = new SimplePointChecker<>(relativeThreshold, absoluteThreshold);
final double initialStepBoundFactor = 10;
final double costRelativeTolerance = 1e-10;
final double parRelativeTolerance = 1e-10;
final double orthoTolerance = 1e-10;
final double threshold = Precision.SAFE_MIN;
final LevenbergMarquardtOptimizer optimiser = new LevenbergMarquardtOptimizer(initialStepBoundFactor, costRelativeTolerance, parRelativeTolerance, orthoTolerance, threshold);
try {
// @formatter:off
final LeastSquaresProblem problem = new LeastSquaresBuilder().maxEvaluations(Integer.MAX_VALUE).maxIterations(maxEval).start(startPoint).target(func.calculateTarget()).weight(new DiagonalMatrix(func.calculateWeights())).model(func, func::jacobian).checkerPair(checker).build();
// @formatter:on
final Optimum solution = optimiser.optimize(problem);
if (debug) {
IJ.log(String.format("Eval = %d (Iter = %d), RMS = %f", solution.getEvaluations(), solution.getIterations(), solution.getRMS()));
}
return solution.getPoint().toArray();
} catch (final Exception ex) {
IJ.log("Failed to find an elliptical solution, defaulting to polygon: " + ex.getMessage());
}
return null;
}
Aggregations