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;
}
use of uk.ac.sussex.gdsc.core.annotation.Nullable in project gdsc by aherbert.
the class GaussianFit_PlugIn method fit.
/**
* Fit the data using a 2D Gaussian.
*
* <p>Returns null if fitting failed.
*
* @param data the data
* @param width the width
* @param height the height
* @return The fitted Gaussian parameters (Background, Amplitude, x0, x1, s)
*/
@Nullable
public double[] fit(float[] data, int width, int height) {
SimpleArrayUtils.hasData2D(width, height, data);
try {
// Use reflection to allow building without the SMLM plugins on the classpath
final Class c = Class.forName(GAUSSIAN_FIT_CLASS, true, this.getClass().getClassLoader());
final Method m = c.getDeclaredMethod("fit", float[].class, int.class, int.class);
return (double[]) m.invoke(c.newInstance(), data, width, height);
} catch (final ExceptionInInitializerError | RuntimeException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException ex) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, () -> "Fitting failed: " + ex.getMessage());
return null;
}
}
Aggregations