From dbc985c8eae54a820da81536a80e1babd1942550 Mon Sep 17 00:00:00 2001 From: pete Date: Sat, 7 Aug 2021 21:25:03 +0100 Subject: [PATCH 1/4] Bump version to rc2 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c7de1a0..02005e0 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ ext.moduleName = 'qupath.extension.tensorflow' description = 'QuPath extension to use TensorFlow' -version = "0.3.0-SNAPSHOT" +version = "0.3.0-rc2" dependencies { def tensorflowVersion = "0.3.2" @@ -113,4 +113,4 @@ tasks.withType(org.gradle.jvm.tasks.Jar) { tasks.named('test') { useJUnitPlatform() -} \ No newline at end of file +} From d4f8600efca3e60977e4c2689635b15759983d6c Mon Sep 17 00:00:00 2001 From: pete Date: Sun, 8 Aug 2021 12:33:27 +0100 Subject: [PATCH 2/4] Update for QuPath v0.3.0-rc2 --- README.md | 36 +++++++++++++++++-- build.gradle | 13 +++++-- .../ext/tensorflow/TensorFlowExtension.java | 5 +++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3f3142..c16894b 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,17 @@ and remains in a not-quite-complete state. Its previous main use was to run [StarDist](https://qupath.readthedocs.io/en/0.2/docs/advanced/stardist.html) nucleus identification, although the new [QuPath StarDist extension](https://github.com/qupath/qupath-extension-stardist) does not require that TensorFlow is available. +> **Important!** TensorFlow Java does not currently support Mac computers with Apple silicon. + ## Building +There is no pre-built version of the TensorFlow extension at this time, because most people shouldn't need it and there are lots of different permutations of dependencies that might be required for different platforms. + +For that reason, it needs to be built from source. +If you want to match to a specific version, you can download the source from the [releases](https://github.com/qupath/qupath-extension-tensorflow/releases) page. + + ### Extension + dependencies separately You can build the extension with @@ -37,7 +45,7 @@ extension and all its dependencies with gradlew clean shadowjar ``` -### GPU support +### Alternative platforms The default build process will use TensorFlow for the CPU. @@ -48,11 +56,35 @@ To use any of these, add the platform to any of the building tasks above. For example, to create a single GPU-friendly jar, use ```bash -gradlew clean shadowjar -P platform=gpu +gradlew clean build copyDependencies -P platform=gpu ``` The platforms available at the time of writing are `mkl`, `gpu`, `mkl-gpu`. +> Not all options are available for all operating systems. +> For example, GPU support is not available with macOS. + + +### GPU support + +When using `platform=gpu`, you will need +* an NVIDIA GPU +* CUDA and cuDNN + +Installation may be simplified if you include + +```bash +gradlew clean build copyDependencies -P platform=gpu -Pcuda-redist +``` + +to download the required CUDA files via JavaCPP. + +Before using this option, please check https://github.com/bytedeco/javacpp-presets/tree/master/cuda for +the terms of license agreements for NVIDIA software included in the archives. + +> **Warning!** At the time of writing, the CUDA version used with TensorFlow Java differs from that +> used with OpenCV via JavaCPP. This is likely to cause problems if trying to use both. + ## Installing diff --git a/build.gradle b/build.gradle index 02005e0..e332001 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ version = "0.3.0-rc2" dependencies { def tensorflowVersion = "0.3.2" def cudaVersion = "11.0-8.0-1.5.4" // Warning! This doesn't match version with QuPath v0.3.0 + opencv-platform-gpu - def qupathVersion = "0.3.0-SNAPSHOT" // For now + def qupathVersion = "0.3.0-rc2" // For now def platform = project.findProperty("platform") @@ -62,13 +62,21 @@ dependencies { testImplementation "io.github.qupath:qupath-gui-fx:${qupathVersion}" testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.2" testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' - + } processResources { from ("${projectDir}/LICENSE.txt") { into 'licenses/' } + + doLast { + if (useCuda) { + logger.quiet("Requesting CUDA support - by downloading these archives you agree to the terms of the license " + + " agreements for NVIDIA software included in the archives.") + logger.quiet("See https://github.com/bytedeco/javacpp-presets/tree/master/cuda for more information.") + } + } } tasks.register("copyDependencies", Copy) { @@ -90,6 +98,7 @@ java { withSourcesJar() if (project.properties['javadocs']) withJavadocJar() + } /* diff --git a/src/main/java/qupath/ext/tensorflow/TensorFlowExtension.java b/src/main/java/qupath/ext/tensorflow/TensorFlowExtension.java index aae900e..14abd10 100644 --- a/src/main/java/qupath/ext/tensorflow/TensorFlowExtension.java +++ b/src/main/java/qupath/ext/tensorflow/TensorFlowExtension.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import qupath.lib.common.Version; import qupath.lib.gui.QuPathGUI; import qupath.lib.gui.extensions.GitHubProject; import qupath.lib.gui.extensions.QuPathExtension; @@ -61,6 +62,10 @@ public GitHubRepo getRepository() { return GitHubRepo.create(getName(), "qupath", "qupath-extension-tensorflow"); } + @Override + public Version getQuPathVersion() { + return Version.parse("0.3.0-rc2"); + } } From 51a476181b094a236a26e3b821cc99e4b41e0e1f Mon Sep 17 00:00:00 2001 From: pete Date: Tue, 31 Aug 2021 19:21:12 +0100 Subject: [PATCH 3/4] Bump TensorFlow Java version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e332001..7ceb9b7 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ description = 'QuPath extension to use TensorFlow' version = "0.3.0-rc2" dependencies { - def tensorflowVersion = "0.3.2" + def tensorflowVersion = "0.3.3" def cudaVersion = "11.0-8.0-1.5.4" // Warning! This doesn't match version with QuPath v0.3.0 + opencv-platform-gpu def qupathVersion = "0.3.0-rc2" // For now From 7ddfe2922ff2de9bb007d623007d5a318fe924ea Mon Sep 17 00:00:00 2001 From: pete Date: Wed, 1 Sep 2021 15:31:03 +0100 Subject: [PATCH 4/4] Fix reading input/output tensor names Turns out 'TensorInfo.getName()' isn't the way to go, and it's necessary to use the map key instead. --- .../java/qupath/ext/tensorflow/TensorFlowBundle.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/qupath/ext/tensorflow/TensorFlowBundle.java b/src/main/java/qupath/ext/tensorflow/TensorFlowBundle.java index bbccb1b..8fd01d2 100644 --- a/src/main/java/qupath/ext/tensorflow/TensorFlowBundle.java +++ b/src/main/java/qupath/ext/tensorflow/TensorFlowBundle.java @@ -74,8 +74,8 @@ private TensorFlowBundle(String pathModel) { if (inputs == null || inputs.isEmpty()) { logger.info("Found SignatureDef: {} (method={})", entry.getKey(), sigdef.getMethodName()); signatureDefKey = entry.getKey(); - inputs = sigdef.getInputsMap().values().stream().map(t -> new SimpleTensorInfo(t)).collect(Collectors.toList()); - outputs = sigdef.getOutputsMap().values().stream().map(t -> new SimpleTensorInfo(t)).collect(Collectors.toList()); + inputs = sigdef.getInputsMap().entrySet().stream().map(e -> new SimpleTensorInfo(e.getKey(), e.getValue())).collect(Collectors.toList()); + outputs = sigdef.getOutputsMap().entrySet().stream().map(e -> new SimpleTensorInfo(e.getKey(), e.getValue())).collect(Collectors.toList()); } else { logger.warn("Extra SignatureDef found - will be ignored ({}, method={})", entry.getKey(), sigdef.getMethodName()); } @@ -95,6 +95,7 @@ private TensorFlowBundle(String pathModel) { private static Map cachedBundles = new HashMap<>(); static TensorFlowBundle loadBundle(String path) { + cachedBundles.clear(); return cachedBundles.computeIfAbsent(path, p -> new TensorFlowBundle(p)); } @@ -198,9 +199,9 @@ public static class SimpleTensorInfo { private String name; private long[] shape; - SimpleTensorInfo(TensorInfo info) { + SimpleTensorInfo(String name, TensorInfo info) { this.info = info; - this.name = info.getName(); + this.name = name; if (info.hasTensorShape()) { var dims = info.getTensorShape().getDimList(); shape = new long[dims.size()];