celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- celldetective/__init__.py +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +403 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/downloader.py +137 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +235 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
- celldetective-1.5.0b1.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/downloader.py +0 -111
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
celldetective/tracking.py
CHANGED
|
@@ -7,8 +7,9 @@ from btrack.io.utils import localizations_to_objects
|
|
|
7
7
|
from btrack import BayesianTracker
|
|
8
8
|
|
|
9
9
|
from celldetective.measure import measure_features
|
|
10
|
-
from celldetective.utils import
|
|
11
|
-
from celldetective.
|
|
10
|
+
from celldetective.utils.maths import velocity_per_track
|
|
11
|
+
from celldetective.utils.data_cleaning import rename_intensity_column
|
|
12
|
+
from celldetective.utils.data_loaders import interpret_tracking_configuration
|
|
12
13
|
|
|
13
14
|
import os
|
|
14
15
|
import subprocess
|
|
@@ -95,6 +96,7 @@ def track(
|
|
|
95
96
|
|
|
96
97
|
Examples
|
|
97
98
|
--------
|
|
99
|
+
|
|
98
100
|
>>> labels = np.array([[1, 1, 2, 2, 0, 0],
|
|
99
101
|
[1, 1, 1, 2, 2, 0],
|
|
100
102
|
[0, 0, 1, 2, 0, 0]])
|
|
@@ -473,16 +475,11 @@ def clean_trajectories(
|
|
|
473
475
|
|
|
474
476
|
"""
|
|
475
477
|
|
|
476
|
-
trajectories.reset_index
|
|
478
|
+
trajectories.reset_index(drop=True, inplace=True)
|
|
477
479
|
trajectories.sort_values(
|
|
478
480
|
by=[column_labels["track"], column_labels["time"]], inplace=True
|
|
479
481
|
)
|
|
480
482
|
|
|
481
|
-
if minimum_tracklength > 0:
|
|
482
|
-
trajectories = filter_by_tracklength(
|
|
483
|
-
trajectories.copy(), minimum_tracklength, track_label=column_labels["track"]
|
|
484
|
-
)
|
|
485
|
-
|
|
486
483
|
if np.any([remove_not_in_first, remove_not_in_last]):
|
|
487
484
|
trajectories = filter_by_endpoints(
|
|
488
485
|
trajectories.copy(),
|
|
@@ -503,11 +500,19 @@ def clean_trajectories(
|
|
|
503
500
|
trajectories = interpolate_time_gaps(
|
|
504
501
|
trajectories.copy(), column_labels=column_labels
|
|
505
502
|
)
|
|
503
|
+
# interpolate_time_gaps might leave TRACK_ID in index of some rows or overall
|
|
504
|
+
trajectories.reset_index(drop=True, inplace=True)
|
|
506
505
|
|
|
507
506
|
if interpolate_na:
|
|
508
507
|
trajectories = interpolate_nan_properties(
|
|
509
508
|
trajectories.copy(), track_label=column_labels["track"]
|
|
510
509
|
)
|
|
510
|
+
trajectories.reset_index(drop=True, inplace=True)
|
|
511
|
+
|
|
512
|
+
if minimum_tracklength > 0:
|
|
513
|
+
trajectories = filter_by_tracklength(
|
|
514
|
+
trajectories.copy(), minimum_tracklength, track_label=column_labels["track"]
|
|
515
|
+
)
|
|
511
516
|
|
|
512
517
|
trajectories = trajectories.sort_values(
|
|
513
518
|
by=[column_labels["track"], column_labels["time"]]
|
|
@@ -600,10 +605,15 @@ def interpolate_nan_properties(trajectories, track_label="TRACK_ID"):
|
|
|
600
605
|
|
|
601
606
|
"""
|
|
602
607
|
|
|
603
|
-
trajectories = trajectories.groupby(track_label, group_keys=
|
|
608
|
+
trajectories = trajectories.groupby(track_label, group_keys=True).apply(
|
|
604
609
|
interpolate_per_track
|
|
605
610
|
)
|
|
606
611
|
|
|
612
|
+
if track_label in trajectories.index.names:
|
|
613
|
+
trajectories = trajectories.reset_index(
|
|
614
|
+
level=0, drop=track_label in trajectories.columns
|
|
615
|
+
)
|
|
616
|
+
|
|
607
617
|
return trajectories
|
|
608
618
|
|
|
609
619
|
|
|
@@ -840,11 +850,9 @@ def interpolate_time_gaps(
|
|
|
840
850
|
lambda x: x.interpolate(method="linear")
|
|
841
851
|
)
|
|
842
852
|
trajectories.reset_index(drop=True, inplace=True)
|
|
843
|
-
trajectories[column_labels["time"]] =
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
9,
|
|
847
|
-
)
|
|
853
|
+
trajectories[column_labels["time"]] = (
|
|
854
|
+
trajectories[column_labels["time"]] - pd.Timestamp("1970-01-01")
|
|
855
|
+
).dt.total_seconds()
|
|
848
856
|
# trajectories[column_labels['time']] = trajectories[column_labels['time']].astype('int64')
|
|
849
857
|
trajectories.sort_values(
|
|
850
858
|
by=[column_labels["track"], column_labels["time"]], inplace=True
|
|
@@ -998,7 +1006,14 @@ def extrapolate_tracks(
|
|
|
998
1006
|
[column_labels["track"], column_labels["time"]], inplace=True
|
|
999
1007
|
)
|
|
1000
1008
|
|
|
1001
|
-
return trajectories
|
|
1009
|
+
return trajectories[
|
|
1010
|
+
[column_labels["track"], column_labels["time"]]
|
|
1011
|
+
+ [
|
|
1012
|
+
col
|
|
1013
|
+
for col in trajectories.columns
|
|
1014
|
+
if col not in [column_labels["track"], column_labels["time"]]
|
|
1015
|
+
]
|
|
1016
|
+
]
|
|
1002
1017
|
|
|
1003
1018
|
|
|
1004
1019
|
def compute_instantaneous_velocity(
|
|
File without changes
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Union, Optional
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _segment_image_with_cellpose_model(
|
|
8
|
+
img,
|
|
9
|
+
model=None,
|
|
10
|
+
diameter=None,
|
|
11
|
+
cellprob_threshold=None,
|
|
12
|
+
flow_threshold=None,
|
|
13
|
+
channel_axis=-1,
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Segments an input image using a Cellpose model.
|
|
17
|
+
|
|
18
|
+
This function applies a preloaded Cellpose model to segment an input image and returns the resulting labeled mask.
|
|
19
|
+
The image is rearranged into the format expected by the Cellpose model, with the specified channel axis moved to the first dimension.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
img : ndarray
|
|
24
|
+
The input image to be segmented. It is expected to have a channel axis specified by `channel_axis`.
|
|
25
|
+
model : CellposeModel, optional
|
|
26
|
+
A preloaded Cellpose model instance used for segmentation.
|
|
27
|
+
diameter : float, optional
|
|
28
|
+
The diameter of objects to segment. If `None`, the model's default diameter is used.
|
|
29
|
+
cellprob_threshold : float, optional
|
|
30
|
+
The threshold for the probability of cells used during segmentation. If `None`, the default threshold is used.
|
|
31
|
+
flow_threshold : float, optional
|
|
32
|
+
The threshold for flow error during segmentation. If `None`, the default threshold is used.
|
|
33
|
+
channel_axis : int, optional
|
|
34
|
+
The axis of the input image that represents the channels. Default is `-1` (channel-last format).
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
ndarray
|
|
39
|
+
A labeled mask of the same spatial dimensions as the input image, with segmented regions assigned unique
|
|
40
|
+
integer labels. The dtype of the mask is `uint16`.
|
|
41
|
+
|
|
42
|
+
Notes
|
|
43
|
+
-----
|
|
44
|
+
- The `img` array is internally rearranged to move the specified `channel_axis` to the first dimension to comply
|
|
45
|
+
with the Cellpose model's input requirements.
|
|
46
|
+
- Ensure the provided `model` is a properly initialized Cellpose model instance.
|
|
47
|
+
- Parameters `diameter`, `cellprob_threshold`, and `flow_threshold` allow fine-tuning of the segmentation process.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
img = np.moveaxis(img, channel_axis, 0)
|
|
51
|
+
lbl, _, _ = model.eval(
|
|
52
|
+
img,
|
|
53
|
+
diameter=diameter,
|
|
54
|
+
cellprob_threshold=cellprob_threshold,
|
|
55
|
+
flow_threshold=flow_threshold,
|
|
56
|
+
channels=None,
|
|
57
|
+
normalize=False,
|
|
58
|
+
model_loaded=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return lbl.astype(np.uint16)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _prep_cellpose_model(
|
|
65
|
+
model_name: str,
|
|
66
|
+
path: Union[str, Path],
|
|
67
|
+
use_gpu: bool = False,
|
|
68
|
+
n_channels: int = 2,
|
|
69
|
+
scale: Optional[float] = None,
|
|
70
|
+
):
|
|
71
|
+
"""
|
|
72
|
+
Prepares and loads a Cellpose model for segmentation tasks.
|
|
73
|
+
|
|
74
|
+
This function initializes a Cellpose model with the specified parameters, configures GPU usage if available,
|
|
75
|
+
and calculates or applies a scaling factor for the model based on image resolution.
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
model_name : str
|
|
80
|
+
The name of the pretrained Cellpose model to load.
|
|
81
|
+
path : str
|
|
82
|
+
The directory where the model is stored.
|
|
83
|
+
use_gpu : bool, optional
|
|
84
|
+
If `True`, the model will use GPU acceleration for computations. Default is `False`.
|
|
85
|
+
n_channels : int, optional
|
|
86
|
+
The number of input channels expected by the model. Default is `2`.
|
|
87
|
+
scale : float, optional
|
|
88
|
+
A scaling factor to adjust the model's output to match the image resolution. If not provided, the scale is
|
|
89
|
+
automatically calculated based on the model's diameter parameters.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
tuple
|
|
94
|
+
- model : CellposeModel
|
|
95
|
+
The loaded Cellpose model configured with the specified parameters.
|
|
96
|
+
- scale_model : float
|
|
97
|
+
The scaling factor applied to the model, calculated or provided.
|
|
98
|
+
|
|
99
|
+
Notes
|
|
100
|
+
-----
|
|
101
|
+
- Ensure the Cellpose package is installed and the model files are correctly stored in the provided path.
|
|
102
|
+
- GPU support depends on the availability of compatible hardware and software setup.
|
|
103
|
+
- The scale is calculated as `(diam_mean / diam_labels)` if `scale` is not provided, where `diam_mean` and
|
|
104
|
+
`diam_labels` are attributes of the model.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
import torch
|
|
108
|
+
|
|
109
|
+
if not use_gpu:
|
|
110
|
+
device = torch.device("cpu")
|
|
111
|
+
else:
|
|
112
|
+
device = torch.device("cuda")
|
|
113
|
+
|
|
114
|
+
from cellpose.models import CellposeModel
|
|
115
|
+
|
|
116
|
+
model = CellposeModel(
|
|
117
|
+
gpu=use_gpu,
|
|
118
|
+
device=device,
|
|
119
|
+
pretrained_model=path + model_name,
|
|
120
|
+
model_type=None,
|
|
121
|
+
nchan=n_channels,
|
|
122
|
+
) # diam_mean=30.0,
|
|
123
|
+
if scale is None:
|
|
124
|
+
scale_model = model.diam_mean / model.diam_labels
|
|
125
|
+
else:
|
|
126
|
+
scale_model = scale * model.diam_mean / model.diam_labels
|
|
127
|
+
|
|
128
|
+
print(f"Cell size in model: {model.diam_mean} pixels...")
|
|
129
|
+
print(f"Cell size in training set: {model.diam_labels} pixels...")
|
|
130
|
+
print(f"Rescaling factor to apply: {scale_model}...")
|
|
131
|
+
|
|
132
|
+
print(f"Cellpose model {model_name} successfully loaded...")
|
|
133
|
+
return model, scale_model
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
def color_from_status(status, recently_modified=False):
|
|
2
|
+
|
|
3
|
+
if not recently_modified:
|
|
4
|
+
if status == 0:
|
|
5
|
+
return "tab:blue"
|
|
6
|
+
elif status == 1:
|
|
7
|
+
return "tab:red"
|
|
8
|
+
elif status == 2:
|
|
9
|
+
return "yellow"
|
|
10
|
+
else:
|
|
11
|
+
return "k"
|
|
12
|
+
else:
|
|
13
|
+
if status == 0:
|
|
14
|
+
return "tab:cyan"
|
|
15
|
+
elif status == 1:
|
|
16
|
+
return "tab:orange"
|
|
17
|
+
elif status == 2:
|
|
18
|
+
return "tab:olive"
|
|
19
|
+
else:
|
|
20
|
+
return "k"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def color_from_class(cclass, recently_modified=False):
|
|
24
|
+
|
|
25
|
+
if not recently_modified:
|
|
26
|
+
if cclass == 0:
|
|
27
|
+
return "tab:red"
|
|
28
|
+
elif cclass == 1:
|
|
29
|
+
return "tab:blue"
|
|
30
|
+
elif cclass == 2:
|
|
31
|
+
return "yellow"
|
|
32
|
+
else:
|
|
33
|
+
return "k"
|
|
34
|
+
else:
|
|
35
|
+
if cclass == 0:
|
|
36
|
+
return "tab:orange"
|
|
37
|
+
elif cclass == 1:
|
|
38
|
+
return "tab:cyan"
|
|
39
|
+
elif cclass == 2:
|
|
40
|
+
return "tab:olive"
|
|
41
|
+
else:
|
|
42
|
+
return "k"
|