dclab 0.67.0__cp314-cp314-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.
Potentially problematic release.
This version of dclab might be problematic. Click here for more details.
- dclab/__init__.py +41 -0
- dclab/_version.py +34 -0
- dclab/cached.py +97 -0
- dclab/cli/__init__.py +10 -0
- dclab/cli/common.py +237 -0
- dclab/cli/task_compress.py +126 -0
- dclab/cli/task_condense.py +223 -0
- dclab/cli/task_join.py +229 -0
- dclab/cli/task_repack.py +98 -0
- dclab/cli/task_split.py +154 -0
- dclab/cli/task_tdms2rtdc.py +186 -0
- dclab/cli/task_verify_dataset.py +75 -0
- dclab/definitions/__init__.py +79 -0
- dclab/definitions/feat_const.py +202 -0
- dclab/definitions/feat_logic.py +182 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cpython-314-darwin.so +0 -0
- dclab/downsampling.pyx +230 -0
- dclab/external/__init__.py +4 -0
- dclab/external/packaging/LICENSE +3 -0
- dclab/external/packaging/LICENSE.APACHE +177 -0
- dclab/external/packaging/LICENSE.BSD +23 -0
- dclab/external/packaging/__init__.py +6 -0
- dclab/external/packaging/_structures.py +61 -0
- dclab/external/packaging/version.py +505 -0
- dclab/external/skimage/LICENSE +28 -0
- dclab/external/skimage/__init__.py +2 -0
- dclab/external/skimage/_find_contours.py +216 -0
- dclab/external/skimage/_find_contours_cy.cpython-314-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cpython-314-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.pxd +6 -0
- dclab/external/skimage/_shared/geometry.pyx +55 -0
- dclab/external/skimage/measure.py +7 -0
- dclab/external/skimage/pnpoly.py +53 -0
- dclab/external/statsmodels/LICENSE +35 -0
- dclab/external/statsmodels/__init__.py +6 -0
- dclab/external/statsmodels/nonparametric/__init__.py +1 -0
- dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
- dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
- dclab/external/statsmodels/nonparametric/kernels.py +36 -0
- dclab/features/__init__.py +9 -0
- dclab/features/bright.py +81 -0
- dclab/features/bright_bc.py +93 -0
- dclab/features/bright_perc.py +63 -0
- dclab/features/contour.py +161 -0
- dclab/features/emodulus/__init__.py +339 -0
- dclab/features/emodulus/load.py +252 -0
- dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
- dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
- dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
- dclab/features/emodulus/pxcorr.py +135 -0
- dclab/features/emodulus/scale_linear.py +247 -0
- dclab/features/emodulus/viscosity.py +260 -0
- dclab/features/fl_crosstalk.py +95 -0
- dclab/features/inert_ratio.py +377 -0
- dclab/features/volume.py +242 -0
- dclab/http_utils.py +322 -0
- dclab/isoelastics/__init__.py +468 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
- dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
- dclab/kde/__init__.py +1 -0
- dclab/kde/base.py +459 -0
- dclab/kde/contours.py +222 -0
- dclab/kde/methods.py +313 -0
- dclab/kde_contours.py +10 -0
- dclab/kde_methods.py +11 -0
- dclab/lme4/__init__.py +5 -0
- dclab/lme4/lme4_template.R +94 -0
- dclab/lme4/rsetup.py +204 -0
- dclab/lme4/wrapr.py +386 -0
- dclab/polygon_filter.py +398 -0
- dclab/rtdc_dataset/__init__.py +15 -0
- dclab/rtdc_dataset/check.py +902 -0
- dclab/rtdc_dataset/config.py +533 -0
- dclab/rtdc_dataset/copier.py +353 -0
- dclab/rtdc_dataset/core.py +896 -0
- dclab/rtdc_dataset/export.py +867 -0
- dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
- dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
- dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
- dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
- dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
- dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
- dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
- dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
- dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
- dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
- dclab/rtdc_dataset/feat_basin.py +762 -0
- dclab/rtdc_dataset/feat_temp.py +102 -0
- dclab/rtdc_dataset/filter.py +263 -0
- dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
- dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
- dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
- dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
- dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
- dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
- dclab/rtdc_dataset/fmt_dict.py +103 -0
- dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
- dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
- dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
- dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
- dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
- dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
- dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
- dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
- dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
- dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
- dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
- dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
- dclab/rtdc_dataset/fmt_http.py +102 -0
- dclab/rtdc_dataset/fmt_s3.py +354 -0
- dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
- dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
- dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
- dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
- dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
- dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
- dclab/rtdc_dataset/load.py +77 -0
- dclab/rtdc_dataset/meta_table.py +25 -0
- dclab/rtdc_dataset/writer.py +1019 -0
- dclab/statistics.py +226 -0
- dclab/util.py +176 -0
- dclab/warn.py +15 -0
- dclab-0.67.0.dist-info/METADATA +153 -0
- dclab-0.67.0.dist-info/RECORD +142 -0
- dclab-0.67.0.dist-info/WHEEL +6 -0
- dclab-0.67.0.dist-info/entry_points.txt +8 -0
- dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
- dclab-0.67.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Computation of event contour from event mask"""
|
|
2
|
+
from collections import deque
|
|
3
|
+
import numbers
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
# equivalent to
|
|
8
|
+
# from skimage.measure import find_contours
|
|
9
|
+
from ..external.skimage.measure import find_contours
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NoValidContourFoundError(BaseException):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LazyContourList(object):
|
|
17
|
+
def __init__(self, masks, max_events=1000):
|
|
18
|
+
"""A list-like object that computes contours upon indexing
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
masks: array-like
|
|
23
|
+
3D array of masks, may be an HDF5 dataset or any other
|
|
24
|
+
structure that supports indexing
|
|
25
|
+
max_events: int
|
|
26
|
+
maximum number of contours to keep in the contour list;
|
|
27
|
+
set to 0/False/None to cache all contours
|
|
28
|
+
|
|
29
|
+
.. versionchanged:: 0.58.3
|
|
30
|
+
|
|
31
|
+
Added the `max_events` parameter which now makes this class
|
|
32
|
+
a lazy, least-recently-used contour list. To achieve the
|
|
33
|
+
old behavior (which may fill up your memory), set
|
|
34
|
+
`max_events=None`.
|
|
35
|
+
"""
|
|
36
|
+
self.masks = masks
|
|
37
|
+
self.contours = deque(maxlen=max_events or None)
|
|
38
|
+
self.indices = deque(maxlen=max_events or None)
|
|
39
|
+
#: used for hashing in ancillary features
|
|
40
|
+
self.identifier = str(masks[0][:].tobytes())
|
|
41
|
+
self.shape = len(masks), np.nan, 2
|
|
42
|
+
|
|
43
|
+
def __getitem__(self, idx):
|
|
44
|
+
"""Compute contour(s) if not already in self.contours"""
|
|
45
|
+
if not isinstance(idx, numbers.Integral):
|
|
46
|
+
# slicing!
|
|
47
|
+
indices = np.arange(len(self))[idx]
|
|
48
|
+
output = []
|
|
49
|
+
# populate the output list
|
|
50
|
+
for evid in indices:
|
|
51
|
+
output.append(self.__getitem__(evid))
|
|
52
|
+
return output
|
|
53
|
+
else:
|
|
54
|
+
try:
|
|
55
|
+
# Is the contour already available?
|
|
56
|
+
idx_q = self.indices.index(idx)
|
|
57
|
+
except ValueError:
|
|
58
|
+
# The contour is not there. Compute it.
|
|
59
|
+
try:
|
|
60
|
+
cont = get_contour(self.masks[idx])
|
|
61
|
+
except BaseException as e:
|
|
62
|
+
e.args = (f"Event {idx}, {e.args[0]}",)
|
|
63
|
+
raise
|
|
64
|
+
else:
|
|
65
|
+
# Get the contour from deque
|
|
66
|
+
cont = self.contours[idx_q]
|
|
67
|
+
# If we got here, it means that we have computed a contour
|
|
68
|
+
# successfully. Store it in the deque.
|
|
69
|
+
self.contours.append(cont)
|
|
70
|
+
self.indices.append(idx)
|
|
71
|
+
return cont
|
|
72
|
+
|
|
73
|
+
def __len__(self):
|
|
74
|
+
return len(self.masks)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_contour(mask):
|
|
78
|
+
"""Compute the image contour from a mask
|
|
79
|
+
|
|
80
|
+
The contour is computed in a very inefficient way using scikit-image
|
|
81
|
+
and a conversion of float coordinates to pixel coordinates.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
mask: binary ndarray of shape (M,N) or (K,M,N)
|
|
86
|
+
The mask outlining the pixel positions of the event.
|
|
87
|
+
If a 3d array is given, then `K` indexes the individual
|
|
88
|
+
contours.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
cont: ndarray or list of K ndarrays of shape (J,2)
|
|
93
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
94
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
95
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
96
|
+
correspond to the x- and y-coordinates of the contour.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
|
|
99
|
+
mask = [mask]
|
|
100
|
+
ret_list = False
|
|
101
|
+
else:
|
|
102
|
+
ret_list = True
|
|
103
|
+
contours = []
|
|
104
|
+
|
|
105
|
+
for mi in mask:
|
|
106
|
+
# This is only 10% slower than doing:
|
|
107
|
+
# conts, _ = cv2.findContours(np.array(mi, dtype=np.uint8),
|
|
108
|
+
# cv2.RETR_EXTERNAL,
|
|
109
|
+
# cv2.CHAIN_APPROX_NONE)
|
|
110
|
+
# c2 = conts[0].reshape(-1, 2)
|
|
111
|
+
conts = find_contours(mi.transpose(),
|
|
112
|
+
level=.9999,
|
|
113
|
+
positive_orientation="low",
|
|
114
|
+
fully_connected="high")
|
|
115
|
+
# get the longest contour
|
|
116
|
+
c0 = sorted(conts, key=lambda x: len(x))[-1]
|
|
117
|
+
# round all coordinates to pixel values
|
|
118
|
+
c1 = np.asarray(np.round(c0), int)
|
|
119
|
+
# remove duplicates
|
|
120
|
+
c2 = remove_duplicates(c1)
|
|
121
|
+
if len(c2) == 0:
|
|
122
|
+
raise NoValidContourFoundError("No contour found!")
|
|
123
|
+
contours.append(c2)
|
|
124
|
+
if ret_list:
|
|
125
|
+
return contours
|
|
126
|
+
else:
|
|
127
|
+
return contours[0]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def get_contour_lazily(mask):
|
|
131
|
+
"""Like :func:`get_contour`, but computes contours on demand
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
mask: binary ndarray of shape (M,N) or (K,M,N)
|
|
136
|
+
The mask outlining the pixel positions of the event.
|
|
137
|
+
If a 3d array is given, then `K` indexes the individual
|
|
138
|
+
contours.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
cont: ndarray or LazyContourList of K ndarrays of shape (J,2)
|
|
143
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
144
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
145
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
146
|
+
correspond to the x- and y-coordinates of the contour.
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
|
|
149
|
+
# same behavior as `get_contour`
|
|
150
|
+
cont = get_contour(mask=mask)
|
|
151
|
+
else:
|
|
152
|
+
cont = LazyContourList(masks=mask)
|
|
153
|
+
return cont
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def remove_duplicates(cont):
|
|
157
|
+
"""Remove duplicates in a circular contour"""
|
|
158
|
+
x = np.resize(cont, (len(cont) + 1, 2))
|
|
159
|
+
selection = np.ones(len(x), dtype=bool)
|
|
160
|
+
selection[1:] = ~np.prod((x[1:] == x[:-1]), axis=1, dtype=bool)
|
|
161
|
+
return x[selection][:-1]
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""Computation of apparent Young's modulus for RT-DC measurements"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import numbers
|
|
5
|
+
import pathlib
|
|
6
|
+
from typing import Literal
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import scipy.interpolate as spint
|
|
11
|
+
|
|
12
|
+
from ...warn import PipelineWarning
|
|
13
|
+
from .load import load_lut, load_mtext, register_lut # noqa: F401
|
|
14
|
+
from .pxcorr import get_pixelation_delta
|
|
15
|
+
from .pxcorr import get_pixelation_delta_pair # noqa: F401
|
|
16
|
+
# TODO: remove deprecated `convert`
|
|
17
|
+
from .scale_linear import convert # noqa: F401
|
|
18
|
+
from .scale_linear import scale_emodulus, scale_feature
|
|
19
|
+
from .viscosity import get_viscosity
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
#: Set this to True to globally enable spline extrapolation when the
|
|
23
|
+
#: `area_um`/`deform` data are outside the LUT. This is discouraged and
|
|
24
|
+
#: a :class:`KnowWhatYouAreDoingWarning` warning will be issued.
|
|
25
|
+
INACCURATE_SPLINE_EXTRAPOLATION = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class KnowWhatYouAreDoingWarning(PipelineWarning):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class YoungsModulusLookupTableExceededWarning(PipelineWarning):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
|
|
37
|
+
deform_thresh=.05, inplace=True):
|
|
38
|
+
"""Use spline interpolation to fill in nan-values
|
|
39
|
+
|
|
40
|
+
When points (`datax`, `deform`) are outside the convex
|
|
41
|
+
hull of the lut, then :func:`scipy.interpolate.griddata` returns
|
|
42
|
+
nan-valules.
|
|
43
|
+
|
|
44
|
+
With this function, some of these nan-values are extrapolated
|
|
45
|
+
using :class:`scipy.interpolate.SmoothBivariateSpline`. The
|
|
46
|
+
supported extrapolation values are currently limited to those
|
|
47
|
+
where the deformation is above 0.05.
|
|
48
|
+
|
|
49
|
+
A warning will be issued, because this is not really
|
|
50
|
+
recommended.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
lut: ndarray of shape (N, 3)
|
|
55
|
+
The normalized (!! see :func:`normalize`) LUT (first axis is
|
|
56
|
+
points, second axis enumerates datax, deform, and emodulus)
|
|
57
|
+
datax: ndarray of size N
|
|
58
|
+
The normalized x data (corresponding to `lut[:, 0]`)
|
|
59
|
+
deform: ndarray of size N
|
|
60
|
+
The normalized deform (corresponding to `lut[:, 1]`)
|
|
61
|
+
emod: ndarray of size N
|
|
62
|
+
The emodulus (corresponding to `lut[:, 2]`); If `emod`
|
|
63
|
+
does not contain nan-values, there is nothing to do here.
|
|
64
|
+
deform_norm: float
|
|
65
|
+
The normalization value used to normalize `lut[:, 1]` and
|
|
66
|
+
`deform`.
|
|
67
|
+
deform_thresh: float
|
|
68
|
+
Not the entire LUT is used for bivariate spline interpolation.
|
|
69
|
+
Only the points where `lut[:, 1] > deform_thresh/deform_norm`
|
|
70
|
+
are used. This is necessary, because for small deformations,
|
|
71
|
+
the LUT has an extreme slope that kills any meaningful
|
|
72
|
+
spline interpolation.
|
|
73
|
+
inplace: bool
|
|
74
|
+
If True (default), replaces nan values in `emod` in-place.
|
|
75
|
+
If False, `emod` is not modified.
|
|
76
|
+
"""
|
|
77
|
+
if not inplace:
|
|
78
|
+
emod = np.array(emod, copy=True)
|
|
79
|
+
# unknowns are nans and deformation values above the threshold
|
|
80
|
+
unkn = np.logical_and(np.isnan(emod),
|
|
81
|
+
deform > deform_thresh/deform_norm)
|
|
82
|
+
|
|
83
|
+
if np.sum(unkn) == 0:
|
|
84
|
+
# nothing to do
|
|
85
|
+
return emod
|
|
86
|
+
|
|
87
|
+
warnings.warn("LUT extrapolation is barely tested and may yield "
|
|
88
|
+
+ "unphysical values!",
|
|
89
|
+
KnowWhatYouAreDoingWarning)
|
|
90
|
+
|
|
91
|
+
lut_crop = lut[lut[:, 1] > deform_thresh/deform_norm, :]
|
|
92
|
+
|
|
93
|
+
itp = spint.SmoothBivariateSpline(x=lut_crop[:, 0],
|
|
94
|
+
y=lut_crop[:, 1],
|
|
95
|
+
z=lut_crop[:, 2],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
emod[unkn] = itp.ev(datax[unkn], deform[unkn])
|
|
99
|
+
return emod
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_emodulus(deform: float | np.array,
|
|
103
|
+
area_um: float | np.array | None = None,
|
|
104
|
+
volume: float | np.array | None = None,
|
|
105
|
+
medium: float | str = "0.49% MC-PBS",
|
|
106
|
+
channel_width: float = 20.0,
|
|
107
|
+
flow_rate: float = 0.16,
|
|
108
|
+
px_um: float = 0.34,
|
|
109
|
+
temperature: float | np.ndarray | None = 23.0,
|
|
110
|
+
lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19",
|
|
111
|
+
visc_model: Literal['herold-2017',
|
|
112
|
+
'herold-2017-fallback',
|
|
113
|
+
'buyukurganci-2022',
|
|
114
|
+
'kestin-1978',
|
|
115
|
+
None] = "herold-2017-fallback",
|
|
116
|
+
extrapolate: bool = INACCURATE_SPLINE_EXTRAPOLATION,
|
|
117
|
+
copy: bool = True):
|
|
118
|
+
"""Compute apparent Young's modulus using a look-up table
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
area_um: float or ndarray
|
|
123
|
+
Apparent (2D image) area [µm²] of the event(s)
|
|
124
|
+
deform: float or ndarray
|
|
125
|
+
Deformation (1-circularity) of the event(s)
|
|
126
|
+
volume: float or ndarray
|
|
127
|
+
Apparent volume of the event(s). It is not possible to define
|
|
128
|
+
`volume` and `area_um` at the same time (makes no sense).
|
|
129
|
+
|
|
130
|
+
.. versionadded:: 0.25.0
|
|
131
|
+
medium: str or float
|
|
132
|
+
The medium to compute the viscosity for. If a string
|
|
133
|
+
is given, the viscosity is computed. If a float is given,
|
|
134
|
+
this value is used as the viscosity in mPa*s (Note that
|
|
135
|
+
`temperature` and `visc_model` must be set to None in this case).
|
|
136
|
+
channel_width: float
|
|
137
|
+
The channel width [µm]
|
|
138
|
+
flow_rate: float
|
|
139
|
+
Flow rate [µL/s]
|
|
140
|
+
px_um: float
|
|
141
|
+
The detector pixel size [µm] used for pixelation correction.
|
|
142
|
+
Set to zero to disable.
|
|
143
|
+
temperature: float, ndarray, or None
|
|
144
|
+
Temperature [°C] of the event(s)
|
|
145
|
+
lut_data: path, str, or tuple of (np.ndarray of shape (N, 3), dict)
|
|
146
|
+
The LUT data to use. If it is a built-in identifier,
|
|
147
|
+
then the respective LUT will be used. Otherwise, a path to a
|
|
148
|
+
file on disk or a tuple (LUT array, metadata) is possible.
|
|
149
|
+
The LUT metadata is used to check whether the given features
|
|
150
|
+
(e.g. `area_um` and `deform`) are valid interpolation choices.
|
|
151
|
+
|
|
152
|
+
.. versionadded:: 0.25.0
|
|
153
|
+
visc_model: str
|
|
154
|
+
The viscosity model to use,
|
|
155
|
+
see :func:`dclab.features.emodulus.viscosity.get_viscosity`
|
|
156
|
+
extrapolate: bool
|
|
157
|
+
Perform extrapolation using :func:`extrapolate_emodulus`. This
|
|
158
|
+
is discouraged!
|
|
159
|
+
copy: bool
|
|
160
|
+
Copy input arrays. If set to false, input arrays are
|
|
161
|
+
overridden.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
elasticity: float or ndarray
|
|
166
|
+
Apparent Young's modulus in kPa
|
|
167
|
+
|
|
168
|
+
Notes
|
|
169
|
+
-----
|
|
170
|
+
- The look-up table used was computed with finite elements methods
|
|
171
|
+
according to :cite:`Mokbel2017` and complemented with analytical
|
|
172
|
+
isoelastics from :cite:`Mietke2015`. The original simulation
|
|
173
|
+
results are available on figshare :cite:`FigshareWittwer2020`.
|
|
174
|
+
- The computation of the Young's modulus takes into account
|
|
175
|
+
a correction for the viscosity (medium, channel width, flow rate,
|
|
176
|
+
and temperature) :cite:`Mietke2015` and a correction for
|
|
177
|
+
pixelation for the deformation which were derived
|
|
178
|
+
from a (pixelated) image :cite:`Herold2017`.
|
|
179
|
+
- Note that while deformation is pixelation-corrected, area_um and
|
|
180
|
+
volume are scaled to match the LUT data. This is somewhat
|
|
181
|
+
fortunate, because we don't have to worry about the order of
|
|
182
|
+
applying pixelation correction and scale conversion.
|
|
183
|
+
- By using external LUTs, it is possible to interpolate on the
|
|
184
|
+
volume-deformation plane. This feature was added in version
|
|
185
|
+
0.25.0.
|
|
186
|
+
|
|
187
|
+
See Also
|
|
188
|
+
--------
|
|
189
|
+
dclab.features.emodulus.viscosity.get_viscosity: compute viscosity
|
|
190
|
+
for known media
|
|
191
|
+
"""
|
|
192
|
+
# Get lut data
|
|
193
|
+
lut, lut_meta = load_lut(lut_data)
|
|
194
|
+
# Get the correct features (sanity checks)
|
|
195
|
+
featx, featy, _ = lut_meta["column features"]
|
|
196
|
+
if featx == "area_um" and featy == "deform":
|
|
197
|
+
assert volume is None, "Don't define area_um and volume at same time!"
|
|
198
|
+
datax = np.array(area_um, dtype=float, copy=copy)
|
|
199
|
+
elif featx == "volume" and featy == "deform":
|
|
200
|
+
assert area_um is None, "Don't define area_um and volume at same time!"
|
|
201
|
+
datax = np.array(volume, dtype=float, copy=copy)
|
|
202
|
+
else:
|
|
203
|
+
raise KeyError("No recipe for '{}' and '{}'!".format(featx, featy))
|
|
204
|
+
|
|
205
|
+
# copy deform so we can use in-place pixel correction
|
|
206
|
+
deform = np.array(deform, dtype=float, copy=copy)
|
|
207
|
+
if px_um:
|
|
208
|
+
# Correct deformation for pixelation effect (subtract ddelt).
|
|
209
|
+
# It is important to do this before scaling datax (area_um/volume).
|
|
210
|
+
deform -= get_pixelation_delta(feat_corr=featy,
|
|
211
|
+
feat_absc=featx,
|
|
212
|
+
data_absc=datax,
|
|
213
|
+
px_um=px_um)
|
|
214
|
+
|
|
215
|
+
# Compute viscosity
|
|
216
|
+
if isinstance(medium, numbers.Number):
|
|
217
|
+
visco = medium
|
|
218
|
+
if temperature is not None:
|
|
219
|
+
raise ValueError("If `medium` is given in Pa*s, then "
|
|
220
|
+
+ "`temperature` must be set to None!")
|
|
221
|
+
if visc_model is not None:
|
|
222
|
+
warnings.warn("If `medium` is given in Pa*s, then `visc_model` "
|
|
223
|
+
"must be set to None. An exception will be raised "
|
|
224
|
+
"in future versions of dclab.",
|
|
225
|
+
DeprecationWarning)
|
|
226
|
+
else:
|
|
227
|
+
visco = get_viscosity(medium=medium, channel_width=channel_width,
|
|
228
|
+
flow_rate=flow_rate, temperature=temperature,
|
|
229
|
+
model=visc_model)
|
|
230
|
+
|
|
231
|
+
if isinstance(visco, np.ndarray):
|
|
232
|
+
# New in dclab 0.20.0: Computation for viscosities array
|
|
233
|
+
# Convert the input area_um to that of the LUT (deform does not change)
|
|
234
|
+
scale_kw = {"channel_width_in": channel_width,
|
|
235
|
+
"channel_width_out": lut_meta["channel_width"],
|
|
236
|
+
"inplace": False}
|
|
237
|
+
datax_4lut = scale_feature(feat=featx, data=datax, **scale_kw)
|
|
238
|
+
deform_4lut = np.array(deform, dtype=float, copy=copy)
|
|
239
|
+
|
|
240
|
+
# Normalize interpolation data such that the spacing for
|
|
241
|
+
# area and deformation is about the same during interpolation.
|
|
242
|
+
featx_norm = lut[:, 0].max()
|
|
243
|
+
normalize(lut[:, 0], featx_norm)
|
|
244
|
+
normalize(datax_4lut, featx_norm)
|
|
245
|
+
|
|
246
|
+
defo_norm = lut[:, 1].max()
|
|
247
|
+
normalize(lut[:, 1], defo_norm)
|
|
248
|
+
normalize(deform_4lut, defo_norm)
|
|
249
|
+
|
|
250
|
+
# Perform interpolation
|
|
251
|
+
emod = spint.griddata((lut[:, 0], lut[:, 1]), lut[:, 2],
|
|
252
|
+
(datax_4lut, deform_4lut),
|
|
253
|
+
method='linear')
|
|
254
|
+
|
|
255
|
+
if extrapolate:
|
|
256
|
+
# New in dclab 0.23.0: Perform extrapolation outside of the LUT
|
|
257
|
+
# This is not well-tested and thus discouraged!
|
|
258
|
+
extrapolate_emodulus(lut=lut,
|
|
259
|
+
datax=datax_4lut,
|
|
260
|
+
deform=deform_4lut,
|
|
261
|
+
emod=emod,
|
|
262
|
+
deform_norm=defo_norm,
|
|
263
|
+
inplace=True)
|
|
264
|
+
|
|
265
|
+
# Convert the LUT-interpolated emodulus back
|
|
266
|
+
backscale_kw = {"channel_width_in": lut_meta["channel_width"],
|
|
267
|
+
"channel_width_out": channel_width,
|
|
268
|
+
"flow_rate_in": lut_meta["flow_rate"],
|
|
269
|
+
"flow_rate_out": flow_rate,
|
|
270
|
+
"viscosity_in": lut_meta["fluid_viscosity"],
|
|
271
|
+
"viscosity_out": visco,
|
|
272
|
+
"inplace": True}
|
|
273
|
+
# deformation is not scaled (no units)
|
|
274
|
+
scale_feature(feat=featx, data=datax_4lut, **backscale_kw)
|
|
275
|
+
scale_emodulus(emod, **backscale_kw)
|
|
276
|
+
else:
|
|
277
|
+
# Corrections
|
|
278
|
+
# We correct the LUT, because it contains less points than
|
|
279
|
+
# the event data. Furthermore, the lut could be cached
|
|
280
|
+
# in the future, if this takes up a lot of time.
|
|
281
|
+
scale_kw = {"channel_width_in": lut_meta["channel_width"],
|
|
282
|
+
"channel_width_out": channel_width,
|
|
283
|
+
"flow_rate_in": lut_meta["flow_rate"],
|
|
284
|
+
"flow_rate_out": flow_rate,
|
|
285
|
+
"viscosity_in": lut_meta["fluid_viscosity"],
|
|
286
|
+
"viscosity_out": visco,
|
|
287
|
+
"inplace": True}
|
|
288
|
+
# deformation is not scaled (no units)
|
|
289
|
+
scale_feature(feat=featx, data=lut[:, 0], **scale_kw)
|
|
290
|
+
scale_emodulus(lut[:, 2], **scale_kw)
|
|
291
|
+
|
|
292
|
+
# Normalize interpolation data such that the spacing for
|
|
293
|
+
# area and deformation is about the same during interpolation.
|
|
294
|
+
featx_norm = lut[:, 0].max()
|
|
295
|
+
normalize(lut[:, 0], featx_norm)
|
|
296
|
+
normalize(datax, featx_norm)
|
|
297
|
+
|
|
298
|
+
defo_norm = lut[:, 1].max()
|
|
299
|
+
normalize(lut[:, 1], defo_norm)
|
|
300
|
+
normalize(deform, defo_norm)
|
|
301
|
+
|
|
302
|
+
# Perform interpolation
|
|
303
|
+
emod = spint.griddata((lut[:, 0], lut[:, 1]), lut[:, 2],
|
|
304
|
+
(datax, deform),
|
|
305
|
+
method='linear')
|
|
306
|
+
|
|
307
|
+
if extrapolate:
|
|
308
|
+
# New in dclab 0.23.0: Perform extrapolation outside of the LUT
|
|
309
|
+
# This is not well-tested and thus discouraged!
|
|
310
|
+
extrapolate_emodulus(lut=lut,
|
|
311
|
+
datax=datax,
|
|
312
|
+
deform=deform,
|
|
313
|
+
emod=emod,
|
|
314
|
+
deform_norm=defo_norm,
|
|
315
|
+
inplace=True)
|
|
316
|
+
|
|
317
|
+
# Let the user know when the emodulus contains too many nan values
|
|
318
|
+
nans = np.sum(np.isnan(emod))
|
|
319
|
+
if nans / emod.size > 0.1:
|
|
320
|
+
warnings.warn("The Young's modulus could not be computed for "
|
|
321
|
+
+ "{:.0f}% of the data. ".format(nans/emod.size*100)
|
|
322
|
+
+ "This is because they are not covered by the "
|
|
323
|
+
+ "look-up table '{}'.".format(lut_data),
|
|
324
|
+
YoungsModulusLookupTableExceededWarning)
|
|
325
|
+
|
|
326
|
+
return emod
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def normalize(data, dmax):
|
|
330
|
+
"""Perform normalization in-place for interpolation
|
|
331
|
+
|
|
332
|
+
Note that :func:`scipy.interpolate.griddata` has a `rescale`
|
|
333
|
+
option which rescales the data onto the unit cube. For some
|
|
334
|
+
reason this does not work well with LUT data, so we
|
|
335
|
+
just normalize it by dividing by the maximum value.
|
|
336
|
+
"""
|
|
337
|
+
assert isinstance(data, np.ndarray)
|
|
338
|
+
data /= dmax
|
|
339
|
+
return data
|