use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class ServerTools method getPaddedRequest.
/**
* Get a raster, padded by the specified amount, to the left, right, above and below.
* <p>
* Note that the padding is defined in terms of the <i>destination</i> pixels.
* <p>
* In other words, a specified padding of 5 should actually result in 20 pixels being added in each dimension
* if the {@code request.getDownsample() == 4}.
*
* @param server
* @param request
* @param padding
* @return
* @throws IOException
*/
public static BufferedImage getPaddedRequest(ImageServer<BufferedImage> server, RegionRequest request, Padding padding) throws IOException {
// If we don't have any padding, just return directly
if (padding.isEmpty())
return server.readBufferedImage(request);
// Get the expected bounds
double downsample = request.getDownsample();
int x = (int) Math.round(request.getX() - padding.getX1() * downsample);
int y = (int) Math.round(request.getY() - padding.getY1() * downsample);
int x2 = (int) Math.round((request.getX() + request.getWidth()) + padding.getX2() * downsample);
int y2 = (int) Math.round((request.getY() + request.getHeight()) + padding.getY2() * downsample);
// If we're out of range, we'll need to work a bit harder
int padLeft = 0, padRight = 0, padUp = 0, padDown = 0;
boolean outOfRange = false;
if (x < 0) {
padLeft = (int) Math.round(-x / downsample);
x = 0;
outOfRange = true;
}
if (y < 0) {
padUp = (int) Math.round(-y / downsample);
y = 0;
outOfRange = true;
}
if (x2 > server.getWidth()) {
padRight = (int) Math.round((x2 - server.getWidth()) / downsample);
x2 = server.getWidth();
outOfRange = true;
}
if (y2 > server.getHeight()) {
padDown = (int) Math.round((y2 - server.getHeight()) / downsample);
y2 = server.getHeight();
outOfRange = true;
}
// If everything is within range, this should be relatively straightforward
RegionRequest request2 = RegionRequest.createInstance(request.getPath(), downsample, x, y, x2 - x, y2 - y, request.getZ(), request.getT());
BufferedImage img = server.readBufferedImage(request2);
if (outOfRange) {
WritableRaster raster = img.getRaster();
WritableRaster rasterPadded = raster.createCompatibleWritableRaster(raster.getWidth() + padLeft + padRight, raster.getHeight() + padUp + padDown);
rasterPadded.setRect(padLeft, padUp, raster);
// Add padding above
if (padUp > 0) {
WritableRaster row = raster.createWritableChild(0, 0, raster.getWidth(), 1, 0, 0, null);
for (int r = 0; r < padUp; r++) rasterPadded.setRect(padLeft, r, row);
}
// Add padding below
if (padDown > 0) {
WritableRaster row = raster.createWritableChild(0, raster.getHeight() - 1, raster.getWidth(), 1, 0, 0, null);
for (int r = padUp + raster.getHeight(); r < rasterPadded.getHeight(); r++) rasterPadded.setRect(padLeft, r, row);
}
// Add padding to the left
if (padLeft > 0) {
WritableRaster col = rasterPadded.createWritableChild(padLeft, 0, 1, rasterPadded.getHeight(), 0, 0, null);
for (int c = 0; c < padLeft; c++) rasterPadded.setRect(c, 0, col);
}
// Add padding to the right
if (padRight > 0) {
WritableRaster col = rasterPadded.createWritableChild(rasterPadded.getWidth() - padRight - 1, 0, 1, rasterPadded.getHeight(), 0, 0, null);
for (int c = padLeft + raster.getWidth(); c < rasterPadded.getWidth(); c++) rasterPadded.setRect(c, 0, col);
}
// TODO: The padding seems to work - but something to be cautious with...
img = new BufferedImage(img.getColorModel(), rasterPadded, img.isAlphaPremultiplied(), null);
}
return img;
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class SparseImageServer method readTile.
@Override
protected BufferedImage readTile(final TileRequest tileRequest) throws IOException {
WritableRaster raster = null;
for (ImageRegion subRegion : manager.getRegions()) {
if (subRegion.getZ() != tileRequest.getZ() + originZ || subRegion.getT() != tileRequest.getT() + originT)
continue;
double downsample = tileRequest.getRegionRequest().getDownsample();
if (subRegion.intersects(tileRequest.getImageX() + originX, tileRequest.getImageY() + originY, tileRequest.getImageWidth(), tileRequest.getImageHeight())) {
// If we overlap, request the overlapping portion
ImageServer<BufferedImage> serverTemp = manager.getServer(subRegion, downsample);
// Get image coordinates for bounding box of valid region
int x1 = Math.max(tileRequest.getImageX() + originX, subRegion.getX());
int y1 = Math.max(tileRequest.getImageY() + originY, subRegion.getY());
int x2 = Math.min(tileRequest.getImageX() + originX + tileRequest.getImageWidth(), subRegion.getX() + subRegion.getWidth());
int y2 = Math.min(tileRequest.getImageY() + originY + tileRequest.getImageHeight(), subRegion.getY() + subRegion.getHeight());
// Determine request coordinates
// TODO: Test whether sparse images with pyramidal regions work, or images stored as single planes at pre-specified downsamples
int xr = x1 - subRegion.getX();
int yr = y1 - subRegion.getY();
int xr2 = x2 - subRegion.getX();
int yr2 = y2 - subRegion.getY();
double requestDownsample = downsample;
// if (requestDownsample > 1 && serverTemp.nResolutions() == 1) {
// requestDownsample = serverTemp.getDownsampleForResolution(0);
// double scale = requestDownsample / downsample;
// xr = (int)Math.round(xr * scale);
// yr = (int)Math.round(yr * scale);
// xr2 = (int)Math.round(xr2 * scale);
// yr2 = (int)Math.round(yr2 * scale);
// System.err.println(downsample + ", " + scale + ": " + serverTemp.getPath());
// }
RegionRequest requestTemp = RegionRequest.createInstance(serverTemp.getPath(), requestDownsample, xr, yr, xr2 - xr, yr2 - yr, tileRequest.getZ() + originZ, tileRequest.getT() + originT);
BufferedImage imgTemp = null;
synchronized (serverTemp) {
imgTemp = serverTemp.readBufferedImage(requestTemp);
}
if (imgTemp == null)
continue;
// If we don't have an output image yet, create a compatible one
if (raster == null) {
raster = imgTemp.getRaster().createCompatibleWritableRaster(tileRequest.getTileWidth(), tileRequest.getTileHeight());
}
int x = (int) Math.round((x1 - tileRequest.getImageX() - originX) / downsample);
int y = (int) Math.round((y1 - tileRequest.getImageY() - originY) / downsample);
int w = Math.min(imgTemp.getWidth(), raster.getWidth() - x);
int h = Math.min(imgTemp.getHeight(), raster.getHeight() - y);
raster.setDataElements(x, y, w, h, imgTemp.getRaster().getDataElements(0, 0, w, h, null));
}
}
// reusing an existing raster where possible to reduce memory requirements.
if (raster == null) {
return getEmptyTile(tileRequest.getTileWidth(), tileRequest.getTileHeight(), true);
}
// System.err.println(String.format("%.2f - %.2f", (double)tileRequest.getImageHeight()/raster.getHeight(), tileRequest.getDownsample()));
return new BufferedImage(colorModel, raster, false, null);
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class CroppedImageServer method readBufferedImage.
@Override
public BufferedImage readBufferedImage(final RegionRequest request) throws IOException {
RegionRequest request2 = RegionRequest.createInstance(request.getPath(), request.getDownsample(), request.getX() + region.getX(), request.getY() + region.getY(), request.getWidth(), request.getHeight(), request.getZ(), request.getT());
BufferedImage img = getWrappedServer().readBufferedImage(request2);
// TODO: Mask as ellipse, if necessary?
return img;
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class ImageJMacroRunner method runMacro.
static void runMacro(final ParameterList params, final ImageData<BufferedImage> imageData, final ImageDisplay imageDisplay, final PathObject pathObject, final String macroText) {
// Don't try if interrupted
if (Thread.currentThread().isInterrupted()) {
logger.warn("Skipping macro for {} - thread interrupted", pathObject);
return;
}
PathImage<ImagePlus> pathImage;
// Extract parameters
double downsampleFactor = params.getDoubleParameterValue("downsampleFactor");
boolean sendROI = params.getBooleanParameterValue("sendROI");
boolean sendOverlay = params.getBooleanParameterValue("sendOverlay");
ROI pathROI = pathObject.getROI();
ImageDisplay imageDisplay2 = params.containsKey("useTransform") && Boolean.TRUE.equals(params.getBooleanParameterValue("useTransform")) ? imageDisplay : null;
ImageServer<BufferedImage> server = imageDisplay2 == null || imageDisplay2.availableChannels().isEmpty() ? imageData.getServer() : ChannelDisplayTransformServer.createColorTransformServer(imageData.getServer(), imageDisplay.availableChannels());
RegionRequest region = RegionRequest.createInstance(imageData.getServer().getPath(), downsampleFactor, pathROI);
// Check the size of the region to extract - abort if it is too large of if ther isn't enough RAM
try {
IJTools.isMemorySufficient(region, imageData);
} catch (Exception e1) {
Dialogs.showErrorMessage("ImageJ macro error", e1.getMessage());
return;
}
try {
if (sendOverlay)
pathImage = IJExtension.extractROIWithOverlay(server, pathObject, imageData.getHierarchy(), region, sendROI, null);
else
pathImage = IJExtension.extractROI(server, pathObject, region, sendROI);
} catch (IOException e) {
logger.error("Unable to extract image region " + region, e);
return;
}
// IJHelpers.getImageJInstance();
// ImageJ ij = IJHelpers.getImageJInstance();
// if (ij != null && WindowManager.getIDList() == null)
// ij.setVisible(false);
// Determine a sensible argument to pass
String argument;
if (pathObject instanceof TMACoreObject || !pathObject.hasROI())
argument = pathObject.getDisplayedName();
else
argument = String.format("Region (%d, %d, %d, %d)", region.getX(), region.getY(), region.getWidth(), region.getHeight());
// Check if we have an image already - if so, we need to be more cautious so we don't accidentally use it...
// boolean hasImage = WindowManager.getCurrentImage() != null;
// Actually run the macro
final ImagePlus imp = pathImage.getImage();
imp.setProperty("QuPath region", argument);
WindowManager.setTempCurrentImage(imp);
// Ensure we've requested an instance, since this also loads any required extra plugins
IJExtension.getImageJInstance();
// TODO: Pay attention to how threading should be done... I think Swing EDT ok?
try {
// SwingUtilities.invokeAndWait(() -> {
boolean cancelled = false;
ImagePlus impResult = null;
try {
IJ.redirectErrorMessages();
Interpreter interpreter = new Interpreter();
impResult = interpreter.runBatchMacro(macroText, imp);
// If we had an error, return
if (interpreter.wasError()) {
Thread.currentThread().interrupt();
return;
}
// Get the resulting image, if available
if (impResult == null)
impResult = WindowManager.getCurrentImage();
} catch (RuntimeException e) {
logger.error(e.getLocalizedMessage());
// DisplayHelpers.showErrorMessage("ImageJ macro error", e.getLocalizedMessage());
Thread.currentThread().interrupt();
cancelled = true;
} finally {
// IJ.runMacro(macroText, argument);
WindowManager.setTempCurrentImage(null);
// IJ.run("Close all");
}
if (cancelled)
return;
// Get the current image when the macro has finished - which may or may not be the same as the original
if (impResult == null)
impResult = imp;
boolean changes = false;
if (params.getBooleanParameterValue("clearObjects") && pathObject.hasChildren()) {
pathObject.clearPathObjects();
changes = true;
}
if (params.getBooleanParameterValue("getROI") && impResult.getRoi() != null) {
Roi roi = impResult.getRoi();
Calibration cal = impResult.getCalibration();
PathObject pathObjectNew = roi == null ? null : IJTools.convertToAnnotation(roi, cal.xOrigin, cal.yOrigin, downsampleFactor, region.getPlane());
if (pathObjectNew != null) {
// If necessary, trim any returned annotation
if (pathROI != null && !(pathROI instanceof RectangleROI) && pathObjectNew.isAnnotation() && RoiTools.isShapeROI(pathROI) && RoiTools.isShapeROI(pathObjectNew.getROI())) {
ROI roiNew = RoiTools.combineROIs(pathROI, pathObjectNew.getROI(), CombineOp.INTERSECT);
((PathAnnotationObject) pathObjectNew).setROI(roiNew);
}
// Only add if we have something
if (pathObjectNew.getROI() instanceof LineROI || !pathObjectNew.getROI().isEmpty()) {
pathObject.addPathObject(pathObjectNew);
// imageData.getHierarchy().addPathObject(IJHelpers.convertToPathObject(imp, imageData.getServer(), imp.getRoi(), downsampleFactor, false), true);
changes = true;
}
}
}
boolean exportAsDetection = ((String) params.getChoiceParameterValue("getOverlayAs")).equals("Detections") ? true : false;
if (params.getBooleanParameterValue("getOverlay") && impResult.getOverlay() != null) {
var overlay = impResult.getOverlay();
List<PathObject> childObjects = QuPath_Send_Overlay_to_QuPath.createObjectsFromROIs(imp, Arrays.asList(overlay.toArray()), downsampleFactor, exportAsDetection, true, region.getPlane());
if (!childObjects.isEmpty()) {
pathObject.addPathObjects(childObjects);
changes = true;
}
// for (Roi roi : impResult.getOverlay().toArray()) {
// pathObject.addPathObject(IJTools.convertToPathObject(imp, imageData.getServer(), roi, downsampleFactor, true));
// changes = true;
// }
}
if (changes) {
Platform.runLater(() -> imageData.getHierarchy().fireHierarchyChangedEvent(null));
}
// });
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
use of qupath.lib.regions.RegionRequest in project qupath by qupath.
the class TestBioFormatsImageServer method testProject.
void testProject(Project<BufferedImage> project) {
// We're not really testing Bio-Formats... and the messages can get in the way
DebugTools.enableIJLogging(false);
DebugTools.setRootLevel("error");
List<ProjectImageEntry<BufferedImage>> entries = project.getImageList();
System.out.println("Testing project with " + entries.size() + " entries: " + Project.getNameFromURI(project.getURI()));
for (ProjectImageEntry<BufferedImage> entry : entries) {
// String serverPath = entry.getServerPath();
// System.out.println("Opening: " + serverPath);
// String pathFile = BioFormatsImageServer.splitFilePathAndSeriesName(serverPath)[0];
// if (!new File(pathFile).exists()) {
// System.err.println("File does not exist for server path " + serverPath + " - will skip");
// }
BioFormatsImageServer server = null;
BufferedImage img = null;
BufferedImage imgThumbnail = null;
ImagePlus imp = null;
int tileSize = 256;
int z = 0;
int t = 0;
try {
// Create the server
server = (BioFormatsImageServer) entry.getServerBuilder().build();
// server = (BioFormatsImageServer)ImageServerProvider.buildServer(serverPath, BufferedImage.class, "--classname", BioFormatsServerBuilder.class.getName());
// Read a thumbnail
imgThumbnail = server.getDefaultThumbnail(server.nZSlices() / 2, 0);
// Read from the center of the image
int w = server.getWidth() < tileSize ? server.getWidth() : tileSize;
int h = server.getHeight() < tileSize ? server.getHeight() : tileSize;
z = (int) (server.nZSlices() / 2);
t = (int) (server.nTimepoints() / 2);
RegionRequest request = RegionRequest.createInstance(server.getPath(), 1, (server.getWidth() - w) / 2, (server.getHeight() - h) / 2, w, h, z, t);
img = server.readBufferedImage(request);
// (Skip if we have multiple series, as the setFlattenedResolutions() status can cause confusion)
if (server.getPreferredDownsamples().length == 1 || !server.containsSubImages()) {
ImporterOptions options = new ImporterOptions();
int series = server.getSeries();
options.setId(server.getFile().getAbsolutePath());
options.setOpenAllSeries(false);
options.setSeriesOn(series, true);
options.setCrop(true);
options.setCropRegion(server.getSeries(), new Region(request.getX(), request.getY(), request.getWidth(), request.getHeight()));
try {
ImagePlus[] imps = BF.openImagePlus(options);
assert imps.length == 1;
imp = imps[0];
} catch (Exception e) {
logger.warn("Unable to open with ImageJ: " + server, e);
}
} else {
logger.warn("Multiple multi-resolution series in file - skipping ImageJ check");
}
} catch (Exception e) {
e.printStackTrace();
}
// Check if we got a server at all
assertNotNull(server);
// Check if we got an image
assertNotNull(img);
// Get the thumbnail
assertNotNull(imgThumbnail);
// Check channel numbers
assertEquals(img.getRaster().getNumBands(), server.nChannels());
assertEquals(imgThumbnail.getRaster().getNumBands(), server.nChannels());
// Check pixel sizes
if (imp != null) {
PixelCalibration cal = server.getPixelCalibration();
if ("micron".equals(imp.getCalibration().getXUnit()))
assertEquals(imp.getCalibration().pixelWidth, cal.getPixelWidthMicrons(), 0.00001);
else
assertTrue(Double.isNaN(cal.getPixelWidthMicrons()));
if ("micron".equals(imp.getCalibration().getXUnit()))
assertEquals(imp.getCalibration().pixelHeight, cal.getPixelHeightMicrons(), 0.00001);
else
assertTrue(Double.isNaN(cal.getPixelHeightMicrons()));
// Check z-slices, if appropriate
if (server.nZSlices() > 1) {
if ("micron".equals(imp.getCalibration().getZUnit()))
assertEquals(imp.getCalibration().pixelDepth, cal.getZSpacingMicrons(), 0.00001);
else
assertTrue(Double.isNaN(cal.getZSpacingMicrons()));
}
// Check dimensions by comparison with ImageJ
assertEquals(img.getWidth(), imp.getWidth());
assertEquals(img.getHeight(), imp.getHeight());
assertEquals(server.nChannels(), imp.getNChannels());
assertEquals(server.nTimepoints(), imp.getNFrames());
assertEquals(server.nZSlices(), imp.getNSlices());
// Check actual pixel values
float[] samples = null;
float[] samplesIJ = null;
for (int c = 0; c < server.nChannels(); c++) {
samples = img.getRaster().getSamples(0, 0, img.getWidth(), img.getHeight(), c, samples);
imp.setPosition(c + 1, z + 1, t + 1);
samplesIJ = (float[]) imp.getProcessor().convertToFloatProcessor().getPixels();
// I am having some trouble comparing the full array with Java 10... trouble appears on IJ side..?
assertEquals(samples[0], samplesIJ[0], (float) 0.00001);
// assertArrayEquals(samples, samplesIJ, (float)0.00001);
}
}
printSummary(server);
}
}
Aggregations