Interacting with the file system
Sometimes, you need to write out files for your own use, despite QuPath’s built in Measurement exporter. Or perhaps you need to access results from some analysis outside of QuPath and read them into QuPath objects.
This will all be from a Windows-centric point of view, if you have any suggestions for Mac users, feel free to message me updates on the image.sc forum and I will try and add them.
Image names
One of the most frequently useful commands to know when interacting with the file system is the name of the image QuPath is currently working with - which you can then modify to create folders named after that image, export subsections of the image, measurements, coordinates, or anything else.
name = GeneralTools.getNameWithoutExtension(imageData.getServer().getMetadata().getName())
Project folder location, and creating folders
Another important bit of code is used to access the folder where the project is currently stored - a file location you know will always exist (unlike the “F” drive, or other fixed destinations).
Build file paths to locations inside of your QuPath Project folder.
pathOutput = buildFilePath(PROJECT_BASE_DIR, "files", name+".csv")
PROJECT_BASE_DIR is the file path to your Project folder. Each comma is a subfolder, and the value after the final comma is the file name. As seen by "files" and the + sign, standard strings can be used as inputs.
Make a directory if one does not already exist. No action is taken if the directory exists.
mkdirs(someDirectoryPath)
This does not make a file - just a folder. Even if you use the line ending in .csv from above. You will end up with a folder with .csv at the end.
To use this, we can create a folder called DeleteMe in the project folder:
name1 = "Delete" name2 = "Me" newFolderPath = buildFilePath(PROJECT_BASE_DIR, name1+name2) mkdirs(newFolderPath)
Create a new File object in a location.
Here we will generate a script that will create a file object. Since this script was written for Windows, you need a double backslash, or a forward slash. This is another way of acting on a file location. It expects that there will be a folder in the E drive called Test.
def rootdir = new File("E:\\Test")
The next two lines create an empty list, and then fill it with all folders within Test that have data
def subdir =[] rootdir.traverse(maxDepth:0){subdir.add(it)}
To see this in action using the ScriptingDemo
directory = buildFilePath(PROJECT_BASE_DIR, "filedemo") folder = new File(directory) files =[] folder.traverse(maxDepth:0){files.add(it)} files.each{print it}
The directory is a file path, which is then converted into a File variable that we can access and do something with. Imagine the directory variable is simply an address. You cannot simply go into the building with an address, you need to use the address to get to the building (which is the File in this case).
"folder" is the variable that holds the "filedemo" folder found inside the Project folder. We can then create an empty list of "files" within "folder" and search for what is directly inside folder. Traverse acts on all files found at a given maximum depth below the folder, and since that is 0, it will not go any deeper. All of those files are "add"ed to the "files" list variable.
The last line cycles through all of the files at that level 0 and prints each one, one at a time.
Replacing the maxDepth of 0 with 1 will go another folder down, and you should get a list of 5 files instead of three (from the demo).
There are a variety of other posts on the forum and on the official documents that show how to use this information to write various types of files. Here are a few examples:
Writing to a file using JSON
Exporting measurements to a CSV
Exporting individual object measurements to individual files per image
Exporting TMA cores and some formatting information
Saving and loading objects from a file
Exporting objects after downsample (JSON) for use in a paired image.
Calling another Groovy script from within a script - plus warnings