use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class AbstractImageRegionStore method getThumbnail.
/* (non-Javadoc)
* @see qupath.lib.images.stores.ImageRegionStore#getThumbnail(qupath.lib.images.servers.ImageServer, int, int, boolean)
*/
@Override
@SuppressWarnings("unchecked")
public synchronized T getThumbnail(ImageServer<T> server, int zPosition, int tPosition, boolean addToCache) {
RegionRequest request = getThumbnailRequest(server, zPosition, tPosition);
Object result = requestImageTile(server, request, thumbnailCache, true);
if (!(result instanceof TileWorker<?>))
return (T) result;
logger.debug("Thumbnail request for {}, ({}, {})", server, zPosition, tPosition);
TileWorker<T> worker = (TileWorker<T>) result;
try {
return worker.get();
} catch (InterruptedException e) {
logger.error(e.getLocalizedMessage());
} catch (ExecutionException e) {
logger.error(e.getLocalizedMessage());
}
try {
// Last resort... shouldn't happen
logger.warn("Fallback to requesting thumbnail directly...");
return server.readBufferedImage(request);
} catch (IOException e) {
logger.error("Unable to obtain thumbnail for " + request, e);
return null;
}
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class DensityMaps method findHotspots.
/**
* Find hotspots in a density map.
*
* @param hierarchy hierarchy used to obtain selected objects and add hotspots
* @param densityServer the density map to query
* @param channel channel in which to find hotspots (usually 0)
* @param nHotspots maximum number of hotspots to find per selected annotation
* @param radius hotspot radius, in calibrated units
* @param minCount minimum value required in the 'count' channel (the last channel)
* @param hotspotClass the classification to apply to hotspots
* @param deleteExisting optionally delete existing annotations identified as hotspots
* @param peaksOnly optionally restrict hotspots to only include intensity peaks
* @throws IOException
*/
public static void findHotspots(PathObjectHierarchy hierarchy, ImageServer<BufferedImage> densityServer, int channel, int nHotspots, double radius, double minCount, PathClass hotspotClass, boolean deleteExisting, boolean peaksOnly) throws IOException {
if (nHotspots <= 0) {
logger.warn("Number of hotspots requested is {}!", nHotspots);
return;
}
logger.debug("Finding {} hotspots in {} for channel {}, radius {}", nHotspots, densityServer, channel, radius);
Collection<PathObject> parents = new ArrayList<>(hierarchy.getSelectionModel().getSelectedObjects());
if (parents.isEmpty())
parents = Collections.singleton(hierarchy.getRootObject());
double downsample = densityServer.getDownsampleForResolution(0);
var toDelete = new HashSet<PathObject>();
// Handle deleting existing hotspots
if (deleteExisting) {
toDelete.addAll(hierarchy.getAnnotationObjects().stream().filter(p -> p.getPathClass() == hotspotClass && p.isAnnotation() && p.getName() != null && p.getName().startsWith("Hotspot")).collect(Collectors.toList()));
}
// Convert radius to pixels
double radiusPixels = radius / densityServer.getPixelCalibration().getAveragedPixelSize().doubleValue();
try (@SuppressWarnings("unchecked") var scope = new PointerScope()) {
for (var parent : parents) {
ROI roi = parent.getROI();
// We need a ROI to define the area of interest
if (roi == null) {
if (densityServer.nTimepoints() > 1 || densityServer.nZSlices() > 1) {
logger.warn("Hotspot detection without a parent object not supported for images with multiple z-slices/timepoints.");
logger.warn("I will apply detection to the first plane only. If you need hotspots elsewhere, create an annotation first and use it to define the ROI.");
}
roi = ROIs.createRectangleROI(0, 0, densityServer.getWidth(), densityServer.getHeight(), ImagePlane.getDefaultPlane());
}
// Erode the ROI & see if any hotspot could fit
var roiEroded = RoiTools.buffer(roi, -radiusPixels);
if (roiEroded.isEmpty() || roiEroded.getArea() == 0) {
logger.warn("ROI is too small! Cannot detected hotspots with radius {} in {}", radius, parent);
continue;
}
// Read the image
var plane = roi.getImagePlane();
RegionRequest request = RegionRequest.createInstance(densityServer.getPath(), downsample, 0, 0, densityServer.getWidth(), densityServer.getHeight(), plane.getZ(), plane.getT());
var img = densityServer.readBufferedImage(request);
// Create a mask
var imgMask = BufferedImageTools.createROIMask(img.getWidth(), img.getHeight(), roiEroded, request);
// Switch to OpenCV
var mat = OpenCVTools.imageToMat(img);
var matMask = OpenCVTools.imageToMat(imgMask);
// Find hotspots
var channels = OpenCVTools.splitChannels(mat);
var density = channels.get(channel);
if (minCount > 0) {
var thresholdMask = opencv_core.greaterThan(channels.get(channels.size() - 1), minCount).asMat();
opencv_core.bitwise_and(matMask, thresholdMask, matMask);
thresholdMask.close();
}
// TODO: Limit to peaks
if (peaksOnly) {
var matMaxima = OpenCVTools.findRegionalMaxima(density);
var matPeaks = OpenCVTools.shrinkLabels(matMaxima);
matPeaks.put(opencv_core.greaterThan(matPeaks, 0));
opencv_core.bitwise_and(matMask, matPeaks, matMask);
matPeaks.close();
matMaxima.close();
}
// Sort in descending order
var maxima = new ArrayList<>(OpenCVTools.getMaskedPixels(density, matMask));
Collections.sort(maxima, Comparator.comparingDouble((IndexedPixel p) -> p.getValue()).reversed());
// Try to get as many maxima as we need
// Impose minimum separation
var points = maxima.stream().map(p -> new Point2(p.getX() * downsample, p.getY() * downsample)).collect(Collectors.toList());
var hotspotCentroids = new ArrayList<Point2>();
double distSqThreshold = radiusPixels * radiusPixels * 4;
for (var p : points) {
// Check not too close to an existing hotspot
boolean skip = false;
for (var p2 : hotspotCentroids) {
if (p.distanceSq(p2) < distSqThreshold) {
skip = true;
break;
}
}
if (!skip) {
hotspotCentroids.add(p);
if (hotspotCentroids.size() == nHotspots)
break;
}
}
var hotspots = new ArrayList<PathObject>();
int i = 0;
for (var p : hotspotCentroids) {
i++;
var ellipse = ROIs.createEllipseROI(p.getX() - radiusPixels, p.getY() - radiusPixels, radiusPixels * 2, radiusPixels * 2, roi.getImagePlane());
var hotspot = PathObjects.createAnnotationObject(ellipse, hotspotClass);
hotspot.setName("Hotspot " + i);
hotspots.add(hotspot);
}
if (hotspots.isEmpty())
logger.warn("No hotspots found in {}", parent);
else if (hotspots.size() < nHotspots) {
logger.warn("Only {}/{} hotspots could be found in {}", hotspots.size(), nHotspots, parent);
}
parent.addPathObjects(hotspots);
}
hierarchy.fireHierarchyChangedEvent(DensityMaps.class);
if (!toDelete.isEmpty())
hierarchy.removeObjects(toDelete, true);
}
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class LocalBinaryPatternsPlugin 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");
// int d = params.getIntParameterValue("haralickDistance");
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());
// System.out.println(bounds);
// System.out.println("Size: " + size);
BufferedImage img = server.readBufferedImage(region);
// System.out.println("Image size: " + img.getWidth() + " x " + img.getHeight() + " pixels");
// 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.regions.RegionRequest in project qupath by qupath.
the class TMAExplorer method createAndShowStage.
private void createAndShowStage() {
Project<BufferedImage> project = qupath.getProject();
entries.clear();
if (project != null) {
// Create an output directory for the images
File dirBaseImageOutput = Projects.getBaseDirectory(project);
dirBaseImageOutput = new File(dirBaseImageOutput, "TMA");
dirBaseImageOutput = new File(dirBaseImageOutput, "images");
if (!dirBaseImageOutput.exists())
dirBaseImageOutput.mkdirs();
Map<String, RunningStatistics> statsMap = new HashMap<>();
for (ProjectImageEntry<BufferedImage> imageEntry : project.getImageList()) {
// Look for data file
if (!imageEntry.hasImageData())
continue;
File dirImageOutput = new File(dirBaseImageOutput, imageEntry.getImageName());
if (!dirImageOutput.exists())
dirImageOutput.mkdirs();
// Read data
ImageData<BufferedImage> imageData;
try {
imageData = imageEntry.readImageData();
} catch (IOException e) {
logger.error("Error reading ImageData for " + imageEntry.getImageName(), e);
continue;
}
TMAGrid tmaGrid = imageData.getHierarchy().getTMAGrid();
if (tmaGrid == null) {
logger.warn("No TMA data for {}", imageEntry.getImageName());
continue;
}
// Figure out downsample value
ImageServer<BufferedImage> server = imageData.getServer();
double downsample = Math.round(5 / server.getPixelCalibration().getAveragedPixelSizeMicrons());
// Read the TMA entries
int counter = 0;
for (TMACoreObject core : tmaGrid.getTMACoreList()) {
counter++;
String name = core.getName();
if (name == null)
name = Integer.toString(counter);
File fileOutput = new File(dirImageOutput, name + ".jpg");
if (!fileOutput.exists()) {
try {
RegionRequest request = RegionRequest.createInstance(server.getPath(), downsample, core.getROI());
BufferedImage img = server.readBufferedImage(request);
ImageIO.write(img, "jpg", fileOutput);
} catch (Exception e) {
logger.error("Unable to write {}", fileOutput.getAbsolutePath());
}
}
var entry = TMAEntries.createDefaultTMAEntry(imageEntry.getImageName(), fileOutput.getAbsolutePath(), null, core.getName(), core.isMissing());
MeasurementList ml = core.getMeasurementList();
for (int i = 0; i < ml.size(); i++) {
String measurement = ml.getMeasurementName(i);
double val = ml.getMeasurementValue(i);
entry.putMeasurement(measurement, val);
if (!Double.isNaN(val)) {
RunningStatistics stats = statsMap.get(measurement);
if (stats == null) {
stats = new RunningStatistics();
statsMap.put(measurement, stats);
}
stats.addValue(val);
}
}
entries.add(entry);
}
try {
server.close();
} catch (Exception e) {
logger.warn("Problem closing server", e);
}
}
// Loop through all entries and perform outlier count
double k = 3;
for (TMAEntry entry : entries) {
int outlierCount = 0;
for (Entry<String, RunningStatistics> statsEntry : statsMap.entrySet()) {
RunningStatistics stats = statsEntry.getValue();
double val = entry.getMeasurementAsDouble(statsEntry.getKey());
if (!(val >= stats.getMean() - stats.getStdDev() * k && val <= stats.getMean() + stats.getStdDev() * k))
outlierCount++;
}
entry.putMeasurement("Outlier count", outlierCount);
}
}
Stage stage = new Stage();
stage.initOwner(qupath.getStage());
TMASummaryViewer summaryViewer = new TMASummaryViewer(stage);
summaryViewer.setTMAEntries(entries);
summaryViewer.getStage().show();
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class OpenCVTools method extractZStack.
/**
* Extract a list of Mats, where each Mat corresponds to a z-slice.
*
* @param server
* @param request
* @param zMin first z slice, inclusive
* @param zMax last z slice, exclusive
* @return
* @throws IOException
*/
public static List<Mat> extractZStack(ImageServer<BufferedImage> server, RegionRequest request, int zMin, int zMax) throws IOException {
List<Mat> list = new ArrayList<>();
for (int z = zMin; z < zMax; z++) {
RegionRequest request2 = RegionRequest.createInstance(server.getPath(), request.getDownsample(), request.getX(), request.getY(), request.getWidth(), request.getHeight(), z, request.getT());
BufferedImage img = server.readBufferedImage(request2);
list.add(imageToMat(img));
}
return list;
}
Aggregations