Skip to content

Commit

Permalink
Refactor 092024 (#52)
Browse files Browse the repository at this point in the history
* fixes confusion between per tile and global preprocessing methods

* fixes formatting

* fixes rounding of downsampling

fixes typos, formatting and comment contents

* updates logic of handling tiles

* updates demo scripts

* fixes IntelliJ warnings

* bumps version number
  • Loading branch information
lacan authored Sep 12, 2024
1 parent 9d75c28 commit b97efda
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 185 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ext.qupathVersion = gradle.ext.qupathVersion

description = 'QuPath extension to use Cellpose'

version = "0.9.6-SNAPSHOT"
version = "0.9.6"

dependencies {
implementation "io.github.qupath:qupath-gui-fx:${qupathVersion}"
Expand Down
268 changes: 125 additions & 143 deletions src/main/java/qupath/ext/biop/cellpose/Cellpose2D.java

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/main/java/qupath/ext/biop/cellpose/CellposeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public CellposeBuilder preprocess(ImageOp... ops) {
* @param global preprocessing operation
* @return this builder
*/
public CellposeBuilder preprocess(TileOpCreator global) {
public CellposeBuilder preprocessGlobal(TileOpCreator global) {
this.globalPreprocessing = global;
return this;
}
Expand Down Expand Up @@ -761,7 +761,7 @@ public CellposeBuilder normalizePercentilesGlobal(double percentileMin, double p
this.noCellposeNormalization();

// Add this operation to the preprocessing
return this.preprocess(normOp);
return this.preprocessGlobal(normOp);
}

/**
Expand Down Expand Up @@ -834,7 +834,7 @@ public Cellpose2D build() {

cellpose.extendChannelOp = extendChannelOp;


// TODO make compatible with --all_channels
if (this.channels.length > 2) {
logger.warn("You supplied {} channels, but Cellpose needs two channels at most. Keeping the first two", channels.length);
this.channels = Arrays.copyOf(this.channels, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ public void installExtension(QuPathGUI qupath) {
StringProperty omniposePath = PathPrefs.createPersistentPreference("omniposePythonPath", "");
StringProperty condaPath = PathPrefs.createPersistentPreference("condaPath", "");


//Set options to current values
options.setCellposePythonPath(cellposePath.get());
options.setOmniposePythonPath(omniposePath.get());
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/qupath/ext/biop/cellpose/CellposeSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public class CellposeSetup {
private static final CellposeSetup instance = new CellposeSetup();
private String cellposePythonPath = null;
private String omniposePythonPath = null;

private String condaPath = null;

public static CellposeSetup getInstance() {
Expand Down Expand Up @@ -41,7 +40,7 @@ public void setCondaPath(String condaPath) {

private void checkPath(String path) {
// It should be a file and it should exist
if( !path.equals("")) {
if(!path.trim().isEmpty()) {
File toCheck = new File(path);
if (!toCheck.exists())
Dialogs.showWarningNotification("Cellpose/Omnipose extension: Path not found", "The path to \"" + path + "\" does not exist or does not point to a valid file.");
Expand Down
34 changes: 17 additions & 17 deletions src/main/resources/scripts/Cellpose_detection_template.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,41 @@
// Specify the model name (cyto, nuclei, cyto2, ... or a path to your custom model as a string)
// Other models for Cellpose https://cellpose.readthedocs.io/en/latest/models.html
// And for Omnipose: https://omnipose.readthedocs.io/models.html
def pathModel = 'cyto2'
def pathModel = 'cyto3'
def cellpose = Cellpose2D.builder( pathModel )
.pixelSize( 0.5 ) // Resolution for detection in um
.channels( 'DAPI' ) // Select detection channel(s)
// .tempDirectory( new File( '/tmp' ) ) // Temporary directory to export images to. defaults to 'cellpose-temp' inside the QuPath Project
// .preprocess( ImageOps.Filters.median(1) ) // List of preprocessing ImageOps to run on the images before exporting them
// .normalizePercentilesGlobal(0.1, 99.8, 10) // Convenience global percentile normalization. arguments are percentileMin, percentileMax, dowsample.
// .tileSize(1024) // If your GPU can take it, make larger tiles to process fewer of them. Useful for Omnipose
// .cellposeChannels(1,2) // Overwrites the logic of this plugin with these two values. These will be sent directly to --chan and --chan2
// .cellprobThreshold(0.0) // Threshold for the mask detection, defaults to 0.0
// .flowThreshold(0.4) // Threshold for the flows, defaults to 0.4
// .diameter(15) // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
// .preprocess( ImageOps.Filters.median( 1 ) ) // List of preprocessing ImageOps to run on the images before exporting them
// .normalizePercentilesGlobal( 0.1, 99.8, 10 ) // Convenience global percentile normalization. arguments are percentileMin, percentileMax, dowsample.
// .tileSize( 1024 ) // If your GPU can take it, make larger tiles to process fewer of them. Useful for Omnipose
// .cellposeChannels( 1,2 ) // Overwrites the logic of this plugin with these two values. These will be sent directly to --chan and --chan2
// .cellprobThreshold( 0.0 ) // Threshold for the mask detection, defaults to 0.0
// .flowThreshold( 0.4 ) // Threshold for the flows, defaults to 0.4
// .diameter( 15 ) // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
// .useOmnipose() // Use omnipose instead
// .addParameter("cluster") // Any parameter from cellpose or omnipose not available in the builder.
// .addParameter("save_flows") // Any parameter from cellpose or omnipose not available in the builder.
// .addParameter("anisotropy", "3") // Any parameter from cellpose or omnipose not available in the builder.
// .cellExpansion(5.0) // Approximate cells based upon nucleus expansion
// .cellConstrainScale(1.5) // Constrain cell expansion using nucleus size
// .classify("My Detections") // PathClass to give newly created objects
// .addParameter( "cluster" ) // Any parameter from cellpose or omnipose not available in the builder.
// .addParameter( "save_flows" ) // Any parameter from cellpose or omnipose not available in the builder.
// .addParameter( "anisotropy", "3" ) // Any parameter from cellpose or omnipose not available in the builder.
// .cellExpansion( 5.0 ) // Approximate cells based upon nucleus expansion
// .cellConstrainScale( 1.5 ) // Constrain cell expansion using nucleus size
// .classify( "My Detections" ) // PathClass to give newly created objects
// .measureShape() // Add shape measurements
// .measureIntensity() // Add cell measurements (in all compartments)
// .createAnnotations() // Make annotations instead of detections. This ignores cellExpansion
// .simplify(0) // Simplification 1.6 by default, set to 0 to get the cellpose masks as precisely as possible
// .simplify( 0 ) // Simplification 1.6 by default, set to 0 to get the cellpose masks as precisely as possible
.build()

// Run detection for the selected objects
def imageData = getCurrentImageData()
def pathObjects = getSelectedObjects() // To process only selected annotations, useful while testing
// def pathObjects = getAnnotationObjects() // To process all annotations. For working in batch mode
if (pathObjects.isEmpty()) {
Dialogs.showErrorMessage("Cellpose", "Please select a parent object!")
Dialogs.showErrorMessage( "Cellpose", "Please select a parent object!" )
return
}

cellpose.detectObjects(imageData, pathObjects)
cellpose.detectObjects( imageData, pathObjects )

// You could do some post-processing here, e.g. to remove objects that are too small, but it is usually better to
// do this in a separate script so you can see the results before deleting anything.
Expand Down
24 changes: 12 additions & 12 deletions src/main/resources/scripts/Cellpose_training_template.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@
*/

// First we need to create a Cellpose2D builder and add all parameters that we want to use for training
def cellpose = Cellpose2D.builder("cyto") // Can choose "None" if you want to train from scratch
.channels("DAPI", "CY3") // or work with .cellposeChannels( channel1, channel2 ) and follow the cellpose way
// .preprocess(ImageOps.Filters.gaussianBlur(1)) // Optional preprocessing QuPath Ops
// .epochs(500) // Optional: will default to 500
// .groundTruthDirectory( new File("/my/ground/truth/folder")) // Optional: If you wish to save your GT elsewhere than the QuPath Project
// .learningRate(0.2) // Optional: Will default to 0.2
// .batchSize(8) // Optional: Will default to 8
// .minTrainMasks(5) // Optional: Will default to 5
// .addParameter("save_flows") // Any parameter from cellpose not available in the builder. See https://cellpose.readthedocs.io/en/latest/command.html
// .addParameter("anisotropy", "3") // Any parameter from cellpose not available in the builder. See https://cellpose.readthedocs.io/en/latest/command.html
// .modelDirectory( new File("My/folder/for/models")) // Optional place to store resulting model. Will default to QuPath project root, and make a 'models' folder
// .saveBuilder("My Builder") // Optional: Will save a builder json file that can be reloaded with Cellpose2D.builder(File builderFile)
def cellpose = Cellpose2D.builder( "cyto3" ) // Can choose "None" if you want to train from scratch
.channels( "DAPI", "CY3" ) // or work with .cellposeChannels( channel1, channel2 ) and follow the cellpose way
// .preprocess( ImageOps.Filters.gaussianBlur( 1 ) ) // Optional preprocessing QuPath Ops
// .epochs(500) // Optional: will default to 500
// .groundTruthDirectory( new File( "/my/ground/truth/folder" ) ) // Optional: If you wish to save your GT elsewhere than the QuPath Project
// .learningRate(0.2) // Optional: Will default to 0.2
// .batchSize(8) // Optional: Will default to 8
// .minTrainMasks(5) // Optional: Will default to 5
// .addParameter("save_flows") // Any parameter from cellpose not available in the builder. See https://cellpose.readthedocs.io/en/latest/command.html
// .addParameter("anisotropy", "3") // Any parameter from cellpose not available in the builder. See https://cellpose.readthedocs.io/en/latest/command.html
// .modelDirectory( new File("D:/models/" ) ) // Optional place to store resulting model. Will default to QuPath project root, and make a 'models' folder
// .saveBuilder("My Builder") // Optional: Will save a builder json file that can be reloaded with Cellpose2D.builder(File builderFile)
.build()

// Now we can train a new model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import qupath.ext.biop.cellpose.Cellpose2D

// Build a Cellpose instance for saving the image pairs
def cellpose = Cellpose2D.builder( "None" ) // No effect, as this script only exports the images
// .channels( "DAPI", "CY3" ) // Optional: Image channels to export
// .channels( "DAPI", "CY3" ) // Optional: Image channels to export
// .preprocess( ImageOps.Filters.gaussianBlur( 1 ) ) // Optional: preprocessing QuPath Ops
.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ if (pathObjects.isEmpty()) {
clearDetections()

// Create a Cellpose detectors for cyto and nuclei
def pathModel_cyto = 'cyto2'
def pathModel_cyto = 'cyto3'
def cellpose_cyto = Cellpose2D.builder( pathModel_cyto )
.channels("HCS","DAPI")
.channels( "HCS","DAPI" )
.pixelSize( 0.3 ) // Resolution for detection
.diameter(30) // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
.diameter(30 ) // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
.measureShape() // Add shape measurements
.measureIntensity() // Add cell measurements (in all compartments)
.build()

def pathModel_nuc = 'cyto2'
def pathModel_nuc = 'cyto3'
def cellpose_nuc = Cellpose2D.builder( pathModel_nuc )
.channels("DAPI")
.channels("DAPI" )
.pixelSize( 0.3 ) // Resolution for detection
.diameter(10) // Median object diameter. Set to 0.0 for the `bact_omni` model or for automatic computation
.build()
Expand Down

0 comments on commit b97efda

Please sign in to comment.