use of qupath.lib.roi.RectangleROI in project qupath by qupath.
the class QuPathViewer method paintViewer.
protected void paintViewer(Graphics g, int w, int h) {
ImageServer<BufferedImage> server = getServer();
if (server == null) {
g.setColor(background);
g.fillRect(0, 0, w, h);
updateRepaintTimestamp();
return;
}
// // Get dimensions
// int w = getWidth();
// int h = getHeight();
Rectangle clip = g.getClipBounds();
boolean clipFull;
if (clip == null) {
clip = new Rectangle(0, 0, w, h);
g.setClip(0, 0, w, h);
clipFull = true;
} else
clipFull = clip.x == 0 && clip.y == 0 && clip.width == w && clip.height == h;
// Ensure we have a sufficiently-large buffer
if (imgBuffer == null || imgBuffer.getWidth() != w || imgBuffer.getHeight() != h) {
// Create buffered images & buffers for RGB pixel values
imgBuffer = createBufferedImage(w, h);
imgBuffer.setAccelerationPriority(1f);
logger.trace("New buffered image created: {}", imgBuffer);
// imgVolatile = createVolatileImage(w, h);
imageUpdated = true;
// If the size changed, ensure the AffineTransform is up-to-date
updateAffineTransform();
}
// Get the displayed region
Shape shapeRegion = getDisplayedRegionShape();
// The visible shape must have changed if there wasn't one previously...
// Otherwise check if it has changed & update accordingly
// This will be used to notify listeners soon
boolean shapeChanged = lastVisibleShape == null || !lastVisibleShape.equals(shapeRegion);
long t1 = System.currentTimeMillis();
// Only repaint the image if this is requested, otherwise only overlays need to be repainted
if (imageUpdated || locationUpdated) {
// || imgVolatile.contentsLost()) {
// Set flags that image no longer requiring an update
// By setting them early, they might still be reset during this run... in which case we don't want to thwart the re-run
imageUpdated = false;
locationUpdated = false;
// updateBufferedImage(imgVolatile, shapeRegion, w, h);
updateBufferedImage(imgBuffer, shapeRegion, w, h);
}
// if (imageUpdated || locationUpdated) {
// updateBufferedImage(imgVolatile, shapeRegion, w, h);
// // updateBufferedImage(imgBuffer, shapeRegion, w, h);
// // logger.info("INITIAL Image drawing time: " + (System.currentTimeMillis() - t1));
// imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
// }
// while (imgVolatile.contentsLost()) {
// imgVolatile.createGraphics().drawImage(imgBuffer, 0, 0, this);
// }
// Store the last shape visible
lastVisibleShape = shapeRegion;
// Draw the image from the buffer
// The call to super.paintComponent is delayed until here to try to stop occasional flickering on Apple's Java 6
g.setColor(background);
if (clipFull)
paintFinalImage(g, imgBuffer, this);
else
// g2d.drawImage(imgBuffer, 0, 0, getWidth(), getHeight(), this);
g.drawImage(imgBuffer, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, clip.x, clip.y, clip.x + clip.width, clip.y + clip.height, null);
if (logger.isTraceEnabled()) {
long t2 = System.currentTimeMillis();
logger.trace("Final image drawing time: {}", (t2 - t1));
}
// Really useful only for debugging graphics
if (!(g instanceof Graphics2D)) {
imageUpdated = false;
// Notify any listeners of shape changes
if (shapeChanged)
fireVisibleRegionChangedEvent(lastVisibleShape);
return;
}
double downsample = getDownsampleFactor();
float opacity = overlayOptions.getOpacity();
Graphics2D g2d = (Graphics2D) g.create();
// Apply required transform to the graphics object (rotation, scaling, shifting...)
g2d.transform(transform);
Composite previousComposite = g2d.getComposite();
boolean paintCompletely = thumbnailIsFullImage || !doFasterRepaint;
// var regionBounds = AwtTools.getImageRegion(clip, getZPosition(), getTPosition());
if (opacity > 0 || PathPrefs.alwaysPaintSelectedObjectsProperty().get()) {
if (opacity < 1) {
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
g2d.setComposite(composite);
}
Color color = getSuggestedOverlayColor();
// Paint the overlay layers
var imageData = this.imageDataProperty.get();
for (PathOverlay overlay : allOverlayLayers.toArray(PathOverlay[]::new)) {
logger.trace("Painting overlay: {}", overlay);
if (overlay instanceof AbstractOverlay)
((AbstractOverlay) overlay).setPreferredOverlayColor(color);
// overlay.paintOverlay(g2d, regionBounds, downsample, null, paintCompletely);
overlay.paintOverlay(g2d, getServerBounds(), downsample, imageData, paintCompletely);
}
// if (hierarchyOverlay != null) {
// hierarchyOverlay.setPreferredOverlayColor(color);
// hierarchyOverlay.paintOverlay(g2d, getServerBounds(), downsampleFactor, null, paintCompletely);
// }
}
// Paint the selected object
PathObjectHierarchy hierarchy = getHierarchy();
PathObject mainSelectedObject = getSelectedObject();
Rectangle2D boundsRect = null;
boolean useSelectedColor = PathPrefs.useSelectedColorProperty().get();
boolean paintSelectedBounds = PathPrefs.paintSelectedBoundsProperty().get();
for (PathObject selectedObject : hierarchy.getSelectionModel().getSelectedObjects().toArray(new PathObject[0])) {
// TODO: Simplify this...
if (selectedObject != null && selectedObject.hasROI() && selectedObject.getROI().getZ() == getZPosition() && selectedObject.getROI().getT() == getTPosition()) {
if (!selectedObject.isDetection()) {
// Ensure a selected ROI can be seen clearly
if (previousComposite != null)
g2d.setComposite(previousComposite);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
Rectangle boundsDisplayed = shapeRegion.getBounds();
ROI pathROI = selectedObject.getROI();
// if ((PathPrefs.getPaintSelectedBounds() || (selectedObject.isDetection() && !PathPrefs.getUseSelectedColor())) && !(pathROI instanceof RectangleROI)) {
if (pathROI != null && (paintSelectedBounds || (!useSelectedColor)) && !(pathROI instanceof RectangleROI) && !pathROI.isEmpty()) {
Shape boundsShape = null;
if (pathROI.isPoint()) {
var hull = pathROI.getConvexHull();
if (hull != null)
boundsShape = hull.getShape();
}
if (boundsShape == null) {
boundsRect = AwtTools.getBounds2D(pathROI, boundsRect);
boundsShape = boundsRect;
}
// Tried to match to pixel boundaries... but resulted in too much jiggling
// boundsShape.setFrame(
// Math.round(boundsShape.getX()/downsampleFactor)*downsampleFactor-downsampleFactor,
// Math.round(boundsShape.getY()/downsampleFactor)*downsampleFactor-downsampleFactor,
// Math.round(boundsShape.getWidth()/downsampleFactor)*downsampleFactor+2*downsampleFactor,
// Math.round(boundsShape.getHeight()/downsampleFactor)*downsampleFactor+2*downsampleFactor);
// boundsShape.setFrame(boundsShape.getX()-downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth()+2*downsampleFactor, boundsShape.getHeight()+2*downsampleFactor);
PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, getSuggestedOverlayColor(), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsample, 1) * 2), null);
// boundsShape.setFrame(boundsShape.getX()+downsampleFactor, boundsShape.getY()-downsampleFactor, boundsShape.getWidth(), boundsShape.getHeight());
// PathHierarchyPaintingHelper.paintShape(boundsShape, g2d, new Color(1f, 1f, 1f, 0.75f), PathHierarchyPaintingHelper.getCachedStroke(Math.max(downsampleFactor, 1)*2), null, downsampleFactor);
}
// in a cached way
if ((selectedObject.isDetection() && PathPrefs.useSelectedColorProperty().get()) || !PathObjectTools.hierarchyContainsObject(hierarchy, selectedObject))
PathHierarchyPaintingHelper.paintObject(selectedObject, false, g2d, boundsDisplayed, overlayOptions, getHierarchy().getSelectionModel(), downsample);
// Paint ROI handles, if required
if (selectedObject == mainSelectedObject && roiEditor.hasROI()) {
Stroke strokeThick = PathHierarchyPaintingHelper.getCachedStroke(PathPrefs.annotationStrokeThicknessProperty().get() * downsample);
Color color = useSelectedColor ? ColorToolsAwt.getCachedColor(PathPrefs.colorSelectedObjectProperty().get()) : null;
if (color == null)
color = ColorToolsAwt.getCachedColor(ColorToolsFX.getDisplayedColorARGB(selectedObject));
g2d.setStroke(strokeThick);
// Draw ROI handles using adaptive size
double maxHandleSize = getMaxROIHandleSize();
double minHandleSize = downsample;
PathHierarchyPaintingHelper.paintHandles(roiEditor, g2d, minHandleSize, maxHandleSize, color, ColorToolsAwt.getTranslucentColor(color));
}
}
}
// Notify any listeners of shape changes
if (shapeChanged)
fireVisibleRegionChangedEvent(lastVisibleShape);
updateRepaintTimestamp();
}
use of qupath.lib.roi.RectangleROI 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.RectangleROI 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();
}
}
Aggregations