Created
July 20, 2018 15:44
-
-
Save petebankhead/98d82f5de92abefaf4cfd87ec0479121 to your computer and use it in GitHub Desktop.
Proof-of-concept script showing one whole slide image as an overlay on top of another within QuPath.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Proof-of-concept script showing one whole slide image as an overlay on top | |
| * of another within QuPath. | |
| * | |
| * Note that this script simply overlays the *same* image that is currently open; | |
| * therefore it may not be immediately obvious that anything has happened. | |
| * | |
| * Uncomment the line | |
| * g2d.translate(1000, 1000) | |
| * if you want to convince yourself that something *has* actually happened, | |
| * and adjust the opacity slider as required. | |
| * | |
| * In practice, it will be more useful to overlay a *different* image... | |
| * | |
| * Note that you can also optionally bind the overlay display to the show/hide detections option. | |
| * | |
| * WARNING! This should work in QuPath v0.1.2, and possibly v0.1.3. | |
| * However, the API will definitely change in the future as overlays become | |
| * a more important part of the software... therefore be prepared to update any | |
| * code based on this. | |
| * | |
| * @author Pete Bankhead | |
| */ | |
| import javafx.application.Platform | |
| import qupath.lib.awt.common.AwtTools | |
| import qupath.lib.gui.viewer.QuPathViewer | |
| import qupath.lib.gui.viewer.overlays.AbstractImageDataOverlay | |
| import qupath.lib.images.ImageData | |
| import qupath.lib.images.servers.ImageServer | |
| import qupath.lib.images.servers.ImageServerProvider | |
| import qupath.lib.regions.ImageRegion | |
| import qupath.lib.scripting.QPEx | |
| import java.awt.Graphics2D | |
| import java.awt.Shape | |
| import java.awt.geom.AffineTransform | |
| import java.awt.image.BufferedImage | |
| import java.awt.image.ImageObserver | |
| // Replace overlay in the Application thread | |
| Platform.runLater { | |
| def viewer = QPEx.getCurrentViewer() | |
| def path = viewer.getServerPath() | |
| def overlay = new WholeSlideImageOverlay(viewer, path, false) | |
| // Remove any existing overlays of this kind (helpful during debugging...) | |
| viewer.getOverlayLayers().findAll({it.getClass().getName().contains('WholeSlideImageOverlay')}).each { | |
| viewer.removeOverlay(it) | |
| } | |
| // Add the new overlay | |
| viewer.addOverlay(overlay) | |
| print 'Done!' | |
| } | |
| /** | |
| * Create an overlay to display one whole slide image on top of another. | |
| */ | |
| class WholeSlideImageOverlay extends AbstractImageDataOverlay { | |
| private boolean initialized = false | |
| private QuPathViewer viewer | |
| private ImageServer<BufferedImage> server | |
| private AffineTransform transform | |
| private boolean bindToObjectDisplay | |
| public WholeSlideImageOverlay(final QuPathViewer viewer, final String path, boolean bindToObjectDisplay) { | |
| super(viewer.getOverlayOptions(), viewer.getImageData()) | |
| this.viewer = viewer | |
| this.server = ImageServerProvider.buildServer(path, BufferedImage.class) | |
| this.initialized = true | |
| this.bindToObjectDisplay = bindToObjectDisplay | |
| } | |
| @Override | |
| boolean supportsImageDataChange() { | |
| return true | |
| } | |
| /** | |
| * If the ImageData for the viewer is changed, remove the overlay. | |
| * | |
| * @param imageData | |
| */ | |
| @Override | |
| public void setImageData(final ImageData<BufferedImage> imageData) { | |
| if (this.imageData == imageData || !initialized) | |
| return | |
| viewer.removeOverlay(this) | |
| this.server.close() | |
| } | |
| @Override | |
| void paintOverlay(Graphics2D g2d, ImageRegion imageRegion, double downsampleFactor, ImageObserver observer, boolean paintCompletely) { | |
| // Don't do anything if opacity is zero | |
| if (getOpacity() == 0) | |
| return | |
| // If we're binding this to the display of objects, don't paint if detections aren't visible | |
| // (this lets us toggle the overlay on & off with the 'h' shortcut) | |
| if (bindToObjectDisplay && !viewer.getOverlayOptions().showObjects) | |
| return | |
| // Create a rectangle for the region | |
| Shape shape = AwtTools.getBounds(imageRegion) | |
| // Apply transform... handy if using the same image, just to check *something* happens | |
| // g2d.translate(1000, 1000) | |
| // Paint region with the help of the image region store (used for caching & painting) | |
| int z = viewer.getZPosition() | |
| int t = viewer.getTPosition() | |
| viewer.getImageRegionStore().paintRegion( | |
| server, g2d, g2d.getClip(), z, t, downsampleFactor, | |
| viewer.getImageRegionStore().getThumbnail(server, z, t, true), | |
| null, null) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment