Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save petebankhead/48c27f0f2c6a13945a812330f80240c5 to your computer and use it in GitHub Desktop.
Save petebankhead/48c27f0f2c6a13945a812330f80240c5 to your computer and use it in GitHub Desktop.
QuPath script to copy simple, single-file images into a project to make the project self-contained.
/**
* QuPath script to copy images into a project.
* This is used to help make the project directory self-contained.
*
* By default, local files are copied into an 'images' subdirectory of the project.
*
* Limitation: This only handles local files that are currently outside the project directory,
* and excludes images that are known to have 'multipart' files (e.g. dicom, mrxs, vsi & zarr).
*
* Warning: This has not been very extensively tested!
* It shouldn't delete or overwrite files (only copy them), but still... please check the code & use with caution.
*
* Originally written for QuPath v0.6.0.
*/
import qupath.lib.common.GeneralTools
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import static qupath.lib.scripting.QP.*
Set multipartFileExtensions = Set.of(".dicom", ".mrxs", ".vsi", ".zarr")
String imagesSubDirName = "images"
// Check we can get a base directory for the project
var project = getProject()
var projectPath = project.getPath()
if (projectPath == null || !Files.exists(projectPath)) {
throw new UnsupportedOperationException("Project path not found!");
}
var baseDir = Files.isDirectory(projectPath) ? projectPath.toRealPath() : projectPath.getParent().toRealPath()
// Get all the unique URIs
var uris = project.getImageList()
.stream()
.flatMap(e -> e.getURIs().stream())
.distinct()
.toList()
// Check if we have anything to do at all
if (!uris.stream().anyMatch(u -> isExternalLocalFile(u, baseDir))) {
println "No paths to update!"
return
}
// Create an images directory within the project
var imagesDir = baseDir.resolve(imagesSubDirName)
if (!Files.exists(imagesDir)) {
println "Creating 'images' subdirectory in project"
Files.createDirectories(imagesDir);
}
// Gather all the URIs for files that are not already contained within the base directory
Map<URI, URI> replacements = new HashMap<>()
for (var uri : uris) {
var path = GeneralTools.toPath(uri)
if (isExternalLocalFile(path, baseDir)) {
String baseName = path.getFileName().toString()
if (GeneralTools.checkExtensions(baseName, multipartFileExtensions.toArray(String[]::new))) {
println "WARN: Skipping potential multi-part file " + path
continue
}
String targetName = baseName
int count = 0
while (Files.exists(imagesDir.resolve(targetName))) {
// TODO: Consider checking file contents, so we don't end up with duplicates of the same image
com.google.common.io.Files.getNameWithoutExtension()
count++
targetName = baseName + "-" + count
}
var targetPath = imagesDir.resolve(targetName)
println "Copying " + path + " -> " + targetPath
Files.copy(path, targetPath, StandardCopyOption.COPY_ATTRIBUTES)
replacements.put(uri, targetPath.toUri())
}
}
// Update the URIs, if required
if (replacements.isEmpty()) {
println "No replacements made!"
} else {
if (replacements.size() == 1)
println "Replaced 1 URI"
else
println "Replaced " + replacements.size() + " URIs"
project.getImageList().stream().forEach(e -> e.updateURIs(replacements))
}
/**
* Check if a URI is a local file outside a specified base directory.
* @param uri the URI to check
* @param baseDir the base directory
* @return true if the URI refers to a file, and it is not located within the baseDir; false otherwise
*/
private static boolean isExternalLocalFile(URI uri, Path baseDir) {
var path = GeneralTools.toPath(uri)
return isExternalLocalFile(path, baseDir)
}
/**
* Check if a path refers to a local file outside a specified base directory.
* @param path the path to check
* @param baseDir the base directory
* @return true if the path refers to a file, and it is not located within the baseDir; false otherwise
*/
private static boolean isExternalLocalFile(Path path, Path baseDir) {
if (path == null || !Files.exists(path))
return null;
path = path.toRealPath()
return !path.toString().startsWith(baseDir.toString())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment