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
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.ndimage import shift
|
|
5
|
+
from skimage.filters import gaussian
|
|
6
|
+
from skimage.util import random_noise
|
|
7
|
+
|
|
8
|
+
def augmenter(
|
|
9
|
+
x,
|
|
10
|
+
y,
|
|
11
|
+
flip=True,
|
|
12
|
+
gauss_blur=True,
|
|
13
|
+
noise_option=True,
|
|
14
|
+
shift=True,
|
|
15
|
+
channel_extinction=True,
|
|
16
|
+
extinction_probability=0.1,
|
|
17
|
+
clip=False,
|
|
18
|
+
max_sigma_blur=4,
|
|
19
|
+
apply_noise_probability=0.5,
|
|
20
|
+
augment_probability=0.9,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
Applies a series of augmentation techniques to images and their corresponding masks for deep learning training.
|
|
24
|
+
|
|
25
|
+
This function randomly applies a set of transformations including flipping, rotation, Gaussian blur,
|
|
26
|
+
additive noise, shifting, and channel extinction to input images (x) and their masks (y) based on specified
|
|
27
|
+
probabilities. These augmentations introduce variability in the training dataset, potentially improving model
|
|
28
|
+
generalization.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
x : ndarray
|
|
33
|
+
The input image to be augmented, with dimensions (height, width, channels).
|
|
34
|
+
y : ndarray
|
|
35
|
+
The corresponding mask or label image for `x`, with the same spatial dimensions.
|
|
36
|
+
flip : bool, optional
|
|
37
|
+
Whether to randomly flip and rotate the images. Default is True.
|
|
38
|
+
gauss_blur : bool, optional
|
|
39
|
+
Whether to apply Gaussian blur to the images. Default is True.
|
|
40
|
+
noise_option : bool, optional
|
|
41
|
+
Whether to add random noise to the images. Default is True.
|
|
42
|
+
shift : bool, optional
|
|
43
|
+
Whether to randomly shift the images. Default is True.
|
|
44
|
+
channel_extinction : bool, optional
|
|
45
|
+
Whether to randomly set entire channels of the image to zero. Default is False.
|
|
46
|
+
extinction_probability : float, optional
|
|
47
|
+
The probability of an entire channel being set to zero. Default is 0.1.
|
|
48
|
+
clip : bool, optional
|
|
49
|
+
Whether to clip the noise-added images to stay within valid intensity values. Default is False.
|
|
50
|
+
max_sigma_blur : int, optional
|
|
51
|
+
The maximum sigma value for Gaussian blur. Default is 4.
|
|
52
|
+
apply_noise_probability : float, optional
|
|
53
|
+
The probability of applying noise to the image. Default is 0.5.
|
|
54
|
+
augment_probability : float, optional
|
|
55
|
+
The overall probability of applying any augmentation to the image. Default is 0.9.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
tuple
|
|
60
|
+
A tuple containing the augmented image and mask `(x, y)`.
|
|
61
|
+
|
|
62
|
+
Raises
|
|
63
|
+
------
|
|
64
|
+
AssertionError
|
|
65
|
+
If `extinction_probability` is not within the range [0, 1].
|
|
66
|
+
|
|
67
|
+
Notes
|
|
68
|
+
-----
|
|
69
|
+
- The augmentations are applied randomly based on the specified probabilities, allowing for
|
|
70
|
+
a diverse set of transformed images from the original inputs.
|
|
71
|
+
- This function is designed to be part of a preprocessing pipeline for training deep learning models,
|
|
72
|
+
especially in tasks requiring spatial invariance and robustness to noise.
|
|
73
|
+
|
|
74
|
+
Examples
|
|
75
|
+
--------
|
|
76
|
+
>>> import numpy as np
|
|
77
|
+
>>> x = np.random.rand(128, 128, 3) # Sample image
|
|
78
|
+
>>> y = np.random.randint(2, size=(128, 128)) # Sample binary mask
|
|
79
|
+
>>> x_aug, y_aug = augmenter(x, y)
|
|
80
|
+
# The returned `x_aug` and `y_aug` are augmented versions of `x` and `y`.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
r = random.random()
|
|
85
|
+
if r <= augment_probability:
|
|
86
|
+
|
|
87
|
+
if flip:
|
|
88
|
+
x, y = random_fliprot(x, y)
|
|
89
|
+
|
|
90
|
+
if gauss_blur:
|
|
91
|
+
x = blur(x, max_sigma=max_sigma_blur)
|
|
92
|
+
|
|
93
|
+
if noise_option:
|
|
94
|
+
x = noise(x, apply_probability=apply_noise_probability, clip_option=clip)
|
|
95
|
+
|
|
96
|
+
if shift:
|
|
97
|
+
x, y = random_shift(x, y)
|
|
98
|
+
|
|
99
|
+
if channel_extinction:
|
|
100
|
+
assert (
|
|
101
|
+
extinction_probability <= 1.0
|
|
102
|
+
), "The extinction probability must be a number between 0 and 1."
|
|
103
|
+
channel_off = [
|
|
104
|
+
np.random.random() < extinction_probability for i in range(x.shape[-1])
|
|
105
|
+
]
|
|
106
|
+
channel_off[0] = False
|
|
107
|
+
x[:, :, np.array(channel_off, dtype=bool)] = 0.0
|
|
108
|
+
|
|
109
|
+
return x, y
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def noise(x, apply_probability=0.5, clip_option=False):
|
|
113
|
+
"""
|
|
114
|
+
Applies random noise to each channel of a multichannel image based on a specified probability.
|
|
115
|
+
|
|
116
|
+
This function introduces various types of random noise to an image. Each channel of the image can be
|
|
117
|
+
modified independently with different noise models chosen randomly from a predefined list. The application
|
|
118
|
+
of noise to any given channel is determined by a specified probability, allowing for selective noise
|
|
119
|
+
addition.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
x : ndarray
|
|
124
|
+
The input multichannel image to which noise will be added. The image should be in format with channels
|
|
125
|
+
as the last dimension (e.g., height x width x channels).
|
|
126
|
+
apply_probability : float, optional
|
|
127
|
+
The probability with which noise is applied to each channel of the image. Default is 0.5.
|
|
128
|
+
clip_option : bool, optional
|
|
129
|
+
Specifies whether to clip the corrupted data to stay within the valid range after noise addition.
|
|
130
|
+
If True, the output array will be clipped to the range [0, 1] or [0, 255] depending on the input
|
|
131
|
+
data type. Default is False.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
ndarray
|
|
136
|
+
The noised image. This output has the same shape as the input but potentially altered intensity values
|
|
137
|
+
due to noise addition.
|
|
138
|
+
|
|
139
|
+
Notes
|
|
140
|
+
-----
|
|
141
|
+
- The types of noise that can be applied include 'gaussian', 'localvar', 'poisson', and 'speckle'.
|
|
142
|
+
- The choice of noise type for each channel is randomized and the noise is only applied if a randomly
|
|
143
|
+
generated number is less than or equal to `apply_probability`.
|
|
144
|
+
- Zero-valued pixels in the input image remain zero in the output to preserve background or masked areas.
|
|
145
|
+
|
|
146
|
+
Examples
|
|
147
|
+
--------
|
|
148
|
+
>>> import numpy as np
|
|
149
|
+
>>> x = np.random.rand(256, 256, 3) # Example 3-channel image
|
|
150
|
+
>>> noised_image = noise(x)
|
|
151
|
+
# The image 'x' may have different types of noise applied to each of its channels with a 50% probability.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
x_noise = x.astype(float).copy()
|
|
155
|
+
loc_i, loc_j, loc_c = np.where(x_noise == 0.0)
|
|
156
|
+
options = ["gaussian", "localvar", "poisson", "speckle"]
|
|
157
|
+
|
|
158
|
+
for k in range(x_noise.shape[-1]):
|
|
159
|
+
mode_order = random.sample(options, len(options))
|
|
160
|
+
for m in mode_order:
|
|
161
|
+
p = np.random.random()
|
|
162
|
+
if p <= apply_probability:
|
|
163
|
+
try:
|
|
164
|
+
x_noise[:, :, k] = random_noise(
|
|
165
|
+
x_noise[:, :, k], mode=m, clip=clip_option
|
|
166
|
+
)
|
|
167
|
+
except:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
x_noise[loc_i, loc_j, loc_c] = 0.0
|
|
171
|
+
|
|
172
|
+
return x_noise
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def random_fliprot(img, mask):
|
|
176
|
+
"""
|
|
177
|
+
Randomly flips and rotates an image and its corresponding mask.
|
|
178
|
+
|
|
179
|
+
This function applies a series of random flips and permutations (rotations) to both the input image and its
|
|
180
|
+
associated mask, ensuring that any transformations applied to the image are also exactly applied to the mask.
|
|
181
|
+
The function is designed to handle multi-dimensional images (e.g., multi-channel images in YXC format where
|
|
182
|
+
channels are last).
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
img : ndarray
|
|
187
|
+
The input image to be transformed. This array is expected to have dimensions where the channel axis is last.
|
|
188
|
+
mask : ndarray
|
|
189
|
+
The mask corresponding to `img`, to be transformed in the same way as the image.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
tuple of ndarray
|
|
194
|
+
A tuple containing the transformed image and mask.
|
|
195
|
+
|
|
196
|
+
Raises
|
|
197
|
+
------
|
|
198
|
+
AssertionError
|
|
199
|
+
If the number of dimensions of the mask exceeds that of the image, indicating incompatible shapes.
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
assert img.ndim >= mask.ndim
|
|
204
|
+
axes = tuple(range(mask.ndim))
|
|
205
|
+
perm = tuple(np.random.permutation(axes))
|
|
206
|
+
img = img.transpose(perm + tuple(range(mask.ndim, img.ndim)))
|
|
207
|
+
mask = mask.transpose(perm)
|
|
208
|
+
for ax in axes:
|
|
209
|
+
if np.random.rand() > 0.5:
|
|
210
|
+
img = np.flip(img, axis=ax)
|
|
211
|
+
mask = np.flip(mask, axis=ax)
|
|
212
|
+
return img, mask
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def random_shift(image, mask, max_shift_amplitude=0.1):
|
|
216
|
+
"""
|
|
217
|
+
Randomly shifts an image and its corresponding mask along the X and Y axes.
|
|
218
|
+
|
|
219
|
+
This function shifts both the image and the mask by a randomly chosen distance up to a maximum
|
|
220
|
+
percentage of the image's dimensions, specified by `max_shift_amplitude`. The shifts are applied
|
|
221
|
+
independently in both the X and Y directions. This type of augmentation can help improve the robustness
|
|
222
|
+
of models to positional variations in images.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
image : ndarray
|
|
227
|
+
The input image to be shifted. Must be in YXC format (height, width, channels).
|
|
228
|
+
mask : ndarray
|
|
229
|
+
The mask corresponding to `image`, to be shifted in the same way as the image.
|
|
230
|
+
max_shift_amplitude : float, optional
|
|
231
|
+
The maximum shift as a fraction of the image's dimension. Default is 0.1 (10% of the image's size).
|
|
232
|
+
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
tuple of ndarray
|
|
236
|
+
A tuple containing the shifted image and mask.
|
|
237
|
+
|
|
238
|
+
Notes
|
|
239
|
+
-----
|
|
240
|
+
- The shift values are chosen randomly within the range defined by the maximum amplitude.
|
|
241
|
+
- Shifting is performed using the 'constant' mode where missing values are filled with zeros (cval=0.0),
|
|
242
|
+
which may introduce areas of zero-padding along the edges of the shifted images and masks.
|
|
243
|
+
- This function is designed to support data augmentation for machine learning and image processing tasks,
|
|
244
|
+
particularly in contexts where spatial invariance is beneficial.
|
|
245
|
+
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
input_shape = image.shape[0]
|
|
249
|
+
max_shift = input_shape * max_shift_amplitude
|
|
250
|
+
|
|
251
|
+
shift_value_x = random.choice(np.arange(max_shift))
|
|
252
|
+
if np.random.random() > 0.5:
|
|
253
|
+
shift_value_x *= -1
|
|
254
|
+
|
|
255
|
+
shift_value_y = random.choice(np.arange(max_shift))
|
|
256
|
+
if np.random.random() > 0.5:
|
|
257
|
+
shift_value_y *= -1
|
|
258
|
+
|
|
259
|
+
image = shift(
|
|
260
|
+
image,
|
|
261
|
+
[shift_value_x, shift_value_y, 0],
|
|
262
|
+
output=np.float32,
|
|
263
|
+
order=3,
|
|
264
|
+
mode="constant",
|
|
265
|
+
cval=0.0,
|
|
266
|
+
)
|
|
267
|
+
mask = shift(
|
|
268
|
+
mask, [shift_value_x, shift_value_y], order=0, mode="constant", cval=0.0
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
return image, mask
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def blur(x, max_sigma=4.0):
|
|
275
|
+
"""
|
|
276
|
+
Applies a random Gaussian blur to an image.
|
|
277
|
+
|
|
278
|
+
This function blurs an image by applying a Gaussian filter with a randomly chosen sigma value. The sigma
|
|
279
|
+
represents the standard deviation for the Gaussian kernel and is selected randomly up to a specified maximum.
|
|
280
|
+
The blurring is applied while preserving the range of the image's intensity values and maintaining any
|
|
281
|
+
zero-valued pixels as they are.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
x : ndarray
|
|
286
|
+
The input image to be blurred. The image can have any number of channels, but must be in a format
|
|
287
|
+
where the channels are the last dimension (YXC format).
|
|
288
|
+
max_sigma : float, optional
|
|
289
|
+
The maximum value for the standard deviation of the Gaussian blur. Default is 4.0.
|
|
290
|
+
|
|
291
|
+
Returns
|
|
292
|
+
-------
|
|
293
|
+
ndarray
|
|
294
|
+
The blurred image. The output will have the same shape and type as the input image.
|
|
295
|
+
|
|
296
|
+
Notes
|
|
297
|
+
-----
|
|
298
|
+
- The function ensures that zero-valued pixels in the input image remain unchanged after the blurring,
|
|
299
|
+
which can be important for maintaining masks or other specific regions within the image.
|
|
300
|
+
- Gaussian blurring is commonly used in image processing to reduce image noise and detail by smoothing.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
sigma = np.random.random() * max_sigma
|
|
304
|
+
loc_i, loc_j, loc_c = np.where(x == 0.0)
|
|
305
|
+
x = gaussian(x, sigma, channel_axis=-1, preserve_range=True)
|
|
306
|
+
x[loc_i, loc_j, loc_c] = 0.0
|
|
307
|
+
|
|
308
|
+
return x
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _fix_no_contrast(frames: np.ndarray, value: Union[float, int] = 1):
|
|
7
|
+
"""
|
|
8
|
+
Ensures that frames with no contrast (i.e., containing only a single unique value) are adjusted.
|
|
9
|
+
|
|
10
|
+
This function modifies frames that lack contrast by adding a small value to the first pixel in
|
|
11
|
+
the affected frame. This prevents downstream issues in image processing pipelines that require
|
|
12
|
+
a minimum level of contrast.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
frames : ndarray
|
|
17
|
+
A 3D array of shape `(H, W, N)`, where:
|
|
18
|
+
- `H` is the height of the frame,
|
|
19
|
+
- `W` is the width of the frame,
|
|
20
|
+
- `N` is the number of frames or channels.
|
|
21
|
+
Each frame (or channel) is independently checked for contrast.
|
|
22
|
+
value : int or float, optional
|
|
23
|
+
The value to add to the first pixel (`frames[0, 0, k]`) of any frame that lacks contrast.
|
|
24
|
+
Default is `1`.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
ndarray
|
|
29
|
+
The modified `frames` array, where frames with no contrast have been adjusted.
|
|
30
|
+
|
|
31
|
+
Notes
|
|
32
|
+
-----
|
|
33
|
+
- A frame is determined to have "no contrast" if all its pixel values are identical.
|
|
34
|
+
- Only the first pixel (`[0, 0, k]`) of a no-contrast frame is modified, leaving the rest
|
|
35
|
+
of the frame unchanged.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
for k in range(frames.shape[2]):
|
|
39
|
+
unique_values = np.unique(frames[:, :, k])
|
|
40
|
+
if len(unique_values) == 1:
|
|
41
|
+
frames[0, 0, k] += value
|
|
42
|
+
return frames
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def interpolate_nan_multichannel(frames):
|
|
46
|
+
frames = np.moveaxis(
|
|
47
|
+
[interpolate_nan(frames[:, :, c].copy()) for c in range(frames.shape[-1])],
|
|
48
|
+
0,
|
|
49
|
+
-1,
|
|
50
|
+
)
|
|
51
|
+
return frames
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def interpolate_nan(img, method="nearest"):
|
|
55
|
+
"""
|
|
56
|
+
Interpolate NaN on single channel array 2D
|
|
57
|
+
"""
|
|
58
|
+
from scipy.interpolate import griddata
|
|
59
|
+
|
|
60
|
+
if np.all(img == 0):
|
|
61
|
+
return img
|
|
62
|
+
|
|
63
|
+
if np.any(img.flatten() != img.flatten()):
|
|
64
|
+
# then need to interpolate
|
|
65
|
+
x_grid, y_grid = np.meshgrid(np.arange(img.shape[1]), np.arange(img.shape[0]))
|
|
66
|
+
mask = [~np.isnan(img)][0]
|
|
67
|
+
x = x_grid[mask].reshape(-1)
|
|
68
|
+
y = y_grid[mask].reshape(-1)
|
|
69
|
+
points = np.array([x, y]).T
|
|
70
|
+
values = img[mask].reshape(-1)
|
|
71
|
+
interp_grid = griddata(points, values, (x_grid, y_grid), method=method)
|
|
72
|
+
return interp_grid
|
|
73
|
+
else:
|
|
74
|
+
return img
|