dclab 0.67.0__cp314-cp314t-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-314t-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-314t-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314t-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-314t-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,95 @@
|
|
|
1
|
+
"""Crosstalk-correction for fluorescence data"""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_compensation_matrix(ct21, ct31, ct12, ct32, ct13, ct23):
|
|
7
|
+
"""Compute crosstalk inversion matrix
|
|
8
|
+
|
|
9
|
+
The spillover matrix is
|
|
10
|
+
|
|
11
|
+
| | c11 c12 c13 |
|
|
12
|
+
| | c21 c22 c23 |
|
|
13
|
+
| | c31 c32 c33 |
|
|
14
|
+
|
|
15
|
+
The diagonal elements are set to 1, i.e.
|
|
16
|
+
|
|
17
|
+
ct11 = c22 = c33 = 1
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
cij: float
|
|
22
|
+
Spill from channel i to channel j
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
inv: np.ndarray
|
|
27
|
+
Compensation matrix (inverted spillover matrix)
|
|
28
|
+
"""
|
|
29
|
+
ct11 = 1
|
|
30
|
+
ct22 = 1
|
|
31
|
+
ct33 = 1
|
|
32
|
+
|
|
33
|
+
if ct21 < 0:
|
|
34
|
+
raise ValueError("ct21 matrix element must not be negative!")
|
|
35
|
+
|
|
36
|
+
if ct31 < 0:
|
|
37
|
+
raise ValueError("ct31 matrix element must not be negative!")
|
|
38
|
+
|
|
39
|
+
if ct12 < 0:
|
|
40
|
+
raise ValueError("ct12 matrix element must not be negative!")
|
|
41
|
+
|
|
42
|
+
if ct32 < 0:
|
|
43
|
+
raise ValueError("ct32 matrix element must not be negative!")
|
|
44
|
+
|
|
45
|
+
if ct13 < 0:
|
|
46
|
+
raise ValueError("ct13 matrix element must not be negative!")
|
|
47
|
+
|
|
48
|
+
if ct23 < 0:
|
|
49
|
+
raise ValueError("ct23 matrix element must not be negative!")
|
|
50
|
+
|
|
51
|
+
crosstalk = np.array([[ct11, ct12, ct13],
|
|
52
|
+
[ct21, ct22, ct23],
|
|
53
|
+
[ct31, ct32, ct33],
|
|
54
|
+
])
|
|
55
|
+
return np.linalg.inv(crosstalk)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def correct_crosstalk(fl1, fl2, fl3, fl_channel,
|
|
59
|
+
ct21=0, ct31=0, ct12=0, ct32=0, ct13=0, ct23=0):
|
|
60
|
+
"""Perform crosstalk correction
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
fli: int, float, or np.ndarray
|
|
65
|
+
Measured fluorescence signals
|
|
66
|
+
fl_channel: int (1, 2, or 3)
|
|
67
|
+
The channel number for which the crosstalk-corrected signal
|
|
68
|
+
should be computed
|
|
69
|
+
cij: float
|
|
70
|
+
Spill (crosstalk or bleed-through) from channel i to channel j
|
|
71
|
+
This spill is computed from the fluorescence signal of e.g.
|
|
72
|
+
single-stained positive control cells; It is defined by the
|
|
73
|
+
ratio of the fluorescence signals of the two channels, i.e
|
|
74
|
+
cij = flj / fli.
|
|
75
|
+
|
|
76
|
+
See Also
|
|
77
|
+
--------
|
|
78
|
+
get_compensation_matrix: compute the inverse crosstalk matrix
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
If there are only two channels (e.g. fl1 and fl2), then the
|
|
83
|
+
crosstalk to and from the other channel (ct31, ct32, ct13, ct23)
|
|
84
|
+
should be set to zero.
|
|
85
|
+
"""
|
|
86
|
+
fl_channel = int(fl_channel)
|
|
87
|
+
if fl_channel not in [1, 2, 3]:
|
|
88
|
+
raise ValueError("`fl_channel` must be 1, 2, or 3!")
|
|
89
|
+
|
|
90
|
+
minv = get_compensation_matrix(ct21=ct21, ct31=ct31, ct12=ct12,
|
|
91
|
+
ct32=ct32, ct13=ct13, ct23=ct23)
|
|
92
|
+
|
|
93
|
+
col = minv[:, fl_channel - 1].flatten()
|
|
94
|
+
flout = col[0] * fl1 + col[1] * fl2 + col[2] * fl3
|
|
95
|
+
return flout
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Computation of inertia ratio from contour data"""
|
|
2
|
+
import numpy as np
|
|
3
|
+
import scipy.spatial as ssp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def cont_moments_cv(cont,
|
|
7
|
+
flt_epsilon=1.19209e-07,
|
|
8
|
+
dbl_epsilon=2.2204460492503131e-16):
|
|
9
|
+
"""Compute the moments of a contour
|
|
10
|
+
|
|
11
|
+
The moments are computed in the same way as they are computed
|
|
12
|
+
in OpenCV's `contourMoments` in `moments.cpp`.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
cont: array of shape (N,2)
|
|
17
|
+
The contour for which to compute the moments.
|
|
18
|
+
flt_epsilon: float
|
|
19
|
+
The value of ``FLT_EPSILON`` in OpenCV/gcc.
|
|
20
|
+
dbl_epsilon: float
|
|
21
|
+
The value of ``DBL_EPSILON`` in OpenCV/gcc.
|
|
22
|
+
|
|
23
|
+
.. versionchanged:: 0.48.2
|
|
24
|
+
|
|
25
|
+
For long channels, an integer overflow could occur in previous
|
|
26
|
+
versions, leading to false contour moments.
|
|
27
|
+
See https://github.com/DC-analysis/dclab/issues/212
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
moments: dict
|
|
32
|
+
A dictionary of moments. If the moment `m00` is smaller
|
|
33
|
+
than half of `flt_epsilon`, `None` is returned.
|
|
34
|
+
"""
|
|
35
|
+
# Make sure we have 64bit integer or floating point values.
|
|
36
|
+
# If the input data was int32, then integer overflows could occur
|
|
37
|
+
# for mu20 and mu02 (https://github.com/DC-analysis/dclab/issues/212).
|
|
38
|
+
if np.issubdtype(cont.dtype, np.integer):
|
|
39
|
+
cont = cont.astype(np.int64)
|
|
40
|
+
elif np.issubdtype(cont.dtype, np.floating):
|
|
41
|
+
cont = cont.astype(np.float64)
|
|
42
|
+
|
|
43
|
+
xi = cont[:, 0]
|
|
44
|
+
yi = cont[:, 1]
|
|
45
|
+
|
|
46
|
+
xi_1 = np.roll(xi, -1)
|
|
47
|
+
yi_1 = np.roll(yi, -1)
|
|
48
|
+
|
|
49
|
+
xi_12 = xi_1**2
|
|
50
|
+
yi_12 = yi_1**2
|
|
51
|
+
|
|
52
|
+
xi2 = xi**2
|
|
53
|
+
yi2 = yi**2
|
|
54
|
+
|
|
55
|
+
dxy = xi_1 * yi - xi * yi_1
|
|
56
|
+
|
|
57
|
+
xii_1 = xi_1 + xi
|
|
58
|
+
yii_1 = yi_1 + yi
|
|
59
|
+
|
|
60
|
+
a00 = np.sum(dxy)
|
|
61
|
+
a10 = np.sum(dxy * xii_1)
|
|
62
|
+
a01 = np.sum(dxy * yii_1)
|
|
63
|
+
a20 = np.sum(dxy * (xi_1 * xii_1 + xi2))
|
|
64
|
+
a11 = np.sum(dxy * (xi_1 * (yii_1 + yi_1) + xi * (yii_1 + yi)))
|
|
65
|
+
a02 = np.sum(dxy * (yi_1 * yii_1 + yi2))
|
|
66
|
+
a30 = np.sum(dxy * xii_1 * (xi_12 + xi2))
|
|
67
|
+
a03 = np.sum(dxy * yii_1 * (yi_12 + yi2))
|
|
68
|
+
a21 = np.sum(dxy * (xi_12 * (3 * yi_1 + yi) + 2 *
|
|
69
|
+
xi * xi_1 * yii_1 + xi2 * (yi_1 + 3 * yi)))
|
|
70
|
+
a12 = np.sum(dxy * (yi_12 * (3 * xi_1 + xi) + 2 *
|
|
71
|
+
yi * yi_1 * xii_1 + yi2 * (xi_1 + 3 * xi)))
|
|
72
|
+
|
|
73
|
+
if abs(a00) > flt_epsilon:
|
|
74
|
+
db1_2 = 0.5
|
|
75
|
+
db1_6 = 0.16666666666666666666666666666667
|
|
76
|
+
db1_12 = 0.083333333333333333333333333333333
|
|
77
|
+
db1_24 = 0.041666666666666666666666666666667
|
|
78
|
+
db1_20 = 0.05
|
|
79
|
+
db1_60 = 0.016666666666666666666666666666667
|
|
80
|
+
|
|
81
|
+
if a00 < 0:
|
|
82
|
+
db1_2 *= -1
|
|
83
|
+
db1_6 *= -1
|
|
84
|
+
db1_12 *= -1
|
|
85
|
+
db1_24 *= -1
|
|
86
|
+
db1_20 *= -1
|
|
87
|
+
db1_60 *= -1
|
|
88
|
+
|
|
89
|
+
m = dict(m00=a00 * db1_2,
|
|
90
|
+
m10=a10 * db1_6,
|
|
91
|
+
m01=a01 * db1_6,
|
|
92
|
+
m20=a20 * db1_12,
|
|
93
|
+
m11=a11 * db1_24,
|
|
94
|
+
m02=a02 * db1_12,
|
|
95
|
+
m30=a30 * db1_20,
|
|
96
|
+
m21=a21 * db1_60,
|
|
97
|
+
m12=a12 * db1_60,
|
|
98
|
+
m03=a03 * db1_20,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if m["m00"] > dbl_epsilon:
|
|
102
|
+
# Center of gravity
|
|
103
|
+
cx = m["m10"]/m["m00"]
|
|
104
|
+
cy = m["m01"]/m["m00"]
|
|
105
|
+
else:
|
|
106
|
+
cx = 0
|
|
107
|
+
cy = 0
|
|
108
|
+
|
|
109
|
+
# central second order moments
|
|
110
|
+
m["mu20"] = m["m20"] - m["m10"]*cx
|
|
111
|
+
m["mu11"] = m["m11"] - m["m10"]*cy
|
|
112
|
+
m["mu02"] = m["m02"] - m["m01"]*cy
|
|
113
|
+
|
|
114
|
+
m["mu30"] = m["m30"] - cx*(3*m["mu20"] + cx*m["m10"])
|
|
115
|
+
m["mu21"] = m["m21"] - cx*(2*m["mu11"] + cx*m["m01"]) - cy*m["mu20"]
|
|
116
|
+
m["mu12"] = m["m12"] - cy*(2*m["mu11"] + cy*m["m10"]) - cx*m["mu02"]
|
|
117
|
+
m["mu03"] = m["m03"] - cy*(3*m["mu02"] + cy*m["m01"])
|
|
118
|
+
return m
|
|
119
|
+
else:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def get_inert_ratio_cvx(cont):
|
|
124
|
+
"""Compute the inertia ratio of the convex hull of a contour
|
|
125
|
+
|
|
126
|
+
The inertia ratio is computed from the central second order of moments
|
|
127
|
+
along x (mu20) and y (mu02) via `sqrt(mu20/mu02)`.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
cont: ndarray or list of ndarrays of shape (N,2)
|
|
132
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
133
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
134
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
135
|
+
correspond to the x- and y-coordinates of the contour.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
inert_ratio_cvx: float or ndarray of size N
|
|
140
|
+
The inertia ratio of the contour's convex hull
|
|
141
|
+
|
|
142
|
+
.. versionchanged:: 0.48.2
|
|
143
|
+
|
|
144
|
+
For long channels, an integer overflow could occur in previous
|
|
145
|
+
versions, leading invalid or nan values.
|
|
146
|
+
See https://github.com/DC-analysis/dclab/issues/212
|
|
147
|
+
|
|
148
|
+
Notes
|
|
149
|
+
-----
|
|
150
|
+
The contour moments mu20 and mu02 are computed the same way they
|
|
151
|
+
are computed in OpenCV's `moments.cpp`.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
See Also
|
|
155
|
+
--------
|
|
156
|
+
get_inert_ratio_raw: Compute inertia ratio of a raw contour
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
References
|
|
160
|
+
----------
|
|
161
|
+
- `<https://en.wikipedia.org/wiki/Image_moment#Central_moments>`__
|
|
162
|
+
- `<https://github.com/opencv/opencv/blob/
|
|
163
|
+
f81370232a651bdac5042efe907bcaa50a66c487/modules/imgproc/src/
|
|
164
|
+
moments.cpp#L93>`__
|
|
165
|
+
"""
|
|
166
|
+
if isinstance(cont, np.ndarray):
|
|
167
|
+
# If cont is an array, it is not a list of contours,
|
|
168
|
+
# because contours can have different lengths.
|
|
169
|
+
cont = [cont]
|
|
170
|
+
ret_list = False
|
|
171
|
+
else:
|
|
172
|
+
ret_list = True
|
|
173
|
+
|
|
174
|
+
length = len(cont)
|
|
175
|
+
|
|
176
|
+
inert_ratio_cvx = np.zeros(length, dtype=np.float64) * np.nan
|
|
177
|
+
|
|
178
|
+
for ii in range(length):
|
|
179
|
+
try:
|
|
180
|
+
chull = ssp.ConvexHull(cont[ii])
|
|
181
|
+
except ssp.QhullError:
|
|
182
|
+
pass
|
|
183
|
+
else:
|
|
184
|
+
hull = cont[ii][chull.vertices, :]
|
|
185
|
+
inert_ratio_cvx[ii] = get_inert_ratio_raw(hull)
|
|
186
|
+
|
|
187
|
+
if not ret_list:
|
|
188
|
+
inert_ratio_cvx = inert_ratio_cvx[0]
|
|
189
|
+
|
|
190
|
+
return inert_ratio_cvx
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_inert_ratio_prnc(cont):
|
|
194
|
+
"""Compute principal inertia ratio of a contour
|
|
195
|
+
|
|
196
|
+
The principal inertia ratio is rotation-invariant, which
|
|
197
|
+
makes it applicable to reservoir measurements where e.g.
|
|
198
|
+
cells are not aligned with the channel.
|
|
199
|
+
|
|
200
|
+
.. versionchanged:: 0.48.2
|
|
201
|
+
|
|
202
|
+
For long channels, an integer overflow could occur in previous
|
|
203
|
+
versions, leading to a principal inertia ratio smaller than one.
|
|
204
|
+
See https://github.com/DC-analysis/dclab/issues/212
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
cont: ndarray or list of ndarrays of shape (N,2)
|
|
209
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
210
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
211
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
212
|
+
correspond to the x- and y-coordinates of the contour.
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
inert_ratio_prnc: float or ndarray of size N
|
|
217
|
+
The principal inertia ratio of the contour
|
|
218
|
+
"""
|
|
219
|
+
if isinstance(cont, np.ndarray):
|
|
220
|
+
# If cont is an array, it is not a list of contours,
|
|
221
|
+
# because contours can have different lengths.
|
|
222
|
+
cont = [cont]
|
|
223
|
+
ret_list = False
|
|
224
|
+
else:
|
|
225
|
+
ret_list = True
|
|
226
|
+
|
|
227
|
+
length = len(cont)
|
|
228
|
+
# np.float32 for compatibility with opencv
|
|
229
|
+
inert_ratio_prnc = np.zeros(length, dtype=np.float32) * np.nan
|
|
230
|
+
|
|
231
|
+
for ii in range(length):
|
|
232
|
+
# As discussed in https://github.com/DC-analysis/dclab/issues/212,
|
|
233
|
+
# `cont_moments_cv` now already properly casts everything. But since
|
|
234
|
+
# we have to create a floating point contour anyway, we can just
|
|
235
|
+
# create a copy here and be safe.
|
|
236
|
+
cc = np.array(cont[ii], dtype=np.float64, copy=True)
|
|
237
|
+
moments = cont_moments_cv(cc)
|
|
238
|
+
|
|
239
|
+
if moments is not None:
|
|
240
|
+
# orientation of the contour
|
|
241
|
+
orient = 0.5 * np.arctan2(2 * moments['mu11'],
|
|
242
|
+
moments['mu02'] - moments['mu20'])
|
|
243
|
+
# rotate contour
|
|
244
|
+
rho = np.sqrt(cc[:, 0]**2 + cc[:, 1]**2)
|
|
245
|
+
phi = np.arctan2(cc[:, 1], cc[:, 0]) + orient + np.pi / 2
|
|
246
|
+
# change contour data in-place (we already created a copy)
|
|
247
|
+
cc[:, 0] = rho * np.cos(phi)
|
|
248
|
+
cc[:, 1] = rho * np.sin(phi)
|
|
249
|
+
# compute inertia ratio of rotated contour
|
|
250
|
+
mprnc = cont_moments_cv(cc)
|
|
251
|
+
inert_ratio_prnc[ii] = np.sqrt(mprnc["mu20"] / mprnc["mu02"])
|
|
252
|
+
|
|
253
|
+
if not ret_list:
|
|
254
|
+
inert_ratio_prnc = inert_ratio_prnc[0]
|
|
255
|
+
|
|
256
|
+
return inert_ratio_prnc
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def get_inert_ratio_raw(cont):
|
|
260
|
+
"""Compute the inertia ratio of a contour
|
|
261
|
+
|
|
262
|
+
The inertia ratio is computed from the central second order of moments
|
|
263
|
+
along x (mu20) and y (mu02) via `sqrt(mu20/mu02)`.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
cont: ndarray or list of ndarrays of shape (N,2)
|
|
268
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
269
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
270
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
271
|
+
correspond to the x- and y-coordinates of the contour.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
inert_ratio_raw: float or ndarray of size N
|
|
276
|
+
The inertia ratio of the contour
|
|
277
|
+
|
|
278
|
+
.. versionchanged:: 0.48.2
|
|
279
|
+
|
|
280
|
+
For long channels, an integer overflow could occur in previous
|
|
281
|
+
versions, leading invalid or nan values.
|
|
282
|
+
See https://github.com/DC-analysis/dclab/issues/212
|
|
283
|
+
|
|
284
|
+
Notes
|
|
285
|
+
-----
|
|
286
|
+
The contour moments mu20 and mu02 are computed the same way they
|
|
287
|
+
are computed in OpenCV's `moments.cpp`.
|
|
288
|
+
|
|
289
|
+
See Also
|
|
290
|
+
--------
|
|
291
|
+
get_inert_ratio_cvx: Compute inertia ratio of the convex hull of
|
|
292
|
+
a contour
|
|
293
|
+
|
|
294
|
+
References
|
|
295
|
+
----------
|
|
296
|
+
- `<https://en.wikipedia.org/wiki/Image_moment#Central_moments>`__
|
|
297
|
+
- `<https://github.com/opencv/opencv/blob/
|
|
298
|
+
f81370232a651bdac5042efe907bcaa50a66c487/modules/imgproc/src/
|
|
299
|
+
moments.cpp#L93>`__
|
|
300
|
+
"""
|
|
301
|
+
if isinstance(cont, np.ndarray):
|
|
302
|
+
# If cont is an array, it is not a list of contours,
|
|
303
|
+
# because contours can have different lengths.
|
|
304
|
+
cont = [cont]
|
|
305
|
+
ret_list = False
|
|
306
|
+
else:
|
|
307
|
+
ret_list = True
|
|
308
|
+
|
|
309
|
+
length = len(cont)
|
|
310
|
+
|
|
311
|
+
inert_ratio_raw = np.zeros(length, dtype=np.float64) * np.nan
|
|
312
|
+
|
|
313
|
+
for ii in range(length):
|
|
314
|
+
moments = cont_moments_cv(cont[ii])
|
|
315
|
+
if moments is not None:
|
|
316
|
+
inert_ratio_raw[ii] = np.sqrt(moments["mu20"]/moments["mu02"])
|
|
317
|
+
|
|
318
|
+
if not ret_list:
|
|
319
|
+
inert_ratio_raw = inert_ratio_raw[0]
|
|
320
|
+
|
|
321
|
+
return inert_ratio_raw
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def get_tilt(cont):
|
|
325
|
+
"""Compute tilt of raw contour relative to channel axis
|
|
326
|
+
|
|
327
|
+
Parameters
|
|
328
|
+
----------
|
|
329
|
+
cont: ndarray or list of ndarrays of shape (N,2)
|
|
330
|
+
A 2D array that holds the contour of an event (in pixels)
|
|
331
|
+
e.g. obtained using `mm.contour` where `mm` is an instance
|
|
332
|
+
of `RTDCBase`. The first and second columns of `cont`
|
|
333
|
+
correspond to the x- and y-coordinates of the contour.
|
|
334
|
+
|
|
335
|
+
Returns
|
|
336
|
+
-------
|
|
337
|
+
tilt: float or ndarray of size N
|
|
338
|
+
Tilt of the contour in the interval [0, PI/2]
|
|
339
|
+
|
|
340
|
+
.. versionchanged:: 0.48.2
|
|
341
|
+
|
|
342
|
+
For long channels, an integer overflow could occur in previous
|
|
343
|
+
versions, leading to invalid tilt values of PI/2.
|
|
344
|
+
See https://github.com/DC-analysis/dclab/issues/212
|
|
345
|
+
|
|
346
|
+
References
|
|
347
|
+
----------
|
|
348
|
+
- `<https://en.wikipedia.org/wiki/Image_moment#Examples_2>`__
|
|
349
|
+
"""
|
|
350
|
+
if isinstance(cont, np.ndarray):
|
|
351
|
+
# If cont is an array, it is not a list of contours,
|
|
352
|
+
# because contours can have different lengths.
|
|
353
|
+
cont = [cont]
|
|
354
|
+
ret_list = False
|
|
355
|
+
else:
|
|
356
|
+
ret_list = True
|
|
357
|
+
|
|
358
|
+
length = len(cont)
|
|
359
|
+
|
|
360
|
+
tilt = np.zeros(length, dtype=np.float64) * np.nan
|
|
361
|
+
|
|
362
|
+
for ii in range(length):
|
|
363
|
+
moments = cont_moments_cv(cont[ii])
|
|
364
|
+
if moments is not None:
|
|
365
|
+
# orientation of the contour
|
|
366
|
+
tilt[ii] = 0.5 * np.arctan2(-2 * moments['mu11'],
|
|
367
|
+
moments['mu20'] - moments['mu02'])
|
|
368
|
+
|
|
369
|
+
tilt = np.abs(tilt)
|
|
370
|
+
|
|
371
|
+
# sanity check
|
|
372
|
+
assert np.all(tilt) <= np.pi/2
|
|
373
|
+
|
|
374
|
+
if not ret_list:
|
|
375
|
+
tilt = tilt[0]
|
|
376
|
+
|
|
377
|
+
return tilt
|