Skip to content

Instantly share code, notes, and snippets.

@adyprat
Created March 6, 2023 15:04
Show Gist options
  • Save adyprat/4140a9ccd11c0ea6430def536c746fa3 to your computer and use it in GitHub Desktop.
Save adyprat/4140a9ccd11c0ea6430def536c746fa3 to your computer and use it in GitHub Desktop.
QuPath export Annotation ROI script for 0.3.2
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