celldetective 1.1.0__py3-none-any.whl → 1.1.1__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/__main__.py +5 -19
- celldetective/extra_properties.py +62 -52
- celldetective/gui/control_panel.py +5 -0
- celldetective/gui/layouts.py +1 -0
- celldetective/gui/measurement_options.py +13 -109
- celldetective/gui/plot_signals_ui.py +1 -0
- celldetective/gui/process_block.py +1 -1
- celldetective/gui/survival_ui.py +7 -1
- celldetective/gui/tableUI.py +280 -28
- celldetective/gui/thresholds_gui.py +7 -5
- celldetective/gui/viewers.py +169 -22
- celldetective/io.py +14 -5
- celldetective/measure.py +13 -238
- celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +29 -0
- celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
- celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +37 -0
- celldetective/neighborhood.py +4 -1
- celldetective/preprocessing.py +483 -143
- celldetective/scripts/segment_cells.py +15 -4
- celldetective/scripts/train_segmentation_model.py +35 -34
- celldetective/segmentation.py +14 -9
- celldetective/signals.py +13 -231
- celldetective/tracking.py +2 -1
- celldetective/utils.py +437 -26
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/METADATA +1 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/RECORD +32 -28
- tests/test_preprocessing.py +37 -0
- tests/test_utils.py +48 -1
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/LICENSE +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/WHEEL +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.0.dist-info → celldetective-1.1.1.dist-info}/top_level.txt +0 -0
celldetective/preprocessing.py
CHANGED
|
@@ -6,65 +6,69 @@ from tqdm import tqdm
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import os
|
|
8
8
|
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 ConfigSectionMap, _extract_channel_indices_from_config, _extract_nbr_channels_from_config, _get_img_num_per_channel
|
|
9
|
+
from celldetective.utils import estimate_unreliable_edge, unpad, mask_edges, ConfigSectionMap, _extract_channel_indices_from_config, _extract_nbr_channels_from_config, _get_img_num_per_channel
|
|
10
10
|
from celldetective.filters import std_filter, gauss_filter
|
|
11
|
+
from celldetective.segmentation import filter_image, threshold_image
|
|
11
12
|
from stardist import fill_label_holes
|
|
12
13
|
from csbdeep.io import save_tiff_imagej_compatible
|
|
13
14
|
from gc import collect
|
|
14
15
|
from lmfit import Parameters, Model, models
|
|
15
|
-
import
|
|
16
|
+
import tifffile.tifffile as tiff
|
|
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", show_progress_per_pos=False, show_progress_per_well=True):
|
|
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):
|
|
18
19
|
|
|
19
20
|
"""
|
|
20
|
-
Estimate the background
|
|
21
|
+
Estimate the background for each condition in an experiment.
|
|
21
22
|
|
|
22
|
-
This function calculates the background
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
across positions within each well.
|
|
23
|
+
This function calculates the background for each well within
|
|
24
|
+
a given experiment by processing image frames using a specified activation
|
|
25
|
+
protocol. It supports time-series and tile-based modes for background
|
|
26
|
+
estimation.
|
|
27
27
|
|
|
28
28
|
Parameters
|
|
29
29
|
----------
|
|
30
|
-
experiment :
|
|
31
|
-
The
|
|
30
|
+
experiment : str
|
|
31
|
+
The path to the experiment directory.
|
|
32
32
|
threshold_on_std : float, optional
|
|
33
|
-
The threshold
|
|
34
|
-
well_option : str,
|
|
35
|
-
The
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- list of int : Select multiple wells by their indices. Defaults to '*'.
|
|
39
|
-
target_channel : str
|
|
40
|
-
The specific channel to be analyzed.
|
|
33
|
+
The threshold value on the standard deviation for masking (default is 1).
|
|
34
|
+
well_option : str, optional
|
|
35
|
+
The option to select specific wells (default is '*').
|
|
36
|
+
target_channel : str, optional
|
|
37
|
+
The name of the target channel for background estimation (default is "channel_name").
|
|
41
38
|
frame_range : list of int, optional
|
|
42
|
-
The range of frames to
|
|
43
|
-
mode :
|
|
44
|
-
The mode of
|
|
39
|
+
The range of frames to consider for background estimation (default is [0, 5]).
|
|
40
|
+
mode : str, optional
|
|
41
|
+
The mode of background estimation, either "timeseries" or "tiles" (default is "timeseries").
|
|
42
|
+
activation_protocol : list of list, optional
|
|
43
|
+
The activation protocol consisting of filters and their respective parameters (default is [['gauss', 2], ['std', 4]]).
|
|
45
44
|
show_progress_per_pos : bool, optional
|
|
46
|
-
|
|
45
|
+
Whether to show progress for each position (default is False).
|
|
47
46
|
show_progress_per_well : bool, optional
|
|
48
|
-
|
|
47
|
+
Whether to show progress for each well (default is True).
|
|
49
48
|
|
|
50
49
|
Returns
|
|
51
50
|
-------
|
|
52
|
-
|
|
53
|
-
A list of dictionaries, each containing
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
list of dict
|
|
52
|
+
A list of dictionaries, each containing the background image (`bg`) and the corresponding well path (`well`).
|
|
53
|
+
|
|
54
|
+
See Also
|
|
55
|
+
--------
|
|
56
|
+
estimate_unreliable_edge : Estimates the unreliable edge value from the activation protocol.
|
|
57
|
+
threshold_image : Thresholds an image based on the specified criteria.
|
|
58
|
+
|
|
59
|
+
Notes
|
|
60
|
+
-----
|
|
61
|
+
This function assumes that the experiment directory structure and the configuration
|
|
62
|
+
files follow a specific format expected by the helper functions used within.
|
|
58
63
|
|
|
59
64
|
Examples
|
|
60
65
|
--------
|
|
61
|
-
>>>
|
|
62
|
-
>>> backgrounds = estimate_background_per_condition(
|
|
63
|
-
>>>
|
|
64
|
-
|
|
66
|
+
>>> experiment_path = "path/to/experiment"
|
|
67
|
+
>>> backgrounds = estimate_background_per_condition(experiment_path, threshold_on_std=1.5, target_channel="GFP", frame_range=[0, 10], mode="tiles")
|
|
68
|
+
>>> for bg in backgrounds:
|
|
69
|
+
... print(bg["well"], bg["bg"].shape)
|
|
65
70
|
"""
|
|
66
71
|
|
|
67
|
-
|
|
68
72
|
config = get_config(experiment)
|
|
69
73
|
wells = get_experiment_wells(experiment)
|
|
70
74
|
len_movie = float(ConfigSectionMap(config,"MovieSettings")["len_movie"])
|
|
@@ -104,12 +108,10 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
|
|
|
104
108
|
frame_mean = np.nanmean(frames, axis=0)
|
|
105
109
|
|
|
106
110
|
frame = frame_mean.copy().astype(float)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
mask =
|
|
111
|
-
mask = fill_label_holes(mask)
|
|
112
|
-
frame[np.where(mask==1)] = np.nan
|
|
111
|
+
std_frame = filter_image(frame.copy(),filters=activation_protocol)
|
|
112
|
+
edge = estimate_unreliable_edge(activation_protocol)
|
|
113
|
+
mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
|
|
114
|
+
frame[np.where(mask.astype(int)==1)] = np.nan
|
|
113
115
|
|
|
114
116
|
elif mode=="tiles":
|
|
115
117
|
|
|
@@ -123,12 +125,10 @@ def estimate_background_per_condition(experiment, threshold_on_std=1, well_optio
|
|
|
123
125
|
continue
|
|
124
126
|
|
|
125
127
|
f = frames[i].copy()
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
mask =
|
|
130
|
-
mask = fill_label_holes(mask)
|
|
131
|
-
f[np.where(mask==1)] = np.nan
|
|
128
|
+
std_frame = filter_image(f.copy(),filters=activation_protocol)
|
|
129
|
+
edge = estimate_unreliable_edge(activation_protocol)
|
|
130
|
+
mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
|
|
131
|
+
f[np.where(mask.astype(int)==1)] = np.nan
|
|
132
132
|
|
|
133
133
|
frames[i,:,:] = f
|
|
134
134
|
|
|
@@ -161,6 +161,7 @@ def correct_background_model_free(
|
|
|
161
161
|
export = False,
|
|
162
162
|
return_stacks = False,
|
|
163
163
|
movie_prefix=None,
|
|
164
|
+
activation_protocol=[['gauss',2],['std',4]],
|
|
164
165
|
export_prefix='Corrected',
|
|
165
166
|
**kwargs,
|
|
166
167
|
):
|
|
@@ -244,7 +245,7 @@ def correct_background_model_free(
|
|
|
244
245
|
well_name, _ = extract_well_name_and_number(well_path)
|
|
245
246
|
|
|
246
247
|
try:
|
|
247
|
-
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)
|
|
248
|
+
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)
|
|
248
249
|
background = background[0]
|
|
249
250
|
background = background['bg']
|
|
250
251
|
except Exception as e:
|
|
@@ -273,6 +274,7 @@ def correct_background_model_free(
|
|
|
273
274
|
operation=operation,
|
|
274
275
|
clip=clip,
|
|
275
276
|
export=export,
|
|
277
|
+
activation_protocol=activation_protocol,
|
|
276
278
|
prefix=export_prefix,
|
|
277
279
|
)
|
|
278
280
|
print('Correction successful.')
|
|
@@ -287,7 +289,7 @@ def correct_background_model_free(
|
|
|
287
289
|
|
|
288
290
|
|
|
289
291
|
|
|
290
|
-
def apply_background_to_stack(stack_path, background, target_channel_index=0, nbr_channels=1, stack_length=45, 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"):
|
|
292
|
+
def apply_background_to_stack(stack_path, background, target_channel_index=0, nbr_channels=1, stack_length=45,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"):
|
|
291
293
|
|
|
292
294
|
"""
|
|
293
295
|
Apply background correction to an image stack.
|
|
@@ -366,16 +368,19 @@ def apply_background_to_stack(stack_path, background, target_channel_index=0, nb
|
|
|
366
368
|
if optimize_option:
|
|
367
369
|
|
|
368
370
|
target_copy = target_img.copy()
|
|
369
|
-
|
|
370
|
-
std_frame =
|
|
371
|
-
|
|
372
|
-
mask =
|
|
373
|
-
target_copy[np.where(mask==1)] = np.nan
|
|
371
|
+
|
|
372
|
+
std_frame = filter_image(target_copy.copy(),filters=activation_protocol)
|
|
373
|
+
edge = estimate_unreliable_edge(activation_protocol)
|
|
374
|
+
mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge)
|
|
375
|
+
target_copy[np.where(mask.astype(int)==1)] = np.nan
|
|
376
|
+
|
|
374
377
|
loss = []
|
|
375
378
|
|
|
376
379
|
# brute-force regression, could do gradient descent instead
|
|
377
380
|
for c in coefficients:
|
|
378
|
-
|
|
381
|
+
target_crop = unpad(target_copy,edge)
|
|
382
|
+
bg_crop = unpad(background, edge)
|
|
383
|
+
diff = np.subtract(target_crop, c*bg_crop, where=target_crop==target_crop)
|
|
379
384
|
s = np.sum(np.abs(diff, where=diff==diff), where=diff==diff)
|
|
380
385
|
loss.append(s)
|
|
381
386
|
c = coefficients[np.argmin(loss)]
|
|
@@ -410,40 +415,134 @@ def apply_background_to_stack(stack_path, background, target_channel_index=0, nb
|
|
|
410
415
|
return corrected_stack
|
|
411
416
|
|
|
412
417
|
def paraboloid(x, y, a, b, c, d, e, g):
|
|
418
|
+
|
|
419
|
+
"""
|
|
420
|
+
Compute the value of a 2D paraboloid function.
|
|
421
|
+
|
|
422
|
+
This function evaluates a paraboloid defined by the equation:
|
|
423
|
+
`a * x ** 2 + b * y ** 2 + c * x * y + d * x + e * y + g`.
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
x : float or ndarray
|
|
428
|
+
The x-coordinate(s) at which to evaluate the paraboloid.
|
|
429
|
+
y : float or ndarray
|
|
430
|
+
The y-coordinate(s) at which to evaluate the paraboloid.
|
|
431
|
+
a : float
|
|
432
|
+
The coefficient of the x^2 term.
|
|
433
|
+
b : float
|
|
434
|
+
The coefficient of the y^2 term.
|
|
435
|
+
c : float
|
|
436
|
+
The coefficient of the x*y term.
|
|
437
|
+
d : float
|
|
438
|
+
The coefficient of the x term.
|
|
439
|
+
e : float
|
|
440
|
+
The coefficient of the y term.
|
|
441
|
+
g : float
|
|
442
|
+
The constant term.
|
|
443
|
+
|
|
444
|
+
Returns
|
|
445
|
+
-------
|
|
446
|
+
float or ndarray
|
|
447
|
+
The value of the paraboloid at the given (x, y) coordinates. If `x` and
|
|
448
|
+
`y` are arrays, the result is an array of the same shape.
|
|
449
|
+
|
|
450
|
+
Examples
|
|
451
|
+
--------
|
|
452
|
+
>>> paraboloid(1, 2, 1, 1, 0, 0, 0, 0)
|
|
453
|
+
5
|
|
454
|
+
>>> paraboloid(np.array([1, 2]), np.array([3, 4]), 1, 1, 0, 0, 0, 0)
|
|
455
|
+
array([10, 20])
|
|
456
|
+
|
|
457
|
+
Notes
|
|
458
|
+
-----
|
|
459
|
+
The paraboloid function is a quadratic function in two variables, commonly used
|
|
460
|
+
to model surfaces in three-dimensional space.
|
|
461
|
+
"""
|
|
462
|
+
|
|
413
463
|
return a * x ** 2 + b * y ** 2 + c * x * y + d * x + e * y + g
|
|
414
464
|
|
|
415
465
|
|
|
416
466
|
def plane(x, y, a, b, c):
|
|
467
|
+
|
|
468
|
+
"""
|
|
469
|
+
Compute the value of a plane function.
|
|
470
|
+
|
|
471
|
+
This function evaluates a plane defined by the equation:
|
|
472
|
+
`a * x + b * y + c`.
|
|
473
|
+
|
|
474
|
+
Parameters
|
|
475
|
+
----------
|
|
476
|
+
x : float or ndarray
|
|
477
|
+
The x-coordinate(s) at which to evaluate the plane.
|
|
478
|
+
y : float or ndarray
|
|
479
|
+
The y-coordinate(s) at which to evaluate the plane.
|
|
480
|
+
a : float
|
|
481
|
+
The coefficient of the x term.
|
|
482
|
+
b : float
|
|
483
|
+
The coefficient of the y term.
|
|
484
|
+
c : float
|
|
485
|
+
The constant term.
|
|
486
|
+
|
|
487
|
+
Returns
|
|
488
|
+
-------
|
|
489
|
+
float or ndarray
|
|
490
|
+
The value of the plane at the given (x, y) coordinates. If `x` and
|
|
491
|
+
`y` are arrays, the result is an array of the same shape.
|
|
492
|
+
|
|
493
|
+
Examples
|
|
494
|
+
--------
|
|
495
|
+
>>> plane(1, 2, 3, 4, 5)
|
|
496
|
+
16
|
|
497
|
+
>>> plane(np.array([1, 2]), np.array([3, 4]), 3, 4, 5)
|
|
498
|
+
array([20, 27])
|
|
499
|
+
|
|
500
|
+
Notes
|
|
501
|
+
-----
|
|
502
|
+
The plane function is a linear function in two variables, commonly used
|
|
503
|
+
to model flat surfaces in three-dimensional space.
|
|
504
|
+
"""
|
|
505
|
+
|
|
417
506
|
return a * x + b * y + c
|
|
418
507
|
|
|
419
508
|
|
|
420
|
-
def fit_plane(image, cell_masks=None):
|
|
509
|
+
def fit_plane(image, cell_masks=None, edge_exclusion=None):
|
|
510
|
+
|
|
421
511
|
"""
|
|
422
512
|
Fit a plane to the given image data.
|
|
423
513
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
514
|
+
This function fits a plane to the provided image data using least squares
|
|
515
|
+
regression. It constructs a mesh grid based on the dimensions of the image
|
|
516
|
+
and fits a plane model to the data points. If cell masks are provided,
|
|
517
|
+
areas covered by cell masks will be excluded from the fitting process.
|
|
428
518
|
|
|
429
|
-
|
|
430
|
-
|
|
519
|
+
Parameters
|
|
520
|
+
----------
|
|
521
|
+
image : numpy.ndarray
|
|
522
|
+
The input image data.
|
|
523
|
+
cell_masks : numpy.ndarray, optional
|
|
524
|
+
An array specifying cell masks. If provided, areas covered by cell masks
|
|
525
|
+
will be excluded from the fitting process (default is None).
|
|
526
|
+
edge_exclusion : int, optional
|
|
527
|
+
The size of the edge to exclude from the fitting process (default is None).
|
|
431
528
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
529
|
+
Returns
|
|
530
|
+
-------
|
|
531
|
+
numpy.ndarray
|
|
532
|
+
The fitted plane.
|
|
435
533
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
[7. 8. 9.]]
|
|
534
|
+
Notes
|
|
535
|
+
-----
|
|
536
|
+
- The `cell_masks` parameter allows excluding areas covered by cell masks from
|
|
537
|
+
the fitting process.
|
|
538
|
+
- The `edge_exclusion` parameter allows excluding edges of the specified size
|
|
539
|
+
from the fitting process to avoid boundary effects.
|
|
443
540
|
|
|
444
|
-
|
|
445
|
-
|
|
541
|
+
See Also
|
|
542
|
+
--------
|
|
543
|
+
plane : The plane function used for fitting.
|
|
446
544
|
"""
|
|
545
|
+
|
|
447
546
|
data = np.empty(image.shape)
|
|
448
547
|
x = np.arange(0, image.shape[1])
|
|
449
548
|
y = np.arange(0, image.shape[0])
|
|
@@ -457,7 +556,14 @@ def fit_plane(image, cell_masks=None):
|
|
|
457
556
|
model = Model(plane, independent_vars=['x', 'y'])
|
|
458
557
|
|
|
459
558
|
weights = np.ones_like(xx, dtype=float)
|
|
460
|
-
|
|
559
|
+
if cell_masks is not None:
|
|
560
|
+
weights[np.where(cell_masks > 0)] = 0.
|
|
561
|
+
|
|
562
|
+
if edge_exclusion is not None:
|
|
563
|
+
xx = unpad(xx, edge_exclusion)
|
|
564
|
+
yy = unpad(yy, edge_exclusion)
|
|
565
|
+
weights = unpad(weights, edge_exclusion)
|
|
566
|
+
image = unpad(image, edge_exclusion)
|
|
461
567
|
|
|
462
568
|
result = model.fit(image,
|
|
463
569
|
x=xx,
|
|
@@ -467,36 +573,48 @@ def fit_plane(image, cell_masks=None):
|
|
|
467
573
|
del model
|
|
468
574
|
collect()
|
|
469
575
|
|
|
470
|
-
|
|
576
|
+
xx, yy = np.meshgrid(x, y)
|
|
577
|
+
|
|
578
|
+
return plane(xx, yy, **result.params)
|
|
579
|
+
|
|
471
580
|
|
|
581
|
+
def fit_paraboloid(image, cell_masks=None, edge_exclusion=None):
|
|
472
582
|
|
|
473
|
-
def fit_paraboloid(image, cell_masks=None):
|
|
474
583
|
"""
|
|
475
584
|
Fit a paraboloid to the given image data.
|
|
476
585
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
586
|
+
This function fits a paraboloid to the provided image data using least squares
|
|
587
|
+
regression. It constructs a mesh grid based on the dimensions of the image
|
|
588
|
+
and fits a paraboloid model to the data points. If cell masks are provided,
|
|
589
|
+
areas covered by cell masks will be excluded from the fitting process.
|
|
481
590
|
|
|
482
|
-
|
|
483
|
-
|
|
591
|
+
Parameters
|
|
592
|
+
----------
|
|
593
|
+
image : numpy.ndarray
|
|
594
|
+
The input image data.
|
|
595
|
+
cell_masks : numpy.ndarray, optional
|
|
596
|
+
An array specifying cell masks. If provided, areas covered by cell masks
|
|
597
|
+
will be excluded from the fitting process (default is None).
|
|
598
|
+
edge_exclusion : int, optional
|
|
599
|
+
The size of the edge to exclude from the fitting process (default is None).
|
|
484
600
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
601
|
+
Returns
|
|
602
|
+
-------
|
|
603
|
+
numpy.ndarray
|
|
604
|
+
The fitted paraboloid.
|
|
488
605
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
[7. 8. 9.]]
|
|
606
|
+
Notes
|
|
607
|
+
-----
|
|
608
|
+
- The `cell_masks` parameter allows excluding areas covered by cell masks from
|
|
609
|
+
the fitting process.
|
|
610
|
+
- The `edge_exclusion` parameter allows excluding edges of the specified size
|
|
611
|
+
from the fitting process to avoid boundary effects.
|
|
496
612
|
|
|
497
|
-
|
|
498
|
-
|
|
613
|
+
See Also
|
|
614
|
+
--------
|
|
615
|
+
paraboloid : The paraboloid function used for fitting.
|
|
499
616
|
"""
|
|
617
|
+
|
|
500
618
|
data = np.empty(image.shape)
|
|
501
619
|
x = np.arange(0, image.shape[1])
|
|
502
620
|
y = np.arange(0, image.shape[0])
|
|
@@ -513,7 +631,14 @@ def fit_paraboloid(image, cell_masks=None):
|
|
|
513
631
|
model = Model(paraboloid, independent_vars=['x', 'y'])
|
|
514
632
|
|
|
515
633
|
weights = np.ones_like(xx, dtype=float)
|
|
516
|
-
|
|
634
|
+
if cell_masks is not None:
|
|
635
|
+
weights[np.where(cell_masks > 0)] = 0.
|
|
636
|
+
|
|
637
|
+
if edge_exclusion is not None:
|
|
638
|
+
xx = unpad(xx, edge_exclusion)
|
|
639
|
+
yy = unpad(yy, edge_exclusion)
|
|
640
|
+
weights = unpad(weights, edge_exclusion)
|
|
641
|
+
image = unpad(image, edge_exclusion)
|
|
517
642
|
|
|
518
643
|
result = model.fit(image,
|
|
519
644
|
x=xx,
|
|
@@ -521,10 +646,12 @@ def fit_paraboloid(image, cell_masks=None):
|
|
|
521
646
|
weights=weights,
|
|
522
647
|
params=params, max_nfev=3000)
|
|
523
648
|
|
|
524
|
-
#print(result.fit_report())
|
|
525
649
|
del model
|
|
526
650
|
collect()
|
|
527
|
-
|
|
651
|
+
|
|
652
|
+
xx, yy = np.meshgrid(x, y)
|
|
653
|
+
|
|
654
|
+
return paraboloid(xx, yy, **result.params)
|
|
528
655
|
|
|
529
656
|
|
|
530
657
|
def correct_background_model(
|
|
@@ -541,10 +668,71 @@ def correct_background_model(
|
|
|
541
668
|
export = False,
|
|
542
669
|
return_stacks = False,
|
|
543
670
|
movie_prefix=None,
|
|
671
|
+
activation_protocol=[['gauss',2],['std',4]],
|
|
544
672
|
export_prefix='Corrected',
|
|
673
|
+
return_stack = True,
|
|
545
674
|
**kwargs,
|
|
546
675
|
):
|
|
547
676
|
|
|
677
|
+
"""
|
|
678
|
+
Correct background in image stacks using a specified model.
|
|
679
|
+
|
|
680
|
+
This function corrects the background in image stacks obtained from an experiment
|
|
681
|
+
using a specified background correction model. It supports various options for
|
|
682
|
+
specifying wells, positions, target channel, and background correction parameters.
|
|
683
|
+
|
|
684
|
+
Parameters
|
|
685
|
+
----------
|
|
686
|
+
experiment : str
|
|
687
|
+
The path to the experiment directory.
|
|
688
|
+
well_option : str, optional
|
|
689
|
+
The option to select specific wells (default is '*').
|
|
690
|
+
position_option : str, optional
|
|
691
|
+
The option to select specific positions (default is '*').
|
|
692
|
+
target_channel : str, optional
|
|
693
|
+
The name of the target channel for background correction (default is "channel_name").
|
|
694
|
+
threshold_on_std : float, optional
|
|
695
|
+
The threshold value on the standard deviation for masking (default is 1).
|
|
696
|
+
model : str, optional
|
|
697
|
+
The background correction model to use, either 'paraboloid' or 'plane' (default is 'paraboloid').
|
|
698
|
+
operation : str, optional
|
|
699
|
+
The operation to apply for background correction, either 'divide' or 'subtract' (default is 'divide').
|
|
700
|
+
clip : bool, optional
|
|
701
|
+
Whether to clip the corrected image to ensure non-negative values (default is False).
|
|
702
|
+
show_progress_per_well : bool, optional
|
|
703
|
+
Whether to show progress for each well (default is True).
|
|
704
|
+
show_progress_per_pos : bool, optional
|
|
705
|
+
Whether to show progress for each position (default is False).
|
|
706
|
+
export : bool, optional
|
|
707
|
+
Whether to export the corrected stacks (default is False).
|
|
708
|
+
return_stacks : bool, optional
|
|
709
|
+
Whether to return the corrected stacks (default is False).
|
|
710
|
+
movie_prefix : str, optional
|
|
711
|
+
The prefix for the movie files (default is None).
|
|
712
|
+
activation_protocol : list of list, optional
|
|
713
|
+
The activation protocol consisting of filters and their respective parameters (default is [['gauss',2],['std',4]]).
|
|
714
|
+
export_prefix : str, optional
|
|
715
|
+
The prefix for exported corrected stacks (default is 'Corrected').
|
|
716
|
+
**kwargs : dict
|
|
717
|
+
Additional keyword arguments to be passed to the underlying correction function.
|
|
718
|
+
|
|
719
|
+
Returns
|
|
720
|
+
-------
|
|
721
|
+
list of numpy.ndarray
|
|
722
|
+
A list of corrected image stacks if `return_stacks` is True, otherwise None.
|
|
723
|
+
|
|
724
|
+
Notes
|
|
725
|
+
-----
|
|
726
|
+
- This function assumes that the experiment directory structure and the configuration
|
|
727
|
+
files follow a specific format expected by the helper functions used within.
|
|
728
|
+
- Supported background correction models are 'paraboloid' and 'plane'.
|
|
729
|
+
- Supported background correction operations are 'divide' and 'subtract'.
|
|
730
|
+
|
|
731
|
+
See Also
|
|
732
|
+
--------
|
|
733
|
+
fit_and_apply_model_background_to_stack : Function to fit and apply background correction to an image stack.
|
|
734
|
+
"""
|
|
735
|
+
|
|
548
736
|
config = get_config(experiment)
|
|
549
737
|
wells = get_experiment_wells(experiment)
|
|
550
738
|
len_movie = float(ConfigSectionMap(config,"MovieSettings")["len_movie"])
|
|
@@ -582,6 +770,8 @@ def correct_background_model(
|
|
|
582
770
|
clip=clip,
|
|
583
771
|
export=export,
|
|
584
772
|
prefix=export_prefix,
|
|
773
|
+
activation_protocol=activation_protocol,
|
|
774
|
+
return_stacks = return_stacks,
|
|
585
775
|
)
|
|
586
776
|
print('Correction successful.')
|
|
587
777
|
if return_stacks:
|
|
@@ -602,9 +792,61 @@ def fit_and_apply_model_background_to_stack(stack_path,
|
|
|
602
792
|
model='paraboloid',
|
|
603
793
|
clip=False,
|
|
604
794
|
export=False,
|
|
605
|
-
|
|
795
|
+
activation_protocol=[['gauss',2],['std',4]],
|
|
796
|
+
prefix="Corrected",
|
|
797
|
+
return_stacks=True,
|
|
606
798
|
):
|
|
607
799
|
|
|
800
|
+
"""
|
|
801
|
+
Fit and apply a background correction model to an image stack.
|
|
802
|
+
|
|
803
|
+
This function fits a background correction model to each frame of the image stack
|
|
804
|
+
and applies the correction accordingly. It supports various options for specifying
|
|
805
|
+
the target channel, number of channels, stack length, threshold on standard deviation,
|
|
806
|
+
correction operation, correction model, clipping, and export.
|
|
807
|
+
|
|
808
|
+
Parameters
|
|
809
|
+
----------
|
|
810
|
+
stack_path : str
|
|
811
|
+
The path to the image stack.
|
|
812
|
+
target_channel_index : int, optional
|
|
813
|
+
The index of the target channel for background correction (default is 0).
|
|
814
|
+
nbr_channels : int, optional
|
|
815
|
+
The number of channels in the image stack (default is 1).
|
|
816
|
+
stack_length : int, optional
|
|
817
|
+
The length of the stack (default is 45).
|
|
818
|
+
threshold_on_std : float, optional
|
|
819
|
+
The threshold value on the standard deviation for masking (default is 1).
|
|
820
|
+
operation : str, optional
|
|
821
|
+
The operation to apply for background correction, either 'divide' or 'subtract' (default is 'divide').
|
|
822
|
+
model : str, optional
|
|
823
|
+
The background correction model to use, either 'paraboloid' or 'plane' (default is 'paraboloid').
|
|
824
|
+
clip : bool, optional
|
|
825
|
+
Whether to clip the corrected image to ensure non-negative values (default is False).
|
|
826
|
+
export : bool, optional
|
|
827
|
+
Whether to export the corrected image stack (default is False).
|
|
828
|
+
activation_protocol : list of list, optional
|
|
829
|
+
The activation protocol consisting of filters and their respective parameters (default is [['gauss',2],['std',4]]).
|
|
830
|
+
prefix : str, optional
|
|
831
|
+
The prefix for exported corrected stacks (default is 'Corrected').
|
|
832
|
+
|
|
833
|
+
Returns
|
|
834
|
+
-------
|
|
835
|
+
numpy.ndarray
|
|
836
|
+
The corrected image stack.
|
|
837
|
+
|
|
838
|
+
Notes
|
|
839
|
+
-----
|
|
840
|
+
- The function loads frames from the image stack, applies background correction to each frame,
|
|
841
|
+
and stores the corrected frames in a new stack.
|
|
842
|
+
- Supported background correction models are 'paraboloid' and 'plane'.
|
|
843
|
+
- Supported background correction operations are 'divide' and 'subtract'.
|
|
844
|
+
|
|
845
|
+
See Also
|
|
846
|
+
--------
|
|
847
|
+
field_correction : Function to apply background correction to an image.
|
|
848
|
+
"""
|
|
849
|
+
|
|
608
850
|
stack_length_auto = auto_load_number_of_frames(stack_path)
|
|
609
851
|
if stack_length_auto is None and stack_length is None:
|
|
610
852
|
print('stack length not provided')
|
|
@@ -612,70 +854,168 @@ def fit_and_apply_model_background_to_stack(stack_path,
|
|
|
612
854
|
if stack_length_auto is not None:
|
|
613
855
|
stack_length = stack_length_auto
|
|
614
856
|
|
|
857
|
+
corrected_stack = []
|
|
858
|
+
|
|
615
859
|
if export:
|
|
616
860
|
path,file = os.path.split(stack_path)
|
|
617
861
|
if prefix is None:
|
|
618
|
-
newfile = file
|
|
862
|
+
newfile = 'temp_'+file
|
|
619
863
|
else:
|
|
620
864
|
newfile = '_'.join([prefix,file])
|
|
865
|
+
|
|
866
|
+
with tiff.TiffWriter(os.sep.join([path,newfile]),imagej=True) as tif:
|
|
621
867
|
|
|
622
|
-
|
|
868
|
+
for i in tqdm(range(0,int(stack_length*nbr_channels),nbr_channels)):
|
|
869
|
+
|
|
870
|
+
frames = load_frames(list(np.arange(i,(i+nbr_channels))), stack_path, normalize_input=False).astype(float)
|
|
871
|
+
target_img = frames[:,:,target_channel_index].copy()
|
|
872
|
+
correction = field_correction(target_img, threshold_on_std=threshold_on_std, operation=operation, model=model, clip=clip, activation_protocol=activation_protocol)
|
|
873
|
+
frames[:,:,target_channel_index] = correction.copy()
|
|
623
874
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
frames = load_frames(list(np.arange(i,(i+nbr_channels))), stack_path, normalize_input=False).astype(float)
|
|
627
|
-
target_img = frames[:,:,target_channel_index].copy()
|
|
628
|
-
correction = field_correction(target_img, threshold_on_std=threshold_on_std, operation=operation, model=model, clip=clip)
|
|
629
|
-
frames[:,:,target_channel_index] = correction.copy()
|
|
630
|
-
corrected_stack.append(frames)
|
|
875
|
+
if return_stacks:
|
|
876
|
+
corrected_stack.append(frames)
|
|
631
877
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
878
|
+
if export:
|
|
879
|
+
tif.write(np.moveaxis(frames,-1,0).astype(np.dtype('f')), contiguous=True)
|
|
880
|
+
del frames
|
|
881
|
+
del target_img
|
|
882
|
+
del correction
|
|
883
|
+
collect()
|
|
636
884
|
|
|
637
|
-
|
|
885
|
+
if prefix is None:
|
|
886
|
+
os.replace(os.sep.join([path,newfile]), os.sep.join([path,file]))
|
|
887
|
+
else:
|
|
888
|
+
for i in tqdm(range(0,int(stack_length*nbr_channels),nbr_channels)):
|
|
889
|
+
|
|
890
|
+
frames = load_frames(list(np.arange(i,(i+nbr_channels))), stack_path, normalize_input=False).astype(float)
|
|
891
|
+
target_img = frames[:,:,target_channel_index].copy()
|
|
892
|
+
correction = field_correction(target_img, threshold_on_std=threshold_on_std, operation=operation, model=model, clip=clip, activation_protocol=activation_protocol)
|
|
893
|
+
frames[:,:,target_channel_index] = correction.copy()
|
|
638
894
|
|
|
639
|
-
|
|
640
|
-
save_tiff_imagej_compatible(os.sep.join([path,newfile]), corrected_stack, axes='TYXC')
|
|
895
|
+
corrected_stack.append(frames)
|
|
641
896
|
|
|
642
|
-
|
|
897
|
+
del frames
|
|
898
|
+
del target_img
|
|
899
|
+
del correction
|
|
900
|
+
collect()
|
|
643
901
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
std_frame = std_filter(f, 4)
|
|
649
|
-
masks = std_frame > threshold_on_std
|
|
650
|
-
masks = fill_label_holes(masks).astype(int)
|
|
902
|
+
if return_stacks:
|
|
903
|
+
return np.array(corrected_stack)
|
|
904
|
+
else:
|
|
905
|
+
return None
|
|
651
906
|
|
|
652
|
-
|
|
907
|
+
def field_correction(img, threshold_on_std=1, operation='divide', model='paraboloid', clip=False, return_bg=False, activation_protocol=[['gauss',2],['std',4]]):
|
|
908
|
+
|
|
909
|
+
"""
|
|
910
|
+
Apply field correction to an image.
|
|
653
911
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
correction[img!=img] = np.nan
|
|
658
|
-
fill_val = 1.0
|
|
912
|
+
This function applies field correction to the given image based on the specified parameters
|
|
913
|
+
including the threshold on standard deviation, operation, background correction model, clipping,
|
|
914
|
+
and activation protocol.
|
|
659
915
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
916
|
+
Parameters
|
|
917
|
+
----------
|
|
918
|
+
img : numpy.ndarray
|
|
919
|
+
The input image to be corrected.
|
|
920
|
+
threshold_on_std : float, optional
|
|
921
|
+
The threshold value on the standard deviation for masking (default is 1).
|
|
922
|
+
operation : str, optional
|
|
923
|
+
The operation to apply for background correction, either 'divide' or 'subtract' (default is 'divide').
|
|
924
|
+
model : str, optional
|
|
925
|
+
The background correction model to use, either 'paraboloid' or 'plane' (default is 'paraboloid').
|
|
926
|
+
clip : bool, optional
|
|
927
|
+
Whether to clip the corrected image to ensure non-negative values (default is False).
|
|
928
|
+
return_bg : bool, optional
|
|
929
|
+
Whether to return the background along with the corrected image (default is False).
|
|
930
|
+
activation_protocol : list of list, optional
|
|
931
|
+
The activation protocol consisting of filters and their respective parameters (default is [['gauss',2],['std',4]]).
|
|
667
932
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
933
|
+
Returns
|
|
934
|
+
-------
|
|
935
|
+
numpy.ndarray or tuple
|
|
936
|
+
The corrected image or a tuple containing the corrected image and the background, depending on the value of `return_bg`.
|
|
672
937
|
|
|
673
|
-
|
|
938
|
+
Notes
|
|
939
|
+
-----
|
|
940
|
+
- This function first estimates the unreliable edge based on the activation protocol.
|
|
941
|
+
- It then applies thresholding to obtain a mask for the background.
|
|
942
|
+
- Next, it fits a background model to the image using the specified model.
|
|
943
|
+
- Depending on the operation specified, it either divides or subtracts the background from the image.
|
|
944
|
+
- If `clip` is True and operation is 'subtract', negative values in the corrected image are clipped to 0.
|
|
945
|
+
- If `return_bg` is True, the function returns a tuple containing the corrected image and the background.
|
|
946
|
+
|
|
947
|
+
See Also
|
|
948
|
+
--------
|
|
949
|
+
fit_background_model : Function to fit a background model to an image.
|
|
950
|
+
threshold_image : Function to apply thresholding to an image.
|
|
951
|
+
"""
|
|
952
|
+
|
|
953
|
+
target_copy = img.copy().astype(float)
|
|
954
|
+
|
|
955
|
+
std_frame = filter_image(target_copy,filters=activation_protocol)
|
|
956
|
+
edge = estimate_unreliable_edge(activation_protocol)
|
|
957
|
+
mask = threshold_image(std_frame, threshold_on_std, 1.0E06, foreground_value=1, edge_exclusion=edge).astype(int)
|
|
958
|
+
background = fit_background_model(img, cell_masks=mask, model=model, edge_exclusion=edge)
|
|
959
|
+
|
|
960
|
+
if operation=="divide":
|
|
961
|
+
correction = np.divide(img, background, where=background==background)
|
|
962
|
+
correction[background!=background] = np.nan
|
|
963
|
+
correction[img!=img] = np.nan
|
|
964
|
+
fill_val = 1.0
|
|
965
|
+
|
|
966
|
+
elif operation=="subtract":
|
|
967
|
+
correction = np.subtract(img, background, where=background==background)
|
|
968
|
+
correction[background!=background] = np.nan
|
|
969
|
+
correction[img!=img] = np.nan
|
|
970
|
+
fill_val = 0.0
|
|
971
|
+
if clip:
|
|
972
|
+
correction[correction<=0.] = 0.
|
|
973
|
+
|
|
974
|
+
if return_bg:
|
|
975
|
+
return correction.copy(), background
|
|
976
|
+
else:
|
|
977
|
+
return correction.copy()
|
|
978
|
+
|
|
979
|
+
def fit_background_model(img, cell_masks=None, model='paraboloid', edge_exclusion=None):
|
|
674
980
|
|
|
981
|
+
"""
|
|
982
|
+
Fit a background model to the given image.
|
|
983
|
+
|
|
984
|
+
This function fits a background model to the given image using either a paraboloid or plane model.
|
|
985
|
+
It supports optional cell masks and edge exclusion for fitting.
|
|
986
|
+
|
|
987
|
+
Parameters
|
|
988
|
+
----------
|
|
989
|
+
img : numpy.ndarray
|
|
990
|
+
The input image data.
|
|
991
|
+
cell_masks : numpy.ndarray, optional
|
|
992
|
+
An array specifying cell masks. If provided, areas covered by cell masks will be excluded from the fitting process.
|
|
993
|
+
model : str, optional
|
|
994
|
+
The background model to fit, either 'paraboloid' or 'plane' (default is 'paraboloid').
|
|
995
|
+
edge_exclusion : int or None, optional
|
|
996
|
+
The size of the border to exclude from fitting (default is None).
|
|
997
|
+
|
|
998
|
+
Returns
|
|
999
|
+
-------
|
|
1000
|
+
numpy.ndarray or None
|
|
1001
|
+
The fitted background model as a numpy array if successful, otherwise None.
|
|
1002
|
+
|
|
1003
|
+
Notes
|
|
1004
|
+
-----
|
|
1005
|
+
- This function fits a background model to the image using either a paraboloid or plane model based on the specified `model`.
|
|
1006
|
+
- If `cell_masks` are provided, areas covered by cell masks will be excluded from the fitting process.
|
|
1007
|
+
- If `edge_exclusion` is provided, a border of the specified size will be excluded from fitting.
|
|
1008
|
+
|
|
1009
|
+
See Also
|
|
1010
|
+
--------
|
|
1011
|
+
fit_paraboloid : Function to fit a paraboloid model to an image.
|
|
1012
|
+
fit_plane : Function to fit a plane model to an image.
|
|
1013
|
+
"""
|
|
1014
|
+
|
|
675
1015
|
if model == "paraboloid":
|
|
676
|
-
bg = fit_paraboloid(img.astype(float), cell_masks=cell_masks).astype(float)
|
|
1016
|
+
bg = fit_paraboloid(img.astype(float), cell_masks=cell_masks, edge_exclusion=edge_exclusion).astype(float)
|
|
677
1017
|
elif model == "plane":
|
|
678
|
-
bg = fit_plane(img.astype(float), cell_masks=cell_masks).astype(float)
|
|
1018
|
+
bg = fit_plane(img.astype(float), cell_masks=cell_masks, edge_exclusion=edge_exclusion).astype(float)
|
|
679
1019
|
|
|
680
1020
|
if bg is not None:
|
|
681
1021
|
bg = np.array(bg)
|