Skip to content
Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ It was developed for imaging data from (clear-tissue) [flamingo microscopes](htt
In addition to the analysis functionality, CochleaNet implements data pre-processing to convert data from flamingo microscopes into a format compatible with [BigStitcher](https://imagej.net/plugins/bigstitcher/) and to export image data and segmentation results to [ome.zarr](https://www.nature.com/articles/s41592-021-01326-w) and [MoBIE](https://mobie.github.io/).
This functionality is applicable to any imaging data from flamingo microscopes, not only clear-tissue data or cochleae. We aim to also extend the segmentation and analysis functionality to other kinds of samples imaged in the flamingo in the future.

For installation and usage instructions, check out [the documentation](https://computational-cell-analytics.github.io/cochlea-net/). For more details on the underlying methodology check out [our preprint](TODO).
For installation and usage instructions, check out [the documentation](https://computational-cell-analytics.github.io/cochlea-net/). For more details on the underlying methodology check out [our preprint](https://doi.org/10.1101/2025.11.16.688700).

<!---
The `flamingo_tools` library implements functionality for:
Expand Down
98 changes: 98 additions & 0 deletions development/check_uploaded_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import os
import subprocess
from shutil import copyfile

import imageio.v3 as imageio
import napari
import pandas as pd
import zarr
from flamingo_tools.test_data import _sample_registry

view = True
data_dict = {
"SGN": "PV",
"IHC": "VGlut3",
"SGN-lowres": "PV-lowres",
"IHC-lowres": "MYO-lowres",
"Synapses": "CTBP2",
}


def check_segmentation_model(model_name, checkpoint_path=None):
output_folder = f"result_{model_name}"
os.makedirs(output_folder, exist_ok=True)
input_path = os.path.join(output_folder, f"{model_name}.tif")
if not os.path.exists(input_path):
data_path = _sample_registry().fetch(data_dict[model_name])
copyfile(data_path, input_path)

output_path = os.path.join(output_folder, "segmentation.zarr")
if not os.path.exists(output_path):
cmd = ["flamingo_tools.run_segmentation", "-i", input_path, "-o", output_folder, "-m", model_name]
if checkpoint_path is not None:
cmd.extend(["-c", checkpoint_path])
subprocess.run(cmd)

if view:
segmentation = zarr.open(output_path)["segmentation"][:]
image = imageio.imread(input_path)
v = napari.Viewer()
v.add_image(image)
v.add_labels(segmentation, name=f"{model_name}-segmentation")
napari.run()


def check_detection_model():
model_name = "Synapses"
output_folder = f"result_{model_name}"
os.makedirs(output_folder, exist_ok=True)
input_path = os.path.join(output_folder, f"{model_name}.tif")
if not os.path.exists(input_path):
data_path = _sample_registry().fetch(data_dict[model_name])
copyfile(data_path, input_path)

output_path = os.path.join(output_folder, "synapse_detection.tsv")
if not os.path.exists(output_path):
subprocess.run(
["flamingo_tools.run_detection", "-i", input_path, "-o", output_folder, "-m", model_name]
)

if view:
prediction = pd.read_csv(output_path, sep="\t")[["z", "y", "x"]]
image = imageio.imread(input_path)
v = napari.Viewer()
v.add_image(image)
v.add_points(prediction)
napari.run()


def main():
# SGN segmentation:
# - Prediction works well on the CPU.
# - Prediction works well on the GPU.
# check_segmentation_model("SGN")

# IHC segmentation:
# - Prediction works well on the CPU.
# - Prediction works well on the GPU.
# check_segmentation_model("IHC")

# TODO: Update model.
# SGN segmentation (lowres):
# - Prediction does not work well on the CPU.
# - Prediction does not work well on the GPU.
check_segmentation_model("SGN-lowres", checkpoint_path="SGN-lowres.pt")

# IHC segmentation (lowres):
# - Prediction works well on the CPU.
# - Prediction works well on the GPU.
# check_segmentation_model("IHC-lowres")

# Synapse detection:
# - Prediction works well on the CPU.
# - Prediction works well on the GPU.
# check_detection_model()


if __name__ == "__main__":
main()
44 changes: 44 additions & 0 deletions development/export_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import torch
from torch_em.util import load_model


def export_sgn():
path = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/trained_models/SGN/v2_cochlea_distance_unet_SGN_supervised_2025-05-27" # noqa
model = load_model(path, device="cpu")
torch.save(model, "SGN.pt")


def export_ihc():
path = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/trained_models/IHC/v4_cochlea_distance_unet_IHC_supervised_2025-07-14" # noqa
model = load_model(path, device="cpu")
torch.save(model, "IHC.pt")


def export_synapses():
path = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/trained_models/Synapses/synapse_detection_model_v3.pt" # noqa
model = torch.load(path, map_location="cpu", weights_only=False)
torch.save(model, "Synapses.pt")


def export_sgn_lowres():
path = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/trained_models/SGN/cochlea_distance_unet_sgn-low-res-v4" # noqa
model = load_model(path, device="cpu")
torch.save(model, "SGN-lowres.pt")


def export_ihc_lowres():
path = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/trained_models/IHC/cochlea_distance_unet_ihc-lowres-v3" # noqa
model = load_model(path, device="cpu")
torch.save(model, "IHC-lowres.pt")


def main():
# export_sgn()
# export_ihc()
# export_synapses()
export_sgn_lowres()
# export_ihc_lowres()


if __name__ == "__main__":
main()
23 changes: 12 additions & 11 deletions doc/documentation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CochleaNet

CochleaNet is a software tool for the analysis of cochleae imaged in light-sheet microscopy.
CochleaNet is a tool for the analysis of cochleae imaged in light-sheet microscopy.
Its main components are:
- A deep neural network for segmenting spiral ganglion neurons (SGNs) from parvalbumin (PV) staining.
- A deep neural network for segmenting inner hair cells (IHCs) from VGlut3 staining.
Expand All @@ -12,11 +12,12 @@ In addition, it contains functionality for data pre-processing and different kin
- Analyzing SGN subtypes (based on additional fluorescent staining).
- Visualizing segmentation results and derived analyses in [MoBIE](https://mobie.github.io/).

The networks and analysis methods were primarily developed for high-resolution isotropic data from a [custom light-sheet microscope](https://www.biorxiv.org/content/10.1101/2025.02.21.639411v2.abstract).
The networks will work best on the respective fluorescent stains they were trained on, but will work on similar stains. For example, we have successfully applied the network for SGN segmentation on a calretinin (CR) stain and the network for IHC segmentation on a myosin7a stain.
The networks and analysis methods were primarily developed for high-resolution isotropic data from a [custom light-sheet microscope](https://www.nature.com/articles/s41587-025-02882-8).
The networks work best for the respective fluorescent stains they were trained on, but will work for similar stains.
For example, we have successfully applied the network for SGN segmentation on a calretinin (CR) stain and the network for IHC segmentation on a Myosin VII A stain.
In addition, CochleaNet provides networks for the segmentation of SGNs and IHCs in anisotropic data from a [commercial light-sheet microscope](https://www.miltenyibiotec.com/DE-en/products/macs-imaging-and-spatial-biology/ultramicroscope-platform.html).

For more information on CochleaNet, check out our [preprint](TODO).
For more information on CochleaNet, check out our [preprint](https://doi.org/10.1101/2025.11.16.688700).

## Installation

Expand Down Expand Up @@ -54,20 +55,20 @@ CochleaNet can be used via:
- The [command line interface](#command-line-interface): enables data conversion, model prediction, and selected analysis workflows for large image data.
- The [python library](#python-library): implements CochleaNet's functionality and can be used to implement flexible prediction and data analysis workflows for large image data.

**Note: the napari plugin was not optimized for processing large data. For processing large image data use the CLI or python library.**
**Note: the napari plugin was not optimized for processing large data. Please use the CLI or python library for processing large data.**

### Napari Plugin

The napari plugin for segmentation (SGNs and IHCS) and detection (ribbon synapses) is available under `Plugins->CochleaNet->Segmentation/Detection` in napari:
The plugins for segmentation (SGNs and IHCS) and detection (ribbon synapses) is available under `Plugins->CochleaNet->Segmentation/Detection` in napari:

The segmentation plugin offers the choice of different models under `Select Model:` (see [Available Models](#available-models) for details). `Image data` enables to choose which image data (layer) the model is applied to. The segmentation is started by clicking the `Run Segmentation` button. After the segmentation has finished, a new segmentation layer with the result (here `IHC`) will be added:
The segmentation plugin offers the choice of different models under `Select Model:` (see [Available Models](#available-models) for details). `Image data` enables the choice which image data (napari layer) the model is applied to.
The segmentation is started by clicking the `Run Segmentation` button. After the segmentation has finished, a new segmentation layer with the result (here `IHC`) will be added:

The detection model works similarly. It currently provides the model for synapse detection. The predictions are added as point layer (``):
The detection model works similarly. It currently provides the model for synapse detection. The predictions are added as a point layer (``):

TODO Video.
For more information on how to use napari, check out the tutorials at [www.napari.org](TODO).
For more information on how to use napari, check out the tutorials at [www.napari.org](https://napari.org/stable/).

**To use the napari plugin you have to install `napari` and `pyqt` in your environment.** See [installation](#installation) for details.
**To use the napari plugin you have to install `napari` and `pyqt` in your environment. See [installation](#installation) for details.**

### Command Line Interface

Expand Down
Binary file added doc/img/cochlea-net-plugin-detection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/img/cochlea-net-plugin-segmentation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/img/cochlea-net-plugin-selection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 4 additions & 6 deletions flamingo_tools/model_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ def get_model_registry() -> None:
"""
registry = {
"SGN": "3058690b49015d6210a8e8414eb341c34189fee660b8fac438f1fdc41bdfff98",
"IHC": "89afbcca08ed302aa6dfbaba5bf2530fc13339c05a604b6f2551d97cf5f12774",
"IHC": "752dab7995b076ec4b8526b0539d1b33ade5de9251aaf6863d9bd8cc9cd036b6",
"Synapses": "2a42712b056f082b4794f15cf41b15678aab0bec1acc922ff9f0dc76abe6747e",
"SGN-lowres": "6accba4b4c65158fccf25623dcd0fb3b14203305d033a0d443a307114ec5dd8c",
"IHC-lowres": "537f1d4afc5a582771b87adeccadfa5635e1defd13636702363992188ef5bdbd",
}
urls = {
"SGN": "https://owncloud.gwdg.de/index.php/s/NZ2vv7hxX1imITG/download",
"IHC": "https://owncloud.gwdg.de/index.php/s/GBBJkPQFraz1ZzU/download",
"IHC": "https://owncloud.gwdg.de/index.php/s/wB7d2MjV5LRTP06/download",
"Synapses": "https://owncloud.gwdg.de/index.php/s/A9W5NmOeBxiyZgY/download",
"SGN-lowres": "https://owncloud.gwdg.de/index.php/s/8hwZjBVzkuYhHLm/download",
"IHC-lowres": "https://owncloud.gwdg.de/index.php/s/EhnV4brhpvFbSsy/download",
Expand Down Expand Up @@ -148,12 +148,10 @@ def get_default_tiling() -> Dict[str, Dict[str, int]]:
tiling = {"tile": tile, "halo": halo}
print(f"Determined tile size for MPS: {tiling}")

# I am not sure what is reasonable on a cpu. For now choosing very small tiling.
# (This will not work well on a CPU in any case.)
else:
tiling = {
"tile": {"x": 96, "y": 96, "z": 16},
"halo": {"x": 16, "y": 16, "z": 8},
"tile": {"x": 64, "y": 64, "z": 64},
"halo": {"x": 32, "y": 32, "z": 16},
}
print(f"Determining default tiling for CPU: {tiling}")

Expand Down
Loading