celldetective 1.4.0__py3-none-any.whl → 1.4.1.post1__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/_version.py +1 -1
- celldetective/exceptions.py +11 -0
- celldetective/filters.py +7 -1
- celldetective/gui/InitWindow.py +4 -1
- celldetective/gui/__init__.py +2 -9
- celldetective/gui/about.py +2 -2
- celldetective/gui/base_annotator.py +786 -0
- celldetective/gui/classifier_widget.py +18 -13
- celldetective/gui/configure_new_exp.py +51 -30
- celldetective/gui/control_panel.py +10 -7
- celldetective/gui/{signal_annotator.py → event_annotator.py} +473 -1437
- celldetective/gui/generic_signal_plot.py +2 -1
- celldetective/gui/gui_utils.py +5 -2
- celldetective/gui/help/neighborhood.json +2 -2
- celldetective/gui/layouts.py +21 -11
- celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +3 -1
- celldetective/gui/process_block.py +129 -91
- celldetective/gui/processes/downloader.py +37 -34
- celldetective/gui/processes/measure_cells.py +14 -8
- celldetective/gui/processes/segment_cells.py +438 -314
- celldetective/gui/processes/track_cells.py +12 -13
- celldetective/gui/settings/__init__.py +7 -0
- celldetective/gui/settings/_settings_base.py +70 -0
- celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +35 -91
- celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +28 -81
- celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +1 -1
- celldetective/gui/settings/_settings_segmentation.py +49 -0
- celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +33 -79
- celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +73 -95
- celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +64 -87
- celldetective/gui/styles.py +2 -1
- celldetective/gui/survival_ui.py +1 -1
- celldetective/gui/tableUI.py +25 -0
- celldetective/gui/table_ops/__init__.py +0 -0
- celldetective/gui/table_ops/merge_groups.py +118 -0
- celldetective/gui/viewers.py +3 -5
- celldetective/gui/workers.py +0 -2
- celldetective/io.py +98 -55
- celldetective/links/zenodo.json +145 -144
- celldetective/measure.py +31 -26
- celldetective/preprocessing.py +34 -21
- celldetective/regionprops/_regionprops.py +16 -5
- celldetective/scripts/measure_cells.py +5 -5
- celldetective/scripts/measure_relative.py +16 -11
- celldetective/scripts/segment_cells.py +4 -4
- celldetective/scripts/segment_cells_thresholds.py +3 -3
- celldetective/scripts/track_cells.py +7 -7
- celldetective/scripts/train_segmentation_model.py +10 -1
- celldetective/tracking.py +10 -4
- celldetective/utils.py +59 -58
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/METADATA +1 -1
- celldetective-1.4.1.post1.dist-info/RECORD +123 -0
- tests/gui/__init__.py +0 -0
- tests/gui/test_new_project.py +228 -0
- tests/{test_qt.py → gui/test_project.py} +22 -26
- tests/test_preprocessing.py +2 -2
- celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
- celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
- celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
- celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
- celldetective/models/signal_detection/NucCond/config_input.json +0 -1
- celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
- celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
- celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
- celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
- celldetective/models/signal_detection/NucCond/scores.npy +0 -0
- celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
- celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
- celldetective-1.4.0.dist-info/RECORD +0 -131
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/WHEEL +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.4.0.dist-info → celldetective-1.4.1.post1.dist-info}/top_level.txt +0 -0
celldetective/measure.py
CHANGED
|
@@ -17,6 +17,7 @@ from math import ceil
|
|
|
17
17
|
from skimage.draw import disk as dsk
|
|
18
18
|
from skimage.feature import blob_dog, blob_log
|
|
19
19
|
|
|
20
|
+
from celldetective.exceptions import EmptyQueryError, MissingColumnsError, QueryError
|
|
20
21
|
from celldetective.utils import rename_intensity_column, create_patch_mask, remove_redundant_features, \
|
|
21
22
|
remove_trajectory_measurements, contour_of_instance_segmentation, extract_cols_from_query, step_function, interpolate_nan, _remove_invalid_cols
|
|
22
23
|
from celldetective.preprocessing import field_correction
|
|
@@ -1432,35 +1433,39 @@ def classify_cells_from_query(df, status_attr, query):
|
|
|
1432
1433
|
If the query is invalid or if there are issues with the DataFrame or query syntax, an error message is printed, and `None` is returned.
|
|
1433
1434
|
|
|
1434
1435
|
"""
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
status_attr = 'status_'+status_attr
|
|
1440
|
-
|
|
1436
|
+
|
|
1437
|
+
if not status_attr.startswith("status_"):
|
|
1438
|
+
status_attr = "status_" + status_attr
|
|
1439
|
+
|
|
1441
1440
|
df = df.copy()
|
|
1442
|
-
df.
|
|
1441
|
+
df = df.replace([np.inf, -np.inf, None], np.nan)
|
|
1442
|
+
#df = df.convert_dtypes()
|
|
1443
|
+
|
|
1444
|
+
df.loc[:, status_attr] = 0
|
|
1443
1445
|
df[status_attr] = df[status_attr].astype(float)
|
|
1444
|
-
|
|
1446
|
+
|
|
1445
1447
|
cols = extract_cols_from_query(query)
|
|
1446
|
-
print(f"{cols=}")
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1448
|
+
print(f"The following DataFrame measurements were identified in the query: {cols=}...")
|
|
1449
|
+
|
|
1450
|
+
if query.strip() == "":
|
|
1451
|
+
raise EmptyQueryError("The provided query is empty.")
|
|
1452
|
+
|
|
1453
|
+
missing_cols = [c for c in cols if c not in df.columns]
|
|
1454
|
+
if missing_cols:
|
|
1455
|
+
raise MissingColumnsError(missing_cols)
|
|
1456
|
+
|
|
1457
|
+
try:
|
|
1458
|
+
sub_df = df.dropna(subset=cols)
|
|
1459
|
+
if len(sub_df) > 0:
|
|
1460
|
+
selection = sub_df.query(query).index
|
|
1461
|
+
null_selection = df[df.loc[:, cols].isna().any(axis=1)].index
|
|
1462
|
+
df.loc[null_selection, status_attr] = np.nan
|
|
1463
|
+
df.loc[selection, status_attr] = 1
|
|
1464
|
+
else:
|
|
1465
|
+
df.loc[:, status_attr] = np.nan
|
|
1466
|
+
except Exception as e:
|
|
1467
|
+
raise QueryError(f"The query could not be understood: {e}")
|
|
1468
|
+
|
|
1464
1469
|
return df.copy()
|
|
1465
1470
|
|
|
1466
1471
|
def classify_tracks_from_query(df, event_name, query, irreversible_event=True, unique_state=False, r2_threshold=0.5, percentile_recovery=50):
|
celldetective/preprocessing.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Copright © 2024 Laboratoire Adhesion et Inflammation, Authored by Remy Torro & Ksenija Dervanova.
|
|
3
3
|
"""
|
|
4
|
+
from typing import List
|
|
4
5
|
|
|
5
6
|
from tqdm import tqdm
|
|
6
7
|
import numpy as np
|
|
7
8
|
import os
|
|
8
9
|
from celldetective.io import get_config, get_experiment_wells, interpret_wells_and_positions, extract_well_name_and_number, get_positions_in_well, extract_position_name, get_position_movie_path, load_frames, auto_load_number_of_frames
|
|
9
|
-
from celldetective.utils import interpolate_nan, estimate_unreliable_edge, unpad,
|
|
10
|
+
from celldetective.utils import interpolate_nan, estimate_unreliable_edge, unpad, config_section_to_dict, _extract_channel_indices_from_config, _extract_nbr_channels_from_config, _get_img_num_per_channel
|
|
10
11
|
from celldetective.segmentation import filter_image, threshold_image
|
|
11
12
|
from csbdeep.io import save_tiff_imagej_compatible
|
|
12
13
|
from gc import collect
|
|
@@ -14,7 +15,7 @@ from lmfit import Parameters, Model
|
|
|
14
15
|
import tifffile.tifffile as tiff
|
|
15
16
|
from scipy.ndimage import shift
|
|
16
17
|
|
|
17
|
-
def estimate_background_per_condition(experiment, threshold_on_std=1, well_option='*', target_channel="channel_name", frame_range=[0,5], mode="timeseries", activation_protocol=[['gauss',2],['std',4]], show_progress_per_pos=False, show_progress_per_well=True, offset=None):
|
|
18
|
+
def estimate_background_per_condition(experiment, threshold_on_std=1, well_option='*', target_channel="channel_name", frame_range=[0,5], mode="timeseries", activation_protocol=[['gauss',2],['std',4]], show_progress_per_pos=False, show_progress_per_well=True, offset=None, fix_nan: bool = False):
|
|
18
19
|
|
|
19
20
|
"""
|
|
20
21
|
Estimate the background for each condition in an experiment.
|
|
@@ -71,8 +72,8 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
|
|
|
71
72
|
|
|
72
73
|
config = get_config(experiment)
|
|
73
74
|
wells = get_experiment_wells(experiment)
|
|
74
|
-
len_movie = float(
|
|
75
|
-
movie_prefix =
|
|
75
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
76
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
76
77
|
|
|
77
78
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, "*")
|
|
78
79
|
|
|
@@ -152,6 +153,8 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
|
|
|
152
153
|
if offset is not None:
|
|
153
154
|
#print("The offset is applied to background...")
|
|
154
155
|
background -= offset
|
|
156
|
+
if fix_nan:
|
|
157
|
+
background = interpolate_nan(background.copy().astype(float))
|
|
155
158
|
backgrounds.append({"bg": background, "well": well_path})
|
|
156
159
|
print(f"Background successfully computed for well {well_name}...")
|
|
157
160
|
except Exception as e:
|
|
@@ -179,6 +182,7 @@ def correct_background_model_free(
|
|
|
179
182
|
export = False,
|
|
180
183
|
return_stacks = False,
|
|
181
184
|
movie_prefix=None,
|
|
185
|
+
fix_nan=False,
|
|
182
186
|
activation_protocol=[['gauss',2],['std',4]],
|
|
183
187
|
export_prefix='Corrected',
|
|
184
188
|
**kwargs,
|
|
@@ -247,9 +251,9 @@ def correct_background_model_free(
|
|
|
247
251
|
|
|
248
252
|
config = get_config(experiment)
|
|
249
253
|
wells = get_experiment_wells(experiment)
|
|
250
|
-
len_movie = float(
|
|
254
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
251
255
|
if movie_prefix is None:
|
|
252
|
-
movie_prefix =
|
|
256
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
253
257
|
|
|
254
258
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, position_option)
|
|
255
259
|
channel_indices = _extract_channel_indices_from_config(config, [target_channel])
|
|
@@ -263,7 +267,7 @@ def correct_background_model_free(
|
|
|
263
267
|
well_name, _ = extract_well_name_and_number(well_path)
|
|
264
268
|
|
|
265
269
|
try:
|
|
266
|
-
background = estimate_background_per_condition(experiment, threshold_on_std=threshold_on_std, well_option=int(well_indices[k]), target_channel=target_channel, frame_range=frame_range, mode=mode, show_progress_per_pos=True, show_progress_per_well=False, activation_protocol=activation_protocol, offset=offset)
|
|
270
|
+
background = estimate_background_per_condition(experiment, threshold_on_std=threshold_on_std, well_option=int(well_indices[k]), target_channel=target_channel, frame_range=frame_range, mode=mode, show_progress_per_pos=True, show_progress_per_well=False, activation_protocol=activation_protocol, offset=offset, fix_nan=fix_nan)
|
|
267
271
|
background = background[0]
|
|
268
272
|
background = background['bg']
|
|
269
273
|
except Exception as e:
|
|
@@ -298,6 +302,7 @@ def correct_background_model_free(
|
|
|
298
302
|
clip = clip,
|
|
299
303
|
offset = offset,
|
|
300
304
|
export = export,
|
|
305
|
+
fix_nan=fix_nan,
|
|
301
306
|
activation_protocol = activation_protocol,
|
|
302
307
|
prefix = export_prefix,
|
|
303
308
|
)
|
|
@@ -315,7 +320,7 @@ def correct_background_model_free(
|
|
|
315
320
|
|
|
316
321
|
|
|
317
322
|
|
|
318
|
-
def apply_background_to_stack(stack_path, background, target_channel_index=0, nbr_channels=1, stack_length=45, offset = None, activation_protocol=[['gauss',2],['std',4]], threshold_on_std=1, optimize_option=True, opt_coef_range=(0.95,1.05), opt_coef_nbr=100, operation='divide', clip=False, export=False, prefix="Corrected"):
|
|
323
|
+
def apply_background_to_stack(stack_path, background, target_channel_index=0, nbr_channels=1, stack_length=45, offset = None, activation_protocol=[['gauss',2],['std',4]], threshold_on_std=1, optimize_option=True, opt_coef_range=(0.95,1.05), opt_coef_nbr=100, operation='divide', clip=False, export=False, prefix="Corrected", fix_nan=False):
|
|
319
324
|
|
|
320
325
|
"""
|
|
321
326
|
Apply background correction to an image stack.
|
|
@@ -420,7 +425,7 @@ def apply_background_to_stack(stack_path, background, target_channel_index=0, nb
|
|
|
420
425
|
loss.append(s)
|
|
421
426
|
|
|
422
427
|
c = coefficients[np.argmin(loss)]
|
|
423
|
-
print(f"
|
|
428
|
+
print(f"IFD {i}; optimal coefficient: {c}...")
|
|
424
429
|
# if c==min(coefficients) or c==max(coefficients):
|
|
425
430
|
# print('Warning... The optimal coefficient is beyond the range provided... Please adjust your coefficient range...')
|
|
426
431
|
else:
|
|
@@ -430,16 +435,20 @@ def apply_background_to_stack(stack_path, background, target_channel_index=0, nb
|
|
|
430
435
|
correction = np.divide(target_img, background*c, where=background==background)
|
|
431
436
|
correction[background!=background] = np.nan
|
|
432
437
|
correction[target_img!=target_img] = np.nan
|
|
433
|
-
fill_val = 1.0
|
|
434
438
|
|
|
435
439
|
elif operation=="subtract":
|
|
436
440
|
correction = np.subtract(target_img, background*c, where=background==background)
|
|
437
441
|
correction[background!=background] = np.nan
|
|
438
442
|
correction[target_img!=target_img] = np.nan
|
|
439
|
-
fill_val = 0.0
|
|
440
443
|
if clip:
|
|
441
444
|
correction[correction<=0.] = 0.
|
|
445
|
+
else:
|
|
446
|
+
print("Operation not supported... Abort.")
|
|
447
|
+
return
|
|
442
448
|
|
|
449
|
+
correction[~np.isfinite(correction)] = np.nan
|
|
450
|
+
if fix_nan:
|
|
451
|
+
correction = interpolate_nan(correction.copy())
|
|
443
452
|
frames[:,:,target_channel_index] = correction
|
|
444
453
|
corrected_stack.append(frames)
|
|
445
454
|
|
|
@@ -771,9 +780,9 @@ def correct_background_model(
|
|
|
771
780
|
|
|
772
781
|
config = get_config(experiment)
|
|
773
782
|
wells = get_experiment_wells(experiment)
|
|
774
|
-
len_movie = float(
|
|
783
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
775
784
|
if movie_prefix is None:
|
|
776
|
-
movie_prefix =
|
|
785
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
777
786
|
|
|
778
787
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, position_option)
|
|
779
788
|
channel_indices = _extract_channel_indices_from_config(config, [target_channel])
|
|
@@ -793,6 +802,10 @@ def correct_background_model(
|
|
|
793
802
|
for pidx,pos_path in enumerate(tqdm(selection, disable=not show_progress_per_pos)):
|
|
794
803
|
|
|
795
804
|
stack_path = get_position_movie_path(pos_path, prefix=movie_prefix)
|
|
805
|
+
if stack_path is None:
|
|
806
|
+
print(f"No stack could be found in {pos_path}... Skip...")
|
|
807
|
+
continue
|
|
808
|
+
|
|
796
809
|
print(f'Applying the correction to position {extract_position_name(pos_path)}...')
|
|
797
810
|
len_movie_auto = auto_load_number_of_frames(stack_path)
|
|
798
811
|
if len_movie_auto is not None:
|
|
@@ -909,7 +922,7 @@ def fit_and_apply_model_background_to_stack(stack_path,
|
|
|
909
922
|
frames = load_frames(list(np.arange(i,(i+nbr_channels))), stack_path, normalize_input=False).astype(float)
|
|
910
923
|
target_img = frames[:,:,target_channel_index].copy()
|
|
911
924
|
|
|
912
|
-
correction = field_correction(target_img,
|
|
925
|
+
correction = field_correction(target_img, threshold=threshold_on_std, operation=operation, model=model, clip=clip, activation_protocol=activation_protocol)
|
|
913
926
|
frames[:,:,target_channel_index] = correction.copy()
|
|
914
927
|
|
|
915
928
|
if return_stacks:
|
|
@@ -930,7 +943,7 @@ def fit_and_apply_model_background_to_stack(stack_path,
|
|
|
930
943
|
frames = load_frames(list(np.arange(i,(i+nbr_channels))), stack_path, normalize_input=False).astype(float)
|
|
931
944
|
target_img = frames[:,:,target_channel_index].copy()
|
|
932
945
|
|
|
933
|
-
correction = field_correction(target_img,
|
|
946
|
+
correction = field_correction(target_img, threshold=threshold_on_std, operation=operation, model=model, clip=clip, activation_protocol=activation_protocol)
|
|
934
947
|
frames[:,:,target_channel_index] = correction.copy()
|
|
935
948
|
|
|
936
949
|
corrected_stack.append(frames)
|
|
@@ -945,7 +958,7 @@ def fit_and_apply_model_background_to_stack(stack_path,
|
|
|
945
958
|
else:
|
|
946
959
|
return None
|
|
947
960
|
|
|
948
|
-
def field_correction(img,
|
|
961
|
+
def field_correction(img: np.ndarray, threshold: float = 1, operation: str = 'divide', model: str = 'paraboloid', clip: bool = False, return_bg: bool = False, activation_protocol: List[List] = [['gauss',2],['std',4]]):
|
|
949
962
|
|
|
950
963
|
"""
|
|
951
964
|
Apply field correction to an image.
|
|
@@ -958,8 +971,8 @@ def field_correction(img, threshold_on_std=1, operation='divide', model='parabol
|
|
|
958
971
|
----------
|
|
959
972
|
img : numpy.ndarray
|
|
960
973
|
The input image to be corrected.
|
|
961
|
-
|
|
962
|
-
The threshold value on the
|
|
974
|
+
threshold : float, optional
|
|
975
|
+
The threshold value on the image, post activation protocol for masking out cells (default is 1).
|
|
963
976
|
operation : str, optional
|
|
964
977
|
The operation to apply for background correction, either 'divide' or 'subtract' (default is 'divide').
|
|
965
978
|
model : str, optional
|
|
@@ -997,7 +1010,7 @@ def field_correction(img, threshold_on_std=1, operation='divide', model='parabol
|
|
|
997
1010
|
|
|
998
1011
|
std_frame = filter_image(target_copy,filters=activation_protocol)
|
|
999
1012
|
edge = estimate_unreliable_edge(activation_protocol)
|
|
1000
|
-
mask = threshold_image(std_frame,
|
|
1013
|
+
mask = threshold_image(std_frame, threshold, np.inf, foreground_value=1, edge_exclusion=edge).astype(int)
|
|
1001
1014
|
background = fit_background_model(img, cell_masks=mask, model=model, edge_exclusion=edge)
|
|
1002
1015
|
|
|
1003
1016
|
if operation=="divide":
|
|
@@ -1086,9 +1099,9 @@ def correct_channel_offset(
|
|
|
1086
1099
|
|
|
1087
1100
|
config = get_config(experiment)
|
|
1088
1101
|
wells = get_experiment_wells(experiment)
|
|
1089
|
-
len_movie = float(
|
|
1102
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
1090
1103
|
if movie_prefix is None:
|
|
1091
|
-
movie_prefix =
|
|
1104
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
1092
1105
|
|
|
1093
1106
|
well_indices, position_indices = interpret_wells_and_positions(experiment, well_option, position_option)
|
|
1094
1107
|
channel_indices = _extract_channel_indices_from_config(config, [target_channel])
|
|
@@ -59,6 +59,22 @@ COL_DTYPES = {
|
|
|
59
59
|
OBJECT_COLUMNS = [col for col, dtype in COL_DTYPES.items() if dtype == object]
|
|
60
60
|
PROP_VALS = set(PROPS.values())
|
|
61
61
|
|
|
62
|
+
_require_intensity_image = (
|
|
63
|
+
'image_intensity',
|
|
64
|
+
'intensity_max',
|
|
65
|
+
'intensity_mean',
|
|
66
|
+
'intensity_median',
|
|
67
|
+
'intensity_min',
|
|
68
|
+
'intensity_std',
|
|
69
|
+
'moments_weighted',
|
|
70
|
+
'moments_weighted_central',
|
|
71
|
+
'centroid_weighted',
|
|
72
|
+
'centroid_weighted_local',
|
|
73
|
+
'moments_weighted_hu',
|
|
74
|
+
'moments_weighted_normalized',
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
62
78
|
class CustomRegionProps(RegionProperties):
|
|
63
79
|
|
|
64
80
|
"""
|
|
@@ -79,11 +95,6 @@ class CustomRegionProps(RegionProperties):
|
|
|
79
95
|
assert len(self.channel_names)==self._intensity_image.shape[-1],'Mismatch between provided channel names and the number of channels in the image...'
|
|
80
96
|
|
|
81
97
|
if attr == "__setstate__":
|
|
82
|
-
# When deserializing this object with pickle, `__setstate__`
|
|
83
|
-
# is accessed before any other attributes like `self._intensity_image`
|
|
84
|
-
# are available which leads to a RecursionError when trying to
|
|
85
|
-
# access them later on in this function. So guard against this by
|
|
86
|
-
# provoking the default AttributeError (gh-6465).
|
|
87
98
|
return self.__getattribute__(attr)
|
|
88
99
|
|
|
89
100
|
if self._intensity_image is None and attr in _require_intensity_image:
|
|
@@ -6,7 +6,7 @@ import argparse
|
|
|
6
6
|
import os
|
|
7
7
|
import json
|
|
8
8
|
from celldetective.io import auto_load_number_of_frames, load_frames, fix_missing_labels, locate_labels, extract_position_name
|
|
9
|
-
from celldetective.utils import extract_experiment_channels,
|
|
9
|
+
from celldetective.utils import extract_experiment_channels, config_section_to_dict, _get_img_num_per_channel, extract_experiment_channels
|
|
10
10
|
from celldetective.utils import _remove_invalid_cols, remove_redundant_features, remove_trajectory_measurements, _extract_coordinates_from_features
|
|
11
11
|
from celldetective.measure import drop_tonal_features, measure_features, measure_isotropic_intensity, center_of_mass_to_abs_coordinates, measure_radial_distance_to_center
|
|
12
12
|
from pathlib import Path, PurePath
|
|
@@ -55,10 +55,10 @@ print("Configuration file: ",config)
|
|
|
55
55
|
print(f"Population: {mode}...")
|
|
56
56
|
|
|
57
57
|
# from exp config fetch spatial calib, channel names
|
|
58
|
-
movie_prefix =
|
|
59
|
-
spatial_calibration = float(
|
|
60
|
-
time_calibration = float(
|
|
61
|
-
len_movie = float(
|
|
58
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
59
|
+
spatial_calibration = float(config_section_to_dict(config, "MovieSettings")["pxtoum"])
|
|
60
|
+
time_calibration = float(config_section_to_dict(config, "MovieSettings")["frametomin"])
|
|
61
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
62
62
|
channel_names, channel_indices = extract_experiment_channels(expfolder)
|
|
63
63
|
nbr_channels = len(channel_names)
|
|
64
64
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
3
|
from celldetective.relative_measurements import measure_pair_signals_at_position, extract_neighborhoods_from_pickles
|
|
4
|
-
from celldetective.utils import
|
|
4
|
+
from celldetective.utils import config_section_to_dict, extract_experiment_channels
|
|
5
5
|
from celldetective.io import get_experiment_populations
|
|
6
6
|
|
|
7
7
|
from pathlib import Path, PurePath
|
|
@@ -31,10 +31,10 @@ assert os.path.exists(config), 'The configuration file for the experiment could
|
|
|
31
31
|
print("Configuration file: ", config)
|
|
32
32
|
|
|
33
33
|
# from exp config fetch spatial calib, channel names
|
|
34
|
-
movie_prefix =
|
|
35
|
-
spatial_calibration = float(
|
|
36
|
-
time_calibration = float(
|
|
37
|
-
len_movie = float(
|
|
34
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
35
|
+
spatial_calibration = float(config_section_to_dict(config, "MovieSettings")["pxtoum"])
|
|
36
|
+
time_calibration = float(config_section_to_dict(config, "MovieSettings")["frametomin"])
|
|
37
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
38
38
|
channel_names, channel_indices = extract_experiment_channels(expfolder)
|
|
39
39
|
nbr_channels = len(channel_names)
|
|
40
40
|
|
|
@@ -79,9 +79,14 @@ if len(all_df_pairs)>1:
|
|
|
79
79
|
df_pairs = pd.merge(df_pairs.round(decimals=6), all_df_pairs[i].round(decimals=6), how="outer", on=cols)
|
|
80
80
|
elif len(all_df_pairs)==1:
|
|
81
81
|
df_pairs = all_df_pairs[0]
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
else:
|
|
83
|
+
df_pairs = None
|
|
84
|
+
print('No dataframe could be computed for the pairs...')
|
|
85
|
+
|
|
86
|
+
if df_pairs is not None:
|
|
87
|
+
print('Writing table...')
|
|
88
|
+
if "reference_population" in list(df_pairs.columns) and "neighbor_population" in list(df_pairs.columns):
|
|
89
|
+
df_pairs = df_pairs.sort_values(by=['reference_population', 'neighbor_population', 'REFERENCE_ID', 'NEIGHBOR_ID', 'FRAME'])
|
|
90
|
+
df_pairs.to_csv(previous_pair_table_path, index=False)
|
|
91
|
+
print('Done.')
|
|
92
|
+
|
|
@@ -7,7 +7,7 @@ import datetime
|
|
|
7
7
|
import os
|
|
8
8
|
import json
|
|
9
9
|
from celldetective.io import locate_segmentation_model, auto_load_number_of_frames, extract_position_name, _load_frames_to_segment, _check_label_dims
|
|
10
|
-
from celldetective.utils import _prep_stardist_model, _prep_cellpose_model, _rescale_labels, _segment_image_with_stardist_model,_segment_image_with_cellpose_model,_get_normalize_kwargs_from_config, _estimate_scale_factor, _extract_channel_indices_from_config,
|
|
10
|
+
from celldetective.utils import _prep_stardist_model, _prep_cellpose_model, _rescale_labels, _segment_image_with_stardist_model,_segment_image_with_cellpose_model,_get_normalize_kwargs_from_config, _estimate_scale_factor, _extract_channel_indices_from_config, config_section_to_dict, _extract_nbr_channels_from_config, _get_img_num_per_channel
|
|
11
11
|
from pathlib import Path, PurePath
|
|
12
12
|
from glob import glob
|
|
13
13
|
from shutil import rmtree
|
|
@@ -91,9 +91,9 @@ normalize_kwargs = _get_normalize_kwargs_from_config(input_config)
|
|
|
91
91
|
|
|
92
92
|
model_type = input_config['model_type']
|
|
93
93
|
|
|
94
|
-
movie_prefix =
|
|
95
|
-
spatial_calibration = float(
|
|
96
|
-
len_movie = float(
|
|
94
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
95
|
+
spatial_calibration = float(config_section_to_dict(config, "MovieSettings")["pxtoum"])
|
|
96
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
97
97
|
|
|
98
98
|
# Try to find the file
|
|
99
99
|
try:
|
|
@@ -7,7 +7,7 @@ import os
|
|
|
7
7
|
import json
|
|
8
8
|
from celldetective.io import auto_load_number_of_frames, load_frames, extract_position_name
|
|
9
9
|
from celldetective.segmentation import segment_frame_from_thresholds
|
|
10
|
-
from celldetective.utils import _extract_channel_indices_from_config,
|
|
10
|
+
from celldetective.utils import _extract_channel_indices_from_config, config_section_to_dict, _extract_nbr_channels_from_config, _get_img_num_per_channel, extract_experiment_channels
|
|
11
11
|
from pathlib import Path, PurePath
|
|
12
12
|
from glob import glob
|
|
13
13
|
from shutil import rmtree
|
|
@@ -70,8 +70,8 @@ print(f'Required channels: {required_channels} located at channel indices {chann
|
|
|
70
70
|
|
|
71
71
|
threshold_instructions.update({'target_channel': channel_indices[0]})
|
|
72
72
|
|
|
73
|
-
movie_prefix =
|
|
74
|
-
len_movie = float(
|
|
73
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
74
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
75
75
|
channel_names, channel_indices = extract_experiment_channels(expfolder)
|
|
76
76
|
threshold_instructions.update({'channel_names': channel_names})
|
|
77
77
|
|
|
@@ -9,7 +9,7 @@ import json
|
|
|
9
9
|
from celldetective.io import _load_frames_to_measure, auto_load_number_of_frames, interpret_tracking_configuration, \
|
|
10
10
|
extract_position_name, \
|
|
11
11
|
locate_labels
|
|
12
|
-
from celldetective.utils import _mask_intensity_measurements, extract_experiment_channels,
|
|
12
|
+
from celldetective.utils import _mask_intensity_measurements, extract_experiment_channels, config_section_to_dict, _get_img_num_per_channel, extract_experiment_channels
|
|
13
13
|
from celldetective.measure import drop_tonal_features, measure_features
|
|
14
14
|
from celldetective.tracking import track
|
|
15
15
|
from pathlib import Path, PurePath
|
|
@@ -67,12 +67,12 @@ print("Configuration file: ",config)
|
|
|
67
67
|
print(f"Population: {mode}...")
|
|
68
68
|
|
|
69
69
|
# from exp config fetch spatial calib, channel names
|
|
70
|
-
movie_prefix =
|
|
71
|
-
spatial_calibration = float(
|
|
72
|
-
time_calibration = float(
|
|
73
|
-
len_movie = float(
|
|
74
|
-
shape_x = int(
|
|
75
|
-
shape_y = int(
|
|
70
|
+
movie_prefix = config_section_to_dict(config, "MovieSettings")["movie_prefix"]
|
|
71
|
+
spatial_calibration = float(config_section_to_dict(config, "MovieSettings")["pxtoum"])
|
|
72
|
+
time_calibration = float(config_section_to_dict(config, "MovieSettings")["frametomin"])
|
|
73
|
+
len_movie = float(config_section_to_dict(config, "MovieSettings")["len_movie"])
|
|
74
|
+
shape_x = int(config_section_to_dict(config, "MovieSettings")["shape_x"])
|
|
75
|
+
shape_y = int(config_section_to_dict(config, "MovieSettings")["shape_y"])
|
|
76
76
|
|
|
77
77
|
channel_names, channel_indices = extract_experiment_channels(expfolder)
|
|
78
78
|
nbr_channels = len(channel_names)
|
|
@@ -232,7 +232,16 @@ elif model_type=='stardist':
|
|
|
232
232
|
'normalization_clip': normalization_clip, 'normalization_values': normalization_values,
|
|
233
233
|
'model_type': 'stardist', 'spatial_calibration': spatial_calibration,'cell_size_um': median_size * spatial_calibration, 'dataset': {'train': files_train, 'validation': files_val}}
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
def make_json_safe(obj):
|
|
236
|
+
if isinstance(obj, np.ndarray):
|
|
237
|
+
return obj.tolist() # convert to list
|
|
238
|
+
if isinstance(obj, (np.int64, np.int32)):
|
|
239
|
+
return int(obj)
|
|
240
|
+
if isinstance(obj, (np.float32, np.float64)):
|
|
241
|
+
return float(obj)
|
|
242
|
+
return str(obj) # fallback
|
|
243
|
+
|
|
244
|
+
json_input_config = json.dumps(config_inputs, indent=4, default=make_json_safe)
|
|
236
245
|
with open(os.sep.join([target_directory, model_name, "config_input.json"]), "w") as outfile:
|
|
237
246
|
outfile.write(json_input_config)
|
|
238
247
|
|
celldetective/tracking.py
CHANGED
|
@@ -189,7 +189,8 @@ def track(labels, configuration=None, stack=None, spatial_calibration=1, feature
|
|
|
189
189
|
|
|
190
190
|
if clean_trajectories_kwargs is not None:
|
|
191
191
|
df = clean_trajectories(df.copy(),**clean_trajectories_kwargs)
|
|
192
|
-
|
|
192
|
+
|
|
193
|
+
df.loc[df["status_firstdetection"].isna(), "status_firstdetection"] = 0
|
|
193
194
|
df['ID'] = np.arange(len(df)).astype(int)
|
|
194
195
|
|
|
195
196
|
invalid_cols = [c for c in list(df.columns) if c.startswith('Unnamed')]
|
|
@@ -1002,7 +1003,10 @@ def write_first_detection_class(df, img_shape=None, edge_threshold=20, column_la
|
|
|
1002
1003
|
positions_x = track_group[column_labels['x']].values
|
|
1003
1004
|
positions_y = track_group[column_labels['y']].values
|
|
1004
1005
|
dt = 1
|
|
1005
|
-
|
|
1006
|
+
|
|
1007
|
+
timeline = track_group['FRAME'].to_numpy()
|
|
1008
|
+
status = np.ones_like(timeline)
|
|
1009
|
+
|
|
1006
1010
|
# Initialize
|
|
1007
1011
|
cclass = 2; t_first = np.nan;
|
|
1008
1012
|
|
|
@@ -1023,16 +1027,18 @@ def write_first_detection_class(df, img_shape=None, edge_threshold=20, column_la
|
|
|
1023
1027
|
t_first = float(t_first) - float(dt)
|
|
1024
1028
|
if t_first==0:
|
|
1025
1029
|
t_first += 0.01
|
|
1026
|
-
|
|
1030
|
+
|
|
1027
1031
|
if edge_test:
|
|
1028
1032
|
cclass = 2
|
|
1029
1033
|
# switch to class 2 but keep time/status information
|
|
1030
1034
|
else:
|
|
1031
1035
|
t_first = -1
|
|
1032
1036
|
cclass = 2
|
|
1033
|
-
|
|
1037
|
+
|
|
1038
|
+
status[timeline < t_first] = 0.
|
|
1034
1039
|
df.loc[indices, 'class_firstdetection'] = cclass
|
|
1035
1040
|
df.loc[indices, 't_firstdetection'] = t_first
|
|
1041
|
+
df.loc[indices, 'status_firstdetection'] = status
|
|
1036
1042
|
|
|
1037
1043
|
return df
|
|
1038
1044
|
|