Created
March 6, 2023 15:04
-
-
Save adyprat/4140a9ccd11c0ea6430def536c746fa3 to your computer and use it in GitHub Desktop.
QuPath export Annotation ROI script for 0.3.2
This file contains 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
import qupath.lib.gui.images.servers.RenderedImageServer | |
import qupath.lib.gui.viewer.overlays.HierarchyOverlay | |
import qupath.lib.gui.viewer.Scalebar | |
import qupath.lib.gui.QuPathGUI | |
import qupath.lib.gui.viewer.OverlayOptions | |
import qupath.lib.regions.* | |
import ij.* | |
import java.awt.Color | |
import java.awt.image.BufferedImage | |
import qupath.lib.roi.RectangleROI | |
import java.awt.Font | |
//CUSTOM: change this to 1.0 for original resolution -- may be slow depending on the ROI size | |
double downsample=1.0 | |
def imageData = getCurrentImageData() | |
def viewer = getCurrentViewer() | |
def server = new RenderedImageServer.Builder(imageData) | |
.display(viewer.getImageDisplay()) | |
.downsamples(downsample) | |
.layers(new HierarchyOverlay(viewer.getImageRegionStore(), viewer.getOverlayOptions(), imageData)) | |
.build() | |
def roi = getSelectedROI() | |
// change this to value to a larger number if the yellow box from the original ROI still shows up | |
def removeBox = 10 | |
// HACK! make a new ROI within the first one to remove the yellow border from showing up | |
// will lost about 10 pixels but should be fine | |
roi2 = new RectangleROI(roi.x+removeBox , roi.y+removeBox , roi.getBoundsWidth()-removeBox*2, roi.getBoundsHeight()-removeBox*2) | |
def requestROI = RegionRequest.createInstance(server.getPath(), downsample, roi2) | |
def img = server.readBufferedImage(requestROI) | |
//find the channels and keep them in the starting order (C1-CX) | |
def imageDisplay = viewer.getImageDisplay() | |
def availableChannels = imageDisplay.availableChannels() | |
def channels = imageDisplay.selectedChannels() | |
def sortedChannels = channels.sorted((c1, c2) -> { | |
// Compare in a better way here... | |
int i1 = availableChannels.indexOf(c1) | |
int i2 = availableChannels.indexOf(c2) | |
return Integer.compare(i1, i2) | |
}) | |
def img2 = new ImagePlus("Image", img)//.show() | |
def imY = img2.getHeight() | |
def imX = img2.getWidth() | |
int fScale = (imX+imY)/2//Math.max(imY,imX) | |
// CUSTOM: Change this to a higher value to get smaller font. | |
// Essentially sets the font size to 1/ScaleFactor of the image size | |
// TODO: may be there are better ways to do this? | |
def ScaleFactor = 45 | |
def fSize = (fScale/ScaleFactor).round() | |
Font font = new Font("Calibri", Font.PLAIN,fSize); | |
ij.process.ImageProcessor ip = img2.getProcessor(); | |
// Change this if the text is too close to origin | |
ij.Prefs.setTransparentIndex(50) | |
def offsetDefault = 5 | |
def offsetX = offsetDefault | |
def offset = offsetDefault | |
def bxheight = [] | |
for (x in sortedChannels){ | |
def y = x | |
if (x==sortedChannels[sortedChannels.size()-1]){ | |
y = x.toString().split('\\(')[0].toString() | |
}else{ | |
y = x.toString().split('\\(')[0].toString() +' ' | |
} | |
ip.setFont(font); | |
//ip.setAntialiasedText(true) | |
bxheight.add(ip.getStringBounds(y).getHeight().toInteger()) | |
offsetX+= ip.getStringWidth(y) | |
} | |
Color bx = new Color(0,0,0) | |
//ip.setColor( bx) | |
//ip.fillRect(0, imY-bxheight.max()-10*offsetDefault, offsetX-2*offsetDefault, bxheight.max()-offsetDefault) | |
// To remove the black text box, comment lines 87-93 | |
// Or remove ", Color.black" in line 92 | |
def y = '' | |
for (x in sortedChannels){ | |
ip.setFont(font); | |
ip.setColor(x.getColor()); | |
y = x.toString().split('\\(')[0].toString() +' ' | |
ip.drawString(y, offset, imY-offsetDefault*4, Color.black); | |
offset+= ip.getStringWidth(y) | |
} | |
offset=5 | |
for (x in sortedChannels){ | |
ip.setFont(font); | |
ip.setColor(x.getColor()); | |
y = x.toString().split('\\(')[0].toString() +' ' | |
ip.drawString(y, offset, imY-offsetDefault*4); | |
offset+= ip.getStringWidth(y) | |
} | |
// SCALEBAR | |
def scLen = 0.1 | |
def round50up = { int x -> | |
x + ( 50 - ( x % 50 ?: 50 ) ) | |
} | |
def cal = server.getPixelCalibration() | |
Color bx2 = new Color(255,255,255,1) | |
int scaleRect = scLen*imX | |
// account for downsampling. the numbers are accurate if downsample=1.0 | |
// for ds=2, the imX is half the size so need to multiply with downsample | |
int scaleValum = downsample*scLen*imX*cal.getPixelWidthMicrons() | |
def scaleVal = round50up(scaleValum).toString()+ ' µm' | |
if (round50up(scaleValum)>1000){ | |
scaleVal = (round50up(scaleValum)/1000).round().toString()+ ' mm' | |
} | |
ip.setColor(bx2) | |
//CUSTOM: To remove Scale bar comment ines 122-124 | |
ip.fillRect(imX-scaleRect-2*offsetDefault, imY-offsetDefault*8, scaleRect, offsetDefault*2) | |
ip.drawString(scaleVal, imX-scaleRect-2*offsetDefault, imY-offsetDefault*10); | |
//img2.show() | |
// By default, saves as a png | |
//CUSTOM: To change from png to something else, edit .png in line 128 | |
fileName = Dialogs.promptToSaveFile(null,null,'rendered.png',null,'.png') | |
println(fileName) | |
IJ.save(img2,fileName.toString()) | |
// TODO: Add scale bar |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment