Last active
January 3, 2024 14:48
-
-
Save adyprat/1903bc182fc3b6ac4ac8df5239d3dd82 to your computer and use it in GitHub Desktop.
Export annotation rendered ROI script with channel names and scale bar for QuPath v0.5.0
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 qupath.fx.dialogs.* | |
import java.awt.Font | |
//CUSTOM: change this to 1.0 for original resolution -- may be slow depending on the ROI size | |
double downsample=1 | |
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); | |
print(font) | |
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) | |
} | |
println(sortedChannels) | |
offset=5 | |
for (x in sortedChannels){ | |
ip.setFont(font); | |
println() | |
bx = new Color(ColorTools.red(x.getColor()),ColorTools.green(x.getColor()),ColorTools.blue(x.getColor())) | |
ip.setColor(bx); | |
//ip.setColor(Color.WHITE); | |
println(x) | |
y = x.toString().split('\\(')[0].toString() +' ' | |
ip.drawString(y, offset, imY-offsetDefault*4, ); | |
offset+= ip.getStringWidth(y) | |
} | |
// SCALEBAR | |
def scLen = 0.1 | |
def roundUp = { int x, int roundTo -> | |
def remainder = x%roundTo | |
if (remainder>0){ | |
x+= (roundTo - remainder) | |
} | |
return x | |
} | |
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() | |
println scaleValum | |
def scaleVal = 0 | |
if (scaleValum<=50) { | |
println "Less than 50" | |
scaleVal = roundUp(scaleValum,10).toString()+ ' µm' | |
}else if (scaleValum>50 & scaleValum<=100) { | |
println "Less than 100" | |
scaleVal = roundUp(scaleValum,20).toString()+ ' µm' | |
} else { | |
println "Greater than than 100" | |
scaleVal = roundUp(scaleValum,50).toString()+ ' µm' | |
} | |
if (roundUp(scaleValum,50)>1000){ | |
scaleVal = (roundUp(scaleValum,50)/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.setColor(Color.WHITE) | |
ip.drawString(scaleVal, imX-scaleRect-2*offsetDefault, imY-offsetDefault*10); | |
println(scaleVal) | |
//img2.show() | |
// By default, saves as a png | |
//CUSTOM: To change from png to something else, edit .png in line 128 | |
fileName = FileChoosers.promptToSaveFile("Export to file", null,FileChoosers.createExtensionFilter("PNG image", ".png")) | |
println(fileName.toString()) | |
IJ.save(img2,fileName.toString()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment