Skip to content

Commit

Permalink
Hopefully use correct downsample based on pixel size
Browse files Browse the repository at this point in the history
  • Loading branch information
alanocallaghan committed Feb 19, 2024
1 parent 1e4f81b commit 3a8bdcd
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
66 changes: 58 additions & 8 deletions src/main/java/qupath/ext/instanseg/core/InstanSegModel.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package qupath.ext.instanseg.core;


import com.google.gson.internal.LinkedTreeMap;
import qupath.bioimageio.spec.BioimageIoSpec;
import qupath.lib.gui.UserDirectoryManager;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.Enumeration;


// local class can have a constructor that takes a path and
// remote class is empty, and fields get populated when we "fetch" it (check locally and download if not)
public class InstanSegModel {

private FileSystem fileSystem = null;
private Path path = null;
private URL modelURL = null;
private BioimageIoSpec.BioimageIoModel model = null;
Expand All @@ -34,29 +37,75 @@ public InstanSegModel(URL modelURL, String name) {
}

public static InstanSegModel createModel(Path path) throws IOException {
if (path.toString().endsWith(".zip")) {
var outfile = path.getParent().resolve(path.toString().replace(".zip", ""));
unzip(path, outfile);
path = outfile;
}
return new InstanSegModel(BioimageIoSpec.parseModel(path.toFile()));
}

public BioimageIoSpec.BioimageIoModel getModel() throws IOException {
public BioimageIoSpec.BioimageIoModel getModel() {
if (model == null) {
fetchModel();
try {
fetchModel();
} catch (IOException e) {
// todo: exception handling here, or...?
throw new RuntimeException(e);
}
}
return model;
}

public Double getPixelSizeX() {
return getPixelSize().get("x");
}

public Double getPixelSizeY() {
return getPixelSize().get("y");
}

private Map<String, Double> getPixelSize() {
return (Map<String, Double>) ((LinkedTreeMap<?, ?>)getModel().getConfig().get("qupath")).get("pixel_size");
}

private void fetchModel() throws IOException {
if (modelURL == null) {
throw new NullPointerException("Model URL should not be null for a local model!");
}
downloadZipFile(modelURL, getUserDir().resolve("instanseg"));
downloadAndUnzip(modelURL, getUserDir().resolve("instanseg"));
}

private static void downloadZipFile(URL url, Path localDirectory) throws IOException {
private static void downloadAndUnzip(URL url, Path localDirectory) throws IOException {
// Open a connection to the URL
try (BufferedInputStream in = new BufferedInputStream(url.openStream())) {
String fileName = url.toString().substring(url.toString().lastIndexOf('/') + 1);
Path localFilePath = localDirectory.resolve(fileName);
Files.copy(in, localFilePath, StandardCopyOption.REPLACE_EXISTING);
Path outdir = localDirectory.resolve(fileName.replace(".zip", ""));
unzip(localFilePath, outdir);
}
}

private static void unzip(Path zipFilePath, Path destDirectory) throws IOException {
if (!Files.exists(destDirectory)) {
Files.createDirectory(destDirectory); // todo: deal with this?
}
try (ZipFile zipFile = new ZipFile(String.valueOf(zipFilePath))) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();

while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
String entryName = zipEntry.getName();
Path entryPath = destDirectory.resolve(entryName);

if (zipEntry.isDirectory()) {
Files.createDirectories(entryPath);
} else {
Files.createDirectories(entryPath.getParent());
Files.copy(zipFile.getInputStream(zipEntry), entryPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}

Expand All @@ -75,6 +124,7 @@ public Path getPath() {
try {
fetchModel();
} catch (IOException e) {
// todo: handle here, or...?
throw new RuntimeException(e);
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/qupath/ext/instanseg/core/InstanSegTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,33 @@
public class InstanSegTask extends Task<Void> {
private static final Logger logger = LoggerFactory.getLogger(InstanSegTask.class);
private final int tileSize, nThreads;
private double downsample;
private final Path modelPath;
private final String deviceName;

public InstanSegTask(Path modelPath, int tileSize, int nThreads, String deviceName) {
public InstanSegTask(Path modelPath, int tileSize, int nThreads, double downsample, String deviceName) {
this.modelPath = modelPath;
this.tileSize = tileSize;
this.nThreads = nThreads;
this.downsample = downsample;
this.deviceName = deviceName;
}

private static void printResourceCount(String title, BaseNDManager manager) {
logger.info(title);
manager.debugDump(2);
}

@Override
protected Void call() throws Exception {
// May need to reduce threads to avoid trouble (especially if using mps/cuda)
// int nThreads = qupath.lib.common.ThreadTools.getParallelism()
logger.info("Using $nThreads threads");
int nPredictors = 1;

// TODO: Set path!
var imageData = QP.getCurrentImageData();

double downsample = 0.5 / imageData.getServer().getPixelCalibration().getAveragedPixelSize().doubleValue();
// todo: based on pixel size
// double downsample = 0.5 / imageData.getServer().getPixelCalibration().getAveragedPixelSize().doubleValue();

int inputWidth = tileSize;
// int inputWidth = 256;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import qupath.lib.common.ThreadTools;
import qupath.lib.gui.QuPathGUI;
import qupath.lib.images.ImageData;
import qupath.lib.images.servers.ImageServer;
import qupath.lib.objects.PathObject;
import qupath.lib.objects.hierarchy.PathObjectHierarchy;
import qupath.lib.objects.hierarchy.events.PathObjectSelectionListener;
Expand Down Expand Up @@ -364,10 +365,15 @@ private static boolean isValidModel(Path path) {

@FXML
private void runInstanSeg() {
// todo: pixel size
var model = modelChoiceBox.getSelectionModel().getSelectedItem();
ImageServer<?> server = qupath.getImageData().getServer();
var task = new InstanSegTask(
modelChoiceBox.getSelectionModel().getSelectedItem().getPath().resolve("instanseg.pt"),
// todo: get weights from inside zipped models
model.getPath().resolve("instanseg.pt"),
InstanSegPreferences.tileSizeProperty().get(),
InstanSegPreferences.numThreadsProperty().getValue(),
model.getPixelSizeX() / (double)server.getPixelCalibration().getAveragedPixelSize(),
deviceChoices.getSelectionModel().getSelectedItem());
pendingTask.set(task);
// Reset the pending task when it completes (either successfully or not)
Expand Down

0 comments on commit 3a8bdcd

Please sign in to comment.