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,476 @@
|
|
|
1
|
+
"""RT-DC .tdms file format"""
|
|
2
|
+
import copy
|
|
3
|
+
import pathlib
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from ...external.packaging import parse as parse_version
|
|
7
|
+
|
|
8
|
+
from .exc import ContourIndexingError # noqa:F401
|
|
9
|
+
from .exc import InvalidTDMSFileFormatError # noqa:F401
|
|
10
|
+
from .exc import IncompleteTDMSFileFormatError # noqa:F401
|
|
11
|
+
from .exc import InvalidVideoFileError # noqa:F401
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import nptdms
|
|
15
|
+
except ModuleNotFoundError:
|
|
16
|
+
NPTDMS_AVAILABLE = False
|
|
17
|
+
else:
|
|
18
|
+
if parse_version(nptdms.__version__) < parse_version("0.23.0"):
|
|
19
|
+
raise ValueError("Please install nptdms>=0.23.0")
|
|
20
|
+
NPTDMS_AVAILABLE = True
|
|
21
|
+
from .event_contour import ContourColumn
|
|
22
|
+
from .event_image import ImageColumn
|
|
23
|
+
from .event_mask import MaskColumn
|
|
24
|
+
from .event_trace import TraceColumn
|
|
25
|
+
|
|
26
|
+
import numpy as np
|
|
27
|
+
|
|
28
|
+
from ... import definitions as dfn
|
|
29
|
+
from ...util import hashobj, hashfile
|
|
30
|
+
|
|
31
|
+
from ..config import Configuration
|
|
32
|
+
from ..core import RTDCBase
|
|
33
|
+
|
|
34
|
+
from . import naming
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RTDC_TDMS(RTDCBase):
|
|
38
|
+
def __init__(self, tdms_path, *args, **kwargs):
|
|
39
|
+
"""TDMS file format for RT-DC measurements
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
tdms_path: str or pathlib.Path
|
|
44
|
+
Path to a '.tdms' measurement file.
|
|
45
|
+
*args:
|
|
46
|
+
Arguments for `RTDCBase`
|
|
47
|
+
**kwargs:
|
|
48
|
+
Keyword arguments for `RTDCBase`
|
|
49
|
+
|
|
50
|
+
Attributes
|
|
51
|
+
----------
|
|
52
|
+
path: pathlib.Path
|
|
53
|
+
Path to the experimental dataset (main .tdms file)
|
|
54
|
+
"""
|
|
55
|
+
if not NPTDMS_AVAILABLE:
|
|
56
|
+
raise ModuleNotFoundError(
|
|
57
|
+
"Package `nptdms` required for TDMS format!")
|
|
58
|
+
# Initialize RTDCBase
|
|
59
|
+
super(RTDC_TDMS, self).__init__(*args, **kwargs)
|
|
60
|
+
|
|
61
|
+
tdms_path = pathlib.Path(tdms_path)
|
|
62
|
+
# Events is a simple dictionary
|
|
63
|
+
self._events = {}
|
|
64
|
+
self._hash = None
|
|
65
|
+
self.path = tdms_path
|
|
66
|
+
self.title = get_project_name_from_path(tdms_path, append_mx=True)
|
|
67
|
+
|
|
68
|
+
# tdms-related convenience properties
|
|
69
|
+
self._fdir = tdms_path.parent
|
|
70
|
+
self._mid = tdms_path.name.split("_")[0]
|
|
71
|
+
|
|
72
|
+
self._init_data_with_tdms(tdms_path)
|
|
73
|
+
|
|
74
|
+
# Add additional features
|
|
75
|
+
# event images
|
|
76
|
+
self._events["image"] = ImageColumn(self)
|
|
77
|
+
# event contours (requires image)
|
|
78
|
+
self._events["contour"] = ContourColumn(self)
|
|
79
|
+
# event masks (requires contour)
|
|
80
|
+
self._events["mask"] = MaskColumn(self)
|
|
81
|
+
# event traces
|
|
82
|
+
self._events["trace"] = TraceColumn(self)
|
|
83
|
+
|
|
84
|
+
def __contains__(self, key):
|
|
85
|
+
ct = False
|
|
86
|
+
if key in ["contour", "image", "mask", "trace"]:
|
|
87
|
+
# Take into account special cases of the tdms file format:
|
|
88
|
+
# tdms features "image", "trace", "contour", and "mask"
|
|
89
|
+
# evaluate to True (len()!=0) if the data exist on disk
|
|
90
|
+
if key in self._events and self._events[key]:
|
|
91
|
+
ct = True
|
|
92
|
+
else:
|
|
93
|
+
ct = super(RTDC_TDMS, self).__contains__(key)
|
|
94
|
+
return ct
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def extract_tdms_config(path,
|
|
98
|
+
features_available=None,
|
|
99
|
+
ret_source_files=False,
|
|
100
|
+
ignore_missing=False):
|
|
101
|
+
"""Extract as much metadata as possible for a .tdms dataset
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
path: str or pathlib.Path
|
|
106
|
+
A path representing the dataset. This could be either a
|
|
107
|
+
.tdms file or an .avi file. The only thing important here
|
|
108
|
+
is the prefix (before the underscore "_") which determines
|
|
109
|
+
the location of the camera.ini and para.ini files.
|
|
110
|
+
features_available: list of str
|
|
111
|
+
List of features known to be available for this dataset.
|
|
112
|
+
Used for defining e.g. fluorescnence metadata.
|
|
113
|
+
ret_source_files: bool
|
|
114
|
+
Return the list of files used to extract metadata from.
|
|
115
|
+
ignore_missing: bool
|
|
116
|
+
Nevermind when para.ini is missing.
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
config: .Configuration
|
|
121
|
+
The metadata Configuration instance
|
|
122
|
+
source_paths: str
|
|
123
|
+
List of metadata file paths, only returned when ret_source_files
|
|
124
|
+
is True
|
|
125
|
+
"""
|
|
126
|
+
if features_available is None:
|
|
127
|
+
features_available = []
|
|
128
|
+
mid = path.name.split("_")[0]
|
|
129
|
+
# Set up configuration
|
|
130
|
+
config_paths = []
|
|
131
|
+
para_ini = path.with_name(mid + "_para.ini")
|
|
132
|
+
if not para_ini.exists() and not ignore_missing:
|
|
133
|
+
raise IncompleteTDMSFileFormatError(f"Could not find {para_ini}!")
|
|
134
|
+
for pp in [para_ini,
|
|
135
|
+
path.with_name(mid + "_camera.ini"),
|
|
136
|
+
path.with_name(mid + "_SoftwareSettings.ini")]:
|
|
137
|
+
if pp.exists():
|
|
138
|
+
config_paths.append(pp)
|
|
139
|
+
|
|
140
|
+
tdms_config = Configuration(files=config_paths, disable_checks=True)
|
|
141
|
+
|
|
142
|
+
dclab_config = Configuration()
|
|
143
|
+
|
|
144
|
+
source_files = copy.copy(config_paths)
|
|
145
|
+
|
|
146
|
+
for cfgii in [naming.configmap, naming.config_map_set]:
|
|
147
|
+
for section in cfgii:
|
|
148
|
+
for pname in cfgii[section]:
|
|
149
|
+
meta = cfgii[section][pname]
|
|
150
|
+
convfunc = dfn.get_config_value_func(section, pname)
|
|
151
|
+
if isinstance(meta, tuple):
|
|
152
|
+
osec, opar = meta
|
|
153
|
+
if osec in tdms_config and opar in tdms_config[osec]:
|
|
154
|
+
val = tdms_config[osec].pop(opar)
|
|
155
|
+
dclab_config[section][pname] = convfunc(val)
|
|
156
|
+
else:
|
|
157
|
+
dclab_config[section][pname] = convfunc(meta)
|
|
158
|
+
|
|
159
|
+
# Additional information from log file
|
|
160
|
+
rtfdc_log = path.with_name(mid + "_log.ini")
|
|
161
|
+
if rtfdc_log.exists():
|
|
162
|
+
source_files.append(rtfdc_log)
|
|
163
|
+
with rtfdc_log.open("r", errors="replace") as fd:
|
|
164
|
+
loglines = fd.readlines()
|
|
165
|
+
for line in loglines:
|
|
166
|
+
if line.startswith("[EVENT LOG]"):
|
|
167
|
+
sv = line.split("]")[1].strip()
|
|
168
|
+
if sv:
|
|
169
|
+
dclab_config["setup"]["software version"] = sv
|
|
170
|
+
|
|
171
|
+
rtfdc_parm = path.with_name("parameters.txt")
|
|
172
|
+
if rtfdc_parm.exists():
|
|
173
|
+
source_files.append(rtfdc_parm)
|
|
174
|
+
with rtfdc_parm.open("r", errors="replace") as fd:
|
|
175
|
+
parlines = fd.readlines()
|
|
176
|
+
p1 = None
|
|
177
|
+
p2 = None
|
|
178
|
+
p3 = None
|
|
179
|
+
for line in parlines:
|
|
180
|
+
if line.startswith("pulse_led"):
|
|
181
|
+
fdur = float(line.split()[1])
|
|
182
|
+
dclab_config["imaging"]["flash duration"] = fdur
|
|
183
|
+
elif line.startswith("numberofchannels"):
|
|
184
|
+
nc = int(line.split()[1])
|
|
185
|
+
dclab_config["fluorescence"]["channel count"] = nc
|
|
186
|
+
elif line.startswith("laser488"):
|
|
187
|
+
p1 = float(line.split()[1])
|
|
188
|
+
dclab_config["fluorescence"]["laser 1 lambda"] = 488
|
|
189
|
+
dclab_config["fluorescence"]["laser 1 power"] = p1
|
|
190
|
+
elif line.startswith("laser561"):
|
|
191
|
+
p2 = float(line.split()[1])
|
|
192
|
+
dclab_config["fluorescence"]["laser 2 lambda"] = 561
|
|
193
|
+
dclab_config["fluorescence"]["laser 2 power"] = p2
|
|
194
|
+
elif line.startswith("laser640"):
|
|
195
|
+
p3 = float(line.split()[1])
|
|
196
|
+
dclab_config["fluorescence"]["laser 3 lambda"] = 640
|
|
197
|
+
dclab_config["fluorescence"]["laser 3 power"] = p3
|
|
198
|
+
elif line.startswith("samplerate"):
|
|
199
|
+
sr = int(float(line.split()[1]))
|
|
200
|
+
dclab_config["fluorescence"]["sample rate"] = sr
|
|
201
|
+
elif line.startswith("samplesperframe"):
|
|
202
|
+
spe = int(line.split()[1])
|
|
203
|
+
dclab_config["fluorescence"]["samples per event"] = spe
|
|
204
|
+
elif line.startswith("Vmin"):
|
|
205
|
+
vmin = float(line.split()[1])
|
|
206
|
+
dclab_config["fluorescence"]["signal min"] = vmin
|
|
207
|
+
elif line.startswith("Vmax"):
|
|
208
|
+
vmax = float(line.split()[1])
|
|
209
|
+
dclab_config["fluorescence"]["signal max"] = vmax
|
|
210
|
+
elif line.startswith("median_pmt"):
|
|
211
|
+
mfs = int(line.split()[1])
|
|
212
|
+
dclab_config["fluorescence"]["trace median"] = mfs
|
|
213
|
+
# Add generic channel names (independent of lasers)
|
|
214
|
+
for ii in range(1, 4):
|
|
215
|
+
chn = "channel {} name".format(ii)
|
|
216
|
+
fln = "fl{}_max".format(ii)
|
|
217
|
+
if (fln in features_available
|
|
218
|
+
and chn not in dclab_config["fluorescence"]):
|
|
219
|
+
dclab_config["fluorescence"][chn] = "FL{}".format(ii)
|
|
220
|
+
lc = bool(p1) + bool(p2) + bool(p3)
|
|
221
|
+
dclab_config["fluorescence"]["laser count"] = lc
|
|
222
|
+
li = (p1 is not None) + (p2 is not None) + (p3 is not None)
|
|
223
|
+
dclab_config["fluorescence"]["lasers installed"] = li
|
|
224
|
+
dclab_config["fluorescence"]["channels installed"] = 3
|
|
225
|
+
|
|
226
|
+
# fluorescence
|
|
227
|
+
if ("fluorescence" in dclab_config
|
|
228
|
+
or "fl1_max" in features_available
|
|
229
|
+
or "fl2_max" in features_available
|
|
230
|
+
or "fl3_max" in features_available):
|
|
231
|
+
# hardware-defined (always the same)
|
|
232
|
+
dclab_config["fluorescence"].setdefault("bit depth", 16)
|
|
233
|
+
dclab_config["fluorescence"].setdefault("laser 1 lambda", 488.)
|
|
234
|
+
dclab_config["fluorescence"].setdefault("laser 2 lambda", 561.)
|
|
235
|
+
dclab_config["fluorescence"].setdefault("laser 3 lambda", 640.)
|
|
236
|
+
|
|
237
|
+
# Additional information from commented-out log-file (manual)
|
|
238
|
+
if para_ini.exists():
|
|
239
|
+
text = para_ini.read_text(errors="replace").split("\n")
|
|
240
|
+
lns = [s[1:].strip() for s in text if s.startswith("#")]
|
|
241
|
+
if lns and lns[0] == "[FLUOR]":
|
|
242
|
+
if ("software version" not in dclab_config["setup"]
|
|
243
|
+
and lns[1].startswith("fRTDC")):
|
|
244
|
+
dclab_config["setup"]["software version"] = lns[1]
|
|
245
|
+
for ll in lns[2:]:
|
|
246
|
+
if ("sample rate" not in dclab_config["fluorescence"]
|
|
247
|
+
and ll.startswith("Samplerate")):
|
|
248
|
+
val = int(float(ll.split("=")[1]))
|
|
249
|
+
dclab_config["fluorescence"]["sample rate"] = val
|
|
250
|
+
elif ("signal min" not in dclab_config["fluorescence"]
|
|
251
|
+
and ll.startswith("ADCmin")):
|
|
252
|
+
val = float(ll.split("=")[1])
|
|
253
|
+
dclab_config["fluorescence"]["signal min"] = val
|
|
254
|
+
elif ("signal max" not in dclab_config["fluorescence"]
|
|
255
|
+
and ll.startswith("ADCmax")):
|
|
256
|
+
val = float(ll.split("=")[1])
|
|
257
|
+
dclab_config["fluorescence"]["signal max"] = val
|
|
258
|
+
|
|
259
|
+
if dclab_config["imaging"].get("frame rate") == 0:
|
|
260
|
+
dclab_config["imaging"].pop("frame rate")
|
|
261
|
+
|
|
262
|
+
if dclab_config["setup"].get("flow rate") == 0:
|
|
263
|
+
dclab_config["setup"].pop("flow rate")
|
|
264
|
+
|
|
265
|
+
if "channel width" not in dclab_config["setup"]:
|
|
266
|
+
if "channel width" in tdms_config.get("general", {}):
|
|
267
|
+
channel_width = tdms_config["general"]["channel width"]
|
|
268
|
+
elif dclab_config["setup"].get("flow rate", 0) < 0.16:
|
|
269
|
+
channel_width = 20.
|
|
270
|
+
else:
|
|
271
|
+
channel_width = 30.
|
|
272
|
+
dclab_config["setup"]["channel width"] = channel_width
|
|
273
|
+
|
|
274
|
+
if "sample" not in dclab_config["experiment"]:
|
|
275
|
+
# Measured sample or user-defined reference
|
|
276
|
+
sample = get_project_name_from_path(path)
|
|
277
|
+
dclab_config["experiment"]["sample"] = sample
|
|
278
|
+
|
|
279
|
+
# imaging
|
|
280
|
+
dclab_config["imaging"].setdefault("pixel size", 0.34)
|
|
281
|
+
|
|
282
|
+
# medium convention for CellCarrierB
|
|
283
|
+
if ("medium" in dclab_config["setup"] and
|
|
284
|
+
dclab_config["setup"]["medium"].lower() == "cellcarrier b"):
|
|
285
|
+
dclab_config["setup"]["medium"] = "CellCarrierB"
|
|
286
|
+
|
|
287
|
+
# replace "+" with ","
|
|
288
|
+
if "module composition" in dclab_config["setup"]:
|
|
289
|
+
mc = dclab_config["setup"]["module composition"]
|
|
290
|
+
if mc.count("+"):
|
|
291
|
+
mc2 = ", ".join([m.strip() for m in mc.split("+")])
|
|
292
|
+
dclab_config["setup"]["module composition"] = mc2
|
|
293
|
+
|
|
294
|
+
dclab_config["imaging"].setdefault("flash device", "LED")
|
|
295
|
+
dclab_config["imaging"].setdefault("flash duration", 2.0)
|
|
296
|
+
dclab_config["imaging"].setdefault("roi position x", 0)
|
|
297
|
+
dclab_config["imaging"].setdefault("roi position y", 0)
|
|
298
|
+
|
|
299
|
+
if mid.startswith("m") and mid[1] in "0123456789":
|
|
300
|
+
run_index = int(mid.strip("mM"))
|
|
301
|
+
else:
|
|
302
|
+
run_index = 1
|
|
303
|
+
dclab_config["experiment"].setdefault("run index", run_index)
|
|
304
|
+
|
|
305
|
+
if ret_source_files:
|
|
306
|
+
return dclab_config, source_files
|
|
307
|
+
else:
|
|
308
|
+
return dclab_config
|
|
309
|
+
|
|
310
|
+
def _init_data_with_tdms(self, tdms_filename):
|
|
311
|
+
"""Initializes the current RT-DC dataset with a tdms file.
|
|
312
|
+
"""
|
|
313
|
+
tdms_file = nptdms.TdmsFile(str(tdms_filename))
|
|
314
|
+
# time is always there
|
|
315
|
+
table = "Cell Track"
|
|
316
|
+
# Edit naming.dclab2tdms to add features
|
|
317
|
+
for arg in naming.tdms2dclab:
|
|
318
|
+
try:
|
|
319
|
+
data = tdms_file[table][arg].data
|
|
320
|
+
except KeyError:
|
|
321
|
+
pass
|
|
322
|
+
else:
|
|
323
|
+
if data is None or len(data) == 0:
|
|
324
|
+
# Ignore empty features. npTDMS treats empty
|
|
325
|
+
# features in the following way:
|
|
326
|
+
# - in nptdms 0.8.2, `data` is `None`
|
|
327
|
+
# - in nptdms 0.9.0, `data` is an array of length 0
|
|
328
|
+
continue
|
|
329
|
+
self._events[naming.tdms2dclab[arg]] = data
|
|
330
|
+
if len(self._events) == 0:
|
|
331
|
+
raise IncompleteTDMSFileFormatError(
|
|
332
|
+
"No usable feature data found in '{}'!".format(tdms_filename))
|
|
333
|
+
|
|
334
|
+
self.config, config_paths = self.extract_tdms_config(
|
|
335
|
+
self.path,
|
|
336
|
+
features_available=sorted(self._events.keys()),
|
|
337
|
+
ret_source_files=True)
|
|
338
|
+
self._complete_config_with_data()
|
|
339
|
+
|
|
340
|
+
# Load log files
|
|
341
|
+
log_files = config_paths
|
|
342
|
+
for name in [self._mid + "_events.txt",
|
|
343
|
+
self._mid + "_log.ini",
|
|
344
|
+
self._mid + "_SoftwareSettings.ini",
|
|
345
|
+
"FG_Config.mcf",
|
|
346
|
+
"parameters.txt"]:
|
|
347
|
+
pl = self.path.with_name(name)
|
|
348
|
+
if pl.exists():
|
|
349
|
+
log_files.append(pl)
|
|
350
|
+
for pp in sorted(set(log_files)): # avoid duplicates
|
|
351
|
+
with pp.open("r", errors="replace") as f:
|
|
352
|
+
cfg = [s.strip() for s in f.readlines()]
|
|
353
|
+
self.logs[pp.name] = cfg
|
|
354
|
+
|
|
355
|
+
def _complete_config_with_data(self):
|
|
356
|
+
# measurement start time
|
|
357
|
+
tse = self.path.stat().st_mtime
|
|
358
|
+
if "time" in self:
|
|
359
|
+
# correct for duration of experiment
|
|
360
|
+
tse -= self["time"][-1]
|
|
361
|
+
loct = time.localtime(tse)
|
|
362
|
+
|
|
363
|
+
# Start time of measurement ('HH:MM:SS')
|
|
364
|
+
timestr = time.strftime("%H:%M:%S", loct)
|
|
365
|
+
self.config["experiment"].setdefault("time", timestr)
|
|
366
|
+
|
|
367
|
+
# Date of measurement ('YYYY-MM-DD')
|
|
368
|
+
datestr = time.strftime("%Y-%m-%d", loct)
|
|
369
|
+
self.config["experiment"].setdefault("date", datestr)
|
|
370
|
+
|
|
371
|
+
# Number of recorded events
|
|
372
|
+
self.config["experiment"].setdefault("event count", len(self))
|
|
373
|
+
|
|
374
|
+
# fmt_tdms
|
|
375
|
+
self.config["fmt_tdms"].setdefault("video frame offset", 1)
|
|
376
|
+
|
|
377
|
+
# setup (compatibility to old tdms formats)
|
|
378
|
+
self.config["setup"].setdefault("flow rate", np.nan)
|
|
379
|
+
|
|
380
|
+
@staticmethod
|
|
381
|
+
def can_open(h5path):
|
|
382
|
+
"""Check whether a given file is in the .tdms file format"""
|
|
383
|
+
return pathlib.Path(h5path).suffix == ".tdms"
|
|
384
|
+
|
|
385
|
+
@property
|
|
386
|
+
def hash(self):
|
|
387
|
+
"""Hash value based on file name and .ini file content"""
|
|
388
|
+
if self._hash is None:
|
|
389
|
+
# Only hash _camera.ini and _para.ini
|
|
390
|
+
fsh = [self.path.with_name(self._mid + "_camera.ini"),
|
|
391
|
+
self.path.with_name(self._mid + "_para.ini")]
|
|
392
|
+
tohash = [hashfile(f) for f in fsh]
|
|
393
|
+
tohash.append(self.path.name)
|
|
394
|
+
# Hash a maximum of ~1MB of the tdms file
|
|
395
|
+
tohash.append(hashfile(self.path, blocksize=65536, count=20))
|
|
396
|
+
self._hash = hashobj(tohash)
|
|
397
|
+
return self._hash
|
|
398
|
+
|
|
399
|
+
def close(self):
|
|
400
|
+
super(RTDC_TDMS, self).close()
|
|
401
|
+
del self._events["image"]._image_data
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def get_project_name_from_path(path, append_mx=False):
|
|
405
|
+
"""Get the project name from a path.
|
|
406
|
+
|
|
407
|
+
For a path "/home/peter/hans/HLC12398/online/M1_13.tdms" or
|
|
408
|
+
For a path "/home/peter/hans/HLC12398/online/data/M1_13.tdms" or
|
|
409
|
+
without the ".tdms" file, this will return always "HLC12398".
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
path: str or pathlib.Path
|
|
414
|
+
path to tdms file
|
|
415
|
+
append_mx: bool
|
|
416
|
+
append measurement number, e.g. "M1"
|
|
417
|
+
"""
|
|
418
|
+
path = pathlib.Path(path)
|
|
419
|
+
if path.suffix == ".tdms":
|
|
420
|
+
dirn = path.parent
|
|
421
|
+
mx = path.name.split("_")[0]
|
|
422
|
+
elif path.is_dir():
|
|
423
|
+
dirn = path
|
|
424
|
+
mx = ""
|
|
425
|
+
else:
|
|
426
|
+
dirn = path.parent
|
|
427
|
+
mx = ""
|
|
428
|
+
|
|
429
|
+
project = ""
|
|
430
|
+
if mx:
|
|
431
|
+
# check para.ini
|
|
432
|
+
para = dirn / (mx + "_para.ini")
|
|
433
|
+
if para.exists():
|
|
434
|
+
with para.open("r", errors="replace") as fd:
|
|
435
|
+
lines = fd.readlines()
|
|
436
|
+
for line in lines:
|
|
437
|
+
if line.startswith("Sample Name ="):
|
|
438
|
+
project = line.split("=")[1].strip()
|
|
439
|
+
break
|
|
440
|
+
|
|
441
|
+
if not project:
|
|
442
|
+
# check if the directory contains data or is online
|
|
443
|
+
root1, trail1 = dirn.parent, dirn.name
|
|
444
|
+
root2, trail2 = root1.parent, root1.name
|
|
445
|
+
trail3 = root2.name
|
|
446
|
+
|
|
447
|
+
if trail1.lower() in ["online", "offline"]:
|
|
448
|
+
# /home/peter/hans/HLC12398/online/
|
|
449
|
+
project = trail2
|
|
450
|
+
elif (trail1.lower() == "data" and
|
|
451
|
+
trail2.lower() in ["online", "offline"]):
|
|
452
|
+
# this is olis new folder sctructure
|
|
453
|
+
# /home/peter/hans/HLC12398/online/data/
|
|
454
|
+
project = trail3
|
|
455
|
+
else:
|
|
456
|
+
project = trail1
|
|
457
|
+
|
|
458
|
+
if append_mx:
|
|
459
|
+
project += " - " + mx
|
|
460
|
+
|
|
461
|
+
return project
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def get_tdms_files(directory):
|
|
465
|
+
"""Recursively find projects based on '.tdms' file endings
|
|
466
|
+
|
|
467
|
+
Searches the `directory` recursively and return a sorted list
|
|
468
|
+
of all found '.tdms' project files, except fluorescence
|
|
469
|
+
data trace files which end with `_traces.tdms`.
|
|
470
|
+
"""
|
|
471
|
+
path = pathlib.Path(directory).resolve()
|
|
472
|
+
# get all tdms files
|
|
473
|
+
tdmslist = [r for r in path.rglob("*.tdms") if r.is_file()]
|
|
474
|
+
# exclude traces files
|
|
475
|
+
tdmslist = [r for r in tdmslist if not r.name.endswith("_traces.tdms")]
|
|
476
|
+
return sorted(tdmslist)
|