use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class DelaunayTools method createGeometryExtractor.
private static Function<PathObject, Collection<Coordinate>> createGeometryExtractor(PixelCalibration cal, boolean preferNucleus, double densifyFactor, double erosion) {
PrecisionModel precision = calibrated(cal) ? null : GeometryTools.getDefaultFactory().getPrecisionModel();
AffineTransformation transform = calibrated(cal) ? AffineTransformation.scaleInstance(cal.getPixelWidth().doubleValue(), cal.getPixelHeight().doubleValue()) : null;
return p -> {
var roi = PathObjectTools.getROI(p, preferNucleus);
if (roi == null || roi.isEmpty())
return Collections.emptyList();
var geom = roi.getGeometry();
if (transform != null)
geom = transform.transform(geom);
// Shared boundaries can be problematic, so try to buffer away from these
double buffer = -Math.abs(erosion);
if (buffer < 0 && geom instanceof Polygonal) {
var geomBefore = geom;
geom = GeometryTools.attemptOperation(geom, g -> g.buffer(buffer));
// Do not permit Geometry to disappear
if (geom.isEmpty()) {
geom = geomBefore;
}
}
// Therefore we take extra care in case empty geometries are being generated accidentally.
if (precision != null) {
var geom2 = GeometryTools.attemptOperation(geom, g -> GeometryPrecisionReducer.reduce(g, precision));
if (!geom2.isEmpty())
geom = geom2;
}
if (densifyFactor > 0) {
var geom2 = GeometryTools.attemptOperation(geom, g -> Densifier.densify(g, densifyFactor));
if (!geom2.isEmpty())
geom = geom2;
}
// if (!(geom instanceof Polygon))
// logger.warn("Unexpected Geometry: {}", geom);
// Making precise is essential! Otherwise there can be small artifacts occurring
var coords = geom.getCoordinates();
var output = new LinkedHashSet<Coordinate>();
var p2 = precision;
Coordinate lastCoordinate = null;
if (p2 == null)
p2 = GeometryTools.getDefaultFactory().getPrecisionModel();
// Add coordinates, unless they are extremely close to an existing coordinate
int n = coords.length;
if (n == 0) {
logger.warn("Empty Geometry found for {}", p);
return Collections.emptyList();
}
double minDistance = densifyFactor * 0.5;
var firstCoordinate = coords[0];
while (n > 2 && firstCoordinate.distance(coords[n - 1]) < minDistance) n--;
for (int i = 0; i < n; i++) {
var c = coords[i];
p2.makePrecise(c);
if (i == 0 || c.distance(lastCoordinate) > minDistance) {
output.add(c);
lastCoordinate = c;
}
}
return output;
};
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class QuPathViewer method paintViewer.
protected void paintViewer(Graphics g, int w, int h) {
ImageServer<BufferedImage> server = getServer();
if (server == null) {
g.setColor(background);
g.fillRect(0, 0, w, h);
updateRepaintTimestamp();
return;
}
// // Get dimensions
// int w = getWidth();
// int h = getHeight();
Rectangle clip = g.getClipBounds();
boolean clipFull;
if (clip == null) {
clip = new Rectangle(0, 0, w, h);
g.setClip(0, 0, w, h);
clipFull = true;
} else
clipFull = clip.x == 0 && clip.y == 0 && clip.width == w && clip.height == h;
// Ensure we have a sufficiently-large buffer
if (imgBuffer == null || imgBuffer.getWidth() != w || imgBuffer.getHeight() != h) {
// Create buffered images & buffers for RGB pixel values
imgBuffer = createBufferedImage(w, h);
imgBuffer.setAccelerationPriority(1f);
logger.trace("New buffered image created: {}", imgBuffer);
// imgVolatile = createVolatileImage(w, h);
imageUpdated = true;
// If the size changed, ensure the AffineTransform is up-to-date
updateAffineTransform();
}
// Get the displayed region
Shape shapeRegion = getDisplayedRegionShape();
// The visible shape must have changed if there wasn't one previously...
// Otherwise check if it has changed & update accordingly
// This will be used to notify listeners soon
boolean shapeChanged = lastVisibleShape == null || !lastVisibleShape.equals(shapeRegion);
long t1 = System.currentTimeMillis();
// Only repaint the image if this is requested, otherwise only overlays need to be repainted
if (imageUpdated || locationUpdated) {
// || imgVolatile.contentsLost()) {
// Set flags that image no longer requiring an update
// By setting them early, they might still be reset during this run... in which case we don't want to thwart the re-run
imageUpdated = false;
locationUpdated = false;
// updateBufferedImage(imgVolatile, shapeRegion, w, h);
updateBufferedImage(imgBuffer, shapeRegion, w, h);
}
// if (imageUpdated || locationUpdated) {
// updateBufferedImage(imgVolatile, shapeRegion, w, h);
// // updateBufferedImage(imgBuffer, shapeRegion, w, h);
// // logger.info("INITIAL Image drawing time: " + (System.currentTimeMillis() - t1));
// imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
// }
// while (imgVolatile.contentsLost()) {
// imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
// }
// Store the last shape visible
lastVisibleShape = shapeRegion;
// Draw the image from the buffer
// The call to super.paintComponent is delayed until here to try to stop occasional flickering on Apple's Java 6
g.setColor(background);
if (clipFull)
paintFinalImage(g, imgBuffer, this);
else
// g2d.drawImage(imgBuffer, 0, 0, getWidth(), getHeight(), this);
g.drawImage(imgBuffer, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, null);
if (logger.isTraceEnabled()) {
long t2 = System.currentTimeMillis();
logger.trace("Final image drawing time: {}", (t2 - t1));
}
// Really useful only for debugging graphics
if (!(g instanceof Graphics2D)) {
imageUpdated = false;
// Notify any listeners of shape changes
if (shapeChanged)
fireVisibleRegionChangedEvent(lastVisibleShape);
return;
}
double downsample = getDownsampleFactor();
float opacity = overlayOptions.getOpacity();
Graphics2D g2d = (Graphics2D) g.create();
// Apply required transform to the graphics object (rotation, scaling, shifting...)
g2d.transform(transform);
Composite previousComposite = g2d.getComposite();
boolean paintCompletely = thumbnailIsFullImage || !doFasterRepaint;
// var regionBounds = AwtTools.getImageRegion(clip, getZPosition(), getTPosition());
if (opacity > 0 || PathPrefs.alwaysPaintSelectedObjectsProperty().get()) {
if (opacity < 1) {
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
g2d.setComposite(composite);
}
Color color = getSuggestedOverlayColor();
// Paint the overlay layers
var imageData = this.imageDataProperty.get();
for (PathOverlay overlay : allOverlayLayers.toArray(PathOverlay[]::new)) {
logger.trace("Painting overlay: {}", overlay);
if (overlay instanceof AbstractOverlay)
((AbstractOverlay) overlay).setPreferredOverlayColor(color);
// overlay.paintOverlay(g2d, regionBounds, downsample, null, paintCompletely);
overlay.paintOverlay(g2d, getServerBounds(), downsample, imageData, paintCompletely);
}
// if (hierarchyOverlay != null) {
// hierarchyOverlay.setPreferredOverlayColor(color);
// hierarchyOverlay.paintOverlay(g2d, getServerBounds(), downsampleFactor, null, paintCompletely);
// }
}
// Paint the selected object
PathObjectHierarchy hierarchy = getHierarchy();
PathObject mainSelectedObject = getSelectedObject();
Rectangle2D boundsRect = null;
boolean useSelectedColor = PathPrefs.useSelectedColorProperty().get();
boolean paintSelectedBounds = PathPrefs.paintSelectedBoundsProperty().get();
for (PathObject selectedObject : hierarchy.getSelectionModel().getSelectedObjects().toArray(new PathObject[0])) {
// TODO: Simplify this...
if (selectedObject != null && selectedObject.hasROI() && selectedObject.getROI().getZ() == getZPosition() && selectedObject.getROI().getT() == getTPosition()) {
if (!selectedObject.isDetection()) {
// Ensure a selected ROI can be seen clearly
if (previousComposite != null)
g2d.setComposite(previousComposite);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
Rectangle boundsDisplayed = shapeRegion.getBounds();
ROI pathROI = selectedObject.getROI();
// if ((PathPrefs.getPaintSelectedBounds() || (selectedObject.isDetection() && !PathPrefs.getUseSelectedColor())) && !(pathROI instanceof RectangleROI)) {
if (pathROI != null && (paintSelectedBounds || (!useSelectedColor)) && !(pathROI instanceof RectangleROI) && !pathROI.isEmpty()) {
Shape boundsShape = null;
if (pathROI.isPoint()) {
var hull = pathROI.getConvexHull();
if (hull != null)
boundsShape = hull.getShape();
}
if (boundsShape == null) {
boundsRect = AwtTools.getBounds2D(pathROI, boundsRect);
boundsShape = boundsRect;
}
// Tried to match to pixel boundaries... but resulted in too much jiggling
// boundsShape.setFrame(
// Math.round(boundsShape.getX()/downsampleFactor)*downsampleFactor-downsampleFactor,
// Math.round(boundsShape.getY()/downsampleFactor)*downsampleFactor-downsampleFactor,
// Math.round(boundsShape.getWidth()/downsampleFactor)*downsampleFactor+2*downsampleFactor,
// Math.round(boundsShape.getHeight()/downsampleFactor)*downsampleFactor+2*downsampleFactor);
// boundsShape.setFrame(boundsShape.getX()-downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth()+2*downsampleFactor, boundsShape.getHeight()+2*downsampleFactor);
PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, getSuggestedOverlayColor(), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsample, 1) * 2), null);
// boundsShape.setFrame(boundsShape.getX()+downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth(), boundsShape.getHeight());
// PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, new Color(1f, 1f, 1f, 0.75f), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsampleFactor, 1)*2), null, downsampleFactor);
}
// in a cached way
if ((selectedObject.isDetection() && PathPrefs.useSelectedColorProperty().get()) || !PathObjectTools.hierarchyContainsObject(hierarchy, selectedObject))
PathHierarchyPaintingHelper.paintObject(selectedObject, false, g2d, boundsDisplayed, overlayOptions, getHierarchy().getSelectionModel(), downsample);
// Paint ROI handles, if required
if (selectedObject == mainSelectedObject && roiEditor.hasROI()) {
Stroke strokeThick = PathHierarchyPaintingHelper.getCachedStroke(PathPrefs.annotationStrokeThicknessProperty().get() * downsample);
Color color = useSelectedColor ? ColorToolsAwt.getCachedColor(PathPrefs.colorSelectedObjectProperty().get()) : null;
if (color == null)
color = ColorToolsAwt.getCachedColor(ColorToolsFX.getDisplayedColorARGB(selectedObject));
g2d.setStroke(strokeThick);
// Draw ROI handles using adaptive size
double maxHandleSize = getMaxROIHandleSize();
double minHandleSize = downsample;
PathHierarchyPaintingHelper.paintHandles(roiEditor, g2d, minHandleSize, maxHandleSize, color, ColorToolsAwt.getTranslucentColor(color));
}
}
}
// Notify any listeners of shape changes
if (shapeChanged)
fireVisibleRegionChangedEvent(lastVisibleShape);
updateRepaintTimestamp();
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class PathHierarchyPaintingHelper method paintPoints.
private static void paintPoints(ROI pathPoints, Graphics2D g2d, double radius, Color colorStroke, Stroke stroke, Color colorFill, double downsample) {
PointsROI pathPointsROI = pathPoints instanceof PointsROI ? (PointsROI) pathPoints : null;
if (pathPointsROI != null && PathPrefs.showPointHullsProperty().get()) {
ROI convexHull = pathPointsROI.getConvexHull();
if (convexHull != null) {
Color colorHull = colorFill != null ? colorFill : colorStroke;
colorHull = ColorToolsAwt.getColorWithOpacity(colorHull, 0.1);
if (colorHull != null)
paintShape(RoiTools.getShape(convexHull), g2d, null, null, colorHull);
// getConvexHull().draw(g, null, colorHull);
}
}
RectangularShape ellipse;
// double radius = pathPointsROI == null ? PointsROI.defaultPointRadiusProperty().get() : pathPointsROI.getPointRadius();
// Ensure that points are drawn with at least a radius of one, after any transforms have been applied
double scale = Math.max(1, downsample);
radius = (Math.max(1 / scale, radius));
// Get clip bounds
Rectangle2D bounds = g2d.getClipBounds();
if (bounds != null) {
bounds.setRect(bounds.getX() - radius, bounds.getY() - radius, bounds.getWidth() + radius * 2, bounds.getHeight() + radius * 2);
}
// Don't fill if we have a small radius, and use a rectangle instead of an ellipse (as this repaints much faster)
Graphics2D g = g2d;
if (radius / downsample < 0.5) {
if (colorStroke == null)
colorStroke = colorFill;
colorFill = null;
ellipse = new Rectangle2D.Double();
// Use opacity to avoid obscuring points completely
int rule = AlphaComposite.SRC_OVER;
float alpha = (float) (radius / downsample);
var composite = g.getComposite();
if (composite instanceof AlphaComposite) {
var temp = (AlphaComposite) composite;
rule = temp.getRule();
alpha = temp.getAlpha() * alpha;
}
// If we are close to completely transparent, do not paint
if (alpha < 0.01f)
return;
composite = AlphaComposite.getInstance(rule, alpha);
g = (Graphics2D) g2d.create();
g.setComposite(composite);
// ellipse = new Ellipse2D.Double();
} else
ellipse = new Ellipse2D.Double();
g.setStroke(stroke);
for (Point2 p : pathPoints.getAllPoints()) {
if (bounds != null && !bounds.contains(p.getX(), p.getY()))
continue;
ellipse.setFrame(p.getX() - radius, p.getY() - radius, radius * 2, radius * 2);
if (colorFill != null) {
g.setColor(colorFill);
g.fill(ellipse);
}
if (colorStroke != null) {
g.setColor(colorStroke);
g.draw(ellipse);
}
}
if (g != g2d)
g.dispose();
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class AbstractPathROITool method mousePressed.
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
if (!e.isPrimaryButtonDown() || e.isConsumed()) {
return;
}
var viewer = getViewer();
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (hierarchy == null)
return;
PathObject currentObject = viewer.getSelectedObject();
ROI currentROI = currentObject == null ? null : currentObject.getROI();
RoiEditor editor = viewer.getROIEditor();
boolean adjustingPolygon = (currentROI instanceof PolygonROI || currentROI instanceof PolylineROI) && editor.getROI() == currentROI && (editor.isTranslating() || editor.hasActiveHandle());
// If we're adjusting a polygon/polyline with an appropriate tool, return at leave it up to the tool to handle the custom things
if (adjustingPolygon) {
if (viewer.getActiveTool() == PathTools.POLYGON || viewer.getActiveTool() == PathTools.POLYLINE)
return;
else {
viewer.getHierarchy().getSelectionModel().clearSelection();
viewer.getHierarchy().fireHierarchyChangedEvent(currentObject);
}
}
// Find out the coordinates in the image domain
Point2D p2 = mouseLocationToImage(e, false, requestPixelSnapping());
double xx = p2.getX();
double yy = p2.getY();
if (xx < 0 || yy < 0 || xx >= viewer.getServerWidth() || yy >= viewer.getServerHeight())
return;
// If we are double-clicking & we don't have a polygon, see if we can access a ROI
if (!PathPrefs.selectionModeProperty().get() && e.getClickCount() > 1) {
// Reset parent... for now
resetConstrainedAreaParent();
tryToSelect(xx, yy, e.getClickCount() - 2, false);
e.consume();
return;
}
// Set the current parent object based on the first click
setConstrainedAreaParent(hierarchy, xx, yy, Collections.emptyList());
// Create a new annotation
PathObject pathObject = createNewAnnotation(e, xx, yy);
if (pathObject == null)
return;
// Start editing the ROI immediately
editor.setROI(pathObject.getROI());
editor.grabHandle(xx, yy, viewer.getMaxROIHandleSize() * 1.5, e.isShiftDown());
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class SubcellularDetection method processObject.
/**
* Initial version of subcellular detection processing.
*
* @param pathObject
* @param params
* @param imageWrapper
* @return
* @throws InterruptedException
* @throws IOException
*/
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageWrapper imageWrapper) throws InterruptedException, IOException {
// Get the base classification for the object as it currently stands
PathClass baseClass = PathClassTools.getNonIntensityAncestorClass(pathObject.getPathClass());
// Variable to hold estimated spot count
double estimatedSpots;
// We assume that after this processing, any previous sub-cellular objects should be removed
pathObject.clearPathObjects();
// Ensure we have no existing subcellular detection measurements - if we do, remove them
String[] existingMeasurements = pathObject.getMeasurementList().getMeasurementNames().stream().filter(n -> n.startsWith("Subcellular:")).toArray(n -> new String[n]);
if (existingMeasurements.length > 0) {
pathObject.getMeasurementList().removeMeasurements(existingMeasurements);
pathObject.getMeasurementList().close();
}
// // If we're part of a TMA core, request the whole core...
// if (pathObject.getParent() instanceof TMACoreObject && pathObject.getParent().hasROI()) {
// regionStore.getImage(server, RegionRequest.createInstance(server.getPath(), 1, pathObject.getParent().getROI()), 25, true);
// }
ROI pathROI = pathObject.getROI();
if (pathROI == null || pathROI.isEmpty())
return false;
// double downsample = 0.5;
double downsample = 1;
// Determine spot size
ImageServer<BufferedImage> server = imageWrapper.getServer();
PixelCalibration cal = server.getPixelCalibration();
double minSpotArea, maxSpotArea, singleSpotArea;
double pixelWidth, pixelHeight;
if (cal.hasPixelSizeMicrons()) {
double spotSizeMicrons = params.getDoubleParameterValue("spotSizeMicrons");
double minSpotSizeMicrons = params.getDoubleParameterValue("minSpotSizeMicrons");
double maxSpotSizeMicrons = params.getDoubleParameterValue("maxSpotSizeMicrons");
pixelWidth = cal.getPixelWidthMicrons() * downsample;
pixelHeight = cal.getPixelHeightMicrons() * downsample;
singleSpotArea = spotSizeMicrons / (pixelWidth * pixelHeight);
minSpotArea = minSpotSizeMicrons / (pixelWidth * pixelHeight);
maxSpotArea = maxSpotSizeMicrons / (pixelWidth * pixelHeight);
} else {
singleSpotArea = params.getDoubleParameterValue("spotSizePixels");
minSpotArea = params.getDoubleParameterValue("minSpotSizePixels");
maxSpotArea = params.getDoubleParameterValue("maxSpotSizePixels");
pixelWidth = downsample;
pixelHeight = downsample;
}
boolean includeClusters = Boolean.TRUE.equals(params.getBooleanParameterValue("includeClusters"));
boolean doSmoothing = Boolean.TRUE.equals(params.getBooleanParameterValue("doSmoothing"));
boolean splitByIntensity = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByIntensity"));
boolean splitByShape = Boolean.TRUE.equals(params.getBooleanParameterValue("splitByShape"));
// Get region to request - give a pixel as border
int xStart = (int) Math.max(0, pathROI.getBoundsX() - 1);
int yStart = (int) Math.max(0, pathROI.getBoundsY() - 1);
int width = (int) Math.min(server.getWidth() - 1, pathROI.getBoundsX() + pathROI.getBoundsWidth() + 1.5) - xStart;
int height = (int) Math.min(server.getHeight() - 1, pathROI.getBoundsY() + pathROI.getBoundsHeight() + 1.5) - yStart;
if (width <= 0 || height <= 0) {
logger.error("Negative ROI size for {}", pathROI);
pathObject.setPathClass(baseClass);
return false;
}
int z = pathROI.getZ();
int t = pathROI.getT();
// Don't associate with channel
int c = -1;
RegionRequest region = RegionRequest.createInstance(server.getPath(), 1.0, xStart, yStart, width, height, z, t);
// Mask to indicate pixels within the cell
byte[] cellMask = null;
for (String channelName : imageWrapper.getChannelNames(true, true)) {
double detectionThreshold = params.getDoubleParameterValue("detection[" + channelName + "]");
if (Double.isNaN(detectionThreshold) || detectionThreshold < 0)
continue;
// // TODO: Consider whether to use channel numbers for non-brightfield images
// if (!imageWrapper.imageData.isBrightfield())
// c++;
SimpleImage img = imageWrapper.getRegion(region, channelName);
// Get an ImageJ-friendly calibration for ROI conversion
Calibration calIJ = new Calibration();
calIJ.xOrigin = -xStart / downsample;
calIJ.yOrigin = -yStart / downsample;
// Create a cell mask
if (cellMask == null) {
BufferedImage imgMask = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = imgMask.createGraphics();
if (downsample != 1)
g2d.scale(1.0 / downsample, 1.0 / downsample);
g2d.translate(-xStart, -yStart);
Shape shape = RoiTools.getShape(pathROI);
g2d.setColor(Color.WHITE);
g2d.fill(shape);
g2d.dispose();
cellMask = (byte[]) ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData(0);
}
// Get a buffer containing the image pixels
int w = img.getWidth();
int h = img.getHeight();
// Identify (& try to separate) spots
// Mask out non-cell areas as we go
FloatProcessor fpDetection = new FloatProcessor(w, h);
if (doSmoothing) {
for (int i = 0; i < w * h; i++) fpDetection.setf(i, img.getValue(i % w, i / w));
fpDetection.smooth();
for (int i = 0; i < w * h; i++) {
if (cellMask[i] == (byte) 0)
fpDetection.setf(i, 0f);
}
} else {
for (int i = 0; i < w * h; i++) {
if (cellMask[i] == (byte) 0)
fpDetection.setf(i, 0f);
else
fpDetection.setf(i, img.getValue(i % w, i / w));
}
}
ByteProcessor bpSpots;
if (splitByIntensity)
bpSpots = new MaximumFinder().findMaxima(fpDetection, detectionThreshold / 10.0, detectionThreshold, MaximumFinder.SEGMENTED, false, false);
else
bpSpots = SimpleThresholding.thresholdAboveEquals(fpDetection, (float) detectionThreshold);
if (splitByShape) {
new EDM().toWatershed(bpSpots);
}
// Loop through spot ROIs & make a decision
bpSpots.setThreshold(1, ImageProcessor.NO_THRESHOLD, ImageProcessor.NO_LUT_UPDATE);
List<PolygonRoi> possibleSpotRois = RoiLabeling.getFilledPolygonROIs(bpSpots, Wand.FOUR_CONNECTED);
List<PathObject> spotObjects = new ArrayList<>();
List<PathObject> clusterObjects = new ArrayList<>();
estimatedSpots = 0;
for (PolygonRoi spotRoi : possibleSpotRois) {
fpDetection.setRoi(spotRoi);
ImageStatistics stats = fpDetection.getStatistics();
// In v0.2
// ImagePlane plane = ImagePlane.getPlaneWithChannel(spotRoi.getCPosition(), spotRoi.getZPosition(), spotRoi.getTPosition());
// In v0.3
ImagePlane plane = ImagePlane.getPlaneWithChannel(c, z, t);
PathObject spotOrCluster = null;
if (stats.pixelCount >= minSpotArea && stats.pixelCount <= maxSpotArea) {
ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
// cluster = new SubcellularObject(roi, 1);
spotOrCluster = createSubcellularObject(roi, 1);
estimatedSpots += 1;
} else if (includeClusters && stats.pixelCount >= minSpotArea) {
// Add a cluster
ROI roi = IJTools.convertToROI(spotRoi, calIJ, downsample, plane);
double nSpots = stats.pixelCount / singleSpotArea;
estimatedSpots += nSpots;
// cluster = new SubcellularObject(roi, nSpots);
spotOrCluster = createSubcellularObject(roi, nSpots);
}
if (spotOrCluster != null) {
boolean isCluster = spotOrCluster.getMeasurementList().getMeasurementValue("Num spots") > 1;
int rgb = imageWrapper.getChannelColor(channelName);
rgb = isCluster ? ColorTools.makeScaledRGB(rgb, 0.5) : ColorTools.makeScaledRGB(rgb, 1.5);
PathClass pathClass = PathClassFactory.getDerivedPathClass(spotOrCluster.getPathClass(), channelName + " object", rgb);
spotOrCluster.setPathClass(pathClass);
spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Area", stats.pixelCount * pixelWidth * pixelHeight);
spotOrCluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Mean channel intensity", stats.mean);
// cluster.getMeasurementList().putMeasurement("Subcellular cluster: " + channelName + ": Max channel intensity", stats.max);
spotOrCluster.getMeasurementList().close();
if (isCluster)
clusterObjects.add(spotOrCluster);
else
spotObjects.add(spotOrCluster);
}
}
// Add measurements
MeasurementList measurementList = pathObject.getMeasurementList();
measurementList.putMeasurement("Subcellular: " + channelName + ": Num spots estimated", estimatedSpots);
measurementList.putMeasurement("Subcellular: " + channelName + ": Num single spots", spotObjects.size());
measurementList.putMeasurement("Subcellular: " + channelName + ": Num clusters", clusterObjects.size());
// Add spots
pathObject.addPathObjects(spotObjects);
pathObject.addPathObjects(clusterObjects);
}
return true;
}
Aggregations