diff --git a/.gitignore b/.gitignore index d4e388e..1d390e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -.DS_Store -__pycache__ -*.pyc -.ipynb_checkpoints/ -segwrap.egg-info/ -site/ +.DS_Store +__pycache__ +*.pyc +.ipynb_checkpoints/ +segwrap.egg-info/ +site/ .vscode/ \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml index e483ad2..5e0758c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,16 +1,16 @@ -# .readthedocs.yml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Build documentation with MkDocs -mkdocs: - configuration: mkdocs.yml - -# Optionally set the version of Python and requirements required to build your docs -python: - version: 3.7 - install: - - requirements: docs/requirements.txt +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation with MkDocs +mkdocs: + configuration: mkdocs.yml + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 + install: + - requirements: docs/requirements.txt diff --git a/LICENSE b/LICENSE index 40d364e..7e6ca34 100644 --- a/LICENSE +++ b/LICENSE @@ -1,26 +1,26 @@ -BSD 3-Clause License - -Copyright © 2020, Florian Mueller -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +BSD 3-Clause License + +Copyright © 2020, Florian Mueller +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/docs/analysis-general-behavior.md b/docs/analysis-general-behavior.md index 8f2a7d8..af934ba 100644 --- a/docs/analysis-general-behavior.md +++ b/docs/analysis-general-behavior.md @@ -1,24 +1,24 @@ -# General behavior - -Here, we describe some general concepts that are shared by most plugins. - -## Recursive search for data - -Some plugins will perform by default a recursive search for data, for others this is an option. - -A recursive search means simply that not only the provided folder will be searched for files fitting the specified criteria, but also all subfolders. - -## Specify folder to save results - -Several possibilities are provided to specify the folder to save the results of the different workflows. Here, the general options are described, if -a plugin deviates from this default behavior it will be described in the respective plugin. - -When asked to define a folder to save results, you can: - -1. Directly define a **full path** where the results should be stored. If the folder doesn't exist, it will be created. -2. Define a folder with a **text replacement**. This option can be useful if many folders are processed, e.g. when a recursive search is performed. - - * Such a replacement operation is indicated with a string in the format `str_orig>>str_new`, - where 'str_orig' is the orginal string, `str_new` is the new string. - * For instance, using the string `acquisition>>analysis` implies that in the folder name +# General behavior + +Here, we describe some general concepts that are shared by most plugins. + +## Recursive search for data + +Some plugins will perform by default a recursive search for data, for others this is an option. + +A recursive search means simply that not only the provided folder will be searched for files fitting the specified criteria, but also all subfolders. + +## Specify folder to save results + +Several possibilities are provided to specify the folder to save the results of the different workflows. Here, the general options are described, if +a plugin deviates from this default behavior it will be described in the respective plugin. + +When asked to define a folder to save results, you can: + +1. Directly define a **full path** where the results should be stored. If the folder doesn't exist, it will be created. +2. Define a folder with a **text replacement**. This option can be useful if many folders are processed, e.g. when a recursive search is performed. + + * Such a replacement operation is indicated with a string in the format `str_orig>>str_new`, + where 'str_orig' is the orginal string, `str_new` is the new string. + * For instance, using the string `acquisition>>analysis` implies that in the folder name `D:\example_data\acquisition`, `acquisition` will be replaced with `analysis`, yielding `D:\example_data\analysis`. \ No newline at end of file diff --git a/docs/analysis-overview.md b/docs/analysis-overview.md index eb6f79d..361f700 100644 --- a/docs/analysis-overview.md +++ b/docs/analysis-overview.md @@ -1,27 +1,27 @@ -## ImJoy plugins -Most of the workflows are implemented as **ImJoy plugins**, with a simple interface to -specify the different workflow parameters. We describe in a dedicated [sections] how to [**use**](imjoy-overview.md) and [**install**](imjoy-installation.md) the **Plugin engine** to run these plugins. - -Once a plugin is installed, it will be available for later use in the ImJoy **workspace `fq-segmentation`** as explained [here](imjoy-overview.md#opening-a-workspace). - -!!! abstract "Quick summary for how to connect ImJoy to Jupyter engine" - 1. Open **anaconda terminal**. - 2. **Activate environment**: `conda activate fq-segmentation` - 3. **Start Jupyter engine**: `imjoy --jupyter` - 4. **Connect** ImJoy to Jupyter Engine with 🚀 button. - -We also provide **Jupyter notebooks** for these workflows, which we recommend only for more experienced Python users. - -## Typical workflows -For most purposes, the segmentation will consist of two steps. Each of these steps is described in a -dedicated section. In this section you can also find a recommended workflow, which allows you to also -process the test data. - -1. **Pre-processing** of the data: segmentation is performed on 2D images. If your images are 3D, you have to either split them, or project them into 2D images. This can be done with the [pre-processing workflow](analysis-preprocessing.md). -2. **Actual segmentation** of your images. We provide different plugins to [**segment**](analysis-segmentation.md). one type of structure, e.g. nuclei or cells, or two, e.g. cells and nuclei. - -Each **workflow** is documented in a dedicated section. We provide - -* **Installation links**. -* Detailed explanations of the different **parameters**. -* A **Recommended workflow** for how to set the parameters following the recommended data organization. +## ImJoy plugins +Most of the workflows are implemented as **ImJoy plugins**, with a simple interface to +specify the different workflow parameters. We describe in a dedicated [sections] how to [**use**](imjoy-overview.md) and [**install**](imjoy-installation.md) the **Plugin engine** to run these plugins. + +Once a plugin is installed, it will be available for later use in the ImJoy **workspace `fq-segmentation`** as explained [here](imjoy-overview.md#opening-a-workspace). + +!!! abstract "Quick summary for how to connect ImJoy to Jupyter engine" + 1. Open **anaconda terminal**. + 2. **Activate environment**: `conda activate fq-segmentation` + 3. **Start Jupyter engine**: `imjoy --jupyter` + 4. **Connect** ImJoy to Jupyter Engine with 🚀 button. + +We also provide **Jupyter notebooks** for these workflows, which we recommend only for more experienced Python users. + +## Typical workflows +For most purposes, the segmentation will consist of two steps. Each of these steps is described in a +dedicated section. In this section you can also find a recommended workflow, which allows you to also +process the test data. + +1. **Pre-processing** of the data: segmentation is performed on 2D images. If your images are 3D, you have to either split them, or project them into 2D images. This can be done with the [pre-processing workflow](analysis-preprocessing.md). +2. **Actual segmentation** of your images. We provide different plugins to [**segment**](analysis-segmentation.md). one type of structure, e.g. nuclei or cells, or two, e.g. cells and nuclei. + +Each **workflow** is documented in a dedicated section. We provide + +* **Installation links**. +* Detailed explanations of the different **parameters**. +* A **Recommended workflow** for how to set the parameters following the recommended data organization. diff --git a/docs/analysis-preprocessing.md b/docs/analysis-preprocessing.md index 7aca6ab..ed730c6 100644 --- a/docs/analysis-preprocessing.md +++ b/docs/analysis-preprocessing.md @@ -1,53 +1,53 @@ - -# Preprocessing of 3D images - -Segmentation is performed on **2D images**. This plugin allows to obtain 2D images from 3D images either by -a projection or by saving each z-plane separately. These images are typically stored in a separate folder. -2D images by applying a projection, and these images are typically stored in a separate folder. - -Install ImJoy plugin `PreProcess` ** from here.** - -## Recommended workflow - -The default settings of the plugins allow to quickly perform the recommended workflow. You only have -to paste your data folder. - -1. 3D images are stored in a folder `acquisition` -2. In the pre-processing step, the folder to save data is obtained with - - 1. the text replacement `acquisition>>analysis`. - 2. Images are stored in a new subfolder `segmentation-input` - - With these options, the images will be saved in a folder `acquisition\segmentation-input` - -## Running the plugin - -1. Before running the plugin, you have to specify a few parameters. Note that you have to perform this - projection for each channel-type. This allows to use different projection methods for a channel. This can be set in the plugin interface, avaible after clicking on the arrow down next to the plugin name. - - ![imjoy-preprocess](img/imjoy-preprocess-ui.png){: style="width:300px"} - - Here the following parameters can be set. - - Option | Type | Default | Description - ---------------- | ---- | ----------- | ----------- - `Path DATA` | str | | Full path to folder containing data to be segmented. - `Path SAVE` | str | acquisition>>analysis | Several options exist. See dedicated section here [below](analysis-general-behavior.md#specify-folder-to-save-your-data) for more details. - `Subfolder` | str | segmentation-input | Sub-folder where results should be saved (if defined) - `Channel string` | str | dapi | Unique string to identify channel that should be processed. - `Img extension` | str | .tif | Extensions of images to be loaded. - `Projection type` | str | mean | Different projection types: `max`, `mean`, `indiv`. The option `indiv` implies that a z-stack is split into individual slices, stored in subfolder for each image. - `Search recursive` | bool | false | Should provided folder be search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) for images (true/false). - - -2. Pressing the button `PreProcess` will **start the pre-processing**. Progress - can be monitored in the plugin log, available by pressing on the `i` next to the plugin name. - -3. **Results** will be saved in the specified folder. For each image a json file with - basic properties of the file, and an image with the same name as the original one will be saved. - - - - - - + +# Preprocessing of 3D images + +Segmentation is performed on **2D images**. This plugin allows to obtain 2D images from 3D images either by +a projection or by saving each z-plane separately. These images are typically stored in a separate folder. +2D images by applying a projection, and these images are typically stored in a separate folder. + +Install ImJoy plugin `PreProcess` ** from here.** + +## Recommended workflow + +The default settings of the plugins allow to quickly perform the recommended workflow. You only have +to paste your data folder. + +1. 3D images are stored in a folder `acquisition` +2. In the pre-processing step, the folder to save data is obtained with + + 1. the text replacement `acquisition>>analysis`. + 2. Images are stored in a new subfolder `segmentation-input` + + With these options, the images will be saved in a folder `acquisition\segmentation-input` + +## Running the plugin + +1. Before running the plugin, you have to specify a few parameters. Note that you have to perform this + projection for each channel-type. This allows to use different projection methods for a channel. This can be set in the plugin interface, avaible after clicking on the arrow down next to the plugin name. + + ![imjoy-preprocess](img/imjoy-preprocess-ui.png){: style="width:300px"} + + Here the following parameters can be set. + + Option | Type | Default | Description + ---------------- | ---- | ----------- | ----------- + `Path DATA` | str | | Full path to folder containing data to be segmented. + `Path SAVE` | str | acquisition>>analysis | Several options exist. See dedicated section here [below](analysis-general-behavior.md#specify-folder-to-save-your-data) for more details. + `Subfolder` | str | segmentation-input | Sub-folder where results should be saved (if defined) + `Channel string` | str | dapi | Unique string to identify channel that should be processed. + `Img extension` | str | .tif | Extensions of images to be loaded. + `Projection type` | str | mean | Different projection types: `max`, `mean`, `indiv`. The option `indiv` implies that a z-stack is split into individual slices, stored in subfolder for each image. + `Search recursive` | bool | false | Should provided folder be search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) for images (true/false). + + +2. Pressing the button `PreProcess` will **start the pre-processing**. Progress + can be monitored in the plugin log, available by pressing on the `i` next to the plugin name. + +3. **Results** will be saved in the specified folder. For each image a json file with + basic properties of the file, and an image with the same name as the original one will be saved. + + + + + + diff --git a/docs/analysis-segmentation.md b/docs/analysis-segmentation.md index b5566d1..a8aa295 100644 --- a/docs/analysis-segmentation.md +++ b/docs/analysis-segmentation.md @@ -1,125 +1,125 @@ -# Segmentation - -We provide different plugins for different segmentation tasks: - -* Install plugin `SegmentObjects` to segment cells OR nuclei **from here.** -* Install plugin `SegmentCellsNuclei` to segment cells AND nuclei **from here.** - -## General behavior - -### Recursive search - -The plugins will search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) the provided subfolder for images to segment. - -By default, all images matching the naming scheme will be processed. An optional parameter allows to specify -in which subfolder the images have to be in order to be segmented. This allows to process nested folder -hierachies and only segmenting images in the relevant subfolders. - -As an example, in the provided data we could specif the folder `example_data\analysis` as the data folder. -This would then scan this folder and all subfolders and potentially find inappropriate files for segmentation. -By defining the `Input subfolder` to be `segmentation-input`, the analysis will be restricted to this folder. - -### Results - -Results will be saved in the specified folder. For each image the following files, results files with different suffixes are created: - -* `..._flow`: these are the predicted distance maps of CellPose. They are an intermediate result, and - not needed for most end-users. -* `..._mask`: these contain the actual segmentation results. Each segmented object is a filled - object with a constant pixel value. If the images were resized during segmentation, the mask is scaled - back up to the original image size. The actually obtained (smaller) mask is saved under the name `mask__rescale_...`. -* `..._seg`: summary plot showing the input image, the predicted distance map, and the segmented - objects. This plot is also shown in the interface. - -![segmentation__nuclei](img/segmentation__nuclei.png) - -### Resizing can speed up prediction & yield better results - -We found that resizing images before segmentation can yield better results for certain images. -Also, it will lead to faster computation time. In case you resize the images, we implemented a post-processing -routine that will resize the predicted masks back to the original image size. - -Resizing can be specified in two ways - -1. **Scaling factor** recommended]: simply add an integer value. The actual image size will then be divided by this factor - to obtain the new size, i.e. a value of 2 will resize an image 512x512 to 256x256. -2. **New size**: you can directly define the new size of the image, e.g. 256x256. Please note that this size - will be applied to all images, independly of their size. This option is hence not suitable if your data-sets - contain differently sized images. - -## Recommended workflow - -The default settings of the plugins allow to quickly perform the recommended workflow. You only have -to paste your data folder. - -1. 2D images are stored in a subfolder `segmentation-input` -2. Segmentation results will be stored in a subfolder `segmentation-results`, this can be achieved by setting - the save path to the string replacement `segmentation-input>>segmentation-results` - -## Segmentation of nuclei OR cells - -![imjoy-segment-objects-ui](img/imjoy-segment-objects-ui.png){: style="width:300px"} - -1. Before running the plugin, you have to **specify a few parameters**. This can be done in the plugin interface, - avaible after clicking on the arrow down next to the plugin name. - - Here the following parameters can be set: - - Option | Type | Default | Description - ---------------- | ---- | ----------- | ----------- - `Path DATA` | str | | Full path to folder containing data to be segmented. - `Input subfolder` | str | | Name of the subfolder containing the images that should be segmented. - `Path SAVE` | str | | Several options exist. See dedicated section [below](analysis-general-behavior.md#specify-folder-to-save-your-data) for more details. - `String channel` | str | `dapi` | Unique identifier to identify channel. - `Object name` | str | `nuclei` | How the object is called. - `Cellpose model` | str | `nuclei` | Cellpose model for segmentation: `cyto` or `nuclei`. Note that for dense nuclei, the cytoplasmic model might work better. - `New size` | int | 2 | Numbers to specify resizing (see above). No resizing if empty. - `Object diameter` | int | 50 | Typical diameter of the object. Better to be set a bit to small. - `Net Average` | Bool | False | Can improve segmentation accuracy, but is slower (Runs the 4 built-in networks and averages them). - `Resample` | Bool | False | Gives more accurate boundaries, but can be very slow (Runs dynamics at original image size). - `String img ext` | str | `.png` | File extension of images that should be segmented. - `New size` | int | 2 | Numbers to specify resizing (see abov. No resizing if empty. - -2. Pressing on the plugin name `SegmentObjects` will start the segmentation. - When using CellPose for the first time, the models for nuclear and cytoplasmic segmentations are downloaded. - - The actual segmentation can take a while, depending on the numberof images that should be segmented - (and their size). Progress will be displayed in the ImJoy status bar, and more details provided in the - plugin log available by pressing on the `i` next to the plugin name. - - Once a image is segmented, the results will be saved (see below). So you can monitor the result folder - to verify on the fly if the segmentation works. - -## Segmentation of cells AND nuclei - -![imjoy-segment-cells-nuclei-ui](img/imjoy-segment-cells-nuclei-ui.png){: style="width:300px"} - -1. Before running the plugin, you have to **specify a few parameters**. This can be done in the plugin interface, - avaible after clicking on the arrow down next to the plugin name. - - Here the following parameters can be set: - - Option | Type | Default | Description - ---------------- | ---- | ----------- | ----------- - `Path DATA` | str | | Full path to folder containing data to be segmented. - `Input subfolder` | str | | Name of the subfolder containing the images that should be segmented. - `Path SAVE` | str | | Path to folder where results should be stored (for more details see above). - `String CELLS` | str | `cy3` | Unique identifier for images of cytoplasmic stain. - `String NUCLEI` | str | `dapi` | Unique identifier for images of nuclear stain. - `String img ext` | str | `.png` | File extension of images that should be segmented. - `New size` | int | 2 | Numbers to specify resizing (see above). No resizing if empty. - `Size CELLS` | int | 100 | Typical size of a cell (in resized image). - `Size NUCLEI` | int | 50 | Typical size of a nucleus (in resized image). - `Net Average` | Bool | False | Can improve segmentation accuracy, but is slower (Runs the 4 built-in networks and averages them). - `Resample` | Bool | False | Gives more accurate boundaries, but can be very slow (Runs dynamics at original image size). - `String img ext` | str | `.png` | File extension of images that should be segmented. - -2. Pressing on the plugin name `SegmentCellsNuclei` will start the segmentation. - When using CellPose for the first time, the models for nuclear and cytoplasmic segmentations are downloaded. - - The actual segmentation can take a while, depending on the numberof images that should be segmented - (and their size). Progress will be displayed in the ImJoy status bar, and more details provided in the - plugin log available by pressing on the `i` next to the plugin name. - - Once a image is segmented, the results will be saved (see below). So you can monitor the result folder - to verify on the fly if the segmentation works. +# Segmentation + +We provide different plugins for different segmentation tasks: + +* Install plugin `SegmentObjects` to segment cells OR nuclei **from here.** +* Install plugin `SegmentCellsNuclei` to segment cells AND nuclei **from here.** + +## General behavior + +### Recursive search + +The plugins will search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) the provided subfolder for images to segment. + +By default, all images matching the naming scheme will be processed. An optional parameter allows to specify +in which subfolder the images have to be in order to be segmented. This allows to process nested folder +hierachies and only segmenting images in the relevant subfolders. + +As an example, in the provided data we could specif the folder `example_data\analysis` as the data folder. +This would then scan this folder and all subfolders and potentially find inappropriate files for segmentation. +By defining the `Input subfolder` to be `segmentation-input`, the analysis will be restricted to this folder. + +### Results + +Results will be saved in the specified folder. For each image the following files, results files with different suffixes are created: + +* `..._flow`: these are the predicted distance maps of CellPose. They are an intermediate result, and + not needed for most end-users. +* `..._mask`: these contain the actual segmentation results. Each segmented object is a filled + object with a constant pixel value. If the images were resized during segmentation, the mask is scaled + back up to the original image size. The actually obtained (smaller) mask is saved under the name `mask__rescale_...`. +* `..._seg`: summary plot showing the input image, the predicted distance map, and the segmented + objects. This plot is also shown in the interface. + +![segmentation__nuclei](img/segmentation__nuclei.png) + +### Resizing can speed up prediction & yield better results + +We found that resizing images before segmentation can yield better results for certain images. +Also, it will lead to faster computation time. In case you resize the images, we implemented a post-processing +routine that will resize the predicted masks back to the original image size. + +Resizing can be specified in two ways + +1. **Scaling factor** recommended]: simply add an integer value. The actual image size will then be divided by this factor + to obtain the new size, i.e. a value of 2 will resize an image 512x512 to 256x256. +2. **New size**: you can directly define the new size of the image, e.g. 256x256. Please note that this size + will be applied to all images, independly of their size. This option is hence not suitable if your data-sets + contain differently sized images. + +## Recommended workflow + +The default settings of the plugins allow to quickly perform the recommended workflow. You only have +to paste your data folder. + +1. 2D images are stored in a subfolder `segmentation-input` +2. Segmentation results will be stored in a subfolder `segmentation-results`, this can be achieved by setting + the save path to the string replacement `segmentation-input>>segmentation-results` + +## Segmentation of nuclei OR cells + +![imjoy-segment-objects-ui](img/imjoy-segment-objects-ui.png){: style="width:300px"} + +1. Before running the plugin, you have to **specify a few parameters**. This can be done in the plugin interface, + avaible after clicking on the arrow down next to the plugin name. + + Here the following parameters can be set: + + Option | Type | Default | Description + ---------------- | ---- | ----------- | ----------- + `Path DATA` | str | | Full path to folder containing data to be segmented. + `Input subfolder` | str | | Name of the subfolder containing the images that should be segmented. + `Path SAVE` | str | | Several options exist. See dedicated section [below](analysis-general-behavior.md#specify-folder-to-save-your-data) for more details. + `String channel` | str | `dapi` | Unique identifier to identify channel. + `Object name` | str | `nuclei` | How the object is called. + `Cellpose model` | str | `nuclei` | Cellpose model for segmentation: `cyto` or `nuclei`. Note that for dense nuclei, the cytoplasmic model might work better. + `New size` | int | 2 | Numbers to specify resizing (see above). No resizing if empty. + `Object diameter` | int | 50 | Typical diameter of the object. Better to be set a bit to small. + `Net Average` | Bool | False | Can improve segmentation accuracy, but is slower (Runs the 4 built-in networks and averages them). + `Resample` | Bool | False | Gives more accurate boundaries, but can be very slow (Runs dynamics at original image size). + `String img ext` | str | `.png` | File extension of images that should be segmented. + `New size` | int | 2 | Numbers to specify resizing (see abov. No resizing if empty. + +2. Pressing on the plugin name `SegmentObjects` will start the segmentation. + When using CellPose for the first time, the models for nuclear and cytoplasmic segmentations are downloaded. + + The actual segmentation can take a while, depending on the numberof images that should be segmented + (and their size). Progress will be displayed in the ImJoy status bar, and more details provided in the + plugin log available by pressing on the `i` next to the plugin name. + + Once a image is segmented, the results will be saved (see below). So you can monitor the result folder + to verify on the fly if the segmentation works. + +## Segmentation of cells AND nuclei + +![imjoy-segment-cells-nuclei-ui](img/imjoy-segment-cells-nuclei-ui.png){: style="width:300px"} + +1. Before running the plugin, you have to **specify a few parameters**. This can be done in the plugin interface, + avaible after clicking on the arrow down next to the plugin name. + + Here the following parameters can be set: + + Option | Type | Default | Description + ---------------- | ---- | ----------- | ----------- + `Path DATA` | str | | Full path to folder containing data to be segmented. + `Input subfolder` | str | | Name of the subfolder containing the images that should be segmented. + `Path SAVE` | str | | Path to folder where results should be stored (for more details see above). + `String CELLS` | str | `cy3` | Unique identifier for images of cytoplasmic stain. + `String NUCLEI` | str | `dapi` | Unique identifier for images of nuclear stain. + `String img ext` | str | `.png` | File extension of images that should be segmented. + `New size` | int | 2 | Numbers to specify resizing (see above). No resizing if empty. + `Size CELLS` | int | 100 | Typical size of a cell (in resized image). + `Size NUCLEI` | int | 50 | Typical size of a nucleus (in resized image). + `Net Average` | Bool | False | Can improve segmentation accuracy, but is slower (Runs the 4 built-in networks and averages them). + `Resample` | Bool | False | Gives more accurate boundaries, but can be very slow (Runs dynamics at original image size). + `String img ext` | str | `.png` | File extension of images that should be segmented. + +2. Pressing on the plugin name `SegmentCellsNuclei` will start the segmentation. + When using CellPose for the first time, the models for nuclear and cytoplasmic segmentations are downloaded. + + The actual segmentation can take a while, depending on the numberof images that should be segmented + (and their size). Progress will be displayed in the ImJoy status bar, and more details provided in the + plugin log available by pressing on the `i` next to the plugin name. + + Once a image is segmented, the results will be saved (see below). So you can monitor the result folder + to verify on the fly if the segmentation works. diff --git a/docs/contact.md b/docs/contact.md index 93ce655..19141ba 100644 --- a/docs/contact.md +++ b/docs/contact.md @@ -1,29 +1,29 @@ -# Reporting bugs - -If you encounter a bug, best is to create a [**GitHub issue**](https://github.com/fish-quant/fq-segmentation/issues). This would allow other users to see -reported bugs and proposed solutions. If possible, please provide the following information - -1. **How** can the bug be produced? -2. Which **browser** and browser version are you using? -3. **Complete plugin log**. Can be obtained by clicking on the `i` next to the FISH-QUANT plugin. Please copy to a text file. This will also contain the version of the plugin and the used code - - ``` bash - Loading plugin SegmentCellsNuclei_i862g1602770955978 (TAG=stable, WORKSPACE=fq-segmentation) - ENGINE_URL=http://127.0.0.1:9527/ - Setting up plugin. - - Plugin SegmentCellsNuclei initialized - * plugin version: 0.1.11 - * segwrap version: 0.1.7 - * utils_cellpose location: c:\users\muellerf\miniconda3\envs\fq-segmentation\lib\site-packages\segwrap\utils_cellpose.py - ``` - -4. **Console log**. The console log of the browser provides further details that can help for debugging. To acces the console log in **Chrome**: - - 1. In the ImJoy app mouse-right-click. - 2. Select `Inspect`. - 3. This will open a new interface on the right size of your browser windows. Select the panel `Console` and copy the entire content, and paste it to a file. - -# Development team - -* Florian Mueller. [Github](https://github.com/muellerflorian). +# Reporting bugs + +If you encounter a bug, best is to create a [**GitHub issue**](https://github.com/fish-quant/fq-segmentation/issues). This would allow other users to see +reported bugs and proposed solutions. If possible, please provide the following information + +1. **How** can the bug be produced? +2. Which **browser** and browser version are you using? +3. **Complete plugin log**. Can be obtained by clicking on the `i` next to the FISH-QUANT plugin. Please copy to a text file. This will also contain the version of the plugin and the used code + + ``` bash + Loading plugin SegmentCellsNuclei_i862g1602770955978 (TAG=stable, WORKSPACE=fq-segmentation) + ENGINE_URL=http://127.0.0.1:9527/ + Setting up plugin. + + Plugin SegmentCellsNuclei initialized + * plugin version: 0.1.11 + * segwrap version: 0.1.7 + * utils_cellpose location: c:\users\muellerf\miniconda3\envs\fq-segmentation\lib\site-packages\segwrap\utils_cellpose.py + ``` + +4. **Console log**. The console log of the browser provides further details that can help for debugging. To acces the console log in **Chrome**: + + 1. In the ImJoy app mouse-right-click. + 2. Select `Inspect`. + 3. This will open a new interface on the right size of your browser windows. Select the panel `Console` and copy the entire content, and paste it to a file. + +# Development team + +* Florian Mueller. [Github](https://github.com/muellerflorian). diff --git a/docs/data.md b/docs/data.md index a3e6caf..4157277 100644 --- a/docs/data.md +++ b/docs/data.md @@ -1,42 +1,42 @@ -# Data - -Please note that we recommend a **data organization**, described below, to facilitate the usage of these tools. - -We provide test data, which allows you to inspect how the data is organized, but then also run a local installation -of these tools to see if everything works correctly. - -## Test data -Already processed test data can be downloaded from [**Dropbox**](https://www.dropbox.com/sh/yr1s5olqwkvyx0i/AADH0QQtdNuWWq7z9wgQpLiOa?dl=0). With these data, you can verify if the different analysis steps are properly executed. - -## Data organization -We strongly recommend the following data-organization on which this workflow has been tested. - -1. Images are store as single-channel multi-z-stack tif files, e.g on tif per position and channel. If your data are not single-channel, see the section on how to split channels with [Fiji](workflows-fiji-split-channels.md). -2. All raw 3D images are stored in a folder `acquisition` -3. All analysis results are stored in subfolder `analysis`, where each analysis step has a separate subfolder. - -The organization of the provided test data is the following - -``` bash -├─ example_data/ -│ ├─ acquisition # Folder with raw data -│ │ ├─ test_pos001_cy3.tif -│ │ ├─ test_pos002_dapi.tif -│ │ ├─ test_pos002_cy3.tif -│ │ ├─ test_pos002_dapi.tif -│ ├─ analysis # Folder with all analysis results -│ │ ├─ segmentation-input # Folder with projected images for segmentation -│ │ │ ├─ img-prop__test_pos001_cy3.json # json file with image properties -│ │ │ ├─ test_pos001_cy3.png # Projected image -│ │ │ ├─ .... -│ │ ├─ segmentation-results # Folder with segmentation results -│ │ │ ├─ test_pos001_cy3.tif -│ │ │ ├─ test_pos001_cy3.tif -│ │ │ ├─ test_pos001_cy3.tif -│ │ │ ├─ .... -│ │ ├─ .... -│ │ ├─ FQ_outline # [Optional] FQ outlines -│ │ │ ├─ test_pos001_cy3_outline.txt -│ │ │ ├─ .... -``` - +# Data + +Please note that we recommend a **data organization**, described below, to facilitate the usage of these tools. + +We provide test data, which allows you to inspect how the data is organized, but then also run a local installation +of these tools to see if everything works correctly. + +## Test data +Already processed test data can be downloaded from [**Dropbox**](https://www.dropbox.com/sh/yr1s5olqwkvyx0i/AADH0QQtdNuWWq7z9wgQpLiOa?dl=0). With these data, you can verify if the different analysis steps are properly executed. + +## Data organization +We strongly recommend the following data-organization on which this workflow has been tested. + +1. Images are store as single-channel multi-z-stack tif files, e.g on tif per position and channel. If your data are not single-channel, see the section on how to split channels with [Fiji](workflows-fiji-split-channels.md). +2. All raw 3D images are stored in a folder `acquisition` +3. All analysis results are stored in subfolder `analysis`, where each analysis step has a separate subfolder. + +The organization of the provided test data is the following + +``` bash +├─ example_data/ +│ ├─ acquisition # Folder with raw data +│ │ ├─ test_pos001_cy3.tif +│ │ ├─ test_pos002_dapi.tif +│ │ ├─ test_pos002_cy3.tif +│ │ ├─ test_pos002_dapi.tif +│ ├─ analysis # Folder with all analysis results +│ │ ├─ segmentation-input # Folder with projected images for segmentation +│ │ │ ├─ img-prop__test_pos001_cy3.json # json file with image properties +│ │ │ ├─ test_pos001_cy3.png # Projected image +│ │ │ ├─ .... +│ │ ├─ segmentation-results # Folder with segmentation results +│ │ │ ├─ test_pos001_cy3.tif +│ │ │ ├─ test_pos001_cy3.tif +│ │ │ ├─ test_pos001_cy3.tif +│ │ │ ├─ .... +│ │ ├─ .... +│ │ ├─ FQ_outline # [Optional] FQ outlines +│ │ │ ├─ test_pos001_cy3_outline.txt +│ │ │ ├─ .... +``` + diff --git a/docs/developer.md b/docs/developer.md index 39622ff..b350bd4 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -1,41 +1,41 @@ - -# Developer - -Here we provide some information for developers who want to contribute to this repository. - -## Documentation - -Documentation - -* is **written** with mkdocs using the readthedocs theme: [mkdocs website](https://www.mkdocs.org/) - -* **Online documentation** is automatically build by [readthedocs.com](https://readthedocs.org/) from the GitHub repository. - -### Local build - -You can build the documentation locally, before pushing to GitHub: - -Install mkdocs: `pip install mkdocs` - -__Basic use__: - -* Launch dev-server: `mkdocs serve` -* Building the site: `mkdocs build` - -## ImJoy plugins - -### Update imjoy manifest - -After changes in ImJoy plugins, update the plugin manifest. - -Install `node.js` with conda - -``` bash -conda install -c conda-forge nodejs -``` - -Run this command in project root path - -``` bash -node update_manifest.js -``` + +# Developer + +Here we provide some information for developers who want to contribute to this repository. + +## Documentation + +Documentation + +* is **written** with mkdocs using the readthedocs theme: [mkdocs website](https://www.mkdocs.org/) + +* **Online documentation** is automatically build by [readthedocs.com](https://readthedocs.org/) from the GitHub repository. + +### Local build + +You can build the documentation locally, before pushing to GitHub: + +Install mkdocs: `pip install mkdocs` + +__Basic use__: + +* Launch dev-server: `mkdocs serve` +* Building the site: `mkdocs build` + +## ImJoy plugins + +### Update imjoy manifest + +After changes in ImJoy plugins, update the plugin manifest. + +Install `node.js` with conda + +``` bash +conda install -c conda-forge nodejs +``` + +Run this command in project root path + +``` bash +node update_manifest.js +``` diff --git a/docs/img/imjoy-logo-powered.svg b/docs/img/imjoy-logo-powered.svg index 36d9643..ca01846 100644 --- a/docs/img/imjoy-logo-powered.svg +++ b/docs/img/imjoy-logo-powered.svg @@ -1,268 +1,268 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Powered by - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Powered by + diff --git a/docs/imjoy-faq.md b/docs/imjoy-faq.md index 5858679..84f8643 100644 --- a/docs/imjoy-faq.md +++ b/docs/imjoy-faq.md @@ -1,46 +1,46 @@ -# Overview - -[ImJoy](https://imjoy.io/docs/#/) is image processing platform with an easy to use interface running in your browser. - -* While ImJoy is a browser app, **NO** user data will be transferred over the internet. -* For best stability, we recommend using [**Chrome**](https://www.google.com/chrome/) to run the ImJoy app. -* Some of its main **features** are: - - 1. Specific functionality is provided by **plugins**, which can be installed with simple links. Available - plugins are listed in the plugin list on the left part of the interface. Plugins using Python require a Plugin engine to be executed. Installation and usasage is detailed below. - - 2. ImJoy can have several **workspaces**. Each workspace can contain multiple plugins and is - dedicated to a specific data processing task. Workspaces can be selected from little puzzle - symbol in the upper left part of the interface. - -![imjoy-interface](img/imjoy-interface.png) - -## Running Python plugins - -Most of the provided plugins use Python for data processing. To use these plugins, -you have to connect ImJoy to a Plugin engine. For this repository, we use **Jupyter notebooks** as -and engine, which can be installed via Miniconda (see [**installation instructions**](#install-jupyter-engine-for-imjoy)). - -## Installing plugins - -We provide links to install ImJoy plugins for the different workflows. - -If you press on the installation link, the ImJoy app will open and display a -dialog asking if you want to install the specified plugin. To confirm, press -the `install` button. - -![imjoy-plugin-installation](img/imjoy-plugin-installation.png){: style="width:400px"} - -These installation links also specify in which [ImJoy workspaces](imjoy-overview.md#opening-a-workspace) the plugin will be installed. - -## Opening a workspace - -Once a plugin is installed, ImJoy remembers the workspaces and plugins it contains. - -If you want to redo an analysis, you simply have to open the [ImJoy app](https://imjoy.io/#/app) -and select the workspace `fq-segmentation` for this package. - -If **updates** for the installed plugins -are available, you will see a corresponding symbol next to the plugin name. - -![imjoy-workspacer.gif](img/imjoy-workspace.gif){: style="width:500px"} +# Overview + +[ImJoy](https://imjoy.io/docs/#/) is image processing platform with an easy to use interface running in your browser. + +* While ImJoy is a browser app, **NO** user data will be transferred over the internet. +* For best stability, we recommend using [**Chrome**](https://www.google.com/chrome/) to run the ImJoy app. +* Some of its main **features** are: + + 1. Specific functionality is provided by **plugins**, which can be installed with simple links. Available + plugins are listed in the plugin list on the left part of the interface. Plugins using Python require a Plugin engine to be executed. Installation and usasage is detailed below. + + 2. ImJoy can have several **workspaces**. Each workspace can contain multiple plugins and is + dedicated to a specific data processing task. Workspaces can be selected from little puzzle + symbol in the upper left part of the interface. + +![imjoy-interface](img/imjoy-interface.png) + +## Running Python plugins + +Most of the provided plugins use Python for data processing. To use these plugins, +you have to connect ImJoy to a Plugin engine. For this repository, we use **Jupyter notebooks** as +and engine, which can be installed via Miniconda (see [**installation instructions**](#install-jupyter-engine-for-imjoy)). + +## Installing plugins + +We provide links to install ImJoy plugins for the different workflows. + +If you press on the installation link, the ImJoy app will open and display a +dialog asking if you want to install the specified plugin. To confirm, press +the `install` button. + +![imjoy-plugin-installation](img/imjoy-plugin-installation.png){: style="width:400px"} + +These installation links also specify in which [ImJoy workspaces](imjoy-overview.md#opening-a-workspace) the plugin will be installed. + +## Opening a workspace + +Once a plugin is installed, ImJoy remembers the workspaces and plugins it contains. + +If you want to redo an analysis, you simply have to open the [ImJoy app](https://imjoy.io/#/app) +and select the workspace `fq-segmentation` for this package. + +If **updates** for the installed plugins +are available, you will see a corresponding symbol next to the plugin name. + +![imjoy-workspacer.gif](img/imjoy-workspace.gif){: style="width:500px"} diff --git a/docs/imjoy-installation.md b/docs/imjoy-installation.md index 26e2e1e..36ebb47 100644 --- a/docs/imjoy-installation.md +++ b/docs/imjoy-installation.md @@ -1,76 +1,78 @@ -# ImJoy installation - -Here we describe how you can install and maintain the ImJoy Jupyter plugin engine, which is -needed to run the Python code for cell/nuclear segmentation and the pre/post processing -workflows. - -## Install Jupyter engine for ImJoy - -This you only need to do **once**. - -We recommend installing [**Miniconda** with Python](https://docs.conda.io/en/latest/miniconda.html): -choose latest Python version (3.X) and your operating system. You can then use the annoconda prompt -to excecute the commands listed below. - -We recommend creating a **dedicated environment** to run code in this analysis package. To create an environment called `fq-segmentation`, open an anaconda prompt and type (Confirm with `y` when asked if you want to proceed (`Proceed ([y]/n)?`): - -``` bash -conda create --name fq-segmentation python=3.7 -``` - -**Activate the environment**: - -``` bash -conda activate fq-segmentation -``` - -**Install code Jupyter optimized for ImJoy**: - -``` bash -pip install -U imjoy[jupyter] -``` - -## Connect to Jupyter engine - -Once installed Jupyter is installed, you can start a Jupyter Notebook in the anaconda terminal, -to which ImJoy can connect. - -* Please note that this engine runs on your local machine, so no data-transfer over the internet is taking place. -* In order to connect a Jupyter engine, you need to specify its url that also contains a token (a passphrase). - If you launch a Jupyter notebook for the first time, you have to provide this URL to ImJoy - (indicated below with **First time only**). After this, ImJoy will remember it. - -![terminal-launch-jupyter.png](img/terminal-launch-jupyter.png) - -1. Launch a **Jupyter Engine for ImJoy**: - - 1. Start an **anaconda terminal**. - 2. **Activate the environment**: `conda activate fq-segmentation` - 3. **Start Jupyter engine**: `imjoy --jupyter` - 4. **First time only**: copy the provided URL including the token, - e.g. `http://127.0.0.1:8888/?token=8b4885e452db1af7cd7b3cfa6c62036cbae46995e473c25e` - -2. **Connect ImJoy to Jupyter Engine**: - - 1. In the ImJoy app, press on the rocket symbol in the upper right corner. - * **First time only**: select `Add Jupyter-Engine`, paste the URL from the step above, and you can give a new name to the engine - * **Subsequent use**: press on the pre-defined plugin engine to connect to it (of course you have to launch it first). - -3. If this is the only engine, plugins will be **automatically connected**. You can verify this, - by clicking on the puzzle symbol next to the plugin name. Depending on the plugin, installation - might take a while, during this period the plugin name will be in red. - - If yoy have multiple engines, you have to choose on which engine the plugin should be running - (more details [below](#plugin-running-on-wrong-engine)). - - ![imjoy-connect-jupyter.gif](img/imjoy-connect-jupyter.gif) - -## Managing plugin engines - -ImJoy remembers the plugin engines it connected to (including the token). You can obtain the list of all -registered engines by pressing on the rocket symbol. - -![imjoy-plugin-manager.png](img/imjoy-plugin-manager.png){: style="width:300px"} - -* **Connected engines** will be shown with their name in black, and a red cross next to the name. Pressing the cross will disconnect ImJoy from the engine, but it will remain in the list. -* **Known engines** (but not connected) will be shown with their name in gray, with a little trash symbol next to them. Pressing on the trash symbol will remove the engine. +# ImJoy installation + +Here we describe how you can install and maintain the ImJoy Jupyter plugin engine, which is +needed to run the Python code for cell/nuclear segmentation and the pre/post processing +workflows. + +## Install Jupyter engine for ImJoy + +This you only need to do **once**. + +We recommend installing [**Miniconda** with Python](https://docs.conda.io/en/latest/miniconda.html): +choose latest Python version (3.X) and your operating system. You can then use the annoconda prompt +to excecute the commands listed below. + +We recommend creating a **dedicated environment** to run code in this analysis package. To create an environment called `fq-segmentation`, open an anaconda prompt and type (Confirm with `y` when asked if you want to proceed (`Proceed ([y]/n)?`): + +``` bash +conda create --name fq-segmentation pytorch=1.8.2 cudatoolkit=10.2 -c pytorch-lts +``` + +Note (31-3-2022): specifying the environment seems necessary due to a change in the Pytorch channel (more [information](https://github.com/MouseLand/cellpose/issues/481)). + +**Activate the environment**: + +``` bash +conda activate fq-segmentation +``` + +**Install code Jupyter optimized for ImJoy**: + +``` bash +pip install -U imjoy[jupyter] +``` + +## Connect to Jupyter engine + +Once installed Jupyter is installed, you can start a Jupyter Notebook in the anaconda terminal, +to which ImJoy can connect. + +* Please note that this engine runs on your local machine, so no data-transfer over the internet is taking place. +* In order to connect a Jupyter engine, you need to specify its url that also contains a token (a passphrase). + If you launch a Jupyter notebook for the first time, you have to provide this URL to ImJoy + (indicated below with **First time only**). After this, ImJoy will remember it. + +![terminal-launch-jupyter.png](img/terminal-launch-jupyter.png) + +1. Launch a **Jupyter Engine for ImJoy**: + + 1. Start an **anaconda terminal**. + 2. **Activate the environment**: `conda activate fq-segmentation` + 3. **Start Jupyter engine**: `imjoy --jupyter` + 4. **First time only**: copy the provided URL including the token, + e.g. `http://127.0.0.1:8888/?token=8b4885e452db1af7cd7b3cfa6c62036cbae46995e473c25e` + +2. **Connect ImJoy to Jupyter Engine**: + + 1. In the ImJoy app, press on the rocket symbol in the upper right corner. + * **First time only**: select `Add Jupyter-Engine`, paste the URL from the step above, and you can give a new name to the engine + * **Subsequent use**: press on the pre-defined plugin engine to connect to it (of course you have to launch it first). + +3. If this is the only engine, plugins will be **automatically connected**. You can verify this, + by clicking on the puzzle symbol next to the plugin name. Depending on the plugin, installation + might take a while, during this period the plugin name will be in red. + + If yoy have multiple engines, you have to choose on which engine the plugin should be running + (more details [below](#plugin-running-on-wrong-engine)). + + ![imjoy-connect-jupyter.gif](img/imjoy-connect-jupyter.gif) + +## Managing plugin engines + +ImJoy remembers the plugin engines it connected to (including the token). You can obtain the list of all +registered engines by pressing on the rocket symbol. + +![imjoy-plugin-manager.png](img/imjoy-plugin-manager.png){: style="width:300px"} + +* **Connected engines** will be shown with their name in black, and a red cross next to the name. Pressing the cross will disconnect ImJoy from the engine, but it will remain in the list. +* **Known engines** (but not connected) will be shown with their name in gray, with a little trash symbol next to them. Pressing on the trash symbol will remove the engine. diff --git a/docs/imjoy-overview.md b/docs/imjoy-overview.md index 3479873..4b62433 100644 --- a/docs/imjoy-overview.md +++ b/docs/imjoy-overview.md @@ -1,46 +1,46 @@ -# Overview - -[ImJoy](https://imjoy.io/docs/#/) is image processing platform with an easy to use interface running in your browser. - -* While ImJoy is a browser app, **NO** user data will be transferred over the internet. -* For best stability, we recommend using [**Chrome**](https://www.google.com/chrome/) to run the ImJoy app. -* Some of its main **features** are: - - 1. Specific functionality is provided by **plugins**, which can be installed with simple links. Available - plugins are listed in the plugin list on the left part of the interface. Plugins using Python require a Plugin engine to be executed. Installation and usasage is detailed below. - - 2. ImJoy can have several **workspaces**. Each workspace can contain multiple plugins and is - dedicated to a specific data processing task. Workspaces can be selected from little puzzle - symbol in the upper left part of the interface. - -![imjoy-interface](img/imjoy-interface.png) - -## Running Python plugins - -Most of the provided plugins use Python for data processing. To use these plugins, -you have to connect ImJoy to a Plugin engine. For this repository, we use **Jupyter notebooks** as -and engine, which can be installed via Miniconda (see [**installation instructions**](#install-jupyter-engine-for-imjoy)). - -## Installing plugins - -We provide links to install ImJoy plugins for the different workflows. - -If you press on the installation link, the ImJoy app will open and display a -dialog asking if you want to install the specified plugin. To confirm, press -the `install` button. - -![imjoy-plugin-installation](img/imjoy-plugin-installation.png){: style="width:400px"} - -These installation links also specify in which [ImJoy workspaces](imjoy-overview.md#opening-a-workspace) the plugin will be installed. - -## Opening a workspace - -Once a plugin is installed, ImJoy remembers the workspaces and plugins it contains. - -If you want to redo an analysis, you simply have to open the [ImJoy app](https://imjoy.io/#/app) -and select the workspace `fq-segmentation` for this package. - -If **updates** for the installed plugins -are available, you will see a corresponding symbol next to the plugin name. - -![imjoy-workspacer.gif](img/imjoy-workspace.gif){: style="width:500px"} +# Overview + +[ImJoy](https://imjoy.io/docs/#/) is image processing platform with an easy to use interface running in your browser. + +* While ImJoy is a browser app, **NO** user data will be transferred over the internet. +* For best stability, we recommend using [**Chrome**](https://www.google.com/chrome/) to run the ImJoy app. +* Some of its main **features** are: + + 1. Specific functionality is provided by **plugins**, which can be installed with simple links. Available + plugins are listed in the plugin list on the left part of the interface. Plugins using Python require a Plugin engine to be executed. Installation and usasage is detailed below. + + 2. ImJoy can have several **workspaces**. Each workspace can contain multiple plugins and is + dedicated to a specific data processing task. Workspaces can be selected from little puzzle + symbol in the upper left part of the interface. + +![imjoy-interface](img/imjoy-interface.png) + +## Running Python plugins + +Most of the provided plugins use Python for data processing. To use these plugins, +you have to connect ImJoy to a Plugin engine. For this repository, we use **Jupyter notebooks** as +and engine, which can be installed via Miniconda (see [**installation instructions**](#install-jupyter-engine-for-imjoy)). + +## Installing plugins + +We provide links to install ImJoy plugins for the different workflows. + +If you press on the installation link, the ImJoy app will open and display a +dialog asking if you want to install the specified plugin. To confirm, press +the `install` button. + +![imjoy-plugin-installation](img/imjoy-plugin-installation.png){: style="width:400px"} + +These installation links also specify in which [ImJoy workspaces](imjoy-overview.md#opening-a-workspace) the plugin will be installed. + +## Opening a workspace + +Once a plugin is installed, ImJoy remembers the workspaces and plugins it contains. + +If you want to redo an analysis, you simply have to open the [ImJoy app](https://imjoy.io/#/app) +and select the workspace `fq-segmentation` for this package. + +If **updates** for the installed plugins +are available, you will see a corresponding symbol next to the plugin name. + +![imjoy-workspacer.gif](img/imjoy-workspace.gif){: style="width:500px"} diff --git a/docs/index.md b/docs/index.md index dcb3bd3..06e54f0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,33 +1,33 @@ - -# Segmentation with ImJoy and Cellpose - -This repository provides convience wrapper code for the **segmentation of cells and nuclei**. - -__How to get started:__ - -1. Read this documentation. -2. Install the ImJoy plugin engine (we explain what this is just a bit further down). -3. Install the FISH-quant plugins. -4. Try to analyze the provided test data. - -!!! example "Example of **nuclear segmentation** and **cytoplasmic segmentation**" - ![segmentation__nuclei](img/segmentation__nuclei.png) - ![segmentation__cells](img/segmentation__cells.png) - -## Python? ImJoy? Plugin engine? - -For new users it might be a bit confusing how the different software packages work together. We hence provide here a small overview of hwo the different pieces work together. There are **three essential parts**: - -![fq-get-version.gif](img/segmentation-overview.png){: style="width:500px"} - -1. **Python**: code performing the actual analysis in two essential parts - * The actual **segmentation** is performed with the [**Cellpose package**](https://github.com/mouseland/cellpose). - * Additional code to pre/post process images, and perform batch processing: - * We provide **convenient wrappers** to apply this approach in batch mode to a large number of images. - * We provide **pre- and post-processing** routines to prepare images for segmentation and extract further information, e.g. distance measurements for objects. - -2. **ImJoy**: ImJoy is a plugin powered computing platform to deploy advanced image analysis tools. Here, we provide as a set of such plugins. Plugins can be installed with a simple installation link. More details about ImJoy and how it can be installed, can be found in the decicated overview section. -3. **Plugin Engine**: the ImJoy app is running in your webbrowser (prefereably Chrome). In order to perform computations, you have to install a -so-called plugin engine. ImJoy can connect to such an engine, and launch data processing tasks. Importantly, this engine can run locally or remotely, but the ImJoy interface will always be the same. YOu have to install this engine once. Each time you want to use FISH-quant, you have to launch it and connect ImJoy to this engine. - - + +# Segmentation with ImJoy and Cellpose + +This repository provides convience wrapper code for the **segmentation of cells and nuclei**. + +__How to get started:__ + +1. Read this documentation. +2. Install the ImJoy plugin engine (we explain what this is just a bit further down). +3. Install the FISH-quant plugins. +4. Try to analyze the provided test data. + +!!! example "Example of **nuclear segmentation** and **cytoplasmic segmentation**" + ![segmentation__nuclei](img/segmentation__nuclei.png) + ![segmentation__cells](img/segmentation__cells.png) + +## Python? ImJoy? Plugin engine? + +For new users it might be a bit confusing how the different software packages work together. We hence provide here a small overview of hwo the different pieces work together. There are **three essential parts**: + +![fq-get-version.gif](img/segmentation-overview.png){: style="width:500px"} + +1. **Python**: code performing the actual analysis in two essential parts + * The actual **segmentation** is performed with the [**Cellpose package**](https://github.com/mouseland/cellpose). + * Additional code to pre/post process images, and perform batch processing: + * We provide **convenient wrappers** to apply this approach in batch mode to a large number of images. + * We provide **pre- and post-processing** routines to prepare images for segmentation and extract further information, e.g. distance measurements for objects. + +2. **ImJoy**: ImJoy is a plugin powered computing platform to deploy advanced image analysis tools. Here, we provide as a set of such plugins. Plugins can be installed with a simple installation link. More details about ImJoy and how it can be installed, can be found in the decicated overview section. +3. **Plugin Engine**: the ImJoy app is running in your webbrowser (prefereably Chrome). In order to perform computations, you have to install a +so-called plugin engine. ImJoy can connect to such an engine, and launch data processing tasks. Importantly, this engine can run locally or remotely, but the ImJoy interface will always be the same. YOu have to install this engine once. Each time you want to use FISH-quant, you have to launch it and connect ImJoy to this engine. + + diff --git a/docs/licence.md b/docs/licence.md index 5c65be5..d132171 100644 --- a/docs/licence.md +++ b/docs/licence.md @@ -1,40 +1,40 @@ -- **Cellpose** is (general) are BSD-licenced (3 clause) - -> Copyright © 2020 Howard Hughes Medical Institute - -> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -Neither the name of HHMI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -- The **rest of the code** provided in this repository is BSD-licenced (3 clause): - ->Copyright © 2020, Florian Mueller ->All rights reserved. -> ->Redistribution and use in source and binary forms, with or without ->modification, are permitted provided that the following conditions are met: -> * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -> * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -> * Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. -> ->THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +- **Cellpose** is (general) are BSD-licenced (3 clause) + +> Copyright © 2020 Howard Hughes Medical Institute + +> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of HHMI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +- The **rest of the code** provided in this repository is BSD-licenced (3 clause): + +>Copyright © 2020, Florian Mueller +>All rights reserved. +> +>Redistribution and use in source and binary forms, with or without +>modification, are permitted provided that the following conditions are met: +> * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +> * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +> * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. +> +>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + https://fish-quant.github.io/segmentation/imjoy-pre-processing/img/imjoy-preprocess-ui.png \ No newline at end of file diff --git a/docs/workflows-create-fq-outlines.md b/docs/workflows-create-fq-outlines.md index aa55a2c..b3bbc1f 100644 --- a/docs/workflows-create-fq-outlines.md +++ b/docs/workflows-create-fq-outlines.md @@ -1,35 +1,35 @@ -# Create FISH-quant [Matlab] outline files -In order to use the segmentation results in the Matlab version of FISH-quant, -FISH-quant outline files have to be created from the mask images. - -For this, we provide a Matlab GUI distributed with the [FISH-quant package](https://bitbucket.org/muellerflorian/fish_quant/src/master/). After installing FISH-quant, you can open this GUI from the command window -with `FQ_seg`. The relevant part of the interface is the central panel _Cell Profiler: generate FQ outlines from segmentation_. - -![](img/fq-create-outlines.png "fq-create-outlines") - - -1. **Specify experimental parameters**. These parameters have to be defined – even if the default parameters are good. - Only then the button to generate the outlines will be enabled. - -0. **Define naming scheme of original images and segmentation results**. You have to define a few parameters regarding - the naming convention of your files. - 1. Unique identifier for the FISH and DAPI images. These identifiers have to be defined in a way that when you take the - full file-name of the FISH image and you replace the identifier for FISH, e.g. `cy3`, by the identifier of the DAPI, e.g. `dapi`, you obtain the DAPI file-name. - 2. Then, you have to define the identifier of your segmentation results. For this workflow, `mask__nuclei__` for the nuclei and - `mask__cells__` for the cells. - 3. File-extensions of the masks and original file-names, e.g. `tif` for the example data. - -0. [Optional] **Generating outline files for a second color**. This option allows to generate outline files for a second color, - e.g. for a dual-color FISH experiment or if the cell segmentation was performed with a different channel than the FISH channel. The outlines for this color will be based on the segmentation results of the first color and the exact cells will be used. This allows a simple comparison between the detection results. As above, the identifier for the second color has to be specified, e.g. `cy5`. You also have to redefine the experimental parameters (most often to adjust the excitation and emission wavelength). - - Additionally, you can choose to not create the outlines for the channel that was used for cell segmentation. This option is useful if the first color does not contain actual smFISH data but results of a dedicated cell segmentation stain. - -0. Several options exist to **specify the folder** where the results will be saved. By default, the FQ outlines will be stored - in a sub-folder `__FQ_outlines` within the folder containing the segmentation results. You can then move it to another location, e.g. directly into the `analysis` folder as done for the example data. - -0. Specify **images that will be analyzed**. You can either choose different images that you want to analyze (`Define images`), - or select an entire folder (`Select folder`). For the latter, you can also specify a recursive search; this means that all subfolders will be searched as well (not recommended for this workflow). The script will only consider images that follow the above explained naming convention – other images will be ignored. - In the example data, the folder `analysis\segmentation-results` contains the relevant data. - -0. **Create outlines**. Lastly, press the button `Create FQ outlines`. The script will then automatically search for the files - describing the segmentation of cells and nuclei. For each image an outline file with the reference to the ORIGINAL 3D image will be generated and nuclei assigned to their respective cells. +# Create FISH-quant [Matlab] outline files +In order to use the segmentation results in the Matlab version of FISH-quant, +FISH-quant outline files have to be created from the mask images. + +For this, we provide a Matlab GUI distributed with the [FISH-quant package](https://bitbucket.org/muellerflorian/fish_quant/src/master/). After installing FISH-quant, you can open this GUI from the command window +with `FQ_seg`. The relevant part of the interface is the central panel _Cell Profiler: generate FQ outlines from segmentation_. + +![](img/fq-create-outlines.png "fq-create-outlines") + + +1. **Specify experimental parameters**. These parameters have to be defined – even if the default parameters are good. + Only then the button to generate the outlines will be enabled. + +0. **Define naming scheme of original images and segmentation results**. You have to define a few parameters regarding + the naming convention of your files. + 1. Unique identifier for the FISH and DAPI images. These identifiers have to be defined in a way that when you take the + full file-name of the FISH image and you replace the identifier for FISH, e.g. `cy3`, by the identifier of the DAPI, e.g. `dapi`, you obtain the DAPI file-name. + 2. Then, you have to define the identifier of your segmentation results. For this workflow, `mask__nuclei__` for the nuclei and + `mask__cells__` for the cells. + 3. File-extensions of the masks and original file-names, e.g. `tif` for the example data. + +0. [Optional] **Generating outline files for a second color**. This option allows to generate outline files for a second color, + e.g. for a dual-color FISH experiment or if the cell segmentation was performed with a different channel than the FISH channel. The outlines for this color will be based on the segmentation results of the first color and the exact cells will be used. This allows a simple comparison between the detection results. As above, the identifier for the second color has to be specified, e.g. `cy5`. You also have to redefine the experimental parameters (most often to adjust the excitation and emission wavelength). + + Additionally, you can choose to not create the outlines for the channel that was used for cell segmentation. This option is useful if the first color does not contain actual smFISH data but results of a dedicated cell segmentation stain. + +0. Several options exist to **specify the folder** where the results will be saved. By default, the FQ outlines will be stored + in a sub-folder `__FQ_outlines` within the folder containing the segmentation results. You can then move it to another location, e.g. directly into the `analysis` folder as done for the example data. + +0. Specify **images that will be analyzed**. You can either choose different images that you want to analyze (`Define images`), + or select an entire folder (`Select folder`). For the latter, you can also specify a recursive search; this means that all subfolders will be searched as well (not recommended for this workflow). The script will only consider images that follow the above explained naming convention – other images will be ignored. + In the example data, the folder `analysis\segmentation-results` contains the relevant data. + +0. **Create outlines**. Lastly, press the button `Create FQ outlines`. The script will then automatically search for the files + describing the segmentation of cells and nuclei. For each image an outline file with the reference to the ORIGINAL 3D image will be generated and nuclei assigned to their respective cells. diff --git a/docs/workflows-create-numbered-labels.md b/docs/workflows-create-numbered-labels.md index c38e956..937178f 100644 --- a/docs/workflows-create-numbered-labels.md +++ b/docs/workflows-create-numbered-labels.md @@ -1,21 +1,21 @@ -# Numbered label image - -Segmentation results in a filled mask for each segmented object (cell or nucleus). Each mask has a unique integer value (0 is reserved for background). This value can be used to uniquely identify each object. An inspection of the resulting label image is possible in any image processing tool, such as FIJI. However, for convience, it might also be useful to have an overview image showing all labels, such as the one shown below - -After segmentation, each object (cell or nucleus) has a unique id. - ![label-image-numbered.png](img/label-image-numbered.png) - -This workflow permits to create such an image from any label image created by the segmentation workflow. - -You can install the plugin from **here.** - -## Workflow - -The default settings of the plugins allow to quickly perform the recommended workflow. You only have -to paste the path to folder containing the segmentation results (usually a folder called `segmentation-results`). The plugin will then loop over all files in this folder, and select the onces fitting the other two parameters: - -- `File string`: specify a string that the file-name has to contain. The default `mask__` fits for both segmentation of cells and nuclei. -- `Img extension: file-extension of the label images. The default is `.png`, which is the extension used by our segmentation pipeline. - -The plugin will then create a new subfolder called `labels_numbered` where the numbered label images +# Numbered label image + +Segmentation results in a filled mask for each segmented object (cell or nucleus). Each mask has a unique integer value (0 is reserved for background). This value can be used to uniquely identify each object. An inspection of the resulting label image is possible in any image processing tool, such as FIJI. However, for convience, it might also be useful to have an overview image showing all labels, such as the one shown below + +After segmentation, each object (cell or nucleus) has a unique id. + ![label-image-numbered.png](img/label-image-numbered.png) + +This workflow permits to create such an image from any label image created by the segmentation workflow. + +You can install the plugin from **here.** + +## Workflow + +The default settings of the plugins allow to quickly perform the recommended workflow. You only have +to paste the path to folder containing the segmentation results (usually a folder called `segmentation-results`). The plugin will then loop over all files in this folder, and select the onces fitting the other two parameters: + +- `File string`: specify a string that the file-name has to contain. The default `mask__` fits for both segmentation of cells and nuclei. +- `Img extension: file-extension of the label images. The default is `.png`, which is the extension used by our segmentation pipeline. + +The plugin will then create a new subfolder called `labels_numbered` where the numbered label images will be stored under their original file-name with an added suffix `__numbered.png`. \ No newline at end of file diff --git a/docs/workflows-distance-objects.md b/docs/workflows-distance-objects.md index e404af1..990d838 100644 --- a/docs/workflows-distance-objects.md +++ b/docs/workflows-distance-objects.md @@ -1,48 +1,48 @@ -# Closest object in image - -For several workflows, it might be interesting to know the object that is the closest each pixel in the. - -![obj-dist-example.png](img/obj-dist-example.png) - -This workflow takes the image of a segmentation results as an input (right image above shows a -nuclear segmentation), and calculates two images of the same size, where each pixel value corresponds to - -1. Distance to the closted object (left image). -2. Index of clostest object (central image). - -With these two images, question about proximity can be answered: for each position in the image, the closest -object and the distance to this object can now be easily identified. - -In the **example data**, you can find processed images for the nuclei segmentation: `example_data\analysis\distance-maps`. - -## Recommended workflow - -The default settings of the plugins allow to quickly perform the recommended workflow. You only have -to paste your data folder. - -1. Segmentation results are stored `segmentation-input` -2. In the pre-processing step, the folder to save data is obtained with - - 1. the text replacement `segmentation-results˃˃distance-maps`. - 2. Images are stored in a new subfolder `segmentation-input` - - With these options, the images will be saved in a folder `distance-maps`. - -## Implementations - -To **calculate these images** for all masks in a folder, you can use either - -* **Jupyter notebook**: notebooks\distance-closest-object.ipynb -* **ImJoy plugin**: `ObjectDist`, which you -**install from here.** - -In either case, the following **parameters** have to be specified - -Option | Type | Description ----------------- | ---- | ----------- -`Path DATA` | str | Full path to folder containing data to be analyzed. -`String label` | str | Unique string identified the mask image that you want to analyze, e.g. `mask__nuclei__` -`String save` | tuple | Pair of strings defining the names under which the images with the index of the object, and the distance to this object will be saved, e.g. `('nuclei_close_ind__', 'nuclei_close_dist__')` -`Trunc distance` | int | Threshold above which distances will be clipped. -`Path SAVE` | str | Several options exist. See dedicated section here [below](data.md#specify-folder-to-save-your-data) for more details. -`Search recursive` | bool | Should provided folder be search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) for images (true/false). +# Closest object in image + +For several workflows, it might be interesting to know the object that is the closest each pixel in the. + +![obj-dist-example.png](img/obj-dist-example.png) + +This workflow takes the image of a segmentation results as an input (right image above shows a +nuclear segmentation), and calculates two images of the same size, where each pixel value corresponds to + +1. Distance to the closted object (left image). +2. Index of clostest object (central image). + +With these two images, question about proximity can be answered: for each position in the image, the closest +object and the distance to this object can now be easily identified. + +In the **example data**, you can find processed images for the nuclei segmentation: `example_data\analysis\distance-maps`. + +## Recommended workflow + +The default settings of the plugins allow to quickly perform the recommended workflow. You only have +to paste your data folder. + +1. Segmentation results are stored `segmentation-input` +2. In the pre-processing step, the folder to save data is obtained with + + 1. the text replacement `segmentation-results˃˃distance-maps`. + 2. Images are stored in a new subfolder `segmentation-input` + + With these options, the images will be saved in a folder `distance-maps`. + +## Implementations + +To **calculate these images** for all masks in a folder, you can use either + +* **Jupyter notebook**: notebooks\distance-closest-object.ipynb +* **ImJoy plugin**: `ObjectDist`, which you +**install from here.** + +In either case, the following **parameters** have to be specified + +Option | Type | Description +---------------- | ---- | ----------- +`Path DATA` | str | Full path to folder containing data to be analyzed. +`String label` | str | Unique string identified the mask image that you want to analyze, e.g. `mask__nuclei__` +`String save` | tuple | Pair of strings defining the names under which the images with the index of the object, and the distance to this object will be saved, e.g. `('nuclei_close_ind__', 'nuclei_close_dist__')` +`Trunc distance` | int | Threshold above which distances will be clipped. +`Path SAVE` | str | Several options exist. See dedicated section here [below](data.md#specify-folder-to-save-your-data) for more details. +`Search recursive` | bool | Should provided folder be search [**recursively**](analysis-general-behavior.md#recursive-search-for-data) for images (true/false). diff --git a/docs/workflows-fiji-split-channels.md b/docs/workflows-fiji-split-channels.md index eaa2e01..5ac3cb9 100644 --- a/docs/workflows-fiji-split-channels.md +++ b/docs/workflows-fiji-split-channels.md @@ -1,48 +1,48 @@ -# Split channels - -This workflow requires that each channel is stored as a separate z-stack. -If your images are stored as multi-channel z-stacks you have to split these images into -individual channels. This can be done with different software packages, below we describe some options. - -## Manual conversion with Fiji - -For this you need to install [Fiji](https://fiji.sc/). - -1. Create a new folder for each multi-channel image. -2. Open image stack in FIJI. -3. Split channels (FQ only supports mono-channel input images) - 1. From menu: `Image` > `Color` > `Split channels` - 2. Save each channels with a unique channel identifier, e.g. `C1-` or `DAPI_`. - -## Batch conversion with Fiji - -We provide a FIJI macro that allows to recursively search for multi-channel images -and convert them to mono-channel images, which will be saved in a newly created subfolder. - -For this you need to - -* Install [Fiji](https://fiji.sc/). -* Use the provided macro `MacroBatchSplitChannel.ijm`, provided in the folder `workflows`. - -### Workflow - -Once you start the macro, it will perform the following steps - -1. Scan the provided folder for images ending in the specified suffix. -2. Images will be opened, split in different channels and maximum intensity projections (MIP) created -3. Results will be saved in a new folder. Will create additional subfolders 'MIP' to store the MIPs. - -### How to use the macro - -1. By default your data has to be in a folder `multi_channel_stacks`, but you can change this in the macro -2. Open Fiji and drag the macro file in Fiji. -3. This will open the macro, here you can modify two parameters - * `suffix` (default `ome.tif`): last part of the file-name. Only files containing this string will be processed. - * `folder_img` (default `multi_channel_stacks`): part of the file-name that will be replaced by `acquisition` in order to save the results (see below). -4. Press the `Run` button and you will be asked to specify the folder that should be processed. - -### Filename and path for saved images - -* For each multi-channel image, the individual channels will be saved as a separate tif file, with **a suffix in -the file-name** indicating the channel index, e.g. `_ch1_` for the first channel. -* Files will be saved in a newly created folder, where the string specified in `folder_img` will be replaced with `acquisition`. This allows to keep nested folder structures when larger data-sets are processed. +# Split channels + +This workflow requires that each channel is stored as a separate z-stack. +If your images are stored as multi-channel z-stacks you have to split these images into +individual channels. This can be done with different software packages, below we describe some options. + +## Manual conversion with Fiji + +For this you need to install [Fiji](https://fiji.sc/). + +1. Create a new folder for each multi-channel image. +2. Open image stack in FIJI. +3. Split channels (FQ only supports mono-channel input images) + 1. From menu: `Image` > `Color` > `Split channels` + 2. Save each channels with a unique channel identifier, e.g. `C1-` or `DAPI_`. + +## Batch conversion with Fiji + +We provide a FIJI macro that allows to recursively search for multi-channel images +and convert them to mono-channel images, which will be saved in a newly created subfolder. + +For this you need to + +* Install [Fiji](https://fiji.sc/). +* Use the provided macro `MacroBatchSplitChannel.ijm`, provided in the folder `workflows`. + +### Workflow + +Once you start the macro, it will perform the following steps + +1. Scan the provided folder for images ending in the specified suffix. +2. Images will be opened, split in different channels and maximum intensity projections (MIP) created +3. Results will be saved in a new folder. Will create additional subfolders 'MIP' to store the MIPs. + +### How to use the macro + +1. By default your data has to be in a folder `multi_channel_stacks`, but you can change this in the macro +2. Open Fiji and drag the macro file in Fiji. +3. This will open the macro, here you can modify two parameters + * `suffix` (default `ome.tif`): last part of the file-name. Only files containing this string will be processed. + * `folder_img` (default `multi_channel_stacks`): part of the file-name that will be replaced by `acquisition` in order to save the results (see below). +4. Press the `Run` button and you will be asked to specify the folder that should be processed. + +### Filename and path for saved images + +* For each multi-channel image, the individual channels will be saved as a separate tif file, with **a suffix in +the file-name** indicating the channel index, e.g. `_ch1_` for the first channel. +* Files will be saved in a newly created folder, where the string specified in `folder_img` will be replaced with `acquisition`. This allows to keep nested folder structures when larger data-sets are processed. diff --git a/docs/workflows-overview.md b/docs/workflows-overview.md index 037b990..cc6e5b3 100644 --- a/docs/workflows-overview.md +++ b/docs/workflows-overview.md @@ -1,9 +1,9 @@ - -Here we provide a description of less frequently used workflows. - -* [**Split multi-channel images**](workflows-fiji-split-channels.md) to obain single-channel images for analysis. -* [**Create numbered label images**](workflows-create-numbered-labels.md) to create an image with color-code segmentation masks including their identifer. -* [**Calculate distance to objects**](workflows-distance-objects.md) to calculate images summarizing which object is closest to each pixel. -* [**Convert segmentation results in FQ outlines**](workflows-create-fq-outlines.md) that can be used by the older Matlab version of FISH-quant. - + +Here we provide a description of less frequently used workflows. + +* [**Split multi-channel images**](workflows-fiji-split-channels.md) to obain single-channel images for analysis. +* [**Create numbered label images**](workflows-create-numbered-labels.md) to create an image with color-code segmentation masks including their identifer. +* [**Calculate distance to objects**](workflows-distance-objects.md) to calculate images summarizing which object is closest to each pixel. +* [**Convert segmentation results in FQ outlines**](workflows-create-fq-outlines.md) that can be used by the older Matlab version of FISH-quant. + Each workflow is documented in a dedicated section. \ No newline at end of file diff --git a/imjoy-plugins/ObjectDist.imjoy.html b/imjoy-plugins/ObjectDist.imjoy.html index aa9496b..1018e37 100644 --- a/imjoy-plugins/ObjectDist.imjoy.html +++ b/imjoy-plugins/ObjectDist.imjoy.html @@ -1,113 +1,113 @@ - -ImJoy plugin to calculate images facilitating distance calculations. - - - -{ - "name": "ObjectDist", - "type": "native-python", - "version": "0.2.1", - "description": "Pre-process 3D images to obtain 2D images for segmentation.", - "tags": ["stable","dev"], - "ui": [ - "Path DATA: {id: 'path_scan', type: 'string', placeholder: 'paste-path-to-data'}", - "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'segmentation-results˃˃distance-maps'}", - "String channel: {id: 'str_label', type: 'string', placeholder: 'mask__nuclei__'}", - "String save INDEX: {id: 'str_save_ind', type: 'string', placeholder: 'nuclei_close_ind__'}", - "String save DIST: {id: 'str_save_dist', type: 'string', placeholder: 'nuclei_close_dist__'}", - "Truncate distance: {id: 'truncate_distance', type: 'number', placeholder: 255}", - "Search recursive: {id: 'search_recursive', type: 'choose', options: ['false', 'true'], placeholder: 'false'}" - ], - "cover": "", - "inputs": null, - "outputs": null, - "flags": [], - "icon": "extension", - "api_version": "0.1.7", - "env": "", - "permissions": [], - "requirements": { - "dev": [""], - "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master scikit-image"] - }, - "dependencies": [] -} - - - \ No newline at end of file diff --git a/imjoy-plugins/PreProcess.imjoy.html b/imjoy-plugins/PreProcess.imjoy.html index f4d2d83..5e1bbc5 100644 --- a/imjoy-plugins/PreProcess.imjoy.html +++ b/imjoy-plugins/PreProcess.imjoy.html @@ -1,121 +1,121 @@ - -ImJoy plugin to per-process images for cell segmentation with -the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). - - - -{ - "name": "PreProcess", - "type": "native-python", - "version": "0.2.0", - "description": "Pre-process 3D images to obtain 2D images for segmentation.", - "tags": ["stable","dev"], - "ui": [ - "Path DATA: {id: 'path_process', type: 'string', placeholder: 'paste-path-to-data'}", - "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'acquisition˃˃analysis'}", - "Subfolder: {id: 'subfolder', type: 'string', placeholder: 'segmentation-input'}", - "Channel string: {id: 'channel_ident', type: 'string', placeholder: 'dapi'}", - "Img extension: {id: 'img_ext', type: 'string', placeholder: '.tif'}", - "Projection type: {id: 'projection_type', type: 'choose', options: ['max', 'mean','indiv'], placeholder: 'max'}", - "Search recursive: {id: 'search_recursive', type: 'choose', options: ['false', 'true'], placeholder: 'false'}" - ], - "cover": "", - "inputs": null, - "outputs": null, - "flags": [], - "icon": "extension", - "api_version": "0.1.7", - "env": "", - "permissions": [], - "requirements": { - "dev": [""], - "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] - }, - "dependencies": [] -} - - - + +ImJoy plugin to per-process images for cell segmentation with +the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). + + + +{ + "name": "PreProcess", + "type": "native-python", + "version": "0.2.0", + "description": "Pre-process 3D images to obtain 2D images for segmentation.", + "tags": ["stable","dev"], + "ui": [ + "Path DATA: {id: 'path_process', type: 'string', placeholder: 'paste-path-to-data'}", + "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'acquisition˃˃analysis'}", + "Subfolder: {id: 'subfolder', type: 'string', placeholder: 'segmentation-input'}", + "Channel string: {id: 'channel_ident', type: 'string', placeholder: 'dapi'}", + "Img extension: {id: 'img_ext', type: 'string', placeholder: '.tif'}", + "Projection type: {id: 'projection_type', type: 'choose', options: ['max', 'mean','indiv'], placeholder: 'max'}", + "Search recursive: {id: 'search_recursive', type: 'choose', options: ['false', 'true'], placeholder: 'false'}" + ], + "cover": "", + "inputs": null, + "outputs": null, + "flags": [], + "icon": "extension", + "api_version": "0.1.7", + "env": "", + "permissions": [], + "requirements": { + "dev": [""], + "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] + }, + "dependencies": [] +} + + + diff --git a/imjoy-plugins/SegmentCellsNuclei.imjoy.html b/imjoy-plugins/SegmentCellsNuclei.imjoy.html index 36d8bfd..d87fa3c 100644 --- a/imjoy-plugins/SegmentCellsNuclei.imjoy.html +++ b/imjoy-plugins/SegmentCellsNuclei.imjoy.html @@ -1,155 +1,155 @@ - -ImJoy plugin to perform cell segmentation with -the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). - - - -{ - "name": "SegmentCellsNuclei", - "type": "native-python", - "version": "0.2.2", - "description": "Segment cells AND nuclei.", - "tags": ["stable","dev"], - "ui": [ - "Path DATA: {id: 'path_scan', type: 'string', placeholder: 'paste-path-to-data'}", - "Input subfolder: {id: 'input_subfolder', type: 'string', placeholder: 'segmentation-input'}", - "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'segmentation-input˃˃segmentation-results'}", - "String CELLS: {id: 'str_cyto', type: 'string', placeholder: 'cy3'}", - "String NUCLEI: {id: 'str_nuclei', type: 'string', placeholder: 'dapi'}", - "Scaling factor/New size: : {id: 'new_size', type: 'string', placeholder: '2'}", - "Size CELLS: {id: 'size_cells', type: 'number', placeholder: 200}", - "Size NUCLEI: {id: 'size_nuclei', type: 'number', placeholder: 100}", - "Net Average: {id: 'net_avg', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", - "Resample: {id: 'resample', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", - "String img ext: {id: 'img_ext', type: 'string', placeholder: '.png'}" - ], - "cover": "", - "inputs": null, - "outputs": null, - "flags": [], - "icon": "extension", - "api_version": "0.1.7", - "env": "", - "permissions": [], - "requirements": { - "dev": [""], - "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] - }, - "dependencies": [], - "cover": "https://fq-segmentation.readthedocs.io/en/latest/img/segmentation__cells.png" -} - - - + +ImJoy plugin to perform cell segmentation with +the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). + + + +{ + "name": "SegmentCellsNuclei", + "type": "native-python", + "version": "0.2.2", + "description": "Segment cells AND nuclei.", + "tags": ["stable","dev"], + "ui": [ + "Path DATA: {id: 'path_scan', type: 'string', placeholder: 'paste-path-to-data'}", + "Input subfolder: {id: 'input_subfolder', type: 'string', placeholder: 'segmentation-input'}", + "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'segmentation-input˃˃segmentation-results'}", + "String CELLS: {id: 'str_cyto', type: 'string', placeholder: 'cy3'}", + "String NUCLEI: {id: 'str_nuclei', type: 'string', placeholder: 'dapi'}", + "Scaling factor/New size: : {id: 'new_size', type: 'string', placeholder: '2'}", + "Size CELLS: {id: 'size_cells', type: 'number', placeholder: 200}", + "Size NUCLEI: {id: 'size_nuclei', type: 'number', placeholder: 100}", + "Net Average: {id: 'net_avg', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", + "Resample: {id: 'resample', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", + "String img ext: {id: 'img_ext', type: 'string', placeholder: '.png'}" + ], + "cover": "", + "inputs": null, + "outputs": null, + "flags": [], + "icon": "extension", + "api_version": "0.1.7", + "env": "", + "permissions": [], + "requirements": { + "dev": [""], + "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] + }, + "dependencies": [], + "cover": "https://fq-segmentation.readthedocs.io/en/latest/img/segmentation__cells.png" +} + + + diff --git a/imjoy-plugins/SegmentObjects.imjoy.html b/imjoy-plugins/SegmentObjects.imjoy.html index 9f9b8b0..6cff88b 100644 --- a/imjoy-plugins/SegmentObjects.imjoy.html +++ b/imjoy-plugins/SegmentObjects.imjoy.html @@ -1,156 +1,156 @@ - -ImJoy plugin to perform object segmentation with -the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). - - - -{ - "name": "SegmentObjects", - "type": "native-python", - "version": "0.2.2", - "description": "Segment cells OR nuclei.", - "tags": ["stable","dev"], - "ui": [ - "Path DATA: {id: 'path_scan', type: 'string', placeholder: 'paste-path-to-data'}", - "Input subfolder: {id: 'input_subfolder', type: 'string', placeholder: 'segmentation-input'}", - "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'segmentation-input˃˃segmentation-results'}", - "String channel: {id: 'str_channel', type: 'string', placeholder: 'dapi'}", - "Object name: {id: 'obj_name', type: 'string', placeholder: 'nuclei'}", - "Cellpose model: {id: 'model_type', type: 'choose', options: ['nuclei', 'cyto'], placeholder: 'nuclei'}", - "Scaling factor/New size:: {id: 'new_size', type: 'string', placeholder: '1'}", - "Object diameter: {id: 'diameter', type: 'number', placeholder: 100}", - "Net Average: {id: 'net_avg', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", - "Resample: {id: 'resample', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", - "String img ext: {id: 'img_ext', type: 'string', placeholder: '.png'}" - ], - "cover": "", - "inputs": null, - "outputs": null, - "flags": [], - "icon": "extension", - "api_version": "0.1.7", - "env": "", - "permissions": [], - "requirements": { - "dev": [""], - "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] - }, - "dependencies": [], - "cover": "https://fq-segmentation.readthedocs.io/en/latest/img/segmentation__cells.png" -} - - - + +ImJoy plugin to perform object segmentation with +the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). + + + +{ + "name": "SegmentObjects", + "type": "native-python", + "version": "0.2.2", + "description": "Segment cells OR nuclei.", + "tags": ["stable","dev"], + "ui": [ + "Path DATA: {id: 'path_scan', type: 'string', placeholder: 'paste-path-to-data'}", + "Input subfolder: {id: 'input_subfolder', type: 'string', placeholder: 'segmentation-input'}", + "Path SAVE: {id: 'path_save', type: 'string', placeholder: 'segmentation-input˃˃segmentation-results'}", + "String channel: {id: 'str_channel', type: 'string', placeholder: 'dapi'}", + "Object name: {id: 'obj_name', type: 'string', placeholder: 'nuclei'}", + "Cellpose model: {id: 'model_type', type: 'choose', options: ['nuclei', 'cyto'], placeholder: 'nuclei'}", + "Scaling factor/New size:: {id: 'new_size', type: 'string', placeholder: '1'}", + "Object diameter: {id: 'diameter', type: 'number', placeholder: 100}", + "Net Average: {id: 'net_avg', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", + "Resample: {id: 'resample', type: 'choose', options: ['false', 'true'], placeholder: 'false'}", + "String img ext: {id: 'img_ext', type: 'string', placeholder: '.png'}" + ], + "cover": "", + "inputs": null, + "outputs": null, + "flags": [], + "icon": "extension", + "api_version": "0.1.7", + "env": "", + "permissions": [], + "requirements": { + "dev": [""], + "stable":["pip: -U git+https://github.com/fish-quant/fq-segmentation@master"] + }, + "dependencies": [], + "cover": "https://fq-segmentation.readthedocs.io/en/latest/img/segmentation__cells.png" +} + + + diff --git a/imjoy-plugins/The_size_of_RNA_as_an_ideal_branched_polymer.pdf b/imjoy-plugins/The_size_of_RNA_as_an_ideal_branched_polymer.pdf new file mode 100644 index 0000000..27400c5 Binary files /dev/null and b/imjoy-plugins/The_size_of_RNA_as_an_ideal_branched_polymer.pdf differ diff --git a/mkdocs.yml b/mkdocs.yml index 5546dad..32920b1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,88 +1,88 @@ -# Project information -site_name: Cell segmentation -site_description: Wrapper code to use the generalist cell/nuclei segmentation package [**Cellpose**] -site_author: Florian MUELLER -site_url: https://github.com/fish-quant/fq-segmentation - -# Repository -repo_name: GitHub -repo_url: https://github.com/fish-quant/fq-segmentation.git -edit_uri: "" - -# Copyright -copyright: 'Copyright © 2019 Florian MUELLER' - -# Configuration -theme: - name: 'readthedocs' - #name: mkdocs - # 404 page - static_templates: - - 404.html - - # Don't include MkDocs' JavaScript - #include_search_page: false - #search_index_only: true - - # Default values, taken from mkdocs_theme.yml - language: en - feature: - tabs: true - palette: - primary: indigo - accent: indigo - font: - text: Roboto - code: Roboto Mono - favicon: /img/icons8-eukaryotic-cells-filled-100.png - logo: - icon: wb_sunny - -# Customization -extra: - social: - - type: github-alt - link: https://github.com/fish-quant/fq-segmentation - -# MD extension -markdown_extensions: - - admonition - - pymdownx.details - - pymdownx.tasklist - - pymdownx.superfences - - pymdownx.tilde - - attr_list - - toc: - permalink: true - -nav: - - Overview: index.md # Needed for material theme but not for other - - - ImJoy: - - Overview: imjoy-overview.md - - Installation: imjoy-installation.md - - FAQ: imjoy-faq.md - - - ImJoy Analysis: - - Overview: analysis-overview.md - - General behavior: analysis-general-behavior.md - - Data: data.md - - Preprocessing: analysis-preprocessing.md - - Segmentation: analysis-segmentation.md - - - Diverse workflows: - - Overview: workflows-overview.md - - Split channels: workflows-fiji-split-channels.md - - Numbered label image: workflows-create-numbered-labels.md - - Distance to objects: workflows-distance-objects.md - - Create FQ outline files: workflows-create-fq-outlines.md - - - Advanced: - - Developers: developer.md - - - More: - - Licence: licence.md - - Contact: contact.md - -#theme: -# name: 'material' +# Project information +site_name: Cell segmentation +site_description: Wrapper code to use the generalist cell/nuclei segmentation package [**Cellpose**] +site_author: Florian MUELLER +site_url: https://github.com/fish-quant/fq-segmentation + +# Repository +repo_name: GitHub +repo_url: https://github.com/fish-quant/fq-segmentation.git +edit_uri: "" + +# Copyright +copyright: 'Copyright © 2019 Florian MUELLER' + +# Configuration +theme: + name: 'readthedocs' + #name: mkdocs + # 404 page + static_templates: + - 404.html + + # Don't include MkDocs' JavaScript + #include_search_page: false + #search_index_only: true + + # Default values, taken from mkdocs_theme.yml + language: en + feature: + tabs: true + palette: + primary: indigo + accent: indigo + font: + text: Roboto + code: Roboto Mono + favicon: /img/icons8-eukaryotic-cells-filled-100.png +# logo: +# icon: wb_sunny + +# Customization +extra: + social: + - type: github-alt + link: https://github.com/fish-quant/fq-segmentation + +# MD extension +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.tasklist + - pymdownx.superfences + - pymdownx.tilde + - attr_list + - toc: + permalink: true + +nav: + - Overview: index.md # Needed for material theme but not for other + + - ImJoy: + - Overview: imjoy-overview.md + - Installation: imjoy-installation.md + - FAQ: imjoy-faq.md + + - ImJoy Analysis: + - Overview: analysis-overview.md + - General behavior: analysis-general-behavior.md + - Data: data.md + - Preprocessing: analysis-preprocessing.md + - Segmentation: analysis-segmentation.md + + - Diverse workflows: + - Overview: workflows-overview.md + - Split channels: workflows-fiji-split-channels.md + - Numbered label image: workflows-create-numbered-labels.md + - Distance to objects: workflows-distance-objects.md + - Create FQ outline files: workflows-create-fq-outlines.md + + - Advanced: + - Developers: developer.md + + - More: + - Licence: licence.md + - Contact: contact.md + +#theme: +# name: 'material' diff --git a/notebooks/distance-closest-object.ipynb b/notebooks/distance-closest-object.ipynb index ff7fbf0..31cff34 100644 --- a/notebooks/distance-closest-object.ipynb +++ b/notebooks/distance-closest-object.ipynb @@ -1,75 +1,75 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Notebook to perform segmentation of either cells or nuclei with cellpose.\n", - "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", - " ```\n", - " pip install git+https://github.com/muellerflorian/fq-segmentation/ --upgrade\n", - " ```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Imports \n", - "import pathlib\n", - "from pathlib import Path\n", - "\n", - "from segwrap import utils_masks # Either on sys path or pip installed" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# >>>> Function call\n", - "\n", - "# >> Parameters\n", - "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", - "\n", - "str_label = 'mask__nuclei__'\n", - "strs_save = ('nuclei_close_ind__', 'nuclei_close_dist__')\n", - "truncate_distance = 255 # Distance above which distances will be truncated. \n", - "path_save = None\n", - "\n", - "# >> Call processing function\n", - "importlib.reload(utils_masks)\n", - "utils_masks.create_img_closest_obj(path_scan=path_scan,\n", - " str_label=str_label,\n", - " strs_save=strs_save,\n", - " path_save = path_save,\n", - " search_recursive=false,\n", - " truncate_distance=255)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", - "language": "python", - "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Notebook to perform segmentation of either cells or nuclei with cellpose.\n", + "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", + " ```\n", + " pip install git+https://github.com/muellerflorian/fq-segmentation/ --upgrade\n", + " ```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Imports \n", + "import pathlib\n", + "from pathlib import Path\n", + "\n", + "from segwrap import utils_masks # Either on sys path or pip installed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# >>>> Function call\n", + "\n", + "# >> Parameters\n", + "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", + "\n", + "str_label = 'mask__nuclei__'\n", + "strs_save = ('nuclei_close_ind__', 'nuclei_close_dist__')\n", + "truncate_distance = 255 # Distance above which distances will be truncated. \n", + "path_save = None\n", + "\n", + "# >> Call processing function\n", + "importlib.reload(utils_masks)\n", + "utils_masks.create_img_closest_obj(path_scan=path_scan,\n", + " str_label=str_label,\n", + " strs_save=strs_save,\n", + " path_save = path_save,\n", + " search_recursive=false,\n", + " truncate_distance=255)\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", + "language": "python", + "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } \ No newline at end of file diff --git a/notebooks/pre-processing.ipynb b/notebooks/pre-processing.ipynb index bbac6b0..f02ce40 100644 --- a/notebooks/pre-processing.ipynb +++ b/notebooks/pre-processing.ipynb @@ -1,81 +1,81 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Notebook to perform preprocessing of images for segmentation\n", - "* Takes 3D images and performs a 2D projection to obtain 2D png that can be used in the segmentation algorithm.\n", - "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", - " ```\n", - " pip install git+https://github.com/muellerflorian/fq-segmentation/ --upgrade\n", - " ```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# >>> Imports \n", - "import segwrap\n", - "from segwrap import utils_segmentation\n", - "from pathlib import Path \n", - "\n", - "# Print version and location of package\n", - "print(f' * segwrap version: {segwrap.__version__}')\n", - "print(f' * utils_segmentation location: {utils_segmentation.__file__}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# >>> Function call\n", - "\n", - "# Parameters\n", - "path_process = Path(r'paste-path-to-data') # For example data: \\example_data\\acquisition\n", - "path_save = 'acquisition>>analysis' # If defined with >>, a string replacement will be performed (see https://fq-segmentation--27.org.readthedocs.build/en/27/analysis-general-behavior/#specify-folder-to-save-results)\n", - "subbfolder = 'segmentation-input' # Name of subfolder to store results.\n", - "channel_ident = 'dapi' # Identifier of channel that should be pre-processed \n", - "img_ext = '.tif' # Extension of images that should be processed\n", - "projection_type = 'max' # Projection type (mean, max, indiv)\n", - "search_recursive = False # Search folder recursively for data?\n", - "\n", - "# Call pre-processing function\n", - "utils_segmentation.folder_prepare_prediction(\n", - " path_process=path_process,\n", - " channel_ident=channel_ident,\n", - " img_ext=img_ext,\n", - " path_save=path_save,\n", - " projection_type=projection_type,\n", - " subfolder=subbfolder,\n", - " search_recursive=search_recursive)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", - "language": "python", - "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Notebook to perform preprocessing of images for segmentation\n", + "* Takes 3D images and performs a 2D projection to obtain 2D png that can be used in the segmentation algorithm.\n", + "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", + " ```\n", + " pip install git+https://github.com/muellerflorian/fq-segmentation/ --upgrade\n", + " ```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# >>> Imports \n", + "import segwrap\n", + "from segwrap import utils_segmentation\n", + "from pathlib import Path \n", + "\n", + "# Print version and location of package\n", + "print(f' * segwrap version: {segwrap.__version__}')\n", + "print(f' * utils_segmentation location: {utils_segmentation.__file__}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# >>> Function call\n", + "\n", + "# Parameters\n", + "path_process = Path(r'paste-path-to-data') # For example data: \\example_data\\acquisition\n", + "path_save = 'acquisition>>analysis' # If defined with >>, a string replacement will be performed (see https://fq-segmentation--27.org.readthedocs.build/en/27/analysis-general-behavior/#specify-folder-to-save-results)\n", + "subbfolder = 'segmentation-input' # Name of subfolder to store results.\n", + "channel_ident = 'dapi' # Identifier of channel that should be pre-processed \n", + "img_ext = '.tif' # Extension of images that should be processed\n", + "projection_type = 'max' # Projection type (mean, max, indiv)\n", + "search_recursive = False # Search folder recursively for data?\n", + "\n", + "# Call pre-processing function\n", + "utils_segmentation.folder_prepare_prediction(\n", + " path_process=path_process,\n", + " channel_ident=channel_ident,\n", + " img_ext=img_ext,\n", + " path_save=path_save,\n", + " projection_type=projection_type,\n", + " subfolder=subbfolder,\n", + " search_recursive=search_recursive)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", + "language": "python", + "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } \ No newline at end of file diff --git a/notebooks/segment-cells-nuclei.ipynb b/notebooks/segment-cells-nuclei.ipynb index f0d5bdd..155ab18 100644 --- a/notebooks/segment-cells-nuclei.ipynb +++ b/notebooks/segment-cells-nuclei.ipynb @@ -1,89 +1,89 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Notebook to perform segmentation of cells and nuclei with cellpose.\n", - "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", - " ```\n", - " pip install git+https://github.com/fish-quant/fq-segmentation/ --upgrade\n", - " ```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Imports \n", - "import segwrap \n", - "from segwrap import utils_cellpose\n", - "from pathlib import Path\n", - "\n", - "# Print version and location of package\n", - "print(f' * segwrap version: {segwrap.__version__}')\n", - "print(f' * utils_cellpose location: {utils_cellpose.__file__}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# >> Function call\n", - "\n", - "# Parameters\n", - "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", - "input_subfolder = 'segmentation-input' # Name of subfolder containing the images to be segmented\n", - "path_save = 'segmentation-input>>segmentation-results' # For example data: example_data\\analysis\\segmentation-results\n", - "str_cyto = 'cy3' # Identifier of channel for cytoplasmic segmentation\n", - "str_nuclei = 'dapi' # Identifier of channel for nuclear segmentation\n", - "img_ext = '.png' # Extension of images to be segmented\n", - "\n", - "new_size = (2,) # Size of images (when resize should be applied, empty tuple otherwise)\n", - "diameter_cells = 100 # Typical size (diameter) of cells\n", - "diameter_nuclei = 50 # Typical size (diameter) of nuclei\n", - "\n", - "net_avg=False # runs the 4 built-in networks and averages them if True, runs one network if False\n", - "resample=False # run dynamics at original image size (will be (much) slower but create more accurate boundaries)\n", - "\n", - "# Call segmentation function\n", - "utils_cellpose.segment_cells_nuclei_indiv(\n", - " path_scan=path_scan, \n", - " str_channels=str_channels, \n", - " img_ext=img_ext, \n", - " new_size=new_size,\n", - " model_types=('cyto','nuclei'), \n", - " diameters = (diameter_cells, diameter_nuclei), \n", - " net_avg=net_avg, \n", - " resample=resample, \n", - " path_save=path_save,\n", - " input_subfolder=input_subfolder)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", - "language": "python", - "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Notebook to perform segmentation of cells and nuclei with cellpose.\n", + "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", + " ```\n", + " pip install git+https://github.com/fish-quant/fq-segmentation/ --upgrade\n", + " ```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Imports \n", + "import segwrap \n", + "from segwrap import utils_cellpose\n", + "from pathlib import Path\n", + "\n", + "# Print version and location of package\n", + "print(f' * segwrap version: {segwrap.__version__}')\n", + "print(f' * utils_cellpose location: {utils_cellpose.__file__}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# >> Function call\n", + "\n", + "# Parameters\n", + "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", + "input_subfolder = 'segmentation-input' # Name of subfolder containing the images to be segmented\n", + "path_save = 'segmentation-input>>segmentation-results' # For example data: example_data\\analysis\\segmentation-results\n", + "str_cyto = 'cy3' # Identifier of channel for cytoplasmic segmentation\n", + "str_nuclei = 'dapi' # Identifier of channel for nuclear segmentation\n", + "img_ext = '.png' # Extension of images to be segmented\n", + "\n", + "new_size = (2,) # Size of images (when resize should be applied, empty tuple otherwise)\n", + "diameter_cells = 100 # Typical size (diameter) of cells\n", + "diameter_nuclei = 50 # Typical size (diameter) of nuclei\n", + "\n", + "net_avg=False # runs the 4 built-in networks and averages them if True, runs one network if False\n", + "resample=False # run dynamics at original image size (will be (much) slower but create more accurate boundaries)\n", + "\n", + "# Call segmentation function\n", + "utils_cellpose.segment_cells_nuclei_indiv(\n", + " path_scan=path_scan, \n", + " str_channels=str_channels, \n", + " img_ext=img_ext, \n", + " new_size=new_size,\n", + " model_types=('cyto','nuclei'), \n", + " diameters = (diameter_cells, diameter_nuclei), \n", + " net_avg=net_avg, \n", + " resample=resample, \n", + " path_save=path_save,\n", + " input_subfolder=input_subfolder)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", + "language": "python", + "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } \ No newline at end of file diff --git a/notebooks/segment-nuclei.ipynb b/notebooks/segment-nuclei.ipynb index e1564aa..1074e08 100644 --- a/notebooks/segment-nuclei.ipynb +++ b/notebooks/segment-nuclei.ipynb @@ -1,92 +1,92 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Notebook to perform segmentation of either cells or nuclei with cellpose.\n", - "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", - " ```\n", - " pip install git+https://github.com/fish-quant/fq-segmentation/ --upgrade\n", - " ```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Imports \n", - "import segwrap \n", - "from segwrap import utils_cellpose\n", - "from pathlib import Path\n", - "\n", - "# Print version and location of package\n", - "print(f' * segwrap version: {segwrap.__version__}')\n", - "print(f' * utils_cellpose location: {utils_cellpose.__file__}')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# >> Function call\n", - "\n", - "# Parameters\n", - "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", - "input_subfolder = 'segmentation-input' # Name of subfolder containing the images to be segmented\n", - "path_save = 'segmentation-input>>segmentation-results' # For example data: example_data\\analysis\\segmentation-results\n", - "obj_name='nuclei', # Name of object that should be segmented\n", - "str_channel = 'dapi' # Identifier of channel for nuclear segmentation\n", - "img_ext = '.png' # Extension of images to be segmented\n", - "\n", - "new_size = (2,) # Size of images (when resize should be applied, empty tuple otherwise)\n", - "diameter = 50 # Typical size (diameter) of nuclei\n", - "\n", - "model_type = 'nuclei' # Can be 'nuclei' or 'cyto', for densely packed nuclei 'cyto' might work well. \n", - "net_avg=False # runs the 4 built-in networks and averages them if True, runs one network if False\n", - "resample=False # run dynamics at original image size (will be (much) slower but create more accurate boundaries)\n", - "\n", - "# Call segmentation function\n", - "utils_cellpose.segment_obj_indiv(\n", - " path_scan=path_scan, \n", - " obj_name=obj_name, \n", - " str_channel=str_channel, \n", - " img_ext=img_ext,\n", - " new_size=new_size, \n", - " model_type=model_type, \n", - " diameter = diameter, \n", - " net_avg=net_avg, \n", - " resample=resample, \n", - " path_save=path_save\n", - " input_subfolder=input_subfolder)\n", - "\n", - "\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", - "language": "python", - "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 4 +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Notebook to perform segmentation of either cells or nuclei with cellpose.\n", + "* Requires that `cellpose` and the code of this respository are installed. One way to do this is with a pip install (recommended in a dedicated conda environment as explained in the documentation).\n", + " ```\n", + " pip install git+https://github.com/fish-quant/fq-segmentation/ --upgrade\n", + " ```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Imports \n", + "import segwrap \n", + "from segwrap import utils_cellpose\n", + "from pathlib import Path\n", + "\n", + "# Print version and location of package\n", + "print(f' * segwrap version: {segwrap.__version__}')\n", + "print(f' * utils_cellpose location: {utils_cellpose.__file__}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# >> Function call\n", + "\n", + "# Parameters\n", + "path_scan = Path(r'paste-path-to-data') # For example data: example_data\\analysis\\segmentation-input\n", + "input_subfolder = 'segmentation-input' # Name of subfolder containing the images to be segmented\n", + "path_save = 'segmentation-input>>segmentation-results' # For example data: example_data\\analysis\\segmentation-results\n", + "obj_name='nuclei', # Name of object that should be segmented\n", + "str_channel = 'dapi' # Identifier of channel for nuclear segmentation\n", + "img_ext = '.png' # Extension of images to be segmented\n", + "\n", + "new_size = (2,) # Size of images (when resize should be applied, empty tuple otherwise)\n", + "diameter = 50 # Typical size (diameter) of nuclei\n", + "\n", + "model_type = 'nuclei' # Can be 'nuclei' or 'cyto', for densely packed nuclei 'cyto' might work well. \n", + "net_avg=False # runs the 4 built-in networks and averages them if True, runs one network if False\n", + "resample=False # run dynamics at original image size (will be (much) slower but create more accurate boundaries)\n", + "\n", + "# Call segmentation function\n", + "utils_cellpose.segment_obj_indiv(\n", + " path_scan=path_scan, \n", + " obj_name=obj_name, \n", + " str_channel=str_channel, \n", + " img_ext=img_ext,\n", + " new_size=new_size, \n", + " model_type=model_type, \n", + " diameter = diameter, \n", + " net_avg=net_avg, \n", + " resample=resample, \n", + " path_save=path_save\n", + " input_subfolder=input_subfolder)\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.7.6 64-bit ('cellpose': conda)", + "language": "python", + "name": "python37664bitcellposecondafe33b8fac7d34fcb85d1f0e07041e8c2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 } \ No newline at end of file diff --git a/readme.md b/readme.md index e929799..103fd98 100644 --- a/readme.md +++ b/readme.md @@ -1,60 +1,60 @@ - -[![powered by ImJoy](https://imjoy.io/static/badge/powered-by-imjoy-badge.svg)](https://imjoy.io/) -![GitHub](https://img.shields.io/github/license/muellerflorian/segmentation) -[![Documentation Status](https://readthedocs.org/projects/fq-segmentation/badge/?version=latest)](https://fq-segmentation.readthedocs.io/en/latest/?badge=latest) -[![Python 3.7](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/) - -# Overview -This repository provides wrapper code to use the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). - -For more information, please consult the dedicated [**documentation**](https://fq-segmentation.readthedocs.io/en/latest/). - -If you encounter a problems, please file an [**issue**](https://github.com/fish-quant/fq-segmentation/issues). - -Example of **nuclear segmentation**: - - - -Example of **cytoplasmic segmentation**: - - - -# Licensing - -- **Cellpose** is BSD-licenced (3 clause) - -> Copyright © 2020 Howard Hughes Medical Institute -> -> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -> -> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -Neither the name of HHMI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -- The rest of the code provided in this repository is BSD-licenced (3 clause): ->Copyright © 2020, Florian Mueller ->All rights reserved. -> ->Redistribution and use in source and binary forms, with or without ->modification, are permitted provided that the following conditions are met: -> * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -> * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -> * Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. -> ->THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +[![powered by ImJoy](https://imjoy.io/static/badge/powered-by-imjoy-badge.svg)](https://imjoy.io/) +![GitHub](https://img.shields.io/github/license/muellerflorian/segmentation) +[![Documentation Status](https://readthedocs.org/projects/fq-segmentation/badge/?version=latest)](https://fq-segmentation.readthedocs.io/en/latest/?badge=latest) +[![Python 3.7](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/) + +# Overview +This repository provides wrapper code to use the generalist cell/nuclei segmentation package [**Cellpose**](https://github.com/mouseland/cellpose). + +For more information, please consult the dedicated [**documentation**](https://fq-segmentation.readthedocs.io/en/latest/). + +If you encounter a problems, please file an [**issue**](https://github.com/fish-quant/fq-segmentation/issues). + +Example of **nuclear segmentation**: + + + +Example of **cytoplasmic segmentation**: + + + +# Licensing + +- **Cellpose** is BSD-licenced (3 clause) + +> Copyright © 2020 Howard Hughes Medical Institute +> +> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +> +> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of HHMI nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +- The rest of the code provided in this repository is BSD-licenced (3 clause): +>Copyright © 2020, Florian Mueller +>All rights reserved. +> +>Redistribution and use in source and binary forms, with or without +>modification, are permitted provided that the following conditions are met: +> * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +> * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +> * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. +> +>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/requirements.txt b/requirements.txt index d5304cb..b6bae75 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -cellpose == 0.6.5 -matplotlib == 3.4.2 +cellpose == 1.0.2 +matplotlib == 3.5.1 scikit-image == 0.19.2 \ No newline at end of file diff --git a/segwrap/__init__.py b/segwrap/__init__.py index 3d6124d..c8c5318 100644 --- a/segwrap/__init__.py +++ b/segwrap/__init__.py @@ -1,15 +1,15 @@ -# -*- coding: utf-8 -*- -# Author: Florian Mueller -# License: BSD 3 clause - -""" -segmentation package. -""" - - -# keep a MAJOR.MINOR.PATCH format -# MAJOR: major API changes -# MINOR: new features -# PATCH: backwards compatible bug fixes -# MAJOR.MINOR.PATCHdev means a version under development -__version__ = "0.3.3" +# -*- coding: utf-8 -*- +# Author: Florian Mueller +# License: BSD 3 clause + +""" +segmentation package. +""" + + +# keep a MAJOR.MINOR.PATCH format +# MAJOR: major API changes +# MINOR: new features +# PATCH: backwards compatible bug fixes +# MAJOR.MINOR.PATCHdev means a version under development +__version__ = "0.3.4" diff --git a/segwrap/utils_cellpose.py b/segwrap/utils_cellpose.py index 0a91e5b..a6089b8 100644 --- a/segwrap/utils_cellpose.py +++ b/segwrap/utils_cellpose.py @@ -1,487 +1,475 @@ -# Imports -import numpy as np -from tqdm import tqdm -import time -import matplotlib.pyplot as plt -import pathlib -from pathlib import Path -import json -import cv2 - -# Imports of CellPose specific libraries -from cellpose import models, io, plot -from segwrap.utils_general import log_message, create_output_path - -# Call predict function -def cellpose_predict(data, config, path_save, callback_log=None): - """ Perform prediction with CellPose. - - Parameters - ---------- - data : dict - Contains data on which prediction should be performed. - config : dict - Configuration of CellPose prediction. - path_save : pathline Path object - Path where results will be saved. - """ - - # Get data - imgs = data['imgs'] - file_names = data['file_names'] - channels = data['channels'] - obj_name = data['obj_name'] - sizes_orginal = data['sizes_orginal'] - new_size = data['new_size'] - - # Get config - model_type = config['model_type'] - diameter = config['diameter'] - net_avg = config['net_avg'] - resample = config['resample'] - - log_message(f'\nPerforming segmentation of {obj_name}\n', callback_fun=callback_log) - - start_time = time.time() - - if not path_save.is_dir(): - path_save.mkdir() - - # Perform segmentation with CellPose - model = models.Cellpose(gpu=False, model_type=model_type) # model_type can be 'cyto' or 'nuclei' - masks, flows, styles, diams = model.eval(imgs, diameter=diameter, channels=channels, net_avg=net_avg, resample=resample) - - # Display and save results - log_message(f'\n Creating outputs ...\n', callback_fun=callback_log) - n_img = len(imgs) - - for idx in tqdm(range(n_img)): - - # Get images and file-name - file_name = file_names[idx] - maski = masks[idx] - flowi = flows[idx][0] - imgi = imgs[idx] - - # Rescale each channel separately - imgi_norm = imgi.copy() - for idim in range(3): - imgdum = imgi[:, :, idim] - - # Renormalize to 8bit between 1 and 99 percentile - pa = np.percentile(imgdum, 0.5) - pb = np.percentile(imgdum, 99.5) - - if pb > pa: - imgi_norm[:, :, idim] = 255 * (imgdum - pa) / (pb - pa) - - # Save flow - io.imsave(str(path_save / f'{file_name.stem}__flow__{obj_name}.png'), flowi) - - # Resize masks if necessary - if new_size: - mask_full = resize_mask(maski, sizes_orginal[idx]) - - io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), mask_full) - io.imsave(str(path_save / f'{file_name.stem}__mask_resize__{obj_name}.png'), maski) - - else: - io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), maski) - - # Save mask and flow images - #f_mask = str(path_save / f'{file_name.stem}__mask__{obj_name}.png') - #log_message(f'\nMask saved to file: {f_mask}\n', callback_fun=callback_log) - - #io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), maski) - - - # Save overview image - fig = plt.figure(figsize=(12,3)) - plot.show_segmentation(fig, imgi_norm.astype('uint8'), maski, flowi) - plt.tight_layout() - fig.savefig(str(path_save / f'{file_name.stem}__seg__{obj_name}.png'), dpi=300) - plt.close(fig) - - log_message(f"\nSegmentation of provided images finished ({(time.time() - start_time)}s)", callback_fun=callback_log) - - -def clean_par_dict(par_dict): - """ - Cleam up dictionary containing all parameters such that it can - be written into a json file. - """ - par_dict['path_scan'] = str(par_dict['path_scan']) - par_dict['path_save'] = str(par_dict['path_save']) - par_dict['callback_log'] = str(par_dict['callback_log']) - par_dict['callback_progress'] = str(par_dict['callback_progress']) - par_dict['callback_status'] = str(par_dict['callback_status']) - return par_dict - - -# Function to load and segment objects individually -def segment_obj_indiv(path_scan, obj_name, str_channel, img_ext, new_size, model_type, diameter, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): - """ Will recursively search folder for images to be analyzed! - - Parameters - ---------- - path_scan : [type] - [description] - str_nuclei : [type] - [description] - img_ext : [type] - [description] - diameter : [type] - [description] - model_type : [type] - [description] - path_save : pathlib object or string - Path to save results, - - If Pathlib object, then this absolute path is used. - - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). - input_subfolder : str - Name of subfolder that contains results. If specified ONLY files in this folder will be processed. - callback_log : [type], optional - [description], by default None - callback_status : [type], optional - [description], by default None - callback_progress : [type], optional - [description], by default None - - Returns - ------- - [type] - [description] - """ - - # Print all input parameters - par_dict = locals() - par_dict = clean_par_dict(par_dict) - log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) - - # Configurations - config = {'model_type': model_type, - 'diameter': diameter, - 'net_avg': net_avg, - 'resample': resample} - - channels = [0, 1] - - # Use provided absolute user-path to save images. - if isinstance(path_save, pathlib.PurePath): - path_save_results = path_save - if not path_save_results.is_dir(): - path_save_results.mkdir(parents=True) - - else: - path_save_str_replace = path_save - - if not path_scan.is_dir(): - log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) - return - - # Search for file to be analyzed - log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) - files_proc = [] - for path_img in path_scan.rglob(f'*{str_channel}*{img_ext}'): - if input_subfolder: - if path_img.parts[-2] == input_subfolder: - files_proc.append(path_img) - else: - files_proc.append(path_img) - n_imgs = len(files_proc) - - if n_imgs == 0: - log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) - return - - # Process files - for idx, path_img in enumerate(files_proc): - imgs = [] - files = [] - sizes_orginal = [] - - log_message(f'Segmenting image : {path_img.name}', callback_fun=callback_log) - - if callback_status: - callback_status(f'Segmenting image : {path_img.name}') - - if callback_progress: - progress = float((idx+1)/n_imgs) - callback_progress(progress) - - # Read images - img = io.imread(str(path_img)) - if img.ndim != 2: - log_message(f'\nERROR\n Input image has to be 2D. Current image is {img.ndim}D', callback_fun=callback_log) - continue - - - # IMPORTANT: CV2 resize is defined as (width, height) - print('>>> process file') - print(f'input file (size): {img.shape}') - - sizes_orginal.append(img.shape) - - # Resize - if new_size: - - # New size can also be defined as a scalar factor - if len(new_size) == 1: - scale_factor = new_size[0] - img_size = img.shape - new_size = tuple(int(ti/scale_factor) for ti in img_size) - - # IMPORTANT: CV2 resize is defined as (width, height) - dsize = (new_size[1], new_size[0]) - img = cv2.resize(img, dsize) - - print(f'resized file (size): {img.shape}') - - - # For object segmentation - img_zeros = np.zeros(img.shape) - img_3d_dpi = np.dstack([img_zeros, img_zeros, img]) - imgs.append(img_3d_dpi) - files.append(path_img) - - # >>> Call function for prediction - data = {'imgs': imgs, - 'file_names': files, - 'channels': channels, - 'obj_name': obj_name, - 'sizes_orginal': sizes_orginal, - 'new_size': new_size} - - # Create new output path if specified - if not isinstance(path_save, pathlib.PurePath): - path_save_results = create_output_path(path_img.parent, path_save_str_replace, subfolder='', create_path=True) - path_save_settings = path_save_results - - cellpose_predict(data, config, path_save=path_save_results, callback_log=callback_log) - - # Save settings - if len(imgs) > 0: - fp = open(str(path_save_results / f'segmentation_settings__{obj_name}.json'), "w") - json.dump(par_dict, fp, indent=4, sort_keys=True) - fp.close() - - log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log) - - -# Function to load and segment cells and nuclei images individually -def segment_cells_nuclei_indiv(path_scan, str_channels, img_ext, new_size, model_types, diameters, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): - """[summary] segment cells and nuclei in bulk, e.g. first all images are loaded and then segmented. - TODO: specify parameters - Parameters - ---------- - path_scan : [type] - [description] - strings : [type] - [description] - img_ext : [type] - [description] - new_size : tuple - Defines resizing of image. If two elements, new size of image. If one element, resizing factor. - If emtpy, no resizing. - sizes : [type] - [description] - models : [type] - [description] - path_save : pathlin object or string - Path to save results, - - If Pathlib object, then this absolute path is used. - - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). - And results will be stored in subfolder 'segmentation-input' - callback_log : [type], optional - [description], by default None - callback_status : [type], optional - [description], by default None - callback_progress : [type], optional - [description], by default None - """ - - par_dict = locals() - par_dict = clean_par_dict(par_dict) - log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) - - # Get parameters - (str_cyto, str_nuclei) = str_channels - (diameter_cells, diameter_nuclei) = diameters - (model_type_cells, model_type_nuclei) = model_types - - # Use provided absolute user-path to save images. - if isinstance(path_save, pathlib.PurePath): - path_save_results = path_save - if not path_save_results.is_dir(): - path_save_results.mkdir(parents=True) - - else: - path_save_str_replace = path_save - - # Configurations - config_nuclei = {'model_type': model_type_nuclei, - 'diameter': diameter_nuclei, - 'net_avg': net_avg, - 'resample': resample} - - config_cyto = {'model_type': model_type_cells, - 'diameter': diameter_cells, - 'net_avg': net_avg, - 'resample': resample} - - channels_cyto = [1, 3] - channels_nuclei = [0, 1] - - # Loop over data - log_message(f'\nLoading image pairs and segment them on the fly', callback_fun=callback_log) - - if not path_scan.is_dir(): - log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) - return - - # Search for file to be analyzed - log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) - files_proc = [] - for path_img in path_scan.rglob(f'*{str_cyto}*{img_ext}'): - if input_subfolder: - if path_img.parts[-2] == input_subfolder: - files_proc.append(path_img) - else: - files_proc.append(path_img) - n_imgs = len(files_proc) - - if n_imgs == 0: - log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) - return - - # Process files - for idx, path_cyto in enumerate(files_proc): - imgs_cyto = [] - imgs_nuclei = [] - files_cyto = [] - files_nuclei = [] - sizes_orginal = [] - - log_message(f'Segmenting image : {path_cyto.name}', callback_fun=callback_log) - - if callback_status: - callback_status(f'Segmenting image : {path_cyto.name}') - - if callback_progress: - progress = float((idx+1)/n_imgs) - callback_progress(progress) - - # DAPI image: existing? - path_nuclei = Path(str(path_cyto).replace(str_cyto, str_nuclei)) - if not path_nuclei.is_file(): - log_message(f'DAPI image not found : {path_nuclei}', callback_fun=callback_log) - continue - - # Read images - img_cyto = io.imread(str(path_cyto)) - if img_cyto.ndim != 2: - log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_cyto.ndim}D', callback_fun=callback_log) - continue - - img_nuclei = io.imread(str(path_nuclei)) - if img_nuclei.ndim != 2: - log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_nuclei.ndim}D', callback_fun=callback_log) - continue - - # Resize image before CellPose if specified - sizes_orginal.append(img_cyto.shape) - - # Resize - if new_size: - - # New size can also be defined as a scalar factor - if len(new_size) == 1: - scale_factor = new_size[0] - img_size = img_cyto.shape - new_size = tuple(int(ti/scale_factor) for ti in img_size) - - # IMPORTANT: CV2 resize is defined as (width, height) - dsize = (new_size[1], new_size[0]) - img_cyto = cv2.resize(img_cyto, dsize) - img_nuclei = cv2.resize(img_nuclei, dsize) - - img_zeros = np.zeros(img_cyto.shape) - - # For cell segmentation - img_3d = np.dstack([img_cyto, img_zeros, img_nuclei]) - imgs_cyto.append(img_3d) - files_cyto.append(path_cyto) - - # For nuclei segmentation - img_3d_dpi = np.dstack([img_zeros, img_zeros, img_nuclei]) - imgs_nuclei.append(img_3d_dpi) - files_nuclei.append(path_nuclei) - - # Create new output path if specified - if not isinstance(path_save, pathlib.PurePath): - path_save_results = create_output_path(path_cyto.parent, path_save_str_replace, subfolder='', create_path=True) - path_save_settings = path_save_results - - # >>> Call function for prediction of cell - data_cyto = {'imgs': imgs_cyto, - 'file_names': files_cyto, - 'sizes_orginal': sizes_orginal, - 'new_size': new_size, - 'channels': channels_cyto, - 'obj_name': 'cells'} - - cellpose_predict(data_cyto, config_cyto, path_save=path_save_results, callback_log=callback_log) - - # >>> Call function for prediction of nuclei - data_nuclei = {'imgs': imgs_nuclei, - 'file_names': files_nuclei, - 'channels': channels_nuclei, - 'obj_name': 'nuclei', - 'sizes_orginal': sizes_orginal, - 'new_size': new_size} - - cellpose_predict(data_nuclei, config_nuclei, path_save=path_save_results, callback_log=callback_log) - - # Save settings - if len(imgs_cyto) > 0: - fp = open(str(path_save_results / 'segmentation_settings__cells_nuclei.json'), "w") - json.dump(par_dict, fp, indent=4, sort_keys=True) - fp.close() - - log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log) - - -def resize_mask(mask_small, size_orginal): - """ Resize a label image. - Parameters - ---------- - mask_small : [type] - [description] - size_orginal : [type] - [description] - Returns - ------- - [type] - [description] - """ - - mask_full = np.zeros(size_orginal).astype('uint16') - maski_template = np.zeros(mask_small.shape).astype('uint8') - - ind_objs = np.unique(mask_small) - ind_objs = np.delete(ind_objs, np.where(ind_objs == 0)) - - if ind_objs.size > 0: - - for obj_int in np.nditer(ind_objs): - - # Create binary mask for current object and find contour - img_obj_loop = np.copy(maski_template) - img_obj_loop[mask_small == obj_int] = 1 - - # IMPORTANT: CV2 resize is defined as (width, height) - dsize = (size_orginal[1], size_orginal[0]) - img_obj_loop_large = cv2.resize(img_obj_loop, dsize).astype('bool') - mask_full[img_obj_loop_large] = obj_int - +# Imports of general libraries +import numpy as np +from tqdm import tqdm +import time +import matplotlib.pyplot as plt +import pathlib +from pathlib import Path +import json +import cv2 +from skimage.exposure import rescale_intensity + +# Imports of CellPose specific libraries +from cellpose import models, io, plot +from segwrap.utils_general import log_message, create_output_path + +# Call predict function +def cellpose_predict(data, config, path_save, callback_log=None): + """ Perform prediction with CellPose. + + Parameters + ---------- + data : dict + Contains data on which prediction should be performed. + config : dict + Configuration of CellPose prediction. + path_save : pathline Path object + Path where results will be saved. + """ + + # Get data + imgs = data['imgs'] + file_names = data['file_names'] + channels = data['channels'] + obj_name = data['obj_name'] + sizes_orginal = data['sizes_orginal'] + new_size = data['new_size'] + + # Get config + model_type = config['model_type'] + diameter = config['diameter'] + net_avg = config['net_avg'] + resample = config['resample'] + + log_message(f'\nPerforming segmentation of {obj_name}\n', callback_fun=callback_log) + + start_time = time.time() + + if not path_save.is_dir(): + path_save.mkdir() + + # Perform segmentation with CellPose + model = models.Cellpose(gpu=False, model_type=model_type) # model_type can be 'cyto' or 'nuclei' + masks, flows, styles, diams = model.eval(imgs, diameter=diameter, channels=channels, net_avg=net_avg, resample=resample) + + # Display and save results + log_message(f'\n Creating outputs ...\n', callback_fun=callback_log) + n_img = len(imgs) + + for idx in tqdm(range(n_img)): + + # Get images and file-name + file_name = file_names[idx] + maski = masks[idx] + flowi = flows[idx][0] + imgi = imgs[idx] + + # Rescale each channel separately + imgi_scaled = imgi.copy() + for idim in range(3): + imgdum = imgi[:, :, idim] + pa = np.percentile(imgdum, 0.5) + pb = np.percentile(imgdum, 99.5) + imgi_scaled[:, :, idim] = rescale_intensity(imgdum, in_range=(pa, pb),out_range=np.uint8) + + # Save flow + io.imsave(str(path_save / f'{file_name.stem}__flow__{obj_name}.png'), flowi) + + # Resize masks if necessary + if new_size: + mask_full = resize_mask(maski, sizes_orginal[idx]) + io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), mask_full) + io.imsave(str(path_save / f'{file_name.stem}__mask_resize__{obj_name}.png'), maski) + + else: + io.imsave(str(path_save / f'{file_name.stem}__mask__{obj_name}.png'), maski) + + # Save overview image + fig = plt.figure(figsize=(12,3)) + plot.show_segmentation(fig, imgi_scaled, maski, flowi) + plt.tight_layout() + fig.savefig(str(path_save / f'{file_name.stem}__seg__{obj_name}.png'), dpi=300) + plt.close(fig) + + log_message(f"\nSegmentation of provided images finished ({(time.time() - start_time)}s)", callback_fun=callback_log) + + +def clean_par_dict(par_dict): + """ + Clean dictionary containing all parameters such that it can + be written into a json file. + """ + par_dict['path_scan'] = str(par_dict['path_scan']) + par_dict['path_save'] = str(par_dict['path_save']) + par_dict['callback_log'] = str(par_dict['callback_log']) + par_dict['callback_progress'] = str(par_dict['callback_progress']) + par_dict['callback_status'] = str(par_dict['callback_status']) + return par_dict + + +# Function to load and segment objects individually +def segment_obj_indiv(path_scan, obj_name, str_channel, img_ext, new_size, model_type, diameter, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): + """ Will recursively search folder for images to be analyzed! + + Parameters + ---------- + path_scan : [type] + [description] + str_nuclei : [type] + [description] + img_ext : [type] + [description] + diameter : [type] + [description] + model_type : [type] + [description] + path_save : pathlib object or string + Path to save results, + - If Pathlib object, then this absolute path is used. + - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). + input_subfolder : str + Name of subfolder that contains results. If specified ONLY files in this folder will be processed. + callback_log : [type], optional + [description], by default None + callback_status : [type], optional + [description], by default None + callback_progress : [type], optional + [description], by default None + + Returns + ------- + [type] + [description] + """ + + # Print all input parameters + par_dict = locals() + par_dict = clean_par_dict(par_dict) + log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) + + # Configurations + config = {'model_type': model_type, + 'diameter': diameter, + 'net_avg': net_avg, + 'resample': resample} + + channels = [0, 1] + + # Use provided absolute user-path to save images. + if isinstance(path_save, pathlib.PurePath): + path_save_results = path_save + if not path_save_results.is_dir(): + path_save_results.mkdir(parents=True) + + else: + path_save_str_replace = path_save + + if not path_scan.is_dir(): + log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) + return + + # Search for file to be analyzed + log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) + files_proc = [] + for path_img in path_scan.rglob(f'*{str_channel}*{img_ext}'): + if input_subfolder: + if path_img.parts[-2] == input_subfolder: + files_proc.append(path_img) + else: + files_proc.append(path_img) + n_imgs = len(files_proc) + + if n_imgs == 0: + log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) + return + + # Process files + for idx, path_img in enumerate(files_proc): + imgs = [] + files = [] + sizes_orginal = [] + + log_message(f'Segmenting image : {path_img.name}', callback_fun=callback_log) + + if callback_status: + callback_status(f'Segmenting image : {path_img.name}') + + if callback_progress: + progress = float((idx+1)/n_imgs) + callback_progress(progress) + + # Read images + img = io.imread(str(path_img)) + if img.ndim != 2: + log_message(f'\nERROR\n Input image has to be 2D. Current image is {img.ndim}D', callback_fun=callback_log) + continue + + + # IMPORTANT: CV2 resize is defined as (width, height) + print('>>> process file') + print(f'input file (size): {img.shape}') + + sizes_orginal.append(img.shape) + + # Resize + if new_size: + + # New size can also be defined as a scalar factor + if len(new_size) == 1: + scale_factor = new_size[0] + img_size = img.shape + new_size = tuple(int(ti/scale_factor) for ti in img_size) + + # IMPORTANT: CV2 resize is defined as (width, height) + dsize = (new_size[1], new_size[0]) + img = cv2.resize(img, dsize) + + print(f'resized file (size): {img.shape}') + + + # For object segmentation + img_zeros = np.zeros(img.shape) + img_3d_dpi = np.dstack([img_zeros, img_zeros, img]) + imgs.append(img_3d_dpi) + files.append(path_img) + + # >>> Call function for prediction + data = {'imgs': imgs, + 'file_names': files, + 'channels': channels, + 'obj_name': obj_name, + 'sizes_orginal': sizes_orginal, + 'new_size': new_size} + + # Create new output path if specified + if not isinstance(path_save, pathlib.PurePath): + path_save_results = create_output_path(path_img.parent, path_save_str_replace, subfolder='', create_path=True) + + cellpose_predict(data, config, path_save=path_save_results, callback_log=callback_log) + + # Save settings + if len(imgs) > 0: + fp = open(str(path_save_results / f'segmentation_settings__{obj_name}.json'), "w") + json.dump(par_dict, fp, indent=4, sort_keys=True) + fp.close() + + log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log) + + +# Function to load and segment cells and nuclei images individually +def segment_cells_nuclei_indiv(path_scan, str_channels, img_ext, new_size, model_types, diameters, net_avg, resample, path_save, input_subfolder=None, callback_log=None, callback_status=None, callback_progress=None): + """[summary] segment cells and nuclei in bulk, e.g. first all images are loaded and then segmented. + TODO: specify parameters + Parameters + ---------- + path_scan : [type] + [description] + strings : [type] + [description] + img_ext : [type] + [description] + new_size : tuple + Defines resizing of image. If two elements, new size of image. If one element, resizing factor. + If emtpy, no resizing. + sizes : [type] + [description] + models : [type] + [description] + path_save : pathlin object or string + Path to save results, + - If Pathlib object, then this absolute path is used. + - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). + And results will be stored in subfolder 'segmentation-input' + callback_log : [type], optional + [description], by default None + callback_status : [type], optional + [description], by default None + callback_progress : [type], optional + [description], by default None + """ + + par_dict = locals() + par_dict = clean_par_dict(par_dict) + log_message(f"Function (segment_obj_indiv) called with: {str(par_dict)} ", callback_fun=callback_log) + + # Get parameters + (str_cyto, str_nuclei) = str_channels + (diameter_cells, diameter_nuclei) = diameters + (model_type_cells, model_type_nuclei) = model_types + + # Use provided absolute user-path to save images. + if isinstance(path_save, pathlib.PurePath): + path_save_results = path_save + if not path_save_results.is_dir(): + path_save_results.mkdir(parents=True) + + else: + path_save_str_replace = path_save + + # Configurations + config_nuclei = {'model_type': model_type_nuclei, + 'diameter': diameter_nuclei, + 'net_avg': net_avg, + 'resample': resample} + + config_cyto = {'model_type': model_type_cells, + 'diameter': diameter_cells, + 'net_avg': net_avg, + 'resample': resample} + + channels_cyto = [1, 3] + channels_nuclei = [0, 1] + + # Loop over data + log_message(f'\nLoading image pairs and segment them on the fly', callback_fun=callback_log) + + if not path_scan.is_dir(): + log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) + return + + # Search for file to be analyzed + log_message(f'\nLoading images and segment them on the fly', callback_fun=callback_log) + files_proc = [] + for path_img in path_scan.rglob(f'*{str_cyto}*{img_ext}'): + if input_subfolder: + if path_img.parts[-2] == input_subfolder: + files_proc.append(path_img) + else: + files_proc.append(path_img) + n_imgs = len(files_proc) + + if n_imgs == 0: + log_message(f'NO IMAGES FOUND. Check your settings.', callback_fun=callback_log) + return + + # Process files + for idx, path_cyto in enumerate(files_proc): + imgs_cyto = [] + imgs_nuclei = [] + files_cyto = [] + files_nuclei = [] + sizes_orginal = [] + + log_message(f'Segmenting image : {path_cyto.name}', callback_fun=callback_log) + + if callback_status: + callback_status(f'Segmenting image : {path_cyto.name}') + + if callback_progress: + progress = float((idx+1)/n_imgs) + callback_progress(progress) + + # DAPI image: existing? + path_nuclei = Path(str(path_cyto).replace(str_cyto, str_nuclei)) + if not path_nuclei.is_file(): + log_message(f'DAPI image not found : {path_nuclei}', callback_fun=callback_log) + continue + + # Read images + img_cyto = io.imread(str(path_cyto)) + if img_cyto.ndim != 2: + log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_cyto.ndim}D', callback_fun=callback_log) + continue + + img_nuclei = io.imread(str(path_nuclei)) + if img_nuclei.ndim != 2: + log_message(f'\nERROR\n Input image of cell has to be 2D. Current image is {img_nuclei.ndim}D', callback_fun=callback_log) + continue + + # Resize image before CellPose if specified + sizes_orginal.append(img_cyto.shape) + + # Resize + if new_size: + + # New size can also be defined as a scalar factor + if len(new_size) == 1: + scale_factor = new_size[0] + img_size = img_cyto.shape + new_size = tuple(int(ti/scale_factor) for ti in img_size) + + # IMPORTANT: CV2 resize is defined as (width, height) + dsize = (new_size[1], new_size[0]) + img_cyto = cv2.resize(img_cyto, dsize) + img_nuclei = cv2.resize(img_nuclei, dsize) + + img_zeros = np.zeros(img_cyto.shape) + + # For cell segmentation + img_3d = np.dstack([img_cyto, img_zeros, img_nuclei]) + imgs_cyto.append(img_3d) + files_cyto.append(path_cyto) + + # For nuclei segmentation + img_3d_dpi = np.dstack([img_zeros, img_zeros, img_nuclei]) + imgs_nuclei.append(img_3d_dpi) + files_nuclei.append(path_nuclei) + + # Create new output path if specified + if not isinstance(path_save, pathlib.PurePath): + path_save_results = create_output_path(path_cyto.parent, path_save_str_replace, subfolder='', create_path=True) + path_save_settings = path_save_results + + # >>> Call function for prediction of cell + data_cyto = {'imgs': imgs_cyto, + 'file_names': files_cyto, + 'sizes_orginal': sizes_orginal, + 'new_size': new_size, + 'channels': channels_cyto, + 'obj_name': 'cells'} + + cellpose_predict(data_cyto, config_cyto, path_save=path_save_results, callback_log=callback_log) + + # >>> Call function for prediction of nuclei + data_nuclei = {'imgs': imgs_nuclei, + 'file_names': files_nuclei, + 'channels': channels_nuclei, + 'obj_name': 'nuclei', + 'sizes_orginal': sizes_orginal, + 'new_size': new_size} + + cellpose_predict(data_nuclei, config_nuclei, path_save=path_save_results, callback_log=callback_log) + + # Save settings + if len(imgs_cyto) > 0: + fp = open(str(path_save_results / 'segmentation_settings__cells_nuclei.json'), "w") + json.dump(par_dict, fp, indent=4, sort_keys=True) + fp.close() + + log_message(f'\n BATCH SEGMENTATION finished', callback_fun=callback_log) + + +def resize_mask(mask_small, size_orginal): + """ Resize a label image. + Parameters + ---------- + mask_small : [type] + [description] + size_orginal : [type] + [description] + Returns + ------- + [type] + [description] + """ + + mask_full = np.zeros(size_orginal).astype('uint16') + maski_template = np.zeros(mask_small.shape).astype('uint8') + + ind_objs = np.unique(mask_small) + ind_objs = np.delete(ind_objs, np.where(ind_objs == 0)) + + if ind_objs.size > 0: + + for obj_int in np.nditer(ind_objs): + + # Create binary mask for current object and find contour + img_obj_loop = np.copy(maski_template) + img_obj_loop[mask_small == obj_int] = 1 + + # IMPORTANT: CV2 resize is defined as (width, height) + dsize = (size_orginal[1], size_orginal[0]) + img_obj_loop_large = cv2.resize(img_obj_loop, dsize).astype('bool') + mask_full[img_obj_loop_large] = obj_int + return mask_full \ No newline at end of file diff --git a/segwrap/utils_general.py b/segwrap/utils_general.py index b224124..cfe49a8 100644 --- a/segwrap/utils_general.py +++ b/segwrap/utils_general.py @@ -1,71 +1,71 @@ -# Imports -from pathlib import Path -import re - - -# Log message -def log_message(msg, callback_fun=None): - """ Display log, either terminal or any callback accepting a string as input. - - Parameters - ---------- - msg : [string] - [description] - callback_fun : [type], optional - [description], by default None - """ - if callback_fun: - callback_fun(msg) - else: - print(msg) - - -# Create new output path -def create_output_path(path_orig, str_replace, subfolder=None, create_path=True, callback_log=None): - """ Allows to create new path object by replacing a string in a provided path object. - - Parameters - ---------- - path_orig : pathlib object - Original file-name. - str_replace : str - str defining the replacement operation: 'str_orig>>str_new', - where 'str_orig' is the orginal string, 'str_new' is the new string. - For example, 'acquisition>>analysis'. - subfolder : str or pathlib object - Subfolder that should be added to new file-name - create_path : bool - Create new path if it doesn't exist. - callback_log : str - Callback function to be used. If None, then system print will be used. - - Returns - ------- - pathlib object - New path. - """ - - log_message("Creating name to path to store data.", callback_fun=callback_log) - - if (re.search(">>", str_replace)): - str_orig = re.search(r'^(.*)>>(.*)$', str_replace).group(1) - str_rep = re.search(r'^(.*)>>(.*)$', str_replace).group(2) - - path_replace = Path(str(path_orig).replace(str_orig, str_rep)) - - if subfolder: - path_replace = path_replace / subfolder - - log_message(f'Replacement parameters found: original string: {str_orig}, replacement string: {str_rep}', callback_fun=callback_log) - log_message(f'Output path: {path_replace}', callback_fun=callback_log) - - else: - log_message("No match", callback_fun=callback_log) - return None - - # Create default folder to save data if none was defined by the user - if create_path: - if not path_replace.is_dir(): - path_replace.mkdir(parents=True) - +# Imports +from pathlib import Path +import re + + +# Log message +def log_message(msg, callback_fun=None): + """ Display log, either terminal or any callback accepting a string as input. + + Parameters + ---------- + msg : [string] + [description] + callback_fun : [type], optional + [description], by default None + """ + if callback_fun: + callback_fun(msg) + else: + print(msg) + + +# Create new output path +def create_output_path(path_orig, str_replace, subfolder=None, create_path=True, callback_log=None): + """ Allows to create new path object by replacing a string in a provided path object. + + Parameters + ---------- + path_orig : pathlib object + Original file-name. + str_replace : str + str defining the replacement operation: 'str_orig>>str_new', + where 'str_orig' is the orginal string, 'str_new' is the new string. + For example, 'acquisition>>analysis'. + subfolder : str or pathlib object + Subfolder that should be added to new file-name + create_path : bool + Create new path if it doesn't exist. + callback_log : str + Callback function to be used. If None, then system print will be used. + + Returns + ------- + pathlib object + New path. + """ + + log_message("Creating name to path to store data.", callback_fun=callback_log) + + if (re.search(">>", str_replace)): + str_orig = re.search(r'^(.*)>>(.*)$', str_replace).group(1) + str_rep = re.search(r'^(.*)>>(.*)$', str_replace).group(2) + + path_replace = Path(str(path_orig).replace(str_orig, str_rep)) + + if subfolder: + path_replace = path_replace / subfolder + + log_message(f'Replacement parameters found: original string: {str_orig}, replacement string: {str_rep}', callback_fun=callback_log) + log_message(f'Output path: {path_replace}', callback_fun=callback_log) + + else: + log_message("No match", callback_fun=callback_log) + return None + + # Create default folder to save data if none was defined by the user + if create_path: + if not path_replace.is_dir(): + path_replace.mkdir(parents=True) + return path_replace \ No newline at end of file diff --git a/segwrap/utils_masks.py b/segwrap/utils_masks.py index 3bf586b..e06cfce 100644 --- a/segwrap/utils_masks.py +++ b/segwrap/utils_masks.py @@ -1,117 +1,117 @@ -# Imports -from cellpose.io import imread, imsave -import numpy as np -from tqdm import tqdm -from scipy import ndimage -import pathlib - -from segwrap.utils_general import log_message, create_output_path - -# Calculate images summarizing distance to objects -def create_img_closest_obj(path_scan, str_label, strs_save, path_save=None, search_recursive=False, truncate_distance=None, callback_log=None, callback_status=None, callback_progress=None): - """ Function to process label images and facilitate assignment to closest segmented object. - Will create two 2D images with the same size as the label image. Pixel values in either image - encode - 1. Distance to the closted object, e.g. nuclei. - 2. Index of clostest object. - - Parameters - ---------- - path_scan : pathlib Path object - Path to scan for label images. - str_label : str - Unique string contained in label iamge to be saved. - strs_save : tuple of strings - Strings defining how to save the result images, either string will replace str_save. - path_save : pathlib Path object - Path to save results, - - If Pathlib object, then this absolute path is used. - - If 'string' a replacement operation path containing the analyzed mask will be applied (see create_output_path). - truncate_distance : int - Distance above which distances will be truncated. Using a value of 255 has the advantage - that the saved image is 8bit and thus small. - callback_log : callback, optional - Callback function to provide function log. If none, print will be used. - For more details see segwrap.utils_general.log_message - """ - - # Check scan directory - if not path_scan.is_dir(): - log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) - return - - # Path to save results - if isinstance(path_save, pathlib.PurePath): - path_save_results = path_save - if not path_save_results.is_dir(): - path_save_results.mkdir(parents=True) - - else: - path_save_str_replace = path_save - - # Search files: recursively or not - files_proc = [] - if search_recursive: - for path_mask in path_scan.rglob(f'*{str_label}*'): - files_proc.append(path_mask) - else: - for path_mask in path_scan.glob(f'*{str_label}*'): - files_proc.append(path_mask) - - # Process files - for idx, file_label in enumerate(files_proc): - - log_message(f'\n>>> Processing file:\n{file_label}', callback_fun=callback_status) - - # >>> Create path to save data if necessary - if not isinstance(path_save, pathlib.PurePath): - path_save_results = create_output_path(file_label.parent, path_save_str_replace, subfolder=None, create_path=True) - log_message(f'Results will be save here : {path_save_results}', callback_fun=callback_status) - - # >>>> Read label image - img_labels = imread(file_label) - props = regionprops(img_labels) - labels = np.array([prop.label for prop in props]) - n_objs = len(labels) - - # Loop over all nuclei and create create distance map - log_message(f' Creating distance maps for {n_objs} objects. This can take a while ...', callback_fun=callback_log) - dist_mat = np.zeros((img_labels.shape[0], img_labels.shape[1], n_objs), dtype=np.uint16) - mask_fill_indiv = np.zeros((img_labels.shape[0], img_labels.shape[1], n_objs), dtype=np.uint16) - - for indx, obj_int in enumerate(tqdm(np.nditer(labels), total=n_objs)): - - # Create binary mask for current object and find contour - img_label_loop = np.zeros((img_labels.shape[0], img_labels.shape[1])) - img_label_loop[img_labels == obj_int] = 1 - mask_fill_indiv[:, :, indx] = img_label_loop - - dist_obj = ndimage.distance_transform_edt(np.logical_not(img_label_loop)) - if truncate_distance: - dist_obj[dist_obj > truncate_distance] = truncate_distance - dist_mat[:, :, indx] = dist_obj.astype(np.uint16) - - # >>> Condense distmap in two matrixes: index and distance to closest object - dist_obj_ind_3D = np.argsort(dist_mat, axis=2) - dist_obj_dist_3D = np.take_along_axis(dist_mat, dist_obj_ind_3D, axis=2) - - # For index: replace Python matrix index with actual index from label image - ind_obj_closest = np.zeros((img_labels.shape[0], img_labels.shape[1])) - dist_obj_ind_2D = np.copy(dist_obj_ind_3D[:, :, 0]) - - for indx, obj_int in enumerate(np.nditer(labels)): - ind_obj_closest[dist_obj_ind_2D == indx] = obj_int - - # Save index of closest object - name_save_ind = path_save_results / f'{file_label.stem.replace(str_label, strs_save[0])}.png' - if str(name_save_ind) != str(file_label): - imsave(name_save_ind, ind_obj_closest.astype('uint16'), check_contrast=False) - else: - log_message(f'Name to save index matrix could not be established: {name_save_ind}', callback_fun=callback_log) - - # Save distances to closest object - name_save_dist = path_save_results / f'{file_label.stem.replace(str_label, strs_save[1])}.png' - if str(name_save_dist) != str(file_label): - imsave(name_save_dist, dist_obj_dist_3D[:, :, 0].astype('uint16'), check_contrast=False) - else: - log_message(f'Name to save index matrix could not be established: {name_save_dist}', callback_fun=callback_log) +# Imports +from cellpose.io import imread, imsave +import numpy as np +from tqdm import tqdm +from scipy import ndimage +import pathlib + +from segwrap.utils_general import log_message, create_output_path + +# Calculate images summarizing distance to objects +def create_img_closest_obj(path_scan, str_label, strs_save, path_save=None, search_recursive=False, truncate_distance=None, callback_log=None, callback_status=None, callback_progress=None): + """ Function to process label images and facilitate assignment to closest segmented object. + Will create two 2D images with the same size as the label image. Pixel values in either image + encode + 1. Distance to the closted object, e.g. nuclei. + 2. Index of clostest object. + + Parameters + ---------- + path_scan : pathlib Path object + Path to scan for label images. + str_label : str + Unique string contained in label iamge to be saved. + strs_save : tuple of strings + Strings defining how to save the result images, either string will replace str_save. + path_save : pathlib Path object + Path to save results, + - If Pathlib object, then this absolute path is used. + - If 'string' a replacement operation path containing the analyzed mask will be applied (see create_output_path). + truncate_distance : int + Distance above which distances will be truncated. Using a value of 255 has the advantage + that the saved image is 8bit and thus small. + callback_log : callback, optional + Callback function to provide function log. If none, print will be used. + For more details see segwrap.utils_general.log_message + """ + + # Check scan directory + if not path_scan.is_dir(): + log_message(f'Path {path_scan} does not exist.', callback_fun=callback_log) + return + + # Path to save results + if isinstance(path_save, pathlib.PurePath): + path_save_results = path_save + if not path_save_results.is_dir(): + path_save_results.mkdir(parents=True) + + else: + path_save_str_replace = path_save + + # Search files: recursively or not + files_proc = [] + if search_recursive: + for path_mask in path_scan.rglob(f'*{str_label}*'): + files_proc.append(path_mask) + else: + for path_mask in path_scan.glob(f'*{str_label}*'): + files_proc.append(path_mask) + + # Process files + for idx, file_label in enumerate(files_proc): + + log_message(f'\n>>> Processing file:\n{file_label}', callback_fun=callback_status) + + # >>> Create path to save data if necessary + if not isinstance(path_save, pathlib.PurePath): + path_save_results = create_output_path(file_label.parent, path_save_str_replace, subfolder=None, create_path=True) + log_message(f'Results will be save here : {path_save_results}', callback_fun=callback_status) + + # >>>> Read label image + img_labels = imread(file_label) + props = regionprops(img_labels) + labels = np.array([prop.label for prop in props]) + n_objs = len(labels) + + # Loop over all nuclei and create create distance map + log_message(f' Creating distance maps for {n_objs} objects. This can take a while ...', callback_fun=callback_log) + dist_mat = np.zeros((img_labels.shape[0], img_labels.shape[1], n_objs), dtype=np.uint16) + mask_fill_indiv = np.zeros((img_labels.shape[0], img_labels.shape[1], n_objs), dtype=np.uint16) + + for indx, obj_int in enumerate(tqdm(np.nditer(labels), total=n_objs)): + + # Create binary mask for current object and find contour + img_label_loop = np.zeros((img_labels.shape[0], img_labels.shape[1])) + img_label_loop[img_labels == obj_int] = 1 + mask_fill_indiv[:, :, indx] = img_label_loop + + dist_obj = ndimage.distance_transform_edt(np.logical_not(img_label_loop)) + if truncate_distance: + dist_obj[dist_obj > truncate_distance] = truncate_distance + dist_mat[:, :, indx] = dist_obj.astype(np.uint16) + + # >>> Condense distmap in two matrixes: index and distance to closest object + dist_obj_ind_3D = np.argsort(dist_mat, axis=2) + dist_obj_dist_3D = np.take_along_axis(dist_mat, dist_obj_ind_3D, axis=2) + + # For index: replace Python matrix index with actual index from label image + ind_obj_closest = np.zeros((img_labels.shape[0], img_labels.shape[1])) + dist_obj_ind_2D = np.copy(dist_obj_ind_3D[:, :, 0]) + + for indx, obj_int in enumerate(np.nditer(labels)): + ind_obj_closest[dist_obj_ind_2D == indx] = obj_int + + # Save index of closest object + name_save_ind = path_save_results / f'{file_label.stem.replace(str_label, strs_save[0])}.png' + if str(name_save_ind) != str(file_label): + imsave(name_save_ind, ind_obj_closest.astype('uint16'), check_contrast=False) + else: + log_message(f'Name to save index matrix could not be established: {name_save_ind}', callback_fun=callback_log) + + # Save distances to closest object + name_save_dist = path_save_results / f'{file_label.stem.replace(str_label, strs_save[1])}.png' + if str(name_save_dist) != str(file_label): + imsave(name_save_dist, dist_obj_dist_3D[:, :, 0].astype('uint16'), check_contrast=False) + else: + log_message(f'Name to save index matrix could not be established: {name_save_dist}', callback_fun=callback_log) diff --git a/segwrap/utils_segmentation.py b/segwrap/utils_segmentation.py index a8a5ea7..c799770 100644 --- a/segwrap/utils_segmentation.py +++ b/segwrap/utils_segmentation.py @@ -1,124 +1,124 @@ -# Imports -import pathlib -import json -#from skimage.io import imread, imsave -from cellpose.io import imread, imsave -from segwrap.utils_general import log_message, create_output_path - -# Functions -# TODO: allow multiple channel identifiers for segmentation of cells and nuclei (separate by ,) -def folder_prepare_prediction(path_process, channel_ident, img_ext, path_save, projection_type, subfolder=None, search_recursive=False, callback_log=None, callback_status=None, callback_progress=None): - """[summary] - - Parameters - ---------- - path_process : [type] - [description] - channel_ident : str - [description] - img_ext : str - Image extension - path_save : pathlin object or string - Path to save results, - - If Pathlib object, then this absolute path is used. - - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). - And results will be stored in subfolder 'segmentation-input' - projection_type : [type] - [description] - subfolder: str - subfolder where data should be stored. Will only be used when string replacement for path is used. - search_recursive : bool - Recursively search folder, default: false. - callback_log : [type], optional - [description], by default None - callback_status : [type], optional - [description], by default None - callback_progress : [type], optional - [description], by default None - """ - - # Print all input parameters - log_message(f"Function (segment_obj_indiv) called with: {str(locals())} ", callback_fun=callback_log) - - # Use provided absolute user-path to save images. - if isinstance(path_save, pathlib.PurePath): - path_save_results = path_save - if not path_save_results.is_dir(): - path_save_results.mkdir(parents=True) - path_save_settings = path_save_results - - else: - path_save_str_replace = path_save - - # How to look for files - files_proc = [] - if search_recursive: - for path_img in path_process.rglob(f'*{channel_ident}*{img_ext}'): - files_proc.append(path_img) - else: - for path_img in path_process.glob(f'*{channel_ident}*{img_ext}'): - files_proc.append(path_img) - - # Process files - n_files = len(files_proc) - for idx, file_proc in enumerate(files_proc): - - log_message(f'\n>>> Processing file: {file_proc}', callback_fun=callback_status) - - if callback_progress: - progress = float((idx+1)/n_files) - callback_progress(progress) - - name_base = file_proc.stem - - # Create new output path if specified - if not isinstance(path_save, pathlib.PurePath): - path_save_results = create_output_path(file_proc.parent, path_save_str_replace, subfolder=subfolder, create_path=True) - log_message(f'Results will be save here : {path_save_results}', callback_fun=callback_status) - path_save_settings = path_save_results - - # Create subfolder when processing individual images - if projection_type == 'indiv': - path_save_indiv = path_save_results / name_base - - if not path_save_indiv.is_dir(): - path_save_indiv.mkdir(parents=True) - path_save_settings = path_save_indiv - - # Open image - print(file_proc) - img = imread(str(file_proc)) - - img_properties = { "file_process": str(file_proc), - "img_name": file_proc.name, - "img_path": str(file_proc.parent), - "channel_ident": channel_ident, - "projection_type": projection_type} - - name_json = path_save_settings / f'img-prop__{name_base}.json' - with open(name_json, 'w') as fp: - json.dump(img_properties, fp, sort_keys=True, indent=4) - - # Process depending specified option - if projection_type == 'indiv': - - for i in range(img.shape[0]): - name_save = path_save_indiv / f'{name_base}_Z{str(i+1).zfill(3)}.png' - - if name_save.is_file(): - log_message(f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) - imsave(str(name_save), img[i, :, :]) - - else: - - if projection_type == 'mean': - img_proj = img.mean(axis=0) - - elif projection_type == 'max': - img_proj = img.max(axis=0) - - name_save = path_save_results / f'{name_base}.png' - - if name_save.is_file(): - log_message(f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) - imsave(str(name_save), img_proj.astype('uint16')) +# Imports +import pathlib +import json +#from skimage.io import imread, imsave +from cellpose.io import imread, imsave +from segwrap.utils_general import log_message, create_output_path + +# Functions +# TODO: allow multiple channel identifiers for segmentation of cells and nuclei (separate by ,) +def folder_prepare_prediction(path_process, channel_ident, img_ext, path_save, projection_type, subfolder=None, search_recursive=False, callback_log=None, callback_status=None, callback_progress=None): + """[summary] + + Parameters + ---------- + path_process : [type] + [description] + channel_ident : str + [description] + img_ext : str + Image extension + path_save : pathlin object or string + Path to save results, + - If Pathlib object, then this absolute path is used. + - If 'string' a replacement operation on the provided name of the data path will be applied (see create_output_path). + And results will be stored in subfolder 'segmentation-input' + projection_type : [type] + [description] + subfolder: str + subfolder where data should be stored. Will only be used when string replacement for path is used. + search_recursive : bool + Recursively search folder, default: false. + callback_log : [type], optional + [description], by default None + callback_status : [type], optional + [description], by default None + callback_progress : [type], optional + [description], by default None + """ + + # Print all input parameters + log_message(f"Function (segment_obj_indiv) called with: {str(locals())} ", callback_fun=callback_log) + + # Use provided absolute user-path to save images. + if isinstance(path_save, pathlib.PurePath): + path_save_results = path_save + if not path_save_results.is_dir(): + path_save_results.mkdir(parents=True) + path_save_settings = path_save_results + + else: + path_save_str_replace = path_save + + # How to look for files + files_proc = [] + if search_recursive: + for path_img in path_process.rglob(f'*{channel_ident}*{img_ext}'): + files_proc.append(path_img) + else: + for path_img in path_process.glob(f'*{channel_ident}*{img_ext}'): + files_proc.append(path_img) + + # Process files + n_files = len(files_proc) + for idx, file_proc in enumerate(files_proc): + + log_message(f'\n>>> Processing file: {file_proc}', callback_fun=callback_status) + + if callback_progress: + progress = float((idx+1)/n_files) + callback_progress(progress) + + name_base = file_proc.stem + + # Create new output path if specified + if not isinstance(path_save, pathlib.PurePath): + path_save_results = create_output_path(file_proc.parent, path_save_str_replace, subfolder=subfolder, create_path=True) + log_message(f'Results will be save here : {path_save_results}', callback_fun=callback_status) + path_save_settings = path_save_results + + # Create subfolder when processing individual images + if projection_type == 'indiv': + path_save_indiv = path_save_results / name_base + + if not path_save_indiv.is_dir(): + path_save_indiv.mkdir(parents=True) + path_save_settings = path_save_indiv + + # Open image + print(file_proc) + img = imread(str(file_proc)) + + img_properties = { "file_process": str(file_proc), + "img_name": file_proc.name, + "img_path": str(file_proc.parent), + "channel_ident": channel_ident, + "projection_type": projection_type} + + name_json = path_save_settings / f'img-prop__{name_base}.json' + with open(name_json, 'w') as fp: + json.dump(img_properties, fp, sort_keys=True, indent=4) + + # Process depending specified option + if projection_type == 'indiv': + + for i in range(img.shape[0]): + name_save = path_save_indiv / f'{name_base}_Z{str(i+1).zfill(3)}.png' + + if name_save.is_file(): + log_message(f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) + imsave(str(name_save), img[i, :, :]) + + else: + + if projection_type == 'mean': + img_proj = img.mean(axis=0) + + elif projection_type == 'max': + img_proj = img.max(axis=0) + + name_save = path_save_results / f'{name_base}.png' + + if name_save.is_file(): + log_message(f'File already exists. Will be overwritten {name_save}', callback_fun=callback_log) + imsave(str(name_save), img_proj.astype('uint16')) diff --git a/setup.py b/setup.py index db0e458..80b1002 100644 --- a/setup.py +++ b/setup.py @@ -1,34 +1,34 @@ -""" -Setup script. -""" - -import setuptools - -# package version -VERSION = None -with open('segwrap/__init__.py', encoding='utf-8') as f: - for row in f: - if row.startswith('__version__'): - VERSION = row.strip().split()[-1][1:-1] - break - -# package dependencies -with open("requirements.txt", encoding='utf-8') as f: - REQUIREMENTS = [l.strip() for l in f.readlines() if l] - -# setup -setuptools.setup( - name="segwrap", - version=VERSION, - author="Florian MUELLER", - author_email="muellerf.research@gmail.com", - description="wrapper package for cellpose", - url="https://github.com/fish-quant/fq-segmentation", - packages=setuptools.find_packages(), - install_requires=REQUIREMENTS, - include_package_data=True, - classifiers=( - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", - ), -) +""" +Setup script. +""" + +import setuptools + +# package version +VERSION = None +with open('segwrap/__init__.py', encoding='utf-8') as f: + for row in f: + if row.startswith('__version__'): + VERSION = row.strip().split()[-1][1:-1] + break + +# package dependencies +with open("requirements.txt", encoding='utf-8') as f: + REQUIREMENTS = [l.strip() for l in f.readlines() if l] + +# setup +setuptools.setup( + name="segwrap", + version=VERSION, + author="Florian MUELLER", + author_email="muellerf.research@gmail.com", + description="wrapper package for cellpose", + url="https://github.com/fish-quant/fq-segmentation", + packages=setuptools.find_packages(), + install_requires=REQUIREMENTS, + include_package_data=True, + classifiers=( + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ), +) diff --git a/update_manifest.js b/update_manifest.js index 17a5717..9485d1f 100644 --- a/update_manifest.js +++ b/update_manifest.js @@ -1,85 +1,85 @@ -var https = require('https'); -var fs = require('fs'); - -var childProcess = require('child_process'); - -function runScript(scriptPath, callback) { - // keep track of whether callback has been invoked to prevent multiple invocations - var invoked = false; - - var process = childProcess.fork(scriptPath); - - // listen for errors as they may prevent the exit event from firing - process.on('error', function (err) { - if (invoked) return; - invoked = true; - if(callback) callback(err); - }); - - // execute the callback once the process has finished running - process.on('exit', function (code) { - if (invoked) return; - invoked = true; - var err = code === 0 ? null : new Error('exit code ' + code); - if(callback) callback(err); - }); - -} - - -function downloadScripts(){ - return new Promise((resolve, reject)=>{ - https.get("https://raw.githubusercontent.com/oeway/ImJoy/master/web/src/pluginParser.js", (response)=>{ - if(response.statusCode == 200){ - var file = fs.createWriteStream( './pluginParser.js'); - var code = '' - response.on('data', (d) => { - code = code + d.toString() - }); - response.on('end', () => { - code = code.replace('export function', 'function') - code = code + '\nexports.parseComponent = parseComponent;' - file.write(code); - file.close(()=>{ - https.get("https://raw.githubusercontent.com/oeway/ImJoy/master/web/src/buildManifest.js", (response)=>{ - if(response.statusCode == 200){ - var code2 = '' - response.on('data', (d) => { - code2 = code2 + d.toString() - }); - response.on('end', () => { - var file = fs.createWriteStream( './buildManifest.js'); - file.write(code2); - file.close(resolve); - }) - } - else{ - reject(response.statusCode) - } - - }).on('error', function(err) { // Handle errors - reject(err.message); - }); - }); - }) - } - else{ - reject(response.statusCode) - } - - }).on('error', function(err) { // Handle errors - reject(err.message); - }); - }) -} - -downloadScripts().then(()=>{ - runScript('./buildManifest.js', ()=>{ - fs.unlink('./pluginParser.js', ()=>{}) - fs.unlink('./buildManifest.js', ()=>{}) - }) - -}).catch((err)=>{ - console.error(err) - runScript('./buildManifest.js') -}) +var https = require('https'); +var fs = require('fs'); + +var childProcess = require('child_process'); + +function runScript(scriptPath, callback) { + // keep track of whether callback has been invoked to prevent multiple invocations + var invoked = false; + + var process = childProcess.fork(scriptPath); + + // listen for errors as they may prevent the exit event from firing + process.on('error', function (err) { + if (invoked) return; + invoked = true; + if(callback) callback(err); + }); + + // execute the callback once the process has finished running + process.on('exit', function (code) { + if (invoked) return; + invoked = true; + var err = code === 0 ? null : new Error('exit code ' + code); + if(callback) callback(err); + }); + +} + + +function downloadScripts(){ + return new Promise((resolve, reject)=>{ + https.get("https://raw.githubusercontent.com/oeway/ImJoy/master/web/src/pluginParser.js", (response)=>{ + if(response.statusCode == 200){ + var file = fs.createWriteStream( './pluginParser.js'); + var code = '' + response.on('data', (d) => { + code = code + d.toString() + }); + response.on('end', () => { + code = code.replace('export function', 'function') + code = code + '\nexports.parseComponent = parseComponent;' + file.write(code); + file.close(()=>{ + https.get("https://raw.githubusercontent.com/oeway/ImJoy/master/web/src/buildManifest.js", (response)=>{ + if(response.statusCode == 200){ + var code2 = '' + response.on('data', (d) => { + code2 = code2 + d.toString() + }); + response.on('end', () => { + var file = fs.createWriteStream( './buildManifest.js'); + file.write(code2); + file.close(resolve); + }) + } + else{ + reject(response.statusCode) + } + + }).on('error', function(err) { // Handle errors + reject(err.message); + }); + }); + }) + } + else{ + reject(response.statusCode) + } + + }).on('error', function(err) { // Handle errors + reject(err.message); + }); + }) +} + +downloadScripts().then(()=>{ + runScript('./buildManifest.js', ()=>{ + fs.unlink('./pluginParser.js', ()=>{}) + fs.unlink('./buildManifest.js', ()=>{}) + }) + +}).catch((err)=>{ + console.error(err) + runScript('./buildManifest.js') +}) diff --git a/workflows/MacroBatchSplitChannel.ijm b/workflows/MacroBatchSplitChannel.ijm index 4d9f905..4ade76b 100644 --- a/workflows/MacroBatchSplitChannel.ijm +++ b/workflows/MacroBatchSplitChannel.ijm @@ -1,131 +1,131 @@ -// Split channels -// -// This macro batch processes all the files in a folder and any -// subfolders in that folder. It will split each image and save -// the different channels separately. -// -// WORKFLOW: -// 1. Scan recursively the specified folder for files ending in a specified suffix. -// 2. Images will be opened, split in different channels and maximum intensity projections (MIPs) created. -// 3. Results will be saved in a new folder -// a. Macro will replace specified string in original full file path by "acquisition", and create this folder to save the results. -// b. Will create additional subfolders 'mips' to store the MIPs. -/// -// PARAMETERS: -// - Last part of image name, by default 'ome.tif' -// - String contained in input folder which will be replaced, by default "multi_channel_stacks" - -suffix = "ome.tif" -folder_img = "multi_channel_stacks" - -setBatchMode(true); -dir = getDirectory("Choose a Directory"); -print('Processing directory: ' + dir); - -// Process files -n = 0; -processFolder(dir); - -function processFolder(dir) { - IJ.log("processing directory " + dir); - list = getFileList(dir); - - for (i = 0; i < list.length; i++) { - if(File.isDirectory(dir + list[i])) //if it's a directory, go to subfolder - processFolder("" + dir + list[i]); - - if(endsWith(list[i], suffix)) // if file ends with specified suffix, process it - print('Processing file'); - print(dir + list[i]); - processFile(dir, list[i]); - } -} - - -// Create multi-level directory -function createMultiLevelDirectory(path_create){ - - // Check if last part of path is the systems path separator. - // If not replace. This happens on windows machines, since - // getFileList returns directories with and "/". - - if (!endsWith(path_create, File.separator)){ - //print(path_create.replace("/",File.separator)); - path_create=replace(path_create,"/",File.separator); - print('PATH with bad last character. Modified: ' + path_create); - } - - parts = split(path_create, File.separator); - path_level = File.separator + parts[0] + File.separator + parts[1] + File.separator; - for(i=2; i