Skip to content

Commit 5f15a66

Browse files
Doc updates (#71)
Update documentation and function doc strings
1 parent ae2a472 commit 5f15a66

24 files changed

+365
-280
lines changed

README.md

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
1-
# Synaptic Reconstruction
1+
# SynapseNet: Deep Learning for Automatic Synapse Reconstruction
22

3-
Reconstruction of synaptic structures in electron microscopy.
3+
SynapseNet is a tool for segmentation and analysis of synapses in electron microscopy.
44

5-
THIS IS WORK IN PROGRESS!
5+
To learn how to use SynapseNet, check out [the documentation](https://computational-cell-analytics.github.io/synapse-net/).
6+
To learn more about how it works, check out [our preprint](TODO).
67

7-
## Installation
8-
9-
- Make sure conda or mamba is installed.
10-
- If you don't have a conda installation yet we recommend [micromamba](https://mamba.readthedocs.io/en/latest/installation/micromamba-installation.html)
11-
- Create the environment with all required dependencies: `mamba env create -f environment.yaml`
12-
- Activate the environment: `mamba activate synaptic-reconstruction`
13-
- Install the package: `pip install -e .`
14-
15-
## Tools
16-
17-
### Segmentation Correction
18-
19-
https://napari.org/stable/howtos/layers/labels.html
20-
21-
### Distance Measurements
8+
See an example reconstruction of a mossy fibre synapse with SynapseNet.
9+
Automatic segmentation of synaptic vesicles are rendered in orange, active zones in blue and two mitochondria in red and cyan.
10+
![Reconstruction of a mossy fiber synapse](doc/images/synapse-reconstruction.png)

doc/images/synapse-reconstruction.png

902 KB
Loading

doc/start_page.md

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,57 @@
1-
# Synaptic Reconstruction
2-
lorem ipsum...
1+
# SynapseNet: Deep Learning for Automatic Synapse Reconstruction
2+
3+
SynapseNet is a tool for automatic segmentation and analysis of synapses in electron micrographs.
4+
It provides deep neural networks for:
5+
- Synaptic vesicle segmentation in ssTEM (2d data) and (cryo-)electron tomography (3d data)
6+
- Active zone membrane segmentation in electron tomography
7+
- Mitochondrion segmentation in electron tomography
8+
- Synaptic compartment segmentation in electron tomography
9+
- Synaptic ribbon and pre-synaptic density segmentation for ribbon synapses in electron tomography
10+
It also offers functionality for quantifying synaptic ultrastructure based on segmentation results, for example by measuring vesicle or structure morphology, measuring distances between vesicles and structures, or assigning vesicles into different pools.
11+
SynapseNet mainly targets electron tomography, but can also be appled to other types of electron microscopy,
12+
especially throught the [domain adaptation](domain-adaptation) functionality.
13+
14+
SynapseNet offers a [napari plugin](napari-plugin), [command line interface](command-line-interface), and [python library](python-library).
15+
Please cite our [bioRxiv preprint](TODO) if you use it in your research.
16+
17+
**The rest of the documentation will be updated in the next days!**
18+
19+
## Requirements & Installation
20+
21+
- Requirements: Tested on Linux but should work on Mac/Windows.
22+
- GPU needed to use 3d segmentation networks
23+
- Installation via conda and local pip install
24+
- GPU support
25+
26+
- Make sure conda or mamba is installed.
27+
- If you don't have a conda installation yet we recommend [micromamba](https://mamba.readthedocs.io/en/latest/installation/micromamba-installation.html)
28+
- Create the environment with all required dependencies: `mamba env create -f environment.yaml`
29+
- Activate the environment: `mamba activate synaptic-reconstruction`
30+
- Install the package: `pip install -e .`
31+
32+
## Napari Plugin
33+
34+
lorem ipsum
35+
36+
## Command Line Functionality
37+
38+
- segmentation cli
39+
- export to imod
40+
- vesicles / spheres
41+
- objects
42+
43+
## Python Library
44+
45+
- segmentation functions
46+
- distance and morphology measurements
47+
- imod
48+
49+
### Domain Adaptation
50+
51+
- explain domain adaptation
52+
- link to the example script
53+
54+
### Network Training
55+
56+
- explain / diff to domain adaptation
57+
- link to the example script

scripts/cooper/full_reconstruction/qualitative_evaluation.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def eval_compartments():
7575
print("Compartment Evaluation:")
7676
print("Avergage pieces per compartment:", avg, "+-", std)
7777
print("Max pieces per compartment:", max_)
78+
print("Number of compartments:", len(pieces_per_compartment))
7879

7980

8081
def eval_mitos():
@@ -124,11 +125,11 @@ def check_mitos():
124125
def main():
125126
# check_mitos()
126127

127-
eval_mitos()
128-
print()
128+
# eval_mitos()
129+
# print()
129130
eval_compartments()
130-
print()
131-
eval_az()
131+
# print()
132+
# eval_az()
132133

133134

134135
main()

scripts/inner_ear/processing/run_analyis.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
from synaptic_reconstruction.file_utils import get_data_path
1212
from synaptic_reconstruction.distance_measurements import (
13-
measure_segmentation_to_object_distances,
1413
filter_blocked_segmentation_to_object_distances,
14+
load_distances,
15+
measure_segmentation_to_object_distances,
1516
)
1617

1718
from synaptic_reconstruction.morphology import compute_radii, compute_object_morphology
@@ -172,8 +173,9 @@ def load_dist(measurement_path, seg_ids=None):
172173

173174
# Filter out the blocked vesicles.
174175
if apply_extra_filters:
176+
rav_dists, ep1, ep2, all_rav_ids = load_distances(distance_paths["ribbon"])
175177
rav_ids = filter_blocked_segmentation_to_object_distances(
176-
vesicles, distance_paths["ribbon"], seg_ids=rav_ids, line_dilation=4, verbose=True,
178+
vesicles, rav_dists, ep1, ep2, all_rav_ids, filter_seg_ids=rav_ids, line_dilation=4, verbose=True,
177179
)
178180
rav_ids = filter_border_vesicles(vesicles, seg_ids=rav_ids)
179181

synaptic_reconstruction/distance_measurements.py

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
import multiprocessing as mp
3-
from typing import Dict, List, Optional, Tuple
3+
from typing import Dict, List, Optional, Tuple, Union
44

55
import numpy as np
66

@@ -18,7 +18,25 @@
1818
skfmm = None
1919

2020

21-
def compute_geodesic_distances(segmentation, distance_to, resolution=None, unsigned=True):
21+
def compute_geodesic_distances(
22+
segmentation: np.ndarray,
23+
distance_to: np.ndarray,
24+
resolution: Optional[Union[int, float, Tuple[int, int, int]]] = None,
25+
unsigned: bool = True,
26+
) -> np.ndarray:
27+
"""Compute the geodesic distances between a segmentation and a distance target.
28+
29+
This function require scikit-fmm to be installed.
30+
31+
Args:
32+
segmentation: The binary segmentation.
33+
distance_to: The binary distance target.
34+
resolution: The voxel size of the data, used to scale the distances.
35+
unsigned: Whether to return the unsigned or signed distances.
36+
37+
Returns:
38+
Array with the geodesic distance values.
39+
"""
2240
assert skfmm is not None, "Please install scikit-fmm to use compute_geodesic_distance."
2341

2442
invalid = segmentation == 0
@@ -43,14 +61,12 @@ def compute_geodesic_distances(segmentation, distance_to, resolution=None, unsig
4361
return distances
4462

4563

46-
# TODO update this
4764
def _compute_centroid_distances(segmentation, resolution, n_neighbors):
48-
# TODO enable eccentricity centers instead
4965
props = regionprops(segmentation)
5066
centroids = np.array([prop.centroid for prop in props])
5167
if resolution is not None:
52-
pass # TODO scale the centroids
53-
68+
scale_factor = np.array(resolution)[:, None]
69+
centroids *= scale_factor
5470
pair_distances = pairwise_distances(centroids)
5571
return pair_distances
5672

@@ -313,11 +329,13 @@ def create_pairwise_distance_lines(
313329
endpoints1: One set of distance end points.
314330
endpoints2: The other set of distance end points.
315331
seg_ids: The segmentation pair corresponding to each distance.
316-
n_neighbors: ...
317-
pairs: ...
318-
bb: ....
319-
scale: ...
320-
remove_duplicates: ...
332+
n_neighbors: The number of nearest neighbors to take into consideration
333+
for creating the distance lines.
334+
pairs: Optional list of ids to use for creating the distance lines.
335+
bb: Bounding box for restricing the distance line creation.
336+
scale: Scale factor for resizing the distance lines.
337+
Use this if the corresponding segmentations were downscaled for visualization.
338+
remove_duplicates: Remove duplicate id pairs from the distance lines.
321339
322340
Returns:
323341
The lines for plotting in napari.
@@ -386,8 +404,10 @@ def create_object_distance_lines(
386404
endpoints1: One set of distance end points.
387405
endpoints2: The other set of distance end points.
388406
seg_ids: The segmentation ids corresponding to each distance.
389-
max_distance: ...
390-
scale: ...
407+
max_distance: Maximal distance for drawing the distance line.
408+
filter_seg_ids: Segmentation ids to restrict the distance lines.
409+
scale: Scale factor for resizing the distance lines.
410+
Use this if the corresponding segmentations were downscaled for visualization.
391411
392412
Returns:
393413
The lines for plotting in napari.
@@ -416,13 +436,32 @@ def create_object_distance_lines(
416436
return lines, properties
417437

418438

419-
def keep_direct_distances(segmentation, measurement_path, line_dilation=0, scale=None):
420-
"""Filter out all distances that are not direct.
421-
I.e. distances that cross another segmented object.
422-
"""
439+
def keep_direct_distances(
440+
segmentation: np.ndarray,
441+
distances: np.ndarray,
442+
endpoints1: np.ndarray,
443+
endpoints2: np.ndarray,
444+
seg_ids: np.ndarray,
445+
line_dilation: int = 0,
446+
scale: Optional[Tuple[int, int, int]] = None,
447+
) -> List[List[int, int]]:
448+
"""Filter out all distances that are not direct; distances that are occluded by another segmented object.
423449
424-
distances, ep1, ep2, seg_ids = load_distances(measurement_path)
425-
distance_lines, properties = create_object_distance_lines(distances, ep1, ep2, seg_ids, scale=scale)
450+
Args:
451+
segmentation: The segmentation from which the distances are derived.
452+
distances: The measurd distances.
453+
endpoints1: One set of distance end points.
454+
endpoints2: The other set of distance end points.
455+
seg_ids: The segmentation ids corresponding to each distance.
456+
line_dilation: Dilation factor of the distance lines for determining occlusions.
457+
scale: Scaling factor of the segmentation compared to the distance measurements.
458+
459+
Returns:
460+
The list of id pairs that are kept.
461+
"""
462+
distance_lines, properties = create_object_distance_lines(
463+
distances, endpoints1, endpoints2, seg_ids, scale=scale
464+
)
426465

427466
ids_a, ids_b = properties["id_a"], properties["id_b"]
428467
filtered_ids_a, filtered_ids_b = [], []
@@ -459,10 +498,35 @@ def keep_direct_distances(segmentation, measurement_path, line_dilation=0, scale
459498

460499

461500
def filter_blocked_segmentation_to_object_distances(
462-
segmentation, measurement_path, line_dilation=0, scale=None, seg_ids=None, verbose=False,
463-
):
464-
distances, ep1, ep2, seg_ids = load_distances(measurement_path)
465-
distance_lines, properties = create_object_distance_lines(distances, ep1, ep2, seg_ids, scale=scale)
501+
segmentation: np.ndarray,
502+
distances: np.ndarray,
503+
endpoints1: np.ndarray,
504+
endpoints2: np.ndarray,
505+
seg_ids: np.ndarray,
506+
line_dilation: int = 0,
507+
scale: Optional[Tuple[int, int, int]] = None,
508+
filter_seg_ids: Optional[List[int]] = None,
509+
verbose: bool = False,
510+
) -> List[int]:
511+
"""Filter out all distances that are not direct; distances that are occluded by another segmented object.
512+
513+
Args:
514+
segmentation: The segmentation from which the distances are derived.
515+
distances: The measurd distances.
516+
endpoints1: One set of distance end points.
517+
endpoints2: The other set of distance end points.
518+
seg_ids: The segmentation ids corresponding to each distance.
519+
line_dilation: Dilation factor of the distance lines for determining occlusions.
520+
scale: Scaling factor of the segmentation compared to the distance measurements.
521+
filter_seg_ids: Segmentation ids to restrict the distance lines.
522+
verbose: Whether to print progressbar.
523+
524+
Returns:
525+
The list of id pairs that are kept.
526+
"""
527+
distance_lines, properties = create_object_distance_lines(
528+
distances, endpoints1, endpoints2, seg_ids, scale=scale
529+
)
466530
all_seg_ids = properties["id"]
467531

468532
filtered_ids = []

synaptic_reconstruction/file_utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import os
2+
from typing import List, Optional, Union
23

34

4-
def get_data_path(folder, n_tomograms=1):
5+
def get_data_path(folder: str, n_tomograms: Optional[int] = 1) -> Union[str, List[str]]:
6+
"""Get the path to all tomograms stored as .rec or .mrc files in a folder.
7+
8+
Args:
9+
folder: The folder with tomograms.
10+
n_tomograms: The expected number of tomograms.
11+
12+
Returns:
13+
The filepath or list of filepaths of the tomograms in the folder.
14+
"""
515
file_names = os.listdir(folder)
616
tomograms = []
717
for fname in file_names:
@@ -11,7 +21,5 @@ def get_data_path(folder, n_tomograms=1):
1121

1222
if n_tomograms is None:
1323
return tomograms
14-
1524
assert len(tomograms) == n_tomograms, f"{folder}: {len(tomograms)}, {n_tomograms}"
16-
1725
return tomograms[0] if n_tomograms == 1 else tomograms
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
edge_filter.py

synaptic_reconstruction/ground_truth/matching.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,21 @@
44
from skimage.segmentation import relabel_sequential
55

66

7-
def find_additional_objects(ground_truth, segmentation, matching_threshold=0.5):
7+
def find_additional_objects(
8+
ground_truth: np.ndarray,
9+
segmentation: np.ndarray,
10+
matching_threshold: float = 0.5
11+
) -> np.ndarray:
12+
"""Compare ground-truth annotations with a segmentation to find objects not in the annotation.
13+
14+
Args:
15+
ground_trut:
16+
segmentation:
17+
matching_threshold:
18+
19+
Returns:
20+
"""
21+
822
segmentation = relabel_sequential(segmentation)[0]
923

1024
# Match the objects in the segmentation to the ground-truth.

0 commit comments

Comments
 (0)