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.
* This method has been deprecated, since its signature was misleading (the server was not used).
*
* @param imp
* @param server
* @param roi
* @param downsampleFactor
* @param creator
* @param plane
* @return
* @deprecated use instead {@link #convertToPathObject(Roi, double, double, double, Function, ImagePlane)}
*/
@Deprecated
public static PathObject convertToPathObject(ImagePlus imp, ImageServer<?> server, Roi roi, double downsampleFactor, Function<ROI, PathObject> creator, ImagePlane plane) {
Calibration cal = imp == null ? null : imp.getCalibration();
if (plane == null)
plane = getImagePlane(roi, imp);
ROI pathROI = IJTools.convertToROI(roi, cal, downsampleFactor, plane);
if (pathROI == null)
return null;
PathObject pathObject = creator.apply(pathROI);
calibrateObject(pathObject, roi);
return pathObject;
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class TestRoiTools method testTilingPerformance.
/**
* Test tiling for a complex (mult)ipolygon.
*/
@Test
public void testTilingPerformance() {
ROI roiMain = null;
try (var reader = new InputStreamReader(this.getClass().getResourceAsStream("/data/polygon.geojson"))) {
roiMain = GsonTools.getInstance().fromJson(reader, ROI.class);
} catch (IOException e1) {
logger.warn("Unable to read polygon! Will try to generate one instead.");
var geometry = createRandomPolygon(100, GeometryTools.getDefaultFactory(), 10000, 10, 10000, 10000);
roiMain = GeometryTools.geometryToROI(geometry, ImagePlane.getDefaultPlane());
}
// Repeat for filled & unfilled ROIs - and use filled twice for some 'warm-up' time useful if looking at performance
var roiFilled = RoiTools.fillHoles(roiMain);
var rois = Arrays.asList(roiFilled, roiFilled, roiMain);
for (var roi : rois) {
int overlap = 10;
var dim = ImmutableDimension.getInstance(512, 400);
var dimMax = ImmutableDimension.getInstance(1000, 1024);
long startTime = System.currentTimeMillis();
var tiles = RoiTools.computeTiledROIs(roi, dim, dimMax, false, overlap);
long middleTime = System.currentTimeMillis();
var tilesLegacy = RoiTools.computeTiledROIsLegacy(roi, dim, dimMax, false, overlap);
long endTime = System.currentTimeMillis();
long legacyTime = endTime - middleTime;
long newTime = middleTime - startTime;
// Note that this requires some warning up (i.e. running once isn't very representative)
logger.trace("Legacy tiling time: {}, Current tiling time: {}", legacyTime, newTime);
// Should have the same number of tile ROIs
assertEquals(tiles.size(), tilesLegacy.size());
// Should be on the same plane
assertTrue(tiles.stream().allMatch(r -> r.getImagePlane().equals(ImagePlane.getDefaultPlane())));
// Get normalized geometries
var tilesGeometry = tiles.stream().map(r -> r.getGeometry()).collect(Collectors.toList());
tilesGeometry.stream().forEach(g -> g.normalize());
var tilesLegacyGeometry = tiles.stream().map(r -> r.getGeometry()).collect(Collectors.toList());
tilesLegacyGeometry.stream().forEach(g -> g.normalize());
assertEquals(tilesGeometry, tilesLegacyGeometry);
}
}
use of qupath.lib.roi.interfaces.ROI 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.roi.interfaces.ROI in project qupath by qupath.
the class TestROIs method testAreas.
/**
* Compare areas as returned from ROIs and after converting to JTS Geometry objects.
*/
@Test
public void testAreas() {
double delta = 0.01;
// Because of Geometry precision reduction, sometimes a larger delta is needed (still < 1 pixel)
double deltaLarge = 0.5;
// Sample pixel sizes
double pixelWidth = 0.5;
double pixelHeight = 0.75;
ROI rectangle = ROIs.createRectangleROI(0, 0, 1000, 1000, ImagePlane.getDefaultPlane());
double targetAreaRectangle = 1000.0 * 1000.0;
assertEquals(rectangle.getArea(), targetAreaRectangle, delta);
checkROIMeasurements(rectangle, 1, 1, delta);
checkROIMeasurements(rectangle, pixelWidth, pixelHeight, delta);
assertTrue(rectangle.getGeometry().isValid());
ROI ellipse = ROIs.createEllipseROI(50, 00, 500, 300, ImagePlane.getDefaultPlane());
double targetAreaEllipse = Math.PI * 250 * 150;
assertEquals(targetAreaEllipse, ellipse.getArea(), delta);
// Flattening the path results in a more substantial area difference
assertTrue(Math.abs(targetAreaEllipse - ellipse.getGeometry().getArea()) / targetAreaEllipse < 0.01);
checkROIMeasurements(ellipse, 1, 1, targetAreaEllipse / 100.0);
checkROIMeasurements(ellipse, pixelWidth, pixelHeight, targetAreaEllipse / 100.0);
assertTrue(ellipse.getGeometry().isValid());
// ROIs with holes can be troublesome, JTS may consider holes as 'positive' regions
ROI areaSubtracted = RoiTools.combineROIs(rectangle, ellipse, CombineOp.SUBTRACT);
assertEquals(areaSubtracted.getArea(), areaSubtracted.getGeometry().getArea(), delta);
assertNotEquals(areaSubtracted.getArea(), rectangle.getArea());
checkROIMeasurements(areaSubtracted, 1, 1, delta);
checkROIMeasurements(areaSubtracted, pixelWidth, pixelHeight, delta);
assertTrue(areaSubtracted.getGeometry().isValid());
ROI areaAdded = RoiTools.combineROIs(rectangle, ellipse, CombineOp.ADD);
assertEquals(areaAdded.getArea(), areaAdded.getGeometry().getArea(), delta);
assertEquals(rectangle.getArea(), areaAdded.getArea(), delta);
checkROIMeasurements(areaAdded, 1, 1, delta);
checkROIMeasurements(areaAdded, pixelWidth, pixelHeight, delta);
assertTrue(areaAdded.getGeometry().isValid());
File fileHierarchy = new File("src/test/resources/data/test-objects.hierarchy");
try (InputStream stream = Files.newInputStream(fileHierarchy.toPath())) {
PathObjectHierarchy hierarchy = (PathObjectHierarchy) new ObjectInputStream(stream).readObject();
List<ROI> rois = hierarchy.getFlattenedObjectList(null).stream().filter(p -> p.hasROI()).map(p -> p.getROI()).collect(Collectors.toList());
assertNotEquals(0L, rois.size());
for (ROI roi : rois) {
Geometry geom = roi.getGeometry();
assertEquals(roi.isEmpty(), geom.isEmpty());
assertTrue(geom.isValid());
if (roi.isArea()) {
assertEquals(roi.isEmpty(), geom.isEmpty());
if (roi instanceof EllipseROI) {
assertTrue(Math.abs(roi.getArea() - geom.getArea()) / roi.getArea() < 0.01);
} else
assertEquals(roi.getArea(), geom.getArea(), deltaLarge);
} else if (roi.isLine()) {
assertEquals(roi.getLength(), geom.getLength(), delta);
} else if (roi.isPoint()) {
assertEquals(roi.getNumPoints(), geom.getNumPoints(), delta);
}
assertEquals(roi.getCentroidX(), geom.getCentroid().getX(), delta);
assertEquals(roi.getCentroidY(), geom.getCentroid().getY(), delta);
checkROIMeasurements(areaAdded, 1, 1, delta);
checkROIMeasurements(areaAdded, pixelWidth, pixelHeight, delta);
for (var split : RoiTools.splitROI(roi)) {
checkROIMeasurements(split, 1, 1, deltaLarge);
checkROIMeasurements(split, pixelWidth, pixelHeight, deltaLarge);
}
}
} catch (Exception e) {
e.printStackTrace();
fail(e.getLocalizedMessage());
}
}
use of qupath.lib.roi.interfaces.ROI in project qupath by qupath.
the class ViewTrackerPlayback method setViewerForFrame.
static void setViewerForFrame(final QuPathViewer viewer, final ViewRecordingFrame frame) {
// Resize the viewer (if necessary)
resizeViewer(viewer, frame.getSize());
// Set downsample
viewer.setDownsampleFactor(frame.getDownsampleFactor());
// Set location
Rectangle imageBounds = frame.getImageBounds();
viewer.setCenterPixelLocation(imageBounds.x + imageBounds.width * .5, imageBounds.y + imageBounds.height * .5);
// Set rotation
viewer.setRotation(frame.getRotation());
if (frame.hasEyePosition()) {
Point2D p2d = frame.getEyePosition();
ROI point = ROIs.createPointsROI(p2d.getX(), p2d.getY(), ImagePlane.getDefaultPlane());
// // if (Boolean.TRUE.equals(frame.isEyeFixated()))
// point.setPointRadius(viewer.getDownsampleFactor() * 10); // This was only removed because setPointRadius was removed!
// // else
// // point.setPointRadius(viewer.getDownsampleFactor() * 8);
PathObject pathObject = PathObjects.createAnnotationObject(point);
pathObject.setName("Eye tracking position");
viewer.setSelectedObject(pathObject);
logger.debug("Eye position: " + p2d);
}
if (frame.hasZOrT()) {
viewer.setZPosition(frame.getZ());
viewer.setTPosition(frame.getT());
}
}
Aggregations