use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class Commands method createFullImageAnnotation.
/**
* Create a full image annotation for the image in the specified viewer.
* The z and t positions of the viewer will be used.
* @param viewer the viewer containing the image to be processed
*/
public static void createFullImageAnnotation(QuPathViewer viewer) {
if (viewer == null)
return;
ImageData<?> imageData = viewer.getImageData();
if (imageData == null)
return;
PathObjectHierarchy hierarchy = imageData.getHierarchy();
// Check if we already have a comparable annotation
int z = viewer.getZPosition();
int t = viewer.getTPosition();
ImageRegion bounds = viewer.getServerBounds();
ROI roi = ROIs.createRectangleROI(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), ImagePlane.getPlane(z, t));
for (PathObject pathObject : hierarchy.getAnnotationObjects()) {
ROI r2 = pathObject.getROI();
if (r2 instanceof RectangleROI && roi.getBoundsX() == r2.getBoundsX() && roi.getBoundsY() == r2.getBoundsY() && roi.getBoundsWidth() == r2.getBoundsWidth() && roi.getBoundsHeight() == r2.getBoundsHeight() && roi.getImagePlane().equals(r2.getImagePlane())) {
logger.info("Full image annotation already exists! {}", pathObject);
viewer.setSelectedObject(pathObject);
return;
}
}
PathObject pathObject = PathObjects.createAnnotationObject(roi);
hierarchy.addPathObject(pathObject);
viewer.setSelectedObject(pathObject);
// Log in the history
if (z == 0 && t == 0)
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Create full image annotation", "createSelectAllObject(true);"));
else
imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Create full image annotation", String.format("createSelectAllObject(true, %d, %d);", z, t)));
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class Commands method combineAnnotations.
/**
* Combine all the annotations that overlap with a selected object.
* <p>
* The selected object should itself be an annotation.
*
* @param hierarchy
* @param pathObjects
* @param op
* @return true if any changes were made, false otherwise
*/
static boolean combineAnnotations(PathObjectHierarchy hierarchy, List<PathObject> pathObjects, RoiTools.CombineOp op) {
if (hierarchy == null || hierarchy.isEmpty() || pathObjects.isEmpty()) {
logger.warn("Combine annotations: Cannot combine - no annotations found");
return false;
}
pathObjects = new ArrayList<>(pathObjects);
PathObject pathObject = pathObjects.get(0);
if (!pathObject.isAnnotation()) {
// || !RoiTools.isShapeROI(pathObject.getROI())) {
logger.warn("Combine annotations: No annotation with ROI selected");
return false;
}
var plane = pathObject.getROI().getImagePlane();
// pathObjects.removeIf(p -> !RoiTools.isShapeROI(p.getROI())); // Remove any null or point ROIs, TODO: Consider supporting points
// Remove any null or point ROIs, TODO: Consider supporting points
pathObjects.removeIf(p -> !p.hasROI() || !p.getROI().getImagePlane().equals(plane));
if (pathObjects.isEmpty()) {
logger.warn("Cannot combine annotations - only one suitable annotation found");
return false;
}
var allROIs = pathObjects.stream().map(p -> p.getROI()).collect(Collectors.toCollection(() -> new ArrayList<>()));
ROI newROI;
switch(op) {
case ADD:
newROI = RoiTools.union(allROIs);
break;
case INTERSECT:
newROI = RoiTools.intersection(allROIs);
break;
case SUBTRACT:
var first = allROIs.remove(0);
newROI = RoiTools.combineROIs(first, RoiTools.union(allROIs), op);
break;
default:
throw new IllegalArgumentException("Unknown combine op " + op);
}
if (newROI == null) {
logger.debug("No changes were made");
return false;
}
PathObject newObject = null;
if (!newROI.isEmpty()) {
newObject = PathObjects.createAnnotationObject(newROI, pathObject.getPathClass());
newObject.setName(pathObject.getName());
newObject.setColorRGB(pathObject.getColorRGB());
}
// Remove previous objects
hierarchy.removeObjects(pathObjects, true);
if (newObject != null)
hierarchy.addPathObject(newObject);
return true;
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class HaralickFeaturesPlugin method processObject.
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageServer<BufferedImage> server, final ColorDeconvolutionStains stains) throws IOException {
String stainsName = (String) params.getChoiceParameterValue("stainChoice");
double mag = params.getDoubleParameterValue("magnification");
int d = params.getIntParameterValue("haralickDistance");
int nBins = params.getIntParameterValue("haralickBins");
boolean includeStats = params.getBooleanParameterValue("includeStats");
boolean doCircular = params.getBooleanParameterValue("doCircular");
double downsample;
boolean hasMagnification = !Double.isNaN(server.getMetadata().getMagnification());
PixelCalibration cal = server.getPixelCalibration();
if (hasMagnification)
downsample = server.getMetadata().getMagnification() / mag;
else if (cal.hasPixelSizeMicrons()) {
downsample = params.getDoubleParameterValue("pixelSizeMicrons") / cal.getAveragedPixelSizeMicrons();
} else
downsample = params.getDoubleParameterValue("downsample");
// double downsample = server.getMagnification() / mag;
// Try to get ROI
ROI pathROI = null;
if (pathObject instanceof PathCellObject && Boolean.TRUE.equals(params.getBooleanParameterValue("useNucleusROIs")))
pathROI = ((PathCellObject) pathObject).getNucleusROI();
else
pathROI = pathObject.getROI();
if (pathROI == null)
return false;
// Get bounds
ImmutableDimension size = getPreferredTileSizePixels(server, params);
RegionRequest region;
boolean createMaskROI = false;
if (size.getWidth() <= 0 || size.getHeight() <= 0) {
region = RegionRequest.createInstance(server.getPath(), downsample, pathObject.getROI());
createMaskROI = true;
doCircular = false;
} else if (size.getWidth() / downsample < 1 || size.getHeight() / downsample < 1)
// Positive size, but insufficient to make measurements
return false;
else {
// RegionRequest region = RegionRequest.createInstance(server.getPath(), downsample, (int)(pathROI.getCentroidX() + .5) - size.width/2, (int)(pathROI.getCentroidY() + .5) - size.height/2, size.width, size.height, pathROI.getT(), pathROI.getZ());
// Try to align with pixel boundaries according to the downsample being used - otherwise, interpolation can cause some strange, pattern artefacts
int xStart = (int) ((int) (pathROI.getCentroidX() / downsample + .5) * downsample) - size.width / 2;
int yStart = (int) ((int) (pathROI.getCentroidY() / downsample + .5) * downsample) - size.height / 2;
int width = Math.min(server.getWidth(), xStart + size.width) - xStart;
int height = Math.min(server.getHeight(), yStart + size.height) - yStart;
region = RegionRequest.createInstance(server.getPath(), downsample, xStart, yStart, width, height, pathROI.getT(), pathROI.getZ());
}
// Check image large enough to do *anything* of value
if (region.getWidth() / downsample < 3 || region.getHeight() / downsample < 3)
return false;
// System.out.println(bounds);
// System.out.println("Size: " + size);
BufferedImage img = server.readBufferedImage(region);
if (img == null) {
logger.error("Could not read image - unable to compute Haralick features for {}", pathObject);
return false;
}
// Create mask ROI if necessary
byte[] maskBytes = null;
if (createMaskROI) {
ROI roi = pathObject.getROI();
// if (pathObject instanceof PathCellObject && ((PathCellObject)pathObject).getNucleusROI() != null)
// roi = ((PathCellObject)pathObject).getNucleusROI();
BufferedImage imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), roi, region);
maskBytes = ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData();
}
double minValue = Double.NaN;
double maxValue = Double.NaN;
// Get a buffer containing the image pixels
int w = img.getWidth();
int h = img.getHeight();
int[] buf = img.getRGB(0, 0, w, h, null, 0, w);
// Create a color transformer to get the images we need
float[] pixels = new float[buf.length];
SimpleModifiableImage pxImg = SimpleImages.createFloatImage(pixels, w, h);
MeasurementList measurementList = pathObject.getMeasurementList();
String postfix = maskBytes == null ? " (" + getDiameterString(server, params) + ")" : "";
if (stainsName.equals("H-DAB")) {
minValue = 0;
maxValue = 2.0;
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "DAB" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("H&E")) {
minValue = 0;
maxValue = 2;
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("H-DAB (8-bit)")) {
minValue = 0;
maxValue = 255;
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB_8_bit, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "DAB 8-bit" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB_8_bit, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("H&E (8-bit)")) {
minValue = 0;
maxValue = 255;
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E_8_bit, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E_8_bit, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("Optical density")) {
minValue = 0;
maxValue = 2.5;
processTransformedImage(pxImg, buf, pixels, measurementList, "OD sum" + postfix, ColorTransformer.ColorTransformMethod.Optical_density_sum, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("RGB")) {
minValue = 0;
maxValue = 255;
processTransformedImage(pxImg, buf, pixels, measurementList, "Red" + postfix, ColorTransformer.ColorTransformMethod.Red, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Green" + postfix, ColorTransformer.ColorTransformMethod.Green, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Blue" + postfix, ColorTransformer.ColorTransformMethod.Blue, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("RGB OD")) {
minValue = 0;
// Actual possible max is around 2.4 for 8-bit input... but this gives a lot of bins for (almost) saturated pixels
maxValue = 1.5;
processTransformedImage(pxImg, buf, pixels, measurementList, "Red OD" + postfix, ColorTransformer.ColorTransformMethod.Red_OD, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Green OD" + postfix, ColorTransformer.ColorTransformMethod.Green_OD, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Blue OD" + postfix, ColorTransformer.ColorTransformMethod.Blue_OD, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("Grayscale")) {
minValue = 0;
maxValue = 255;
processTransformedImage(pxImg, buf, pixels, measurementList, "Grayscale" + postfix, ColorTransformer.ColorTransformMethod.RGB_mean, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
} else if (stainsName.equals("HSB")) {
minValue = 0;
maxValue = 1;
float[] hsb = null;
double sinX = 0;
double cosX = 0;
float[] pixelsBrightness = new float[pixels.length];
float[] pixelsSaturation = new float[pixels.length];
for (int i = 0; i < buf.length; i++) {
if (maskBytes != null && maskBytes[i] == (byte) 0)
continue;
int val = buf[i];
hsb = Color.RGBtoHSB(ColorTools.red(val), ColorTools.green(val), ColorTools.blue(val), hsb);
pixelsSaturation[i] = hsb[1];
pixelsBrightness[i] = hsb[2];
double alpha = hsb[0] * 2 * Math.PI;
sinX += Math.sin(alpha);
cosX += Math.cos(alpha);
}
measurementList.putMeasurement("Mean hue", Math.atan2(sinX, cosX) / (2 * Math.PI) + 0.5);
// measurementList.putMeasurement("Mean saturation", hsb[1]);
// measurementList.putMeasurement("Mean brightness", hsb[2]);
processTransformedImage(SimpleImages.createFloatImage(pixelsSaturation, w, h), buf, pixelsSaturation, measurementList, "Saturation" + postfix, null, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
processTransformedImage(SimpleImages.createFloatImage(pixelsBrightness, w, h), buf, pixelsBrightness, measurementList, "Brightness" + postfix, null, minValue, maxValue, d, nBins, stains, maskBytes, includeStats, doCircular);
}
measurementList.close();
return true;
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class CoherenceFeaturePlugin method processObject.
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageServer<BufferedImage> server, final ColorDeconvolutionStains stains) throws InterruptedException, IOException {
String stainsName = (String) params.getChoiceParameterValue("stainChoice");
double mag = params.getDoubleParameterValue("magnification");
boolean includeStats = params.getBooleanParameterValue("includeStats");
boolean doCircular = params.getBooleanParameterValue("doCircular");
double downsample = server.getMetadata().getMagnification() / mag;
ROI pathROI = pathObject.getROI();
if (pathROI == null)
return false;
// Get bounds
ImmutableDimension size = getPreferredTileSizePixels(server, params);
if (size.getWidth() / downsample < 1 || size.getHeight() / downsample < 1)
return false;
// RegionRequest region = RegionRequest.createInstance(server.getPath(), downsample, (int)(pathROI.getCentroidX() + .5) - size.width/2, (int)(pathROI.getCentroidY() + .5) - size.height/2, size.width, size.height, pathROI.getT(), pathROI.getZ());
// Try to align with pixel boundaries according to the downsample being used - otherwise, interpolation can cause some strange, pattern artefacts
int xStart = (int) ((int) (pathROI.getCentroidX() / downsample + .5) * downsample) - size.width / 2;
int yStart = (int) ((int) (pathROI.getCentroidY() / downsample + .5) * downsample) - size.height / 2;
int width = Math.min(server.getWidth(), xStart + size.width) - xStart;
int height = Math.min(server.getHeight(), yStart + size.height) - yStart;
RegionRequest region = RegionRequest.createInstance(server.getPath(), downsample, xStart, yStart, width, height, pathROI.getT(), pathROI.getZ());
// System.out.println(bounds);
// System.out.println("Size: " + size);
BufferedImage img = server.readBufferedImage(region);
// Get a buffer containing the image pixels
int w = img.getWidth();
int h = img.getHeight();
int[] buf = img.getRGB(0, 0, w, h, null, 0, w);
// Create a color transformer to get the images we need
float[] pixels = new float[buf.length];
SimpleModifiableImage pxImg = SimpleImages.createFloatImage(pixels, w, h);
MeasurementList measurementList = pathObject.getMeasurementList();
String postfix = " (" + getDiameterString(server, params) + ")";
if (stainsName.equals("H-DAB")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "DAB" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB, stains, includeStats, doCircular);
} else if (stainsName.equals("H&E")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E, stains, includeStats, doCircular);
} else if (stainsName.equals("H-DAB (8-bit)")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_DAB_8_bit, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "DAB 8-bit" + postfix, ColorTransformer.ColorTransformMethod.DAB_H_DAB_8_bit, stains, includeStats, doCircular);
} else if (stainsName.equals("H&E (8-bit)")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Hematoxylin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Hematoxylin_H_E_8_bit, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Eosin 8-bit" + postfix, ColorTransformer.ColorTransformMethod.Eosin_H_E_8_bit, stains, includeStats, doCircular);
} else if (stainsName.equals("Optical density")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "OD sum" + postfix, ColorTransformer.ColorTransformMethod.Optical_density_sum, stains, includeStats, doCircular);
} else if (stainsName.equals("RGB")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Red" + postfix, ColorTransformer.ColorTransformMethod.Red, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Green" + postfix, ColorTransformer.ColorTransformMethod.Green, stains, includeStats, doCircular);
processTransformedImage(pxImg, buf, pixels, measurementList, "Blue" + postfix, ColorTransformer.ColorTransformMethod.Blue, stains, includeStats, doCircular);
} else if (stainsName.equals("Grayscale")) {
processTransformedImage(pxImg, buf, pixels, measurementList, "Grayscale" + postfix, ColorTransformer.ColorTransformMethod.RGB_mean, stains, includeStats, doCircular);
}
measurementList.close();
return true;
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class IJTools method convertToPathObject.
/**
* Create a {@link PathObject} for a specific ImageJ Roi.
*
* @param roi the ImageJ ROI
* @param xOrigin the x-origin to translate the Roi; should be {@link Calibration#xOrigin} if available, or 0 otherwise
* @param yOrigin the y-origin to translate the Roi; should be {@link Calibration#yOrigin} if available, or 0 otherwise
* @param downsampleFactor the downsample factor used for rescaling (or 1.0 for no rescaling)
* @param creator a function
* @param plane the specific plane to use for the QuPath ROI; if null, the ImageJ Roi position properties will be used instead, where possible
* @return a {@link PathObject} or null if no object could be created (e.g. the ImageJ roi is null or incompatible)
*
* @see #convertToPathObject(Roi, double, Function, ImagePlus)
* @since v0.4.0
*/
public static PathObject convertToPathObject(Roi roi, double xOrigin, double yOrigin, double downsampleFactor, Function<ROI, PathObject> creator, ImagePlane plane) {
ROI pathROI = IJTools.convertToROI(roi, xOrigin, yOrigin, downsampleFactor, plane);
if (pathROI == null)
return null;
PathObject pathObject = creator.apply(pathROI);
calibrateObject(pathObject, roi);
return pathObject;
}
Aggregations