Skip to content

Instantly share code, notes, and snippets.

@adyprat
Created March 6, 2023 15:06
Show Gist options
  • Save adyprat/ef8949dacb1f8a149ba92a089925c45c to your computer and use it in GitHub Desktop.
Save adyprat/ef8949dacb1f8a149ba92a089925c45c to your computer and use it in GitHub Desktop.
QuPath export annotation ROI for version 0.4.0 and 0.4.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.readRegion(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() as int
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