Skip to content

Commit

Permalink
Threading to allow stitching during acquisition
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelSNelson committed Jan 18, 2024
1 parent cbb1839 commit f17b782
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 58 deletions.
96 changes: 50 additions & 46 deletions src/main/groovy/qupath/ext/qp_scope/functions/QP_scope_GUI.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import qupath.lib.objects.PathObject
import qupath.lib.projects.Project
import qupath.lib.projects.ProjectImageEntry
import qupath.lib.scripting.QP

import java.util.concurrent.Semaphore
import java.awt.geom.AffineTransform
import java.awt.image.BufferedImage

Expand Down Expand Up @@ -472,61 +472,65 @@ class QP_scope_GUI {
logger.info("Scan type with index: " + scanTypeWithIndex)
logger.info(tempTileDirectory)

//TODO NEEDS MASSIVE FIXIN'
// A semaphore to control the stitching process
Semaphore stitchingSemaphore = new Semaphore(1)


for (annotation in annotations) {
String annotationName = annotation.getName()
List args = [projectsFolderPath,
sampleLabel,
scanTypeWithIndex,
annotation.getName()]
annotationName]
//TODO how can we distinguish between a hung python run and one that is taking a long time? - possibly check for new files in target folder?
//Progress bar that updates by checking target folder for new images?
UtilityFunctions.runPythonCommand(virtualEnvPath, pythonScriptPath, args)
//UtilityFunctions.runPythonCommand(virtualEnvPath, "C:\\ImageAnalysis\\python\\py_dummydoc.py", args)
logger.info("Finished Python Command")
String stitchedImageOutputFolder = projectsFolderPath + File.separator + sampleLabel + File.separator + "SlideImages"
//TODO Need to check if stitching is successful, provide error
//TODO get pixel size from somewhere???
//TODO BEGIN SECOND COLLECTION WHILE FIRST IS STITCHING
logger.info("Begin stitching")
String stitchedImagePathStr = UtilityFunctions.stitchImagesAndUpdateProject(projectsFolderPath,
sampleLabel, scanTypeWithIndex as String, annotation.getName(),
qupathGUI, currentQuPathProject, preferences.compression)
logger.info(stitchedImagePathStr)
// String stitchedImagePathStr = StitchingImplementations.stitchCore("Coordinates in TileConfiguration.txt file",
// projectsFolderPath + File.separator + sampleLabel + File.separator + scanTypeWithIndex,
// stitchedImageOutputFolder,
// "J2K_LOSSY",
// 0,
// 1,
// annotation.getName())
// logger.info("Get project")
//
// //UtilityFunctions.showAlertDialog("Wait and complete stitching in other version of QuPath")
//
// //String stitchedImagePathStr = stitchedImageOutputFolder + File.separator + preferences.secondScanType + sampleLabel + ".ome.tif"
// File stitchedImagePath = new File(stitchedImagePathStr)
// UtilityFunctions.addImageToProject(stitchedImagePath, currentQuPathProject)
//
// //open the newly created project
// //https://qupath.github.io/javadoc/docs/qupath/lib/gui/QuPathGUI.html#setProject(qupath.lib.projects.Project)
// def qupathGUI = QPEx.getQuPath()
logger.info("Finished Python Command for $annotationName")
// Start a new thread for stitching
Thread.start {
try {
// Acquire a permit before starting stitching
stitchingSemaphore.acquire()

logger.info("Begin stitching")
String stitchedImagePathStr = UtilityFunctions.stitchImagesAndUpdateProject(projectsFolderPath,
sampleLabel, scanTypeWithIndex as String, annotationName,
qupathGUI, currentQuPathProject, preferences.compression)
logger.info(stitchedImagePathStr)


} catch (InterruptedException e) {
logger.error("Stitching thread interrupted", e)
} finally {
// Release the semaphore for the next stitching process
stitchingSemaphore.release()
}
}
// //TODO Need to check if stitching is successful, provide error
// //TODO get pixel size from somewhere???
// //TODO BEGIN SECOND COLLECTION WHILE FIRST IS STITCHING
// logger.info("Begin stitching")
// String stitchedImagePathStr = UtilityFunctions.stitchImagesAndUpdateProject(projectsFolderPath,
// sampleLabel, scanTypeWithIndex as String, annotation.getName(),
// qupathGUI, currentQuPathProject, preferences.compression)
// logger.info(stitchedImagePathStr)
//
// //qupathGUI.setProject(currentQuPathProject)
// //Find the existing images - there should only be one since the project was just created
// def matchingImage = currentQuPathProject.getImageList().find { image ->
// (new File(image.getImageName()).name == new File(stitchedImagePathStr).name)
// //Check if the tiles should be deleted from the collection folder
// if (preferences.tileHandling == "Delete")
// UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
// if (preferences.tileHandling == "Zip") {
// UtilityFunctions.zipTilesAndMove(tempTileDirectory)
// UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
// }
// qupathGUI.refreshProject()
//Open the first image
//https://qupath.github.io/javadoc/docs/qupath/lib/gui/QuPathGUI.html#openImageEntry(qupath.lib.projects.ProjectImageEntry)

//Check if the tiles should be deleted from the collection folder
if (preferences.tileHandling == "Delete")
UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
if (preferences.tileHandling == "Zip") {
UtilityFunctions.zipTilesAndMove(tempTileDirectory)
UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
}
}
// Post-stitching tasks like deleting or zipping tiles
//Check if the tiles should be deleted from the collection folder
if (preferences.tileHandling == "Delete")
UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
if (preferences.tileHandling == "Zip") {
UtilityFunctions.zipTilesAndMove(tempTileDirectory)
UtilityFunctions.deleteTilesAndFolder(tempTileDirectory)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package qupath.ext.qp_scope.utilities


import javafx.application.Platform
import org.slf4j.LoggerFactory
import qupath.lib.gui.QuPathGUI
import qupath.lib.gui.commands.ProjectCommands
Expand Down Expand Up @@ -148,7 +148,7 @@ class UtilityFunctions {
String stitchedImageOutputFolder = projectsFolderPath + File.separator + sampleLabel + File.separator + "SlideImages"
String tileImageInputFolder = projectsFolderPath + File.separator + sampleLabel + File.separator + scanTypeWithIndex


logger.info("Calling stitchCore with $tileImageInputFolder")
String stitchedImagePathStr = StitchingImplementations.stitchCore("Coordinates in TileConfiguration.txt file",
tileImageInputFolder, stitchedImageOutputFolder, compression,
0, 1, annotationName)
Expand All @@ -161,17 +161,18 @@ class UtilityFunctions {
if (stitchedImagePath.renameTo(adjustedFilePath)) {
stitchedImagePathStr = adjustedFilePath.absolutePath
}
Platform.runLater {
logger.info("Platform.runLater section of stitchImagesAndUpdateProject")
// Add the (possibly renamed) image to the project
addImageToProject(adjustedFilePath, currentQuPathProject)
def matchingImage = currentQuPathProject.getImageList().find { image ->
new File(image.getImageName()).name == adjustedFilePath.name
}

// Add the (possibly renamed) image to the project
addImageToProject(adjustedFilePath, currentQuPathProject)
def matchingImage = currentQuPathProject.getImageList().find { image ->
new File(image.getImageName()).name == adjustedFilePath.name
qupathGUI.openImageEntry(matchingImage)
qupathGUI.setProject(currentQuPathProject)
qupathGUI.refreshProject()
}

qupathGUI.openImageEntry(matchingImage)
qupathGUI.setProject(currentQuPathProject)
qupathGUI.refreshProject()

return stitchedImagePathStr
}

Expand Down Expand Up @@ -284,7 +285,7 @@ class UtilityFunctions {
tissueDetection : "DetectTissue.groovy",
firstScanType : "4x_bf",
secondScanType : "20x_bf",
tileHandling : "Zip",
tileHandling : "none", //"Zip" or "Delete" are functional, anything else does nothing
pixelSizeSource : "7.2",
pixelSizeFirstScanType : "1.105",
pixelSizeSecondScanType: "0.5",
Expand Down

0 comments on commit f17b782

Please sign in to comment.