dclab 0.62.17__cp39-cp39-macosx_11_0_arm64.whl → 0.67.3__cp39-cp39-macosx_11_0_arm64.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.
- dclab/_version.py +16 -3
- dclab/cli/task_tdms2rtdc.py +1 -1
- dclab/cli/task_verify_dataset.py +3 -3
- dclab/definitions/__init__.py +1 -1
- dclab/definitions/feat_const.py +6 -4
- dclab/definitions/feat_logic.py +27 -28
- dclab/downsampling.cpython-39-darwin.so +0 -0
- dclab/downsampling.pyx +12 -7
- dclab/external/skimage/_find_contours_cy.cpython-39-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.cpython-39-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.cpython-39-darwin.so +0 -0
- dclab/features/bright.py +11 -2
- dclab/features/bright_bc.py +13 -2
- dclab/features/bright_perc.py +10 -2
- dclab/features/contour.py +12 -7
- dclab/features/emodulus/__init__.py +33 -27
- dclab/features/emodulus/load.py +8 -6
- dclab/features/emodulus/pxcorr.py +33 -15
- dclab/features/emodulus/scale_linear.py +79 -52
- dclab/features/emodulus/viscosity.py +31 -19
- dclab/features/fl_crosstalk.py +19 -10
- dclab/features/inert_ratio.py +18 -11
- dclab/features/volume.py +24 -14
- dclab/http_utils.py +1 -1
- dclab/kde/base.py +238 -14
- dclab/kde/methods.py +33 -12
- dclab/rtdc_dataset/config.py +1 -1
- dclab/rtdc_dataset/core.py +22 -8
- dclab/rtdc_dataset/export.py +171 -34
- dclab/rtdc_dataset/feat_basin.py +250 -33
- dclab/rtdc_dataset/fmt_dcor/api.py +69 -7
- dclab/rtdc_dataset/fmt_dcor/base.py +103 -4
- dclab/rtdc_dataset/fmt_dcor/logs.py +1 -1
- dclab/rtdc_dataset/fmt_dcor/tables.py +1 -1
- dclab/rtdc_dataset/fmt_hdf5/events.py +20 -1
- dclab/rtdc_dataset/fmt_hierarchy/base.py +1 -1
- dclab/rtdc_dataset/fmt_s3.py +29 -10
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +1 -1
- dclab/rtdc_dataset/fmt_tdms/naming.py +1 -1
- dclab/rtdc_dataset/writer.py +43 -11
- dclab/statistics.py +27 -4
- dclab/warn.py +1 -1
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/METADATA +26 -4
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/RECORD +48 -48
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/WHEEL +1 -1
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/entry_points.txt +0 -0
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/licenses/LICENSE +0 -0
- {dclab-0.62.17.dist-info → dclab-0.67.3.dist-info}/top_level.txt +0 -0
dclab/_version.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '0.
|
|
21
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.67.3'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 67, 3)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'g42c771e2c'
|
dclab/cli/task_tdms2rtdc.py
CHANGED
|
@@ -158,7 +158,7 @@ def tdms2rtdc_parser():
|
|
|
158
158
|
help='Compute features, such as volume or emodulus, '
|
|
159
159
|
+ 'that are otherwise computed on-the-fly. '
|
|
160
160
|
+ 'Use this if you want to minimize analysis '
|
|
161
|
-
+ 'time in e.g.
|
|
161
|
+
+ 'time in e.g. DCscope. CAUTION: ancillary '
|
|
162
162
|
+ 'feature recipes might be subject to change '
|
|
163
163
|
+ '(e.g. if an error is found in the recipe). '
|
|
164
164
|
+ 'Disabling this option maximizes '
|
dclab/cli/task_verify_dataset.py
CHANGED
|
@@ -56,9 +56,9 @@ def verify_dataset(path_in=None):
|
|
|
56
56
|
else:
|
|
57
57
|
# everything is ok
|
|
58
58
|
exit_status = 0
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
|
|
60
|
+
# return sys.exit for testing (monkeypatched)
|
|
61
|
+
return sys.exit(exit_status)
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def verify_dataset_parser():
|
dclab/definitions/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ from .feat_const import (
|
|
|
6
6
|
# these should not be used
|
|
7
7
|
feature_names, feature_labels, feature_name2label,
|
|
8
8
|
# this one should also not be used, but we wait with deprecation,
|
|
9
|
-
# because
|
|
9
|
+
# because DCscope heavily relies on it (it shouldn't)
|
|
10
10
|
scalar_feature_names
|
|
11
11
|
)
|
|
12
12
|
from .feat_logic import (
|
dclab/definitions/feat_const.py
CHANGED
|
@@ -91,11 +91,13 @@ FEATURES_SCALAR = [
|
|
|
91
91
|
# Sum of the pressures applied to sample and sheath flow
|
|
92
92
|
["pressure", "Pressure [mPa]"],
|
|
93
93
|
# QPI features computed from holographic data
|
|
94
|
-
["
|
|
95
|
-
["
|
|
94
|
+
["qpi_dm", "Dry mass [pg]"],
|
|
95
|
+
["qpi_dm_avg", "DEPRECATED Dry mass (average) [pg]"],
|
|
96
|
+
["qpi_dm_dns", "Dry mass density [mg/ml]"],
|
|
97
|
+
["qpi_dm_sd", "DEPRECATED Dry mass (SD) [pg]"],
|
|
96
98
|
["qpi_pha_int", "Integrated phase [rad]"],
|
|
97
99
|
["qpi_ri_avg", "Refractive index (average)"],
|
|
98
|
-
["qpi_ri_sd", "Refractive index (SD)"],
|
|
100
|
+
["qpi_ri_sd", "DEPRECATED Refractive index (SD)"],
|
|
99
101
|
# QPI features from refocused events
|
|
100
102
|
["qpi_focus", "Computed focus distance [µm]"],
|
|
101
103
|
# Size features
|
|
@@ -149,7 +151,7 @@ for _i in range(10):
|
|
|
149
151
|
#: consist of the integer array `[1, 3, 5, 7, ...]` (indexing starts at zero).
|
|
150
152
|
#: The `basinmap1` feature must then be referenced in the corresponding basin
|
|
151
153
|
#: definition. These features should not be presented explicitly to the
|
|
152
|
-
#: normal user (e.g. in
|
|
154
|
+
#: normal user (e.g. in DCscope) to avoid ambiguities, and they should
|
|
153
155
|
#: always be exported alongside basins that refer to them.
|
|
154
156
|
for _j in range(10):
|
|
155
157
|
FEATURES_SCALAR.append([f"basinmap{_j}", f"Basin mapping {_j}"])
|
dclab/definitions/feat_logic.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
1
3
|
from . import feat_const
|
|
2
4
|
|
|
3
5
|
|
|
6
|
+
ML_SCORE_REGEX = re.compile(r"^ml_score_[a-z0-9]{3}$")
|
|
7
|
+
|
|
8
|
+
|
|
4
9
|
def check_feature_shape(name, data):
|
|
5
10
|
"""Check if (non)-scalar feature matches with its data's dimensionality
|
|
6
11
|
|
|
@@ -17,11 +22,15 @@ def check_feature_shape(name, data):
|
|
|
17
22
|
If the data's shape does not match its scalar description
|
|
18
23
|
"""
|
|
19
24
|
if len(data.shape) == 1 and not scalar_feature_exists(name):
|
|
20
|
-
raise ValueError(
|
|
21
|
-
|
|
25
|
+
raise ValueError(
|
|
26
|
+
f"Feature '{name}' is not a scalar feature, but "
|
|
27
|
+
"a 1D array was given for `data`!"
|
|
28
|
+
)
|
|
22
29
|
elif len(data.shape) != 1 and scalar_feature_exists(name):
|
|
23
|
-
raise ValueError(
|
|
24
|
-
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"Feature '{name}' is a scalar feature, but the "
|
|
32
|
+
"`data` array is not 1D!"
|
|
33
|
+
)
|
|
25
34
|
|
|
26
35
|
|
|
27
36
|
def feature_exists(name, scalar_only=False):
|
|
@@ -56,15 +65,9 @@ def feature_exists(name, scalar_only=False):
|
|
|
56
65
|
elif not scalar_only and name in feat_const.feature_names:
|
|
57
66
|
# non-scalar feature
|
|
58
67
|
valid = True
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
if (name.startswith("ml_score_")
|
|
63
|
-
and len(name) == len("ml_score_???")
|
|
64
|
-
and name[-3] in valid_chars
|
|
65
|
-
and name[-2] in valid_chars
|
|
66
|
-
and name[-1] in valid_chars):
|
|
67
|
-
valid = True
|
|
68
|
+
elif ML_SCORE_REGEX.match(name):
|
|
69
|
+
# machine-learning score feature ml_score_???
|
|
70
|
+
valid = True
|
|
68
71
|
return valid
|
|
69
72
|
|
|
70
73
|
|
|
@@ -93,8 +96,10 @@ def feature_register(name, label=None, is_scalar=True):
|
|
|
93
96
|
allowed_chars = "abcdefghijklmnopqrstuvwxyz_1234567890"
|
|
94
97
|
feat = "".join([f for f in name if f in allowed_chars])
|
|
95
98
|
if feat != name:
|
|
96
|
-
raise ValueError(
|
|
97
|
-
|
|
99
|
+
raise ValueError(
|
|
100
|
+
"`feature` must only contain lower-case characters, "
|
|
101
|
+
f"digits, and underscores; got '{name}'!"
|
|
102
|
+
)
|
|
98
103
|
if label is None:
|
|
99
104
|
label = f"User-defined feature {name}"
|
|
100
105
|
if feature_exists(name):
|
|
@@ -156,22 +161,16 @@ def get_feature_label(name, rtdc_ds=None, with_unit=True):
|
|
|
156
161
|
TODO: extract feature label from ancillary information when an rtdc_ds is
|
|
157
162
|
given.
|
|
158
163
|
"""
|
|
159
|
-
# TODO: Is there another way of avoiding this circular import?
|
|
160
|
-
from ..rtdc_dataset.feat_anc_core.ancillary_feature import AncillaryFeature
|
|
161
|
-
assert feature_exists(name)
|
|
162
164
|
if name in feat_const.feature_name2label:
|
|
163
165
|
label = feat_const.feature_name2label[name]
|
|
166
|
+
elif ML_SCORE_REGEX.match(name):
|
|
167
|
+
# use a generic name for machine-learning features
|
|
168
|
+
label = f"ML score {name[-3:].upper()}"
|
|
164
169
|
else:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
labelid = af.data.outputs.index(name)
|
|
170
|
-
label = af.data.output_labels[labelid]
|
|
171
|
-
break
|
|
172
|
-
else:
|
|
173
|
-
# If that did not work, use a generic name.
|
|
174
|
-
label = "ML score {}".format(name[-3:].upper())
|
|
170
|
+
exists = feature_exists(name)
|
|
171
|
+
msg = f"Could not find label for '{name}'"
|
|
172
|
+
msg += " (feature does not exist)" if not exists else ""
|
|
173
|
+
raise ValueError(msg)
|
|
175
174
|
if not with_unit:
|
|
176
175
|
if label.endswith("]") and label.count("["):
|
|
177
176
|
label = label.rsplit("[", 1)[0].strip()
|
|
Binary file
|
dclab/downsampling.pyx
CHANGED
|
@@ -176,14 +176,19 @@ def downsample_grid(a, b, samples, remove_invalid=False, ret_idx=False):
|
|
|
176
176
|
if not remove_invalid:
|
|
177
177
|
diff_bad = (samples_int or keep.size) - np.sum(keep)
|
|
178
178
|
if diff_bad > 0:
|
|
179
|
-
# Add a few of the invalid values so that in the end
|
|
180
|
-
# we have the desired array size.
|
|
181
179
|
add_indices_bad = np.where(bad)[0]
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
if add_indices_bad.size > diff_bad:
|
|
181
|
+
# Add a few of the invalid values so that in the end
|
|
182
|
+
# we have the desired array size.
|
|
183
|
+
np.random.set_state(rs)
|
|
184
|
+
add_bad = np.random.choice(add_indices_bad,
|
|
185
|
+
size=diff_bad,
|
|
186
|
+
replace=False)
|
|
187
|
+
keep[add_bad] = True
|
|
188
|
+
else:
|
|
189
|
+
# We don't have enough bad indices (or just enough),
|
|
190
|
+
# so we add them all.
|
|
191
|
+
keep[add_indices_bad] = True
|
|
187
192
|
|
|
188
193
|
# paulmueller 2024-01-03
|
|
189
194
|
# if samples_int and not remove_invalid:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
dclab/features/bright.py
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
Computation of mean and standard deviation of grayscale values inside the
|
|
3
3
|
RT-DC event image mask.
|
|
4
4
|
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
def get_bright(mask
|
|
11
|
+
def get_bright(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
|
|
12
|
+
image: npt.NDArray | list[npt.NDArray],
|
|
13
|
+
ret_data: str = "avg,sd"
|
|
14
|
+
) -> (float |
|
|
15
|
+
npt.NDArray |
|
|
16
|
+
tuple[float, float] |
|
|
17
|
+
tuple[npt.NDArray, npt.NDArray]):
|
|
9
18
|
"""Compute avg and/or std of the event brightness
|
|
10
19
|
|
|
11
20
|
The event brightness is defined by the gray-scale values of the
|
|
@@ -18,7 +27,7 @@ def get_bright(mask, image, ret_data="avg,sd"):
|
|
|
18
27
|
image: ndarray or list of ndarrays of shape (M,N)
|
|
19
28
|
A 2D array that holds the image in form of grayscale values
|
|
20
29
|
of an event.
|
|
21
|
-
ret_data
|
|
30
|
+
ret_data
|
|
22
31
|
A comma-separated list of metrices to compute
|
|
23
32
|
- "avg": compute the average
|
|
24
33
|
- "sd": compute the standard deviation
|
dclab/features/bright_bc.py
CHANGED
|
@@ -2,10 +2,21 @@
|
|
|
2
2
|
Computation of mean and standard deviation of grayscale values inside the
|
|
3
3
|
RT-DC event image mask with background-correction taken into account.
|
|
4
4
|
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
def get_bright_bc(mask
|
|
11
|
+
def get_bright_bc(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
|
|
12
|
+
image: npt.NDArray | list[npt.NDArray],
|
|
13
|
+
image_bg: npt.NDArray | list[npt.NDArray],
|
|
14
|
+
bg_off: float | npt.NDArray = None,
|
|
15
|
+
ret_data: str = "avg,sd"
|
|
16
|
+
) -> (float |
|
|
17
|
+
npt.NDArray |
|
|
18
|
+
tuple[float, float] |
|
|
19
|
+
tuple[npt.NDArray, npt.NDArray]):
|
|
9
20
|
"""Compute avg and/or std of the background-corrected event brightness
|
|
10
21
|
|
|
11
22
|
The background-corrected event brightness is defined by the
|
|
@@ -24,7 +35,7 @@ def get_bright_bc(mask, image, image_bg, bg_off=None, ret_data="avg,sd"):
|
|
|
24
35
|
bg_off: float or 1D ndarray
|
|
25
36
|
Additional offset value that is added to `image_bg` before
|
|
26
37
|
background correction
|
|
27
|
-
ret_data
|
|
38
|
+
ret_data
|
|
28
39
|
A comma-separated list of metrices to compute
|
|
29
40
|
- "avg": compute the average
|
|
30
41
|
- "sd": compute the standard deviation
|
dclab/features/bright_perc.py
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
Computation of the 10th and 90th percentile of grayscale values inside the
|
|
3
3
|
RT-DC event image mask with background-correction taken into account.
|
|
4
4
|
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
6
9
|
|
|
7
10
|
|
|
8
|
-
def get_bright_perc(mask
|
|
11
|
+
def get_bright_perc(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
|
|
12
|
+
image: npt.NDArray | list[npt.NDArray],
|
|
13
|
+
image_bg: npt.NDArray | list[npt.NDArray],
|
|
14
|
+
bg_off: float | npt.NDArray = None
|
|
15
|
+
) -> (tuple[float, float] |
|
|
16
|
+
tuple[npt.NDArray, npt.NDArray]):
|
|
9
17
|
"""Compute 10th and 90th percentile of the bg-corrected event brightness
|
|
10
18
|
|
|
11
19
|
The background-corrected event brightness is defined by the
|
|
@@ -29,7 +37,7 @@ def get_bright_perc(mask, image, image_bg, bg_off=None):
|
|
|
29
37
|
-------
|
|
30
38
|
bright_perc_10: float or ndarray of size N
|
|
31
39
|
10th percentile of brightness
|
|
32
|
-
|
|
40
|
+
bright_perc_90: float or ndarray of size N
|
|
33
41
|
90th percentile of brightness
|
|
34
42
|
"""
|
|
35
43
|
if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
|
dclab/features/contour.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""Computation of event contour from event mask"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
from collections import deque
|
|
3
5
|
import numbers
|
|
4
6
|
|
|
5
7
|
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
6
9
|
|
|
7
10
|
# equivalent to
|
|
8
11
|
# from skimage.measure import find_contours
|
|
@@ -14,15 +17,15 @@ class NoValidContourFoundError(BaseException):
|
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class LazyContourList(object):
|
|
17
|
-
def __init__(self, masks, max_events=1000):
|
|
20
|
+
def __init__(self, masks: npt.ArrayLike, max_events: int = 1000) -> None:
|
|
18
21
|
"""A list-like object that computes contours upon indexing
|
|
19
22
|
|
|
20
23
|
Parameters
|
|
21
24
|
----------
|
|
22
|
-
masks
|
|
25
|
+
masks
|
|
23
26
|
3D array of masks, may be an HDF5 dataset or any other
|
|
24
27
|
structure that supports indexing
|
|
25
|
-
max_events
|
|
28
|
+
max_events
|
|
26
29
|
maximum number of contours to keep in the contour list;
|
|
27
30
|
set to 0/False/None to cache all contours
|
|
28
31
|
|
|
@@ -40,7 +43,7 @@ class LazyContourList(object):
|
|
|
40
43
|
self.identifier = str(masks[0][:].tobytes())
|
|
41
44
|
self.shape = len(masks), np.nan, 2
|
|
42
45
|
|
|
43
|
-
def __getitem__(self, idx):
|
|
46
|
+
def __getitem__(self, idx) -> npt.NDArray:
|
|
44
47
|
"""Compute contour(s) if not already in self.contours"""
|
|
45
48
|
if not isinstance(idx, numbers.Integral):
|
|
46
49
|
# slicing!
|
|
@@ -74,7 +77,8 @@ class LazyContourList(object):
|
|
|
74
77
|
return len(self.masks)
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def get_contour(mask
|
|
80
|
+
def get_contour(mask: npt.NDArray[np.bool | np.int]
|
|
81
|
+
) -> npt.NDArray | list[npt.NDArray]:
|
|
78
82
|
"""Compute the image contour from a mask
|
|
79
83
|
|
|
80
84
|
The contour is computed in a very inefficient way using scikit-image
|
|
@@ -127,7 +131,8 @@ def get_contour(mask):
|
|
|
127
131
|
return contours[0]
|
|
128
132
|
|
|
129
133
|
|
|
130
|
-
def get_contour_lazily(mask
|
|
134
|
+
def get_contour_lazily(mask: npt.NDArray[np.bool | np.int]
|
|
135
|
+
) -> npt.NDArray | LazyContourList:
|
|
131
136
|
"""Like :func:`get_contour`, but computes contours on demand
|
|
132
137
|
|
|
133
138
|
Parameters
|
|
@@ -153,7 +158,7 @@ def get_contour_lazily(mask):
|
|
|
153
158
|
return cont
|
|
154
159
|
|
|
155
160
|
|
|
156
|
-
def remove_duplicates(cont):
|
|
161
|
+
def remove_duplicates(cont: npt.NDArray) -> npt.NDArray:
|
|
157
162
|
"""Remove duplicates in a circular contour"""
|
|
158
163
|
x = np.resize(cont, (len(cont) + 1, 2))
|
|
159
164
|
selection = np.ones(len(x), dtype=bool)
|
|
@@ -7,6 +7,7 @@ from typing import Literal
|
|
|
7
7
|
import warnings
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
|
+
import numpy.typing as npt
|
|
10
11
|
import scipy.interpolate as spint
|
|
11
12
|
|
|
12
13
|
from ...warn import PipelineWarning
|
|
@@ -18,7 +19,6 @@ from .scale_linear import convert # noqa: F401
|
|
|
18
19
|
from .scale_linear import scale_emodulus, scale_feature
|
|
19
20
|
from .viscosity import get_viscosity
|
|
20
21
|
|
|
21
|
-
|
|
22
22
|
#: Set this to True to globally enable spline extrapolation when the
|
|
23
23
|
#: `area_um`/`deform` data are outside the LUT. This is discouraged and
|
|
24
24
|
#: a :class:`KnowWhatYouAreDoingWarning` warning will be issued.
|
|
@@ -33,8 +33,13 @@ class YoungsModulusLookupTableExceededWarning(PipelineWarning):
|
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def extrapolate_emodulus(lut
|
|
37
|
-
|
|
36
|
+
def extrapolate_emodulus(lut: npt.NDArray,
|
|
37
|
+
datax: npt.NDArray,
|
|
38
|
+
deform: npt.NDArray,
|
|
39
|
+
emod: npt.NDArray,
|
|
40
|
+
deform_norm: float,
|
|
41
|
+
deform_thresh: float = .05,
|
|
42
|
+
inplace: bool = True) -> npt.NDArray:
|
|
38
43
|
"""Use spline interpolation to fill in nan-values
|
|
39
44
|
|
|
40
45
|
When points (`datax`, `deform`) are outside the convex
|
|
@@ -61,16 +66,16 @@ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
|
|
|
61
66
|
emod: ndarray of size N
|
|
62
67
|
The emodulus (corresponding to `lut[:, 2]`); If `emod`
|
|
63
68
|
does not contain nan-values, there is nothing to do here.
|
|
64
|
-
deform_norm
|
|
69
|
+
deform_norm
|
|
65
70
|
The normalization value used to normalize `lut[:, 1]` and
|
|
66
71
|
`deform`.
|
|
67
|
-
deform_thresh
|
|
72
|
+
deform_thresh
|
|
68
73
|
Not the entire LUT is used for bivariate spline interpolation.
|
|
69
74
|
Only the points where `lut[:, 1] > deform_thresh/deform_norm`
|
|
70
75
|
are used. This is necessary, because for small deformations,
|
|
71
76
|
the LUT has an extreme slope that kills any meaningful
|
|
72
77
|
spline interpolation.
|
|
73
|
-
inplace
|
|
78
|
+
inplace
|
|
74
79
|
If True (default), replaces nan values in `emod` in-place.
|
|
75
80
|
If False, `emod` is not modified.
|
|
76
81
|
"""
|
|
@@ -99,50 +104,51 @@ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
|
|
|
99
104
|
return emod
|
|
100
105
|
|
|
101
106
|
|
|
102
|
-
def get_emodulus(deform: float |
|
|
103
|
-
area_um: float |
|
|
104
|
-
volume: float |
|
|
107
|
+
def get_emodulus(deform: float | npt.NDArray,
|
|
108
|
+
area_um: float | npt.NDArray | None = None,
|
|
109
|
+
volume: float | npt.NDArray | None = None,
|
|
105
110
|
medium: float | str = "0.49% MC-PBS",
|
|
106
111
|
channel_width: float = 20.0,
|
|
107
112
|
flow_rate: float = 0.16,
|
|
108
113
|
px_um: float = 0.34,
|
|
109
|
-
temperature: float |
|
|
110
|
-
lut_data: str | pathlib.Path |
|
|
114
|
+
temperature: float | npt.NDArray | None = 23.0,
|
|
115
|
+
lut_data: (str | pathlib.Path |
|
|
116
|
+
tuple[npt.NDArray, dict]) = "LE-2D-FEM-19",
|
|
111
117
|
visc_model: Literal['herold-2017',
|
|
112
118
|
'herold-2017-fallback',
|
|
113
119
|
'buyukurganci-2022',
|
|
114
120
|
'kestin-1978',
|
|
115
121
|
None] = "herold-2017-fallback",
|
|
116
122
|
extrapolate: bool = INACCURATE_SPLINE_EXTRAPOLATION,
|
|
117
|
-
copy: bool = True):
|
|
123
|
+
copy: bool = True) -> npt.NDArray:
|
|
118
124
|
"""Compute apparent Young's modulus using a look-up table
|
|
119
125
|
|
|
120
126
|
Parameters
|
|
121
127
|
----------
|
|
122
|
-
|
|
123
|
-
Apparent (2D image) area [µm²] of the event(s)
|
|
124
|
-
deform: float or ndarray
|
|
128
|
+
deform
|
|
125
129
|
Deformation (1-circularity) of the event(s)
|
|
126
|
-
|
|
130
|
+
area_um
|
|
131
|
+
Apparent (2D image) area [µm²] of the event(s)
|
|
132
|
+
volume
|
|
127
133
|
Apparent volume of the event(s). It is not possible to define
|
|
128
134
|
`volume` and `area_um` at the same time (makes no sense).
|
|
129
135
|
|
|
130
136
|
.. versionadded:: 0.25.0
|
|
131
|
-
medium
|
|
137
|
+
medium
|
|
132
138
|
The medium to compute the viscosity for. If a string
|
|
133
139
|
is given, the viscosity is computed. If a float is given,
|
|
134
140
|
this value is used as the viscosity in mPa*s (Note that
|
|
135
141
|
`temperature` and `visc_model` must be set to None in this case).
|
|
136
|
-
channel_width
|
|
142
|
+
channel_width
|
|
137
143
|
The channel width [µm]
|
|
138
|
-
flow_rate
|
|
144
|
+
flow_rate
|
|
139
145
|
Flow rate [µL/s]
|
|
140
|
-
px_um
|
|
146
|
+
px_um
|
|
141
147
|
The detector pixel size [µm] used for pixelation correction.
|
|
142
148
|
Set to zero to disable.
|
|
143
|
-
temperature
|
|
149
|
+
temperature
|
|
144
150
|
Temperature [°C] of the event(s)
|
|
145
|
-
lut_data:
|
|
151
|
+
lut_data: str, path, or tuple of (np.ndarray of shape (N, 3), dict)
|
|
146
152
|
The LUT data to use. If it is a built-in identifier,
|
|
147
153
|
then the respective LUT will be used. Otherwise, a path to a
|
|
148
154
|
file on disk or a tuple (LUT array, metadata) is possible.
|
|
@@ -150,14 +156,14 @@ def get_emodulus(deform: float | np.array,
|
|
|
150
156
|
(e.g. `area_um` and `deform`) are valid interpolation choices.
|
|
151
157
|
|
|
152
158
|
.. versionadded:: 0.25.0
|
|
153
|
-
visc_model
|
|
159
|
+
visc_model
|
|
154
160
|
The viscosity model to use,
|
|
155
161
|
see :func:`dclab.features.emodulus.viscosity.get_viscosity`
|
|
156
|
-
extrapolate
|
|
162
|
+
extrapolate
|
|
157
163
|
Perform extrapolation using :func:`extrapolate_emodulus`. This
|
|
158
164
|
is discouraged!
|
|
159
|
-
copy
|
|
160
|
-
Copy input arrays. If set to
|
|
165
|
+
copy
|
|
166
|
+
Copy input arrays. If set to False, input arrays are
|
|
161
167
|
overridden.
|
|
162
168
|
|
|
163
169
|
Returns
|
|
@@ -326,7 +332,7 @@ def get_emodulus(deform: float | np.array,
|
|
|
326
332
|
return emod
|
|
327
333
|
|
|
328
334
|
|
|
329
|
-
def normalize(data, dmax):
|
|
335
|
+
def normalize(data: npt.NDArray, dmax: float) -> npt.NDArray:
|
|
330
336
|
"""Perform normalization in-place for interpolation
|
|
331
337
|
|
|
332
338
|
Note that :func:`scipy.interpolate.griddata` has a `rescale`
|
dclab/features/emodulus/load.py
CHANGED
|
@@ -12,6 +12,7 @@ import importlib_resources
|
|
|
12
12
|
import warnings
|
|
13
13
|
|
|
14
14
|
import numpy as np
|
|
15
|
+
import numpy.typing as npt
|
|
15
16
|
|
|
16
17
|
from ... import definitions as dfn
|
|
17
18
|
|
|
@@ -24,7 +25,7 @@ EXTERNAL_LUTS = {}
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
@functools.lru_cache()
|
|
27
|
-
def get_internal_lut_names_dict():
|
|
28
|
+
def get_internal_lut_names_dict() -> dict:
|
|
28
29
|
"""Return list of internal lut names"""
|
|
29
30
|
lutfiles = {}
|
|
30
31
|
for pp in importlib_resources.files('dclab.features.emodulus').iterdir():
|
|
@@ -34,10 +35,10 @@ def get_internal_lut_names_dict():
|
|
|
34
35
|
return lutfiles
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def get_lut_path(path_or_id):
|
|
38
|
+
def get_lut_path(path_or_id: str | pathlib.Path) -> pathlib.Path:
|
|
38
39
|
"""Find the path to a LUT
|
|
39
40
|
|
|
40
|
-
path_or_id
|
|
41
|
+
path_or_id
|
|
41
42
|
Identifier of a LUT. This can be either an existing path
|
|
42
43
|
(checked first), or an internal identifier (see
|
|
43
44
|
:func:`get_internal_lut_names_dict`).
|
|
@@ -63,7 +64,8 @@ def get_lut_path(path_or_id):
|
|
|
63
64
|
return lut_path
|
|
64
65
|
|
|
65
66
|
|
|
66
|
-
def load_lut(lut_data: str | pathlib.Path |
|
|
67
|
+
def load_lut(lut_data: str | pathlib.Path | tuple[npt.NDArray, dict] =
|
|
68
|
+
"LE-2D-FEM-19") -> tuple[npt.NDArray, dict]:
|
|
67
69
|
"""Load LUT data from disk
|
|
68
70
|
|
|
69
71
|
Parameters
|
|
@@ -98,7 +100,7 @@ def load_lut(lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19"):
|
|
|
98
100
|
return lut, meta
|
|
99
101
|
|
|
100
102
|
|
|
101
|
-
def load_mtext(path):
|
|
103
|
+
def load_mtext(path: str | pathlib.Path) -> tuple[npt.NDArray, dict]:
|
|
102
104
|
"""Load column-based data from text files with metadata
|
|
103
105
|
|
|
104
106
|
This file format is used for isoelasticity lines and look-up
|
|
@@ -217,7 +219,7 @@ def load_mtext(path):
|
|
|
217
219
|
return data, meta
|
|
218
220
|
|
|
219
221
|
|
|
220
|
-
def register_lut(path, identifier=None):
|
|
222
|
+
def register_lut(path: str | pathlib.Path, identifier: str = None) -> None:
|
|
221
223
|
"""Register an external LUT file in dclab
|
|
222
224
|
|
|
223
225
|
This will add it to :const:`EXTERNAL_LUTS`, which is required
|