Last active
December 10, 2024 00:34
-
-
Save Svidro/5e4c29630e8d2ef36988184987d1028f to your computer and use it in GitHub Desktop.
Coding helper scripts for 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
Collections of scripts harvested mainly from Pete, but also picked up from the forums | |
If you are starting with 0.2.0m4+ START HERE: https://petebankhead.github.io/qupath/2019/08/21/scripting-in-v020.html | |
TOC | |
Access objects in other project images.groovy - in later versions of 0.2.0M#, access the hierarchies of other images | |
Access other project images or data.groovy | |
Access project metadata.groovy - interact with user defined, per image metadata. Useful for sorting projects. | |
Aperio XML Files - import.groovy | |
CLI server selection.groovy - select a server other than QuPath's default when opening a file | |
Coding-List methods for object.groovy - Select an object, see what functions work for it. | |
Color deconvolution vectors - Print.groovy | |
Create Histogram through ImageJ.groovy - shows a histogram representing the pixels in selected Annotations - color deconvolved BF | |
Create project from CLI.groovy | |
CSV file import.groovy - Script to handle CSV files with coordinates | |
CSV file write out data.groovy - How to write a csv file out of QuPath | |
DESCRIBE.groovy - Use describe() to find other functions related to the object you are interested in. Run it last and in small scripts. | |
Edit metadata.groovy - Add pixel values and maginification (and possibly other things). Shouldn't be necessary after m3 | |
Efficiently collecting objects.groovy - More efficient scripting | |
Email alert.py - Python script to send you an email when a long analysis run is completed. | |
Email script ending.groovy - How to use the Python script above. Also good as a general reference for running things through the command | |
line from within QuPath. Could be generalized to any Python script. | |
Error log within functions.groovy - print functions to not normally work within a function, this provides an alternative | |
Export data about the image.groovy - summary data about the size and type of image | |
Export Specific Fields.groovy - When you do not want allllll of those detection measurements. | |
Export Summary of Detection measurements.groovy - Mean+Std Dev for a list of measurements | |
Export annotations as single file.groovy - Good for collecting data | |
Export detections per annotation.groovy - Exports just detections within each annotation as separate files. For downstream analysis in | |
other programs like R. | |
Export file by image name.groovy - A method for naming export files when using "Run for all" | |
Export images automatically for project.groovy - Export a thumbnail image, with and without an overlay, using QuPath. | |
Export polygons.groovy - Exporting objects to a file (similar to transfer annotations between images script in Manipulating Objects) | |
Get current image name without extension.groovy | |
GSON and JSON script examples.groovy - converting between GSON and JSON and some links to examples of code | |
ImageJ plugin macros in a script.groovy - For another example, look in the Workflow enhancers section under Tissue detection. | |
ImageOps examples.groovy - using ImageOps in 0.2.0M12 to adjust StarDist function | |
Import Export annotations.groovy - Duplicate of Export Polygons and version in Manipulating Objects. | |
Import Export objects.groovy - import and export full objects. backup or transfer function. | |
Import TMA information.groovy - Pete's script from forums for importing TMA maps | |
Memory monitoring with tile cache.groovy - Fun to use to check memory, and a good example of creating GUI elements in a script. | |
Merging training object files.groovy - https://github.com/qupath/qupath/issues/256 | |
Metadata - collect from image.groovy - get all accessible metadata from the image - may or may not work depending on format. | |
Print list of source images for composite.groovy - Find out which images were used for a training image montage1 | |
Print a list of detection object measurements.groovy - When you can't quite get the number of whitespaces right in your measurement | |
names, this is priceless. | |
Project Directory.groovy - Folder name stuff. | |
Set number of processors.groovy - Altering preferences for command line scripts. | |
Transfer ROIs to ImageJ.groovy - Send annotations and detections to the ROI manager in ImageJ, possibly with a downsample | |
Variable into ImageJ macro.groovy - Cycle through annotations and export TIFF files that include the annotation name in their | |
file name. | |
Variable into string commands.groovy - It took me a long time to remember this, so keeping it here as a reference. Good for more | |
complex and dynamic scripts. | |
script to load settings.cmd - Registry manipulation script for use outside QuPath, but interesting for setting up new users in facilities. | |
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
//cycle through all images in the project and check the number of annotations. Add it to their metadata in the Project tab | |
//https://forum.image.sc/t/select-slides-without-annotations/35019/2 | |
for (entry in getProject().getImageList()) { | |
def hierarchy = entry.readHierarchy() | |
String nAnnotations = hierarchy.getAnnotationObjects().size() | |
entry.putMetadataValue('Num annotations', nAnnotations) | |
} | |
getQuPath().refreshProject() |
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
//Accessing project image data | |
//https://forum.image.sc/t/read-an-image-that-is-not-open/34824/3 | |
def path = '/path/to/the/image.ext' | |
// Read a small image with ImageIO | |
// ('normal' Java, nothing QuPath-specific) | |
def img = javax.imageio.ImageIO.read(new File(path)) | |
println img | |
// Read a small image with ImageJ as an ImagePlus | |
// ('normal' ImageJ, nothing QuPath-specific) | |
def imp = ij.IJ.openImage(path) | |
println imp | |
// Read any image with QuPath as an ImageServer | |
def server = qupath.lib.images.servers.ImageServerProvider.buildServer(path, BufferedImage) | |
println server | |
// Read another image within a project as an ImageData (from which you can get the server) | |
def name = 'Image name as shown in Project pane' | |
def project = getProject() | |
def entry = project.getImageList().find { it.getImageName() == name } | |
def imageData = entry.readImageData() | |
println imageData | |
//Access object data in other images in a project | |
//https://forum.image.sc/t/m9-multiplex-classifier-script-updates-cell-summary-measurements-visualization/34663/4 | |
def project = QPEx.getProject() | |
def fileNames = project.getImageList() | |
detClasses=[] | |
fileNames.each{ | |
def objs = it.readHierarchy().getDetectionObjects() | |
def imageClasses=objs.collect{it.getPathClass()} | |
detClasses.addAll(imageClasses) | |
} | |
//Also potentially written as: | |
getProject().getImageList().each{ | |
def objs = it.readHierarchy().getDetectionObjects() | |
classes = objs.collect{it?.getPathClass()?.toString()} | |
classNames.addAll(classes) | |
} |
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
// https://forum.image.sc/t/access-project-metadata-by-script/38758/2?u=research_associate | |
// 0.2.0 | |
def key = "Your key" // Your metadata key | |
def value = getProjectEntryMetadataValue(key) | |
print key + ": " + value |
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
// From https://forum.image.sc/t/labelled-annotation-import/37787/11?u=research_associate | |
// Originally https://gist.github.com/DanaCase/9cfc23912fee48e437af03f97763d78e | |
import qupath.lib.scripting.QP | |
import qupath.lib.geom.Point2 | |
import qupath.lib.roi.PolygonROI | |
import qupath.lib.objects.PathAnnotationObject | |
import qupath.lib.images.servers.ImageServer | |
//Aperio Image Scope displays images in a different orientation | |
def rotated = false | |
def server = QP.getCurrentImageData().getServer() | |
def h = server.getHeight() | |
def w = server.getWidth() | |
// need to add annotations to hierarchy so qupath sees them | |
def hierarchy = QP.getCurrentHierarchy() | |
//Prompt user for exported aperio image scope annotation file | |
def path = server.getURIs().getAt(0).getPath(); // HERE | |
path = path.substring(0, path.lastIndexOf(".")) + ".xml" // HERE | |
def file = new File(path) | |
def text = file.getText() | |
def list = new XmlSlurper().parseText(text) | |
list.Annotation.each { | |
// Get the class from your XML | |
def annotationClass = getPathClass(it.@Name.toString()) | |
it.Regions.Region.each { region -> | |
def tmp_points_list = [] | |
region.Vertices.Vertex.each{ vertex -> | |
if (rotated) { | |
X = vertex.@Y.toDouble() | |
Y = h - vertex.@X.toDouble() | |
} | |
else { | |
X = vertex.@X.toDouble() | |
Y = vertex.@Y.toDouble() | |
} | |
tmp_points_list.add(new Point2(X, Y)) | |
} | |
def roi = new PolygonROI(tmp_points_list) | |
def annotation = new PathAnnotationObject(roi) | |
// Set the class here below | |
annotation.setPathClass(annotationClass) | |
hierarchy.addPathObject(annotation, false) | |
} | |
} | |
// Update hierarchy to see changes in QuPath's hierarchy | |
fireHierarchyUpdate() | |
print "Done!" |
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
//0.2.0 | |
//https://forum.image.sc/t/specifying-the-imageserver-image-provider-server-type-eg-bio-formats-or-openslide-in-a-qupath-script/45884/2 | |
def server = getCurrentServer() | |
def uri = server.getURIs()[0] | |
//def server2 = new qupath.lib.images.servers.bioformats.BioFormatsServerBuilder().buildServer(uri) | |
def server2 = new qupath.lib.images.servers.openslide.OpenslideServerBuilder().buildServer(uri) | |
print "Original server: ${server}" | |
print "New server: ${server2}" | |
// Create an ImageData if you need one | |
def imageData = new ImageData(server2) |
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
//Helpful list of functions when learning groovy scripting in QuPath | |
// Select an object... any object | |
def myObject = getSelectedROI() | |
// An alternative below; this would show what's available with Ctrl+space in the script editor | |
// def myObject = new qupath.lib.scripting.QPEx() | |
// Print the methods you can have access to | |
for (m in myObject.getClass().getMethods()){ | |
println(m) | |
} |
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
//0.2.0 Sara McCardle | |
import static qupath.lib.gui.scripting.QPEx.* | |
totalString= [] | |
project.getImageList().each { | |
def ImgString = new StringBuilder() | |
ImgString.append(it.getImageName()) | |
ImgString.append(',') | |
def stains = it.readImageData().getColorDeconvolutionStains().getStains(false) | |
stains.each { | |
ImgString.append(it.getName()).append(',').append(it.getRed()).append(',').append(it.getGreen()).append(',').append(it.getBlue()).append(',') | |
} | |
totalString << ImgString | |
} | |
print(totalString) | |
def path = buildFilePath(PROJECT_BASE_DIR, 'vectors') | |
mkdirs(path) | |
String outputName = 'Stain_Vectors.csv' | |
path2 = buildFilePath(path, outputName) | |
outFile= new File(path2) | |
outFile.withPrintWriter { | |
totalString.each{ img-> | |
it.println(img) | |
} | |
} |
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
//0.2.0 | |
// https://forum.image.sc/t/get-pixel-by-pixel-intensities-in-qupath/46626/10?u=research_associate | |
import qupath.lib.images.servers.TransformedServerBuilder | |
import qupath.imagej.gui.IJExtension | |
IJExtension.getImageJInstance() | |
// Create an ImageServer that applies color deconvolution to the current image, using the current stains | |
def imageData = getCurrentImageData() | |
def server = new TransformedServerBuilder(imageData.getServer()) | |
.deconvolveStains(imageData.getColorDeconvolutionStains(), 1, 2) | |
.build() | |
for (annotation in getAnnotationObjects()) { | |
def region = RegionRequest.createInstance(server.getPath(), 1.0, annotation.getROI()) | |
def pathImage = IJTools.convertToImagePlus(server, region) | |
def imp = pathImage.getImage() | |
def roiIJ = IJTools.convertToIJRoi(annotation.getROI(), pathImage) | |
imp.setRoi(roiIJ) | |
imp.setPosition(1) | |
new ij.gui.HistogramWindow(imp) | |
imp.setPosition(2) | |
new ij.gui.HistogramWindow(imp) | |
// imp.show() // show image to check if you want | |
} |
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
//0.2.0 | |
//https://forum.image.sc/t/creating-project-from-command-line/45608/2 | |
import java.awt.image.BufferedImage | |
import qupath.lib.images.servers.ImageServerProvider | |
// Paths | |
def directory = new File("Path/to/your/empty/directory") | |
def imagePath = "D:/QuPath/data/Capture.PNG" | |
// Create project | |
def project = Projects.createProject(directory , BufferedImage.class) | |
// Get serverBuilder | |
def support = ImageServerProvider.getPreferredUriImageSupport(BufferedImage.class, imagePath, "") | |
def builder = support.builders.get(0) | |
// Make sure we don't have null | |
if (builder == null) { | |
print "Image not supported" | |
return | |
} | |
// Add the image as entry to the project | |
project.addImage(builder) | |
// Changes should now be reflected in the project directory | |
project.syncChanges() |
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
//https://forum.image.sc/t/reading-csv-table-in-qupath/32772/3 | |
//From @melvingelbard | |
import java.io.BufferedReader; | |
import java.io.FileReader; | |
import qupath.lib.objects.PathAnnotationObject; | |
import qupath.lib.roi.RectangleROI; | |
def imageData = getCurrentImageData(); | |
// Get location of csv | |
def file = getQuPath().getDialogHelper().promptForFile(null) | |
// Create BufferedReader | |
def csvReader = new BufferedReader(new FileReader(file)); | |
int sizePixels = 1000 | |
row = csvReader.readLine() // first row (header) | |
// Loop through all the rows of the CSV file. | |
while ((row = csvReader.readLine()) != null) { | |
def rowContent = row.split(",") | |
double cx = rowContent[1] as double; | |
double cy = rowContent[2] as double; | |
// Create annotation | |
def roi = new RectangleROI(cx-sizePixels/2, cy-sizePixels/2, sizePixels, sizePixels); | |
def annotation = new PathAnnotationObject(roi, PathClassFactory.getPathClass("Region")); | |
imageData.getHierarchy().addPathObject(annotation, true); | |
} |
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
//Reference sample for writing out some array as a CSV file with a header line and row headers (classes) | |
//Based off of https://forum.image.sc/t/write-polygon-coordinates-to-text-or-csv-file/44811/2 | |
def header = " , a, b, c, d" | |
classes = ["a","b","c","d"] | |
output = "g:/test.csv" | |
File csvFile = new File(output) | |
csvFile.createNewFile() | |
m = [[0,1,2,3],[0,0,0,0],[1,1,1,1],[5,2,4,6]] | |
new File(output).withWriter { fw -> | |
fw.writeLine(header) | |
m.eachWithIndex{l,x-> | |
String line = classes[x]+","+l.join(",") | |
fw.writeLine(line) | |
} | |
} |
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
//0.2.0 | |
// Use describe to access information about a particular object. | |
// https://forum.image.sc/t/qupath-export-individual-annotation-on-entire-size-image-just-like-the-image-size-of-labeled-image/42966/11 | |
imageData = getCurrentImageData() | |
def labelServer = new LabeledImageServer.Builder(imageData) | |
println describe(labelServer) |
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
// https://github.com/qupath/qupath/issues/223 | |
//Note that this only applies while the current image is active. Reopening or switching images resets to the image's own metadata. | |
// Set the magnification & pixel size (be cautious!!!) | |
def metadata = getCurrentImageData().getServer().getOriginalMetadata() | |
metadata.magnification = 40 | |
metadata.pixelWidthMicrons = 0.25 | |
metadata.pixelHeightMicrons = 0.25 | |
// If you want to trigger the 'Image' tab on the left to update, try setting a property to something different (and perhaps back again) | |
type = getCurrentImageData().getImageType() | |
setImageType(null) | |
setImageType(type) | |
//THIS HAS CHANGED IN MORE RECENT VERSIONS. I do not know exactly when, but at least as of M7, adjusting the metada should be done as follows | |
import qupath.lib.images.servers.ImageServerMetadata | |
def imageData = getCurrentImageData() | |
def server = imageData.getServer() | |
def oldMetadata = server.getMetadata() | |
def newMetadata = new ImageServerMetadata.Builder(oldMetadata) | |
.magnification(10.0) | |
.pixelSizeMicrons(1.25, 1.25) | |
.build() | |
imageData.updateServerMetadata(newMetadata) |
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
//A processor efficient way of collecting all objects that do not meet a particular criteria | |
//In this case, only cells with Nucleus: Area or 30 or less are retained as cells_others | |
HashSet cells_others = getCellObjects() | |
cells_some = cells_others.findAll {it.getMeasurementList().getMeasurementValue("Nucleus: Area") > 30} | |
cells_others.removeAll(cells_some) | |
println("cells_others now contains the remaining cells with nuclear area greater than 30") |
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
#This script was largely taken from Stackoverflow and is intended to be called from within | |
#QuPath in order to alert the "recipient" email address that a particular slide had finished processing | |
import smtplib | |
import sys | |
from email.mime.multipart import MIMEMultipart | |
from email.mime.text import MIMEText | |
#free to use spare email account | |
gmailUser = '[email protected]' | |
gmailPassword = 'bizwfscsdbsuildy' | |
recipient = '[email protected]' | |
message = 'QuPath script has completed on '+sys.argv[1] | |
msg = MIMEMultipart() | |
msg['From'] = gmailUser | |
msg['To'] = recipient | |
msg['Subject'] = "QuPath Alert" | |
msg.attach(MIMEText(message)) | |
mailServer = smtplib.SMTP('smtp.gmail.com', 587) | |
mailServer.ehlo() | |
mailServer.starttls() | |
mailServer.ehlo() | |
mailServer.login(gmailUser, gmailPassword) | |
mailServer.sendmail(gmailUser, recipient, msg.as_string()) | |
mailServer.close() |
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
//This script can be added to the end of very slow QuPath runs in order to alert the user by email. | |
//Email address is set in the python file | |
// Get the imageData & server | |
String path = getCurrentImageData().getServer().getPath() | |
//As long as python files can be run at the command line interface using "python", the following should work for the location of a python script | |
def cmdArray = ["python", "c:\\Python\\Test1\\Email alert.py", path] | |
cmdArray.execute() |
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
//Not a script, but an example from komzy on how to get error information out of functions. | |
// https://forum.image.sc/t/button-setonaction-does-not-print-anything-when-scripting/25443/5?u=research_associate | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
Logger logger = LoggerFactory.getLogger(QuPathGUI.class); | |
button.setOnAction { | |
print("Test Print # 2: Button Clicked") | |
logger.info("Test Print # 3: call logger.info on button click") | |
} |
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
//https://groups.google.com/forum/#!topic/qupath-users/GEI99RECuAc | |
/** | |
* Script to combine results tables exported by QuPath. | |
* | |
* This is particularly intended to deal with the fact that results tables of annotations can produce results | |
* with different column names, numbers and orders - making them awkward to combine later manually. | |
* | |
* It prompts for a directory containing exported text files, and then writes a new file in the same directory. | |
* The name of the new file can be modified - see the first lines below. | |
* | |
* Note: This hasn't been tested very extensively - please check the results carefully, and report any problems so they | |
* can be fixed! | |
* | |
* @author Pete Bankhead | |
*/ | |
import qupath.lib.gui.QuPathGUI | |
// Some parameters you might want to change... | |
String ext = '.txt' // File extension to search for | |
String delimiter = '\t' // Use tab-delimiter (this is for the *input*, not the output) | |
String outputName = 'Combined_results.txt' // Name to use for output; use .csv if you really want comma separators | |
// Prompt for directory containing the results | |
//def dirResults = QuPathGUI.getSharedDialogHelper().promptForDirectory() | |
//Use the following line instead to prevent the popup. Should work in V 0.1.2 and 0.1.3 | |
def dirResults = new File(buildFilePath(PROJECT_BASE_DIR, 'annotation results')) | |
if (dirResults == null) | |
return | |
def fileResults = new File(dirResults, outputName) | |
// Get a list of all the files to merge | |
def files = dirResults.listFiles({ | |
File f -> f.isFile() && | |
f.getName().toLowerCase().endsWith(ext) && | |
f.getName() != outputName} as FileFilter) | |
if (files.size() <= 1) { | |
print 'At least two results files needed to merge!' | |
return | |
} else | |
print 'Will try to merge ' + files.size() + ' files' | |
// Represent final results as a 'list of maps' | |
def results = new ArrayList<Map<String, String>>() | |
// Store all column names that we see - not all files necessarily have all columns | |
def allColumns = new LinkedHashSet<String>() | |
allColumns.add('File name') | |
// Loop through the files | |
for (file in files) { | |
// Check if we have anything to read | |
def lines = file.readLines() | |
if (lines.size() <= 1) { | |
print 'No results found in ' + file | |
continue | |
} | |
// Get the header columns | |
def iter = lines.iterator() | |
def columns = iter.next().split(delimiter) | |
allColumns.addAll(columns) | |
// Create the entries | |
while (iter.hasNext()) { | |
def line = iter.next() | |
if (line.isEmpty()) | |
continue | |
def map = ['File name': file.getName()] | |
def values = line.split(delimiter) | |
// Check if we have the expected number of columns | |
if (values.size() != columns.size()) { | |
print String.format('Number of entries (%d) does not match the number of columns (%d)!', columns.size(), values.size()) | |
print('I will stop processing ' + file.getName()) | |
break | |
} | |
// Store the results | |
for (int i = 0; i < columns.size(); i++) | |
map[columns[i]] = values[i] | |
results.add(map) | |
} | |
} | |
// Create a new results file - using a comma delimiter if the extension is csv | |
if (outputName.toLowerCase().endsWith('.csv')) | |
delimiter = ',' | |
int count = 0 | |
fileResults.withPrintWriter { | |
def header = String.join(delimiter, allColumns) | |
it.println(header) | |
// Add each of the results, with blank columns for missing values | |
for (result in results) { | |
for (column in allColumns) { | |
it.print(result.getOrDefault(column, '')) | |
it.print(delimiter) | |
} | |
it.println() | |
count++ | |
} | |
} | |
// Success! Hopefully... | |
print 'Done! ' + count + ' result(s) written to ' + fileResults.getAbsolutePath() |
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
// https://forum.image.sc/t/metadata-batch-export/42668/2 | |
def metadata = [] | |
def delim = '\t' | |
for (entry in getProject().getImageList()) { | |
def imageData = entry.readImageData() | |
def server = imageData.getServer() | |
metadata << String.join(delim, | |
[entry.getImageName(), | |
server.getMetadata().getMagnification(), | |
server.getPixelCalibration().getAveragedPixelSize(), | |
server.getWidth(), | |
server.getHeight()] as String[] | |
) | |
} | |
def output = String.join(System.lineSeparator, metadata) | |
print output | |
// To write to a file, use something like this | |
// new File('/path/to/file.txt').text = output |
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
//Make sure you replace the default cell (or whatever) detection script with your own. | |
import qupath.lib.gui.QuPathGUI | |
//Use either "project" OR "outputFolder" to determine where your detection files will go | |
def project = QuPathGUI.getInstance().getProject().getBaseDirectory() | |
project = project.toString()+"\\detectionMeasurements\\" | |
//Make sure the output folder exists | |
mkdirs(project) | |
//def outputFolder = "D:\\Results\\" | |
//mkdirs(outputFolder) | |
hierarchy = getCurrentHierarchy() | |
def annotations = getAnnotationObjects() | |
int i = 1 | |
clearDetections() //Just in case, so that the first detection list won't end up with extra stuff that was laying around | |
for (annotation in annotations) | |
{ | |
hierarchy.getSelectionModel().clearSelection(); | |
selectObjects{p -> p == annotation} | |
//********************************************************************** | |
//REPLACE THIS PART WITH YOUR OWN DETECTION CREATION SCRIPT | |
runPlugin('qupath.imagej.detect.nuclei.WatershedCellDetection', '{"detectionImageBrightfield": "Hematoxylin OD", "requestedPixelSizeMicrons": 0.5, "backgroundRadiusMicrons": 8.0, "medianRadiusMicrons": 0.0, "sigmaMicrons": 1.5, "minAreaMicrons": 10.0, "maxAreaMicrons": 400.0, "threshold": 0.1, "maxBackground": 2.0, "watershedPostProcess": true, "excludeDAB": false, "cellExpansionMicrons": 5.0, "includeNuclei": true, "smoothBoundaries": true, "makeMeasurements": true}'); | |
//************************************************************************ | |
saveDetectionMeasurements(project+" "+i+"detections.txt",) | |
i+=1 | |
clearDetections() | |
} | |
//Potentially replace all of the detections for viewing, after finishing the export | |
//selectAnnotations() | |
//runPlugin('qupath.imagej.detect.nuclei.WatershedCellDetection', '{"detectionImageBrightfield": "Hematoxylin OD", "requestedPixelSizeMicrons": 0.5, "backgroundRadiusMicrons": 8.0, "medianRadiusMicrons": 0.0, "sigmaMicrons": 1.5, "minAreaMicrons": 10.0, "maxAreaMicrons": 400.0, "threshold": 0.1, "maxBackground": 2.0, "watershedPostProcess": true, "excludeDAB": false, "cellExpansionMicrons": 5.0, "includeNuclei": true, "smoothBoundaries": true, "makeMeasurements": true}'); |
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
import qupath.lib.gui.QuPathGUI | |
def project = QuPathGUI.getInstance().getProject().getBaseDirectory() | |
println(project) | |
outputFolder = "D:\\Results\\" | |
String imageLocation = getCurrentImageData().getServer().getPath() | |
fileNameWithNoExtension = imageLocation.split("[^A-Za-z0-9_ ]")[-2] | |
//also possible to use: def name = getCurrentImageData().getServer().getShortServerName() | |
//alternatively, change the outputFolder to "project" if you wish to save the files into your project folder, wherever it is. | |
saveAnnotationMeasurements(outputFolder+fileNameWithNoExtension+".txt", ) |
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
/** | |
* Export a thumbnail image, with and without an overlay, using QuPath. | |
* | |
* For tissue microarrays, the scripting code written by the 'File -> Export TMA data' | |
* command is probably more appropriate. | |
* | |
* However, for all other kinds of images where batch export is needed this script can be used. | |
* | |
* @author Pete Bankhead | |
*/ | |
import qupath.lib.gui.ImageWriterTools | |
import qupath.lib.gui.QuPathGUI | |
import qupath.lib.gui.viewer.OverlayOptions | |
import qupath.lib.regions.RegionRequest | |
import qupath.lib.scripting.QPEx | |
// Aim for an output resolution of approx 0.5 um/pixel | |
double requestedPixelSize = 0.5 | |
// Define format | |
def formatExtensions = [ | |
'PNG': '.png', | |
'JPEG': '.jpg' | |
] | |
def format = 'PNG' | |
// Create the output directory, if required | |
def path = QPEx.buildFilePath(QPEx.PROJECT_BASE_DIR, "screenshots") | |
QPEx.mkdirs(path) | |
// Get the imageData & server | |
def imageData = QPEx.getCurrentImageData() | |
def server = imageData.getServer() | |
def viewer = QPEx.getCurrentViewer() | |
// Get the file name from the current server | |
def name = server.getShortServerName() | |
// We need to get the display settings (colors, line thicknesses, opacity etc.) from the current viewer, if available | |
def overlayOptions = QuPathGUI.getInstance() == null ? new OverlayOptions() : viewer.getOverlayOptions() | |
// Calculate downsample factor depending on the requested pixel size | |
double downsample = requestedPixelSize / server.getAveragedPixelSizeMicrons() | |
def request = RegionRequest.createInstance(imageData.getServerPath(), downsample, 0, 0, server.getWidth(), server.getHeight()) | |
// Write output image, with and without overlay | |
def dir = new File(path) | |
def fileImage = new File(dir, name + formatExtensions[format]) | |
def img = server.readBufferedImage(request) | |
img = viewer.getImageDisplay().applyTransforms(img, null) | |
javax.imageio.ImageIO.write(img, format, fileImage) | |
def fileImageWithOverlay = new File(dir, name + "-overlay" + formatExtensions[format]) | |
ImageWriterTools.writeImageRegionWithOverlay(img, imageData, overlayOptions, request, fileImageWithOverlay.getAbsolutePath()) |
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
// Create an empty text file | |
def path = buildFilePath(PROJECT_BASE_DIR, 'polygons.txt') | |
def file = new File(path) | |
file.text = '' | |
// Loop through all annotations & write the points to the file | |
for (pathObject in getAnnotationObjects()) { | |
// Check for interrupt (Run -> Kill running script) | |
if (Thread.interrupted()) | |
break | |
// Get the ROI | |
def roi = pathObject.getROI() | |
if (roi == null) | |
continue | |
// Write the points; but beware areas, and also ellipses! | |
file << roi.getPolygonPoints() << System.lineSeparator() | |
} | |
print 'Done!' |
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
// see https://github.com/qupath/qupath/issues/25 | |
saveDetectionMeasurements('/path/to/exported/file.txt', "Measurement", "another measurement") | |
// or if path is a variable that is a String to your export location, an example would be | |
saveDetectionMeasurements(path, "Nucleus: Channel 1 mean", "Nucleus: Channel 2 mean") | |
//Which would limit the giant detection export file to only two columns |
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
// @melvingelbard - https://forum.image.sc/t/script-for-averaging-detection-measurement-data/48805/9?u=research_associate | |
// Source: https://forum.image.sc/t/script-for-averaging-detection-measurement-data/48805/9?u=research_associate | |
columnName = ["CK: Cell: Mean", "CD68: Cell: Mean"] // Change this to your column of interest | |
imageName = getProjectEntry().getImageName() | |
//Save to the project folder | |
var path = buildFilePath(PROJECT_BASE_DIR, "Detection Summary measurements.csv") | |
//print "Mean: " + avg | |
//print "Std.Dev: " + Math.sqrt(sd/stats.length) | |
var pathObjects = getDetectionObjects() | |
var separator = "," | |
File file = new File(path) | |
header = "Image name," | |
var exists = file.exists() | |
file.withWriterAppend { fw -> | |
if (!exists){ | |
columnName.each{ | |
header =header+[it+" Average", it+" Standard deviation"].join(separator)+separator | |
} | |
fw.writeLine(header) | |
} | |
dataLine = getProjectEntry().getImageName()+"," | |
columnName.each{ | |
stats = Histogram.getMeasurementValues(pathObjects, it); | |
var avg = 0 | |
for (int i=0; i < stats.length; i++) { | |
avg += stats[i] | |
} | |
avg /= stats.length | |
var sd = 0 | |
for (int i=0; i < stats.length; i++) { | |
sd = sd + Math.pow(stats[i] - avg, 2); | |
} | |
sd = Math.sqrt(sd/stats.length) | |
dataLine = dataLine + [avg, sd].join(separator)+separator | |
} | |
fw.append(dataLine) | |
fw.append(System.getProperty("line.separator")) | |
} | |
print "Done!" | |
import qupath.lib.analysis.stats.Histogram |
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
//this has changed in 0.2.0m2 | |
imageData = getCurrentImageData(); | |
if (imageData == null) { | |
print("No image open!"); | |
return | |
} | |
def currentImageName = imageData.getServer().getShortServerName() | |
print("Current image name: " + currentImageName); |
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
//https://forum.image.sc/t/labelling-negative-space-in-annotations/32653/3 | |
//https://petebankhead.github.io/qupath/2019/08/21/scripting-in-v020.html#serialization--json | |
// Create JSON | |
def annotations = getAnnotationObjects() | |
def gson = GsonTools.getInstance(true) | |
def json = gson.toJson(annotations) | |
/////////////////////////// | |
// Create JSON string | |
def annotations = getAnnotationObjects() | |
def gson = GsonTools.getInstance(true) | |
def json = gson.toJson(annotations) | |
// Print the string | |
println json | |
// Read the annotations | |
def type = new com.google.gson.reflect.TypeToken<List<qupath.lib.objects.PathObject>>() {}.getType() | |
def deserializedAnnotations = gson.fromJson(json, type) | |
// Set the annotations to have a different name (so we can identify them) & add to the current image | |
deserializedAnnotations.eachWithIndex {annotation, i -> annotation.setName('New annotation ' + (i+1))} | |
addObjects(deserializedAnnotations) |
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
//https://groups.google.com/forum/#!topic/qupath-users/C3GRBF614N8 | |
import qupath.imagej.plugins.ImageJMacroRunner | |
import qupath.lib.plugins.parameters.ParameterList | |
// Create a macro runner so we can check what the parameter list contains | |
def params = new ImageJMacroRunner(getQuPath()).getParameterList() | |
print ParameterList.getParameterListJSON(params, ' ') | |
// Change the value of a parameter, using the JSON to identify the key | |
params.getParameters().get('downsampleFactor').setValue(4.0 as double) | |
print ParameterList.getParameterListJSON(params, ' ') | |
// Get the macro text and other required variables | |
//def macro = new File("myMacroPath/MacroName.ijm").text | |
//Line above from https://github.com/qupath/qupath/issues/176 | |
def macro = 'print("Overlay size: " + Overlay.size)' | |
def imageData = getCurrentImageData() | |
def annotations = getAnnotationObjects() | |
// Loop through the annotations and run the macro | |
for (annotation in annotations) { | |
ImageJMacroRunner.runMacro(params, imageData, null, annotation, macro) | |
} | |
print 'Done!' |
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
// https://forum.image.sc/t/stardist-extension/37696/6?u=research_associate | |
// SCRIPTS NOT INTENDED TO BE RUN AS IS. READ THE FORUM POST | |
def stardist = StarDist2D.builder(pathModel) | |
.threshold(0.5) // Prediction threshold | |
.preprocess( | |
ImageOps.Core.subtract(100), | |
ImageOps.Core.divide(100) | |
) | |
// .normalizePercentiles(1, 99) // Percentile normalization | |
.pixelSize(0.5) // Resolution for detection | |
.includeProbability(true) // Include prediction probability as measurement | |
.build() | |
//////////////////// | |
// Get current image - assumed to have color deconvolution stains set | |
def imageData = getCurrentImageData() | |
def stains = imageData.getColorDeconvolutionStains() | |
// Set everything up with single-channel fluorescence model | |
def pathModel = '/path/to/dsb2018_heavy_augment' | |
def stardist = StarDist2D.builder(pathModel) | |
.preprocess( | |
ImageOps.Channels.deconvolve(stains), | |
ImageOps.Channels.extract(0), | |
ImageOps.Filters.median(2), | |
ImageOps.Core.divide(1.5) | |
) // Optional preprocessing (can chain multiple ops) | |
.pixelSize(0.5) | |
.includeProbability(true) | |
.threshold(0.5) | |
.build() |
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
//Two scripts to save annotations to a file, then restore them. | |
//Useful for redoing cell detection and keeping annotations for a classifier. | |
//Take from: https://groups.google.com/forum/#!topic/qupath-users/UvkNb54fYco | |
def path = buildFilePath(PROJECT_BASE_DIR, 'annotations') | |
def annotations = getAnnotationObjects().collect {new qupath.lib.objects.PathAnnotationObject(it.getROI(), it.getPathClass())} | |
new File(path).withObjectOutputStream { | |
it.writeObject(annotations) | |
} | |
print 'Done!' | |
//****** SECOND SCRIPT***********// | |
def path = buildFilePath(PROJECT_BASE_DIR, 'annotations') | |
def annotations = null | |
new File(path).withObjectInputStream { | |
annotations = it.readObject() | |
} | |
addObjects(annotations) | |
fireHierarchyUpdate() | |
print 'Added ' + annotations |
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
// A pair of scripts to be run separately. | |
//Export objects without measurements. | |
path = buildFilePath(PROJECT_BASE_DIR, 'objects') | |
mkdirs(path) | |
image = getProjectEntry().getImageName() | |
path = buildFilePath(PROJECT_BASE_DIR, 'objects',image) | |
def objects = getAllObjects().findAll{it.getLevel() !=0} | |
//adjust this to getAnnotationObjects() or getDetectionObjects() or getCellObjects() as desired. | |
new File(path).withObjectOutputStream { | |
it.writeObject(objects) | |
} | |
print 'Done!' | |
//////////////////////////////////////////////////////////////////////////// | |
//Second script to import objects from the 'objects' folder within a project | |
//////////////////////////////////////////////////////////////////////////// | |
guiscript = true | |
image = getProjectEntry().getImageName() | |
path = buildFilePath(PROJECT_BASE_DIR, 'objects',image) | |
def objects = null | |
new File(path).withObjectInputStream { | |
objects = it.readObject() | |
} | |
addObjects(objects) | |
resolveHierarchy() | |
fireHierarchyUpdate() | |
print 'Added ' + objects.size()+' objects' | |
for (annotation in getAnnotationObjects()) | |
annotation.setLocked(true) |
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
// From https://forum.image.sc/t/qupath-importing-tma-data-within-script/24188 | |
import static qupath.lib.gui.commands.TMAScoreImportCommand.* | |
def imageName = getProjectEntry().getImageName() | |
def path = buildFilePath(PROJECT_BASE_DIR, imageName + '.qpmap') | |
def file = new File(path) | |
def text = file.text | |
def hierarchy = getCurrentHierarchy() | |
handleImportGrid(hierarchy.getTMAGrid(), text) | |
fireHierarchyUpdate() |
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
/** | |
* A basic GUI to help monitor memory usage in QuPath. | |
* | |
* This helps both to find & address out-of-memory troubles by | |
* 1. Showing how much memory is in use over time | |
* 2. Giving a button to clear the tile cache - which can be | |
* using up precious memory | |
* 3. Giving quick access to control the number of threads used | |
* for parallel processing | |
* | |
* You can run this command in the background while going about your | |
* normal analysis, and check in to see how it is doing. | |
* | |
* If you find QuPath crashing/freezing, look to see if the memory | |
* use is especially high. | |
* | |
* If it crashes when running memory-hungry commands like cell detection | |
* across a large image or TMA, try reducing the number of parallel threads. | |
* | |
* @author Pete Bankhead | |
*/ | |
import javafx.application.Platform | |
import javafx.beans.binding.Bindings | |
import javafx.beans.property.SimpleLongProperty | |
import javafx.beans.value.ChangeListener | |
import javafx.geometry.Insets | |
import javafx.geometry.Side | |
import javafx.scene.Scene | |
import javafx.scene.chart.AreaChart | |
import javafx.scene.chart.NumberAxis | |
import javafx.scene.chart.XYChart | |
import javafx.scene.control.Button | |
import javafx.scene.control.Label | |
import javafx.scene.control.TextField | |
import javafx.scene.layout.BorderPane | |
import javafx.scene.layout.GridPane | |
import javafx.stage.Stage | |
import qupath.lib.gui.QuPathGUI | |
import qupath.lib.gui.prefs.PathPrefs | |
// Create a timer to poll for memory status once per second | |
def timer = new Timer("QuPath memory monitor", true) | |
long sampleFrequency = 1000L | |
// Observable properties to store memory values | |
def maxMemory = new SimpleLongProperty() | |
def totalMemory = new SimpleLongProperty() | |
def usedMemory = new SimpleLongProperty() | |
def tileMemory = new SimpleLongProperty() | |
// Let's sometimes scale to MB, sometimes to GB | |
double scaleMB = 1.0/1024.0/1024.0 | |
double scaleGB = scaleMB/1024.0 | |
// Create a chart to show how memory use evolves over time | |
def xAxis = new NumberAxis() | |
xAxis.setLabel("Time (samples)") | |
def yAxis = new NumberAxis() | |
yAxis.setLabel("Memory (GB)") | |
def chart = new AreaChart(xAxis, yAxis) | |
def seriesTotal = new XYChart.Series() | |
def seriesUsed = new XYChart.Series() | |
def seriesTiles = new XYChart.Series() | |
yAxis.setAutoRanging(false) | |
yAxis.setLowerBound(0.0) | |
yAxis.setTickUnit(1.0) | |
yAxis.setUpperBound(Math.ceil(Runtime.getRuntime().maxMemory() * scaleGB)) | |
xAxis.setAutoRanging(true) | |
// Bind the series names to the latest values, in MB | |
seriesTotal.nameProperty().bind(Bindings.createStringBinding( | |
{-> String.format("Total memory (%.1f MB)", totalMemory.get() * scaleMB)}, totalMemory)) | |
seriesUsed.nameProperty().bind(Bindings.createStringBinding( | |
{-> String.format("Used memory (%.1f MB)", usedMemory.get() * scaleMB)}, usedMemory)) | |
seriesTiles.nameProperty().bind(Bindings.createStringBinding( | |
{-> String.format("Tile memory (%.1f MB)", tileMemory.get() * scaleMB)}, tileMemory)) | |
chart.getData().addAll(seriesTotal, seriesUsed, seriesTiles) | |
chart.setLegendVisible(true) | |
chart.setLegendSide(Side.TOP) | |
chart.setAnimated(false) | |
chart.setCreateSymbols(false) | |
// Add it button to make it possible to clear the tile cache | |
// This is a bit of a hack, since there is no clean way to do it yet | |
def btnClearCache = new Button("Clear tile cache") | |
btnClearCache.setOnAction {e -> | |
try { | |
print "Clearing cache..." | |
QuPathGUI.getInstance().getViewer().getImageRegionStore().cache.clear() | |
System.gc() | |
} catch (Exception e2) { | |
e2.printStackTrace() | |
} | |
} | |
btnClearCache.setMaxWidth(Double.MAX_VALUE) | |
// Add a button to run the garbage collector | |
def btnGarbageCollector = new Button("Reclaim memory") | |
btnGarbageCollector.setOnAction {e -> | |
System.gc() | |
} | |
btnGarbageCollector.setMaxWidth(Double.MAX_VALUE) | |
// Add a text field to adjust the number of parallel threads | |
// This is handy to scale back memory use when running things like cell detection | |
def runtime = Runtime.getRuntime() | |
def labThreads = new Label("Parallel threads") | |
def tfThreads = new TextField(Integer.toString(PathPrefs.getNumCommandThreads())) | |
PathPrefs.numCommandThreadsProperty().addListener({ v, o, n -> | |
def text = Integer.toString(n) | |
if (!text.trim().equals(tfThreads.getText().trim())) | |
tfThreads.setText(text) | |
} as ChangeListener) | |
tfThreads.setPrefColumnCount(4) | |
tfThreads.textProperty().addListener({ v, o, n -> | |
try { | |
PathPrefs.setNumCommandThreads(Integer.parseInt(n.trim())) | |
} catch (Exception e) {} | |
} as ChangeListener) | |
labThreads.setLabelFor(tfThreads) | |
// Create a pane to show it all | |
def paneBottom = new GridPane() | |
int col = 0 | |
int row = 0 | |
paneBottom.add(new Label("Num processors: " + runtime.availableProcessors()), col, row++, 1, 1) | |
paneBottom.add(labThreads, col, row, 1, 1) | |
paneBottom.add(tfThreads, col+1, row++, 1, 1) | |
paneBottom.add(btnClearCache, col, row++, 2, 1) | |
paneBottom.add(btnGarbageCollector, col, row++, 2, 1) | |
paneBottom.add(new Label("Max tile memory: " + QuPathGUI.getInstance().getViewer().getImageRegionStore().cache.maxMemoryBytes/1024.0/1024.0),col, row++, 1, 1) | |
paneBottom.setPadding(new Insets(10)) | |
paneBottom.setVgap(5) | |
def pane = new BorderPane(chart) | |
pane.setRight(paneBottom) | |
// Add a data point for the current memory usage | |
def snapshot = { -> | |
def time = seriesUsed.getData().size() + 1 | |
seriesUsed.getData().add(new XYChart.Data<Number, Number>(time, usedMemory.get()*scaleGB)) | |
seriesTotal.getData().add(new XYChart.Data<Number, Number>(time, totalMemory.get()*scaleGB)) | |
seriesTiles.getData().add(new XYChart.Data<Number, Number>(time, tileMemory.get() * scaleGB)) | |
} | |
// Switch to the application thread... | |
Platform.runLater { | |
// Create a timer that will snapshot the current memory usage & update the chart | |
timer.schedule({ -> | |
Platform.runLater { | |
totalMemory.set(runtime.totalMemory()) | |
maxMemory.set(runtime.maxMemory()) | |
usedMemory.set(runtime.totalMemory() - runtime.freeMemory()) | |
tileMemory.set(QuPathGUI.getInstance().getViewer().getImageRegionStore().cache.memoryBytes) | |
snapshot() | |
} | |
}, 0L, sampleFrequency) | |
// Show the GUI | |
def stage = new Stage() | |
stage.initOwner(QuPathGUI.getInstance().getStage()) | |
stage.setScene(new Scene(pane)) | |
stage.setTitle("Memory monitor") | |
stage.show() | |
stage.setOnHiding {timer.cancel()} | |
} |
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
//https://github.com/qupath/qupath/issues/256 | |
// Paths to training files (here, both relative to the current project) | |
paths = [ | |
buildFilePath(PROJECT_BASE_DIR, 'training', 'my_training.qptrain'), | |
buildFilePath(PROJECT_BASE_DIR, 'training', 'my_training2.qptrain'), | |
] | |
// Path to output training file | |
pathOutput = buildFilePath(PROJECT_BASE_DIR, 'training', 'merged.qptrain') | |
// Count mostly helps to ensure we're adding with unique keys | |
count = 0 | |
// Loop through training files | |
def result = null | |
for (path in paths) { | |
// .qptrain files just have one object but class isn't public, so | |
// we take the first one that is deserialized | |
new File(path).withObjectInputStream { | |
saved = it.readObject() | |
} | |
// Add the training objects, appending an extra number which | |
// (probably, unless very unfortunate with image names?) means they are unique | |
map = new HashMap<>(saved.getMap()) | |
if (result == null) { | |
result = saved | |
result.clear() | |
} | |
for (entry in map.entrySet()) | |
result.put(entry.getKey() + '-' + count, entry.getValue()) | |
count++ | |
} | |
// Check how big the map is & what it contains | |
print result.size() | |
print result.getMap().keySet().each { println it } | |
// Write out a new training file | |
new File(pathOutput).withObjectOutputStream { | |
it.writeObject(result) | |
} |
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
// https://forum.image.sc/t/qupath-scripting-1-using-clupath-to-save-smoothed-image-regions/49525/9?u=research_associate | |
getCurrentServer().dumpMetadata() |
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
//Also useful to see how to create a list of all measurements quickly | |
qupath.lib.classifiers.PathClassificationLabellingHelper.getAvailableFeatures(getDetectionObjects()).each { println(it) } | |
//After 0.2.0m5 | |
PathClassifierTools.getAvailableFeatures(getDetectionObjects()) |
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
// 0.2.0 | |
//https://forum.image.sc/t/training-image-localization-size/46509/3?u=research_associate | |
//print list of source regions in a montage | |
def manager = getCurrentServer().getManager() | |
def regions = manager.getRegions() | |
for (region in regions) { | |
def uris = manager.getServer(region, 1).getURIs().collect {it.toString()} | |
def uriString = String.join("\t", uris) | |
print region.toString() + "\t" + uriString | |
} | |
//////////////////////////////// | |
//Or, print the name of the region that contains a specific object. | |
def roi = getSelectedROI() | |
def manager = getCurrentServer().getManager() | |
def regions = manager.getRegions() | |
if (roi) | |
regions = regions.findAll {it.intersects(ImageRegion.createInstance(roi))} | |
for (region in regions) { | |
def uris = manager.getServer(region, 1).getURIs().collect {it.toString()} | |
def uriString = String.join("\t", uris) | |
print region.toString() + "\t" + uriString | |
} |
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
//Find the project directory from within your groovy script, useful if you have placed other files you want to access within the folder | |
import qupath.lib.gui.QuPathGUI | |
def path = QuPathGUI.getInstance().getProject().getBaseDirectory() | |
//or | |
PROJECT_BASE_DIR |
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
//From: https://gist.github.com/kaischleicher/dda2c5a1abb717336bae2ff656cb91dd | |
//Useful for setting up QuPath (0.1.2?) by checking whether Preferences exist in the registry during first run. This allows new | |
//users to start with access to all extensions and other settings rather than setting up each user individually. | |
//Replace the shortcut to QuPath in the Public folder with a shortcut to the following script. | |
@echo off | |
SET mykey="HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs\io.github.qupath" | |
reg query %mykey% >nul | |
if %errorlevel% equ 0 ( | |
echo "key exists - do nothing" | |
) else ( | |
echo "qupath key missing - importing default settings" | |
reg import C:\Tools\QuPath\qupath-imcf-default-settings.reg | |
) | |
start C:\Tools\QuPath\QuPath.exe |
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
//For command line scripts, you may want to set the maximum number of threads in order to not hamper performance | |
//especially if there is a python script watching for file changes in the background | |
import qupath.lib.gui.prefs.PathPrefs | |
PathPrefs.setNumCommandThreads(3) |
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
//https://forum.image.sc/t/script-for-send-region-to-imagej/39554/18 | |
//0.2.2 | |
import qupath.imagej.gui.IJExtension | |
import ij.plugin.frame.RoiManager | |
double downsample = 4.0 | |
// Define a path to save the ROIs, or null if the ROI Manager should be shown instead | |
String savePath = "/path/to/rois.zip" | |
//String savePath = null | |
boolean showManager = true // If false, just save the ROIs instead | |
def server = getCurrentServer() | |
def region = RegionRequest.createInstance(server, downsample) | |
def options = getCurrentViewer().getOverlayOptions() | |
def overlay = IJExtension.extractOverlay( | |
getCurrentHierarchy(), | |
region, | |
options, | |
null | |
) | |
RoiManager rm | |
if (showManager) { | |
rm = RoiManager.getInstance2() | |
if (rm == null) | |
rm = new RoiManager() | |
} else | |
rm = new RoiManager(false) | |
rm.setOverlay(overlay) | |
if (savePath) | |
rm.runCommand("save", savePath) |
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
//Note you will want to edit the path within the macro, line 26 | |
import qupath.imagej.plugins.ImageJMacroRunner | |
import qupath.lib.plugins.parameters.ParameterList | |
// Create a macro runner so we can check what the parameter list contains | |
def params = new ImageJMacroRunner(getQuPath()).getParameterList() | |
print ParameterList.getParameterListJSON(params, ' ') | |
// Change the value of a parameter, using the JSON to identify the key | |
params.getParameters().get('downsampleFactor').setValue(1.0 as double) | |
print ParameterList.getParameterListJSON(params, ' ') | |
def imageData = getCurrentImageData() | |
getAnnotationObjects().each{ | |
String name = it.getName() | |
println(name) | |
macro = 'test="'+name+'"; saveAs("tif", "C:/OAD/" + getTitle()+"_"+test)' | |
ImageJMacroRunner.runMacro(params, imageData, null, it, macro) | |
} | |
print 'Done!' |
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
//Thresholds for cell detection can be adjusted by using a variable for the threshold in your script, rather than a single value | |
//Script does not work as written! Only intended as an example | |
def GREEN_MEAN = average_Intensity+2*stdDev //or some other value based on other statistics from the image | |
//Scrolllllll =>>>>>>>> | |
runPlugin('qupath.imagej.detect.nuclei.WatershedCellDetection', '{"detectionImageFluorescence": 1, "requestedPixelSizeMicrons": 0.31, "backgroundRadiusMicrons": 0.0, "medianRadiusMicrons": 0.0, "sigmaMicrons": 1.5, "minAreaMicrons": 10.0, "maxAreaMicrons": 50.0, "threshold": '+GREEN_MEAN+', "watershedPostProcess": true, "cellExpansionMicrons": 0.5, "includeNuclei": true, "smoothBoundaries": true, "makeMeasurements": true}'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More scripts to assist when coding, some mostly as a reference. Almost all are purely ripped from the forums or communication with Pete.