From 99c8539e0fd5db50475d36e935537f3664ef46cf Mon Sep 17 00:00:00 2001 From: Alan O'Callaghan Date: Fri, 15 Nov 2024 12:01:49 +0000 Subject: [PATCH 1/3] Remove sout --- src/main/java/qupath/ext/instanseg/core/InstanSegModel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java index 1b53975..ffb6373 100644 --- a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java +++ b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java @@ -193,7 +193,6 @@ public double getPreferredDownsample(PixelCalibration cal) { * @return The exact downsample, unless it's close to an integer, in which case the integer. */ static double getPreferredDownsample(double currentPixelSize, double requestedPixelSize) { - System.out.println(requestedPixelSize); double downsample = requestedPixelSize / currentPixelSize; double downsampleRounded = Math.round(downsample); if (GeneralTools.almostTheSame(downsample, Math.round(downsample), 0.01)) { From 102c0b87cb1a8a563ac7b2d480ba19708d33b38e Mon Sep 17 00:00:00 2001 From: Alan O'Callaghan Date: Wed, 20 Nov 2024 15:23:25 +0000 Subject: [PATCH 2/3] Fix parsing for all cases --- src/main/java/qupath/ext/instanseg/core/InstanSegModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java index ffb6373..5c9a36c 100644 --- a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java +++ b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java @@ -358,8 +358,8 @@ private Optional> getPixelSize() { var config = model.getConfig().getOrDefault("qupath", null); if (config instanceof Map configMap) { var axes = (List) configMap.get("axes"); - String x = (String) ((Map) (axes.get(0))).get("step"); - String y = (String) ((Map) (axes.get(1))).get("step"); + String x = String.valueOf(((Map) (axes.get(0))).get("step")); + String y = String.valueOf(((Map) (axes.get(1))).get("step")); return Optional.of(Map.of( "x", Double.valueOf(x), "y", Double.valueOf(y) From d661aa3e63bcba9689cf4b13234948990130395e Mon Sep 17 00:00:00 2001 From: Alan O'Callaghan Date: Wed, 20 Nov 2024 15:33:57 +0000 Subject: [PATCH 3/3] Implement versioning on my fork --- .../qupath/ext/instanseg/core/InstanSeg.java | 1 + .../ext/instanseg/core/InstanSegModel.java | 52 +++++++++++-------- .../qupath/ext/instanseg/ui/GitHubUtils.java | 2 +- .../ext/instanseg/ui/InstanSegController.java | 21 +++++--- .../java/qupath/ext/instanseg/ui/Watcher.java | 6 +-- 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/main/java/qupath/ext/instanseg/core/InstanSeg.java b/src/main/java/qupath/ext/instanseg/core/InstanSeg.java index b087961..1238cb3 100644 --- a/src/main/java/qupath/ext/instanseg/core/InstanSeg.java +++ b/src/main/java/qupath/ext/instanseg/core/InstanSeg.java @@ -247,6 +247,7 @@ private InstanSegResults runInstanSeg(ImageData imageData, Collec .postProcess(postProcessor) .downsample(downsample) .build(); + processor.processObjects(taskRunner, imageData, pathObjects); int nObjects = pathObjects.stream().mapToInt(PathObject::nChildObjects).sum(); if (predictionProcessor instanceof TilePredictionProcessor tileProcessor) { diff --git a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java index 5c9a36c..421054d 100644 --- a/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java +++ b/src/main/java/qupath/ext/instanseg/core/InstanSegModel.java @@ -30,6 +30,7 @@ public class InstanSegModel { private static final Logger logger = LoggerFactory.getLogger(InstanSegModel.class); + private String version; private URL modelURL = null; /** @@ -44,11 +45,13 @@ public class InstanSegModel { private InstanSegModel(BioimageIoSpec.BioimageIoModel bioimageIoModel) { this.model = bioimageIoModel; this.path = Paths.get(model.getBaseURI()); + this.version = model.getVersion(); this.name = model.getName(); } - private InstanSegModel(String name, URL modelURL) { + private InstanSegModel(String name, String version, URL modelURL) { this.name = name; + this.version = version; this.modelURL = modelURL; } @@ -68,30 +71,29 @@ public static InstanSegModel fromPath(Path path) throws IOException { * @param browserDownloadUrl The download URL from eg GitHub * @return A handle on the created model */ - public static InstanSegModel fromURL(String name, URL browserDownloadUrl) { - return new InstanSegModel(name, browserDownloadUrl); + public static InstanSegModel fromURL(String name, String version, URL browserDownloadUrl) { + return new InstanSegModel(name, version, browserDownloadUrl); } /** * Check if the model has been downloaded already. - * @return True if a flag has been set. + * @return True if the model has a known path that exists and is valid, or if a suitable directory can be found in the localModelPath */ public boolean isDownloaded(Path localModelPath) { // Check path first - *sometimes* the model might be downloaded, but have a name // that doesn't match with the filename (although we'd prefer this didn't happen...) - if (path != null && model != null && Files.exists(path)) + if (path != null && model != null && isValidModel(path)) return true; - // todo: this should also check if the contents are what we expect - if (Files.exists(localModelPath.resolve(name))) { - try { - download(localModelPath); - } catch (IOException e) { - logger.error("Model directory exists but is not valid", e); - } - } else { + if (!Files.exists(localModelPath.resolve(name).resolve(version))) { // The model may have been deleted or renamed - we won't be able to load it return false; } + + try { + download(localModelPath); + } catch (IOException e) { + logger.error("Model directory exists but is not valid", e); + } return path != null && model != null; } @@ -100,14 +102,16 @@ public boolean isDownloaded(Path localModelPath) { * @throws IOException If an error occurs when downloading, unzipping, etc. */ public void download(Path localModelPath) throws IOException { - if (path != null && Files.exists(path) && model != null) + if (path != null && isValidModel(path) && model != null) { return; + } var zipFile = downloadZipIfNeeded( this.modelURL, localModelPath, - name); + name + "-" + version); this.path = unzipIfNeeded(zipFile); this.model = BioimageIoSpec.parseModel(path.toFile()); + this.version = model.getVersion(); } /** @@ -213,8 +217,8 @@ public Optional getPath() { @Override public String toString() { String name = getName(); - String parent = getPath().map(Path::getFileName).map(Path::toString).orElse(null); - String version = getModel().map(BioimageIoSpec.BioimageIoModel::getVersion).orElse(null); + String parent = getPath().map(Path::getParent).map(Path::getFileName).map(Path::toString).orElse(null); + String version = getModel().map(BioimageIoSpec.BioimageIoModel::getVersion).orElse(this.version); if (parent != null && !parent.equals(name)) { name = parent + "/" + name; } @@ -290,16 +294,20 @@ private static boolean isDownloadedAlready(Path zipFile) { return Files.exists(zipFile); } - private static Path unzipIfNeeded(Path zipFile) throws IOException { - var outdir = zipFile.resolveSibling(zipFile.getFileName().toString().replace(".zip", "")); + private Path unzipIfNeeded(Path zipFile) throws IOException { + var zipSpec = BioimageIoSpec.parseModel(zipFile); + String version = zipSpec.getVersion(); + var outdir = zipFile.resolveSibling(zipSpec.getName()).resolve(version); if (!isUnpackedAlready(outdir)) { try { - unzip(zipFile, zipFile.getParent()); + unzip(zipFile, outdir); // Files.delete(zipFile); } catch (IOException e) { + logger.error("Error unzipping model", e); // clean up files just in case! - Files.deleteIfExists(zipFile); Files.deleteIfExists(outdir); + } finally { + Files.deleteIfExists(zipFile); } } return outdir; @@ -311,7 +319,7 @@ private static boolean isUnpackedAlready(Path outdir) { private static void unzip(Path zipFile, Path destination) throws IOException { if (!Files.exists(destination)) { - Files.createDirectory(destination); + Files.createDirectories(destination); } ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile.toFile()))); ZipEntry entry = zipIn.getNextEntry(); diff --git a/src/main/java/qupath/ext/instanseg/ui/GitHubUtils.java b/src/main/java/qupath/ext/instanseg/ui/GitHubUtils.java index 31aee16..8cd204b 100644 --- a/src/main/java/qupath/ext/instanseg/ui/GitHubUtils.java +++ b/src/main/java/qupath/ext/instanseg/ui/GitHubUtils.java @@ -83,7 +83,7 @@ public String toString() { static List getReleases(Path modelDir) { Path cachedReleases = modelDir == null ? null : modelDir.resolve("releases.json"); - String uString = "https://api.github.com/repos/instanseg/InstanSeg/releases"; + String uString = "https://api.github.com/repos/alanocallaghan/InstanSeg/releases"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(uString)) .GET() diff --git a/src/main/java/qupath/ext/instanseg/ui/InstanSegController.java b/src/main/java/qupath/ext/instanseg/ui/InstanSegController.java index ad91d8d..c0025ce 100644 --- a/src/main/java/qupath/ext/instanseg/ui/InstanSegController.java +++ b/src/main/java/qupath/ext/instanseg/ui/InstanSegController.java @@ -55,6 +55,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -549,6 +550,7 @@ private InstanSegModel downloadModel(InstanSegModel model, Path modelDir) { }); } catch (IOException ex) { Dialogs.showErrorNotification(resources.getString("title"), resources.getString("error.downloading")); + logger.error("Error download model", ex); } return model; } @@ -618,15 +620,18 @@ private static List getRemoteModels() { logger.info("No releases found."); return List.of(); } - var release = releases.getFirst(); - var assets = GitHubUtils.getAssets(release); List models = new ArrayList<>(); - for (var asset : assets) { - models.add( - InstanSegModel.fromURL( - asset.getName().replace(".zip", ""), - asset.getUrl()) - ); + for (var release: releases) { + var assets = GitHubUtils.getAssets(release); + for (var asset: assets) { + models.add( + InstanSegModel.fromURL( + asset.getName().replace(".zip", ""), + release.getName(), + asset.getUrl() + ) + ); + } } return List.copyOf(models); } diff --git a/src/main/java/qupath/ext/instanseg/ui/Watcher.java b/src/main/java/qupath/ext/instanseg/ui/Watcher.java index 8757270..95061e5 100644 --- a/src/main/java/qupath/ext/instanseg/ui/Watcher.java +++ b/src/main/java/qupath/ext/instanseg/ui/Watcher.java @@ -74,11 +74,11 @@ private void handleModelDirectoryChange(ObservableValue observab // Currently, we look *only* in the model directory for models // But we could register subdirectories here if we wanted (e.g. 'local', 'downloaded') if (oldPath != null) { - unregister(oldPath); + unregister(oldPath.resolve("local")); } - if (newPath != null && Files.isDirectory(newPath)) { + if (newPath != null && Files.isDirectory(newPath.resolve("local"))) { try { - register(newPath); + register(newPath.resolve("local")); } catch (IOException e) { logger.error("Unable to register new model directory", e); }