use of qupath.lib.objects.PathCellObject in project qupath by qupath.
the class IntensityFeaturesPlugin method processObject.
static boolean processObject(final PathObject pathObject, final ParameterList params, final ImageData<BufferedImage> imageData) throws IOException {
// Determine amount to downsample
var server = imageData.getServer();
var stains = imageData.getColorDeconvolutionStains();
PixelCalibration cal = server.getPixelCalibration();
double downsample = calculateDownsample(cal, params);
if (downsample <= 0) {
logger.warn("Effective downsample must be > 0 (requested value {})", downsample);
}
// Determine region shape
RegionType regionType = (RegionType) params.getChoiceParameterValue("region");
// Try to get ROI
boolean useROI = regionType == RegionType.ROI || regionType == RegionType.NUCLEUS;
ROI roi = null;
if (regionType == RegionType.NUCLEUS) {
if (pathObject instanceof PathCellObject)
roi = ((PathCellObject) pathObject).getNucleusROI();
} else
roi = pathObject.getROI();
// pathROI = ((PathCellObject)pathObject).getNucleusROI();
if (roi == null)
return false;
// Create a map - this is useful for occasions when tiling is needed
Map<FeatureColorTransform, List<FeatureComputer>> map = new LinkedHashMap<>();
if (server.isRGB()) {
for (FeatureColorTransform transform : FeatureColorTransformEnum.values()) {
List<FeatureComputer> list = new ArrayList<>();
map.put(transform, list);
for (FeatureComputerBuilder builder : builders) {
list.add(builder.build());
}
}
} else {
for (FeatureColorTransform transform : getBasicChannelTransforms(server.nChannels())) {
List<FeatureComputer> list = new ArrayList<>();
map.put(transform, list);
for (FeatureComputerBuilder builder : builders) {
list.add(builder.build());
}
}
}
String prefix = getDiameterString(server, params);
// Create tiled ROIs, if required
ImmutableDimension sizePreferred = ImmutableDimension.getInstance((int) (2000 * downsample), (int) (2000 * downsample));
// ImmutableDimension sizePreferred = new ImmutableDimension((int)(200*downsample), (int)(200*downsample));
Collection<? extends ROI> rois = RoiTools.computeTiledROIs(roi, sizePreferred, sizePreferred, false, 0);
if (rois.size() > 1)
logger.info("Splitting {} into {} tiles for intensity measurements", roi, rois.size());
for (ROI pathROI : rois) {
if (Thread.currentThread().isInterrupted()) {
logger.warn("Measurement skipped - thread interrupted!");
return false;
}
// Get bounds
RegionRequest region;
if (useROI) {
region = RegionRequest.createInstance(server.getPath(), downsample, pathROI);
} else {
ImmutableDimension size = getPreferredTileSizePixels(server, params);
// 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) (Math.round(pathROI.getCentroidX() / downsample) * downsample) - size.width / 2;
int yStart = (int) (Math.round(pathROI.getCentroidY() / downsample) * 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 < 1 || region.getHeight() / downsample < 1) {
// logger.trace("Requested region is too small! {}", region);
// 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 intensity features for {}", pathObject);
return false;
}
// Create mask ROI if necessary
// If we just have 1 pixel, we want to use it so that the mean/min/max measurements are valid (even if nothing else is)
byte[] maskBytes = null;
if (useROI && img.getWidth() * img.getHeight() > 1) {
BufferedImage imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), pathROI, region);
maskBytes = ((DataBufferByte) imgMask.getRaster().getDataBuffer()).getData();
}
boolean isRGB = server.isRGB();
List<FeatureColorTransform> transforms;
if (isRGB)
transforms = Arrays.asList(FeatureColorTransformEnum.values());
else
transforms = getBasicChannelTransforms(server.nChannels());
int w = img.getWidth();
int h = img.getHeight();
int[] rgbBuffer = isRGB ? img.getRGB(0, 0, w, h, null, 0, w) : null;
float[] pixels = null;
for (FeatureColorTransform transform : transforms) {
// Check if the color transform is requested
if (params.containsKey(transform.getKey()) && Boolean.TRUE.equals(params.getBooleanParameterValue(transform.getKey()))) {
// Transform the pixels
pixels = transform.getTransformedPixels(img, rgbBuffer, stains, pixels);
// Create the simple image
SimpleModifiableImage pixelImage = SimpleImages.createFloatImage(pixels, w, h);
// Apply any arbitrary mask
if (maskBytes != null) {
for (int i = 0; i < pixels.length; i++) {
if (maskBytes[i] == (byte) 0)
pixelImage.setValue(i % w, i / w, Float.NaN);
}
} else if (regionType == RegionType.CIRCLE) {
// Apply circular tile mask
double cx = (w - 1) / 2;
double cy = (h - 1) / 2;
double radius = Math.max(w, h) * .5;
double distThreshold = radius * radius;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if ((cx - x) * (cx - x) + (cy - y) * (cy - y) > distThreshold)
pixelImage.setValue(x, y, Float.NaN);
}
}
}
// Do the computations
for (FeatureComputer computer : map.get(transform)) {
computer.updateFeatures(pixelImage, transform, params);
}
}
}
}
// Add measurements to the parent object
for (Entry<FeatureColorTransform, List<FeatureComputer>> entry : map.entrySet()) {
String name = prefix + ": " + entry.getKey().getName(imageData, false) + ":";
for (FeatureComputer computer : entry.getValue()) computer.addMeasurements(pathObject, name, params);
}
pathObject.getMeasurementList().close();
// Lock any measurements that require it
if (pathObject instanceof PathAnnotationObject)
((PathAnnotationObject) pathObject).setLocked(true);
else if (pathObject instanceof TMACoreObject)
((TMACoreObject) pathObject).setLocked(true);
return true;
}
use of qupath.lib.objects.PathCellObject 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.objects.PathCellObject in project qupath by qupath.
the class ShapeFeaturesPlugin method addRunnableTasks.
@Override
protected void addRunnableTasks(final ImageData<T> imageData, final PathObject parentObject, List<Runnable> tasks) {
PixelCalibration cal = imageData == null ? null : imageData.getServer().getPixelCalibration();
boolean useMicrons = params.getBooleanParameterValue("useMicrons") && cal != null && cal.hasPixelSizeMicrons();
double pixelWidth = useMicrons ? cal.getPixelWidthMicrons() : 1;
double pixelHeight = useMicrons ? cal.getPixelHeightMicrons() : 1;
String unit = useMicrons ? GeneralTools.micrometerSymbol() : "px";
boolean doArea = params.getBooleanParameterValue("area");
boolean doPerimeter = params.getBooleanParameterValue("perimeter");
boolean doCircularity = params.getBooleanParameterValue("circularity");
ROI roi = (parentObject.hasROI() && parentObject.getROI().isArea()) ? parentObject.getROI() : null;
if (roi != null) {
tasks.add(new Runnable() {
@Override
public void run() {
try {
MeasurementList measurementList = parentObject.getMeasurementList();
ROI roi;
if (parentObject instanceof PathCellObject) {
roi = ((PathCellObject) parentObject).getNucleusROI();
if (roi != null && roi.isArea())
addMeasurements(measurementList, roi, "Nucleus Shape: ", pixelWidth, pixelHeight, unit, doArea, doPerimeter, doCircularity);
roi = parentObject.getROI();
if (roi != null && roi.isArea())
addMeasurements(measurementList, roi, "Cell Shape: ", pixelWidth, pixelHeight, unit, doArea, doPerimeter, doCircularity);
} else {
roi = parentObject.getROI();
if (roi != null && roi.isArea())
addMeasurements(measurementList, roi, "ROI Shape: ", pixelWidth, pixelHeight, unit, doArea, doPerimeter, doCircularity);
}
measurementList.close();
} catch (Exception e) {
e.printStackTrace();
throw (e);
}
}
});
}
}
Aggregations