phasorpy 0.7__cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- phasorpy/__init__.py +9 -0
- phasorpy/__main__.py +7 -0
- phasorpy/_phasorpy.cpython-314t-aarch64-linux-gnu.so +0 -0
- phasorpy/_phasorpy.pyx +2688 -0
- phasorpy/_typing.py +77 -0
- phasorpy/_utils.py +786 -0
- phasorpy/cli.py +160 -0
- phasorpy/cluster.py +200 -0
- phasorpy/color.py +589 -0
- phasorpy/component.py +707 -0
- phasorpy/conftest.py +38 -0
- phasorpy/cursor.py +500 -0
- phasorpy/datasets.py +722 -0
- phasorpy/experimental.py +310 -0
- phasorpy/io/__init__.py +138 -0
- phasorpy/io/_flimlabs.py +360 -0
- phasorpy/io/_leica.py +331 -0
- phasorpy/io/_ometiff.py +444 -0
- phasorpy/io/_other.py +890 -0
- phasorpy/io/_simfcs.py +652 -0
- phasorpy/lifetime.py +2058 -0
- phasorpy/phasor.py +2018 -0
- phasorpy/plot/__init__.py +27 -0
- phasorpy/plot/_functions.py +723 -0
- phasorpy/plot/_lifetime_plots.py +563 -0
- phasorpy/plot/_phasorplot.py +1507 -0
- phasorpy/plot/_phasorplot_fret.py +561 -0
- phasorpy/py.typed +0 -0
- phasorpy/utils.py +172 -0
- phasorpy-0.7.dist-info/METADATA +74 -0
- phasorpy-0.7.dist-info/RECORD +36 -0
- phasorpy-0.7.dist-info/WHEEL +7 -0
- phasorpy-0.7.dist-info/entry_points.txt +2 -0
- phasorpy-0.7.dist-info/licenses/LICENSE.txt +21 -0
- phasorpy-0.7.dist-info/top_level.txt +1 -0
- phasorpy.libs/libgomp-947d5fa1.so.1.0.0 +0 -0
phasorpy/io/_simfcs.py
ADDED
@@ -0,0 +1,652 @@
|
|
1
|
+
"""Read and write SimFCS file formats."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
'phasor_from_simfcs_referenced',
|
7
|
+
'phasor_to_simfcs_referenced',
|
8
|
+
'signal_from_b64',
|
9
|
+
'signal_from_bh',
|
10
|
+
'signal_from_bhz',
|
11
|
+
'signal_from_fbd',
|
12
|
+
'signal_from_z64',
|
13
|
+
]
|
14
|
+
|
15
|
+
import math
|
16
|
+
import os
|
17
|
+
import struct
|
18
|
+
import zlib
|
19
|
+
from typing import TYPE_CHECKING
|
20
|
+
|
21
|
+
from .._utils import chunk_iter, parse_harmonic, xarray_metadata
|
22
|
+
from ..phasor import phasor_from_polar, phasor_to_polar
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from .._typing import (
|
26
|
+
Any,
|
27
|
+
ArrayLike,
|
28
|
+
DataArray,
|
29
|
+
Literal,
|
30
|
+
NDArray,
|
31
|
+
PathLike,
|
32
|
+
Sequence,
|
33
|
+
)
|
34
|
+
|
35
|
+
import numpy
|
36
|
+
|
37
|
+
|
38
|
+
def phasor_to_simfcs_referenced(
|
39
|
+
filename: str | PathLike[Any],
|
40
|
+
mean: ArrayLike,
|
41
|
+
real: ArrayLike,
|
42
|
+
imag: ArrayLike,
|
43
|
+
/,
|
44
|
+
*,
|
45
|
+
size: int | None = None,
|
46
|
+
dims: Sequence[str] | None = None,
|
47
|
+
) -> None:
|
48
|
+
"""Write phasor coordinate images to SimFCS referenced R64 file(s).
|
49
|
+
|
50
|
+
SimFCS referenced R64 files store square-shaped (commonly 256x256)
|
51
|
+
images of the average intensity, and the calibrated phasor coordinates
|
52
|
+
(encoded as phase and modulation) of two harmonics as ZIP-compressed,
|
53
|
+
single precision floating point arrays.
|
54
|
+
The file format does not support any metadata.
|
55
|
+
|
56
|
+
Images with more than two dimensions or larger than square size are
|
57
|
+
chunked to square-sized images and saved to separate files with
|
58
|
+
a name pattern, for example, "filename_T099_Y256_X000.r64".
|
59
|
+
Images or chunks with less than two dimensions or smaller than square size
|
60
|
+
are padded with NaN values.
|
61
|
+
|
62
|
+
Parameters
|
63
|
+
----------
|
64
|
+
filename : str or Path
|
65
|
+
Name of SimFCS referenced R64 file to write.
|
66
|
+
The file extension must be ``.r64``.
|
67
|
+
mean : array_like
|
68
|
+
Average intensity image.
|
69
|
+
real : array_like
|
70
|
+
Image of real component of calibrated phasor coordinates.
|
71
|
+
Multiple harmonics, if any, must be in the first dimension.
|
72
|
+
Harmonics must be starting at and increasing by one.
|
73
|
+
imag : array_like
|
74
|
+
Image of imaginary component of calibrated phasor coordinates.
|
75
|
+
Multiple harmonics, if any, must be in the first dimension.
|
76
|
+
Harmonics must be starting at and increasing by one.
|
77
|
+
size : int, optional
|
78
|
+
Size of X and Y dimensions of square-sized images stored in file.
|
79
|
+
Must be in range [4, 65535].
|
80
|
+
By default, ``size = min(256, max(4, sizey, sizex))``.
|
81
|
+
dims : sequence of str, optional
|
82
|
+
Character codes for `mean` dimensions used to format file names.
|
83
|
+
Only used when chunking multi-dimensional data into multiple files.
|
84
|
+
|
85
|
+
See Also
|
86
|
+
--------
|
87
|
+
phasorpy.io.phasor_from_simfcs_referenced
|
88
|
+
|
89
|
+
Examples
|
90
|
+
--------
|
91
|
+
>>> mean, real, imag = numpy.random.rand(3, 32, 32)
|
92
|
+
>>> phasor_to_simfcs_referenced('_phasorpy.r64', mean, real, imag)
|
93
|
+
|
94
|
+
"""
|
95
|
+
filename, ext = os.path.splitext(filename)
|
96
|
+
if ext.lower() != '.r64':
|
97
|
+
raise ValueError(f'file extension {ext} != .r64')
|
98
|
+
|
99
|
+
# TODO: delay conversions to numpy arrays to inner loop
|
100
|
+
mean = numpy.asarray(mean, numpy.float32)
|
101
|
+
phi, mod = phasor_to_polar(real, imag, dtype=numpy.float32)
|
102
|
+
del real
|
103
|
+
del imag
|
104
|
+
phi = numpy.rad2deg(phi)
|
105
|
+
|
106
|
+
if phi.shape != mod.shape:
|
107
|
+
raise ValueError(f'{phi.shape=} != {mod.shape=}')
|
108
|
+
if mean.shape != phi.shape[-mean.ndim :]:
|
109
|
+
raise ValueError(f'{mean.shape=} != {phi.shape[-mean.ndim:]=}')
|
110
|
+
if phi.ndim == mean.ndim:
|
111
|
+
phi = phi.reshape(1, *phi.shape)
|
112
|
+
mod = mod.reshape(1, *mod.shape)
|
113
|
+
nharmonic = phi.shape[0]
|
114
|
+
|
115
|
+
if mean.ndim < 2:
|
116
|
+
# not an image
|
117
|
+
mean = mean.reshape(1, -1)
|
118
|
+
phi = phi.reshape(nharmonic, 1, -1)
|
119
|
+
mod = mod.reshape(nharmonic, 1, -1)
|
120
|
+
|
121
|
+
# TODO: investigate actual size and harmonics limits of SimFCS
|
122
|
+
sizey, sizex = mean.shape[-2:]
|
123
|
+
if size is None:
|
124
|
+
size = min(256, max(4, sizey, sizex))
|
125
|
+
elif not 4 <= size <= 65535:
|
126
|
+
raise ValueError(f'{size=} out of range [4, 65535]')
|
127
|
+
|
128
|
+
harmonics_per_file = 2 # TODO: make this a parameter?
|
129
|
+
chunk_shape = tuple(
|
130
|
+
[max(harmonics_per_file, 2)] + ([1] * (phi.ndim - 3)) + [size, size]
|
131
|
+
)
|
132
|
+
multi_file = any(i / j > 1 for i, j in zip(phi.shape, chunk_shape))
|
133
|
+
|
134
|
+
if dims is not None and len(dims) == phi.ndim - 1:
|
135
|
+
dims = tuple(dims)
|
136
|
+
dims = ('h' if dims[0].islower() else 'H',) + dims
|
137
|
+
|
138
|
+
chunk = numpy.empty((size, size), dtype=numpy.float32)
|
139
|
+
|
140
|
+
def rawdata_append(
|
141
|
+
rawdata: list[bytes], a: NDArray[Any] | None = None
|
142
|
+
) -> None:
|
143
|
+
if a is None:
|
144
|
+
chunk[:] = numpy.nan
|
145
|
+
rawdata.append(chunk.tobytes())
|
146
|
+
else:
|
147
|
+
sizey, sizex = a.shape[-2:]
|
148
|
+
if sizey == size and sizex == size:
|
149
|
+
rawdata.append(a.tobytes())
|
150
|
+
elif sizey <= size and sizex <= size:
|
151
|
+
chunk[:sizey, :sizex] = a[..., :sizey, :sizex]
|
152
|
+
chunk[:sizey, sizex:] = numpy.nan
|
153
|
+
chunk[sizey:, :] = numpy.nan
|
154
|
+
rawdata.append(chunk.tobytes())
|
155
|
+
else:
|
156
|
+
raise RuntimeError # should not be reached
|
157
|
+
|
158
|
+
for index, label, _ in chunk_iter(
|
159
|
+
phi.shape, chunk_shape, dims, squeeze=False, use_index=True
|
160
|
+
):
|
161
|
+
rawdata = [struct.pack('I', size)]
|
162
|
+
rawdata_append(rawdata, mean[index[1:]])
|
163
|
+
phi_ = phi[index]
|
164
|
+
mod_ = mod[index]
|
165
|
+
for i in range(phi_.shape[0]):
|
166
|
+
rawdata_append(rawdata, phi_[i])
|
167
|
+
rawdata_append(rawdata, mod_[i])
|
168
|
+
if phi_.shape[0] == 1:
|
169
|
+
rawdata_append(rawdata)
|
170
|
+
rawdata_append(rawdata)
|
171
|
+
|
172
|
+
if not multi_file:
|
173
|
+
label = ''
|
174
|
+
with open(filename + label + ext, 'wb') as fh:
|
175
|
+
fh.write(zlib.compress(b''.join(rawdata)))
|
176
|
+
|
177
|
+
|
178
|
+
def phasor_from_simfcs_referenced(
|
179
|
+
filename: str | PathLike[Any],
|
180
|
+
/,
|
181
|
+
*,
|
182
|
+
harmonic: int | Sequence[int] | Literal['all'] | str | None = None,
|
183
|
+
) -> tuple[NDArray[Any], NDArray[Any], NDArray[Any], dict[str, Any]]:
|
184
|
+
"""Return phasor coordinates and metadata from SimFCS REF or R64 file.
|
185
|
+
|
186
|
+
SimFCS referenced REF and R64 files contain square-shaped phasor
|
187
|
+
coordinate images (encoded as phase and modulation) for two harmonics.
|
188
|
+
Phasor coordinates from lifetime-resolved signals are calibrated.
|
189
|
+
Variants of referenced files (RE<n>) written by other software may
|
190
|
+
contain up to eight harmonics and may be uncalibrated.
|
191
|
+
|
192
|
+
Parameters
|
193
|
+
----------
|
194
|
+
filename : str or Path
|
195
|
+
Name of SimFCS REF, R64, or RE<n> file to read.
|
196
|
+
harmonic : int, sequence of int, or 'all', optional
|
197
|
+
Harmonic(s) to include in returned phasor coordinates.
|
198
|
+
By default, only the first harmonic is returned.
|
199
|
+
If 'all', return all available harmonics.
|
200
|
+
If int or sequence, return specified harmonic(s).
|
201
|
+
|
202
|
+
Returns
|
203
|
+
-------
|
204
|
+
mean : ndarray
|
205
|
+
Average intensity image.
|
206
|
+
real : ndarray
|
207
|
+
Image of real component of phasor coordinates.
|
208
|
+
Multiple harmonics, if any, are in the first axis.
|
209
|
+
imag : ndarray
|
210
|
+
Image of imaginary component of phasor coordinates.
|
211
|
+
Multiple harmonics, if any, are in the first axis.
|
212
|
+
attrs : dict
|
213
|
+
Select metadata containing:
|
214
|
+
|
215
|
+
- ``'dims'`` (tuple of str):
|
216
|
+
:ref:`Axes codes <axes>` for `mean` image dimensions.
|
217
|
+
|
218
|
+
Raises
|
219
|
+
------
|
220
|
+
lfdfiles.LfdfileError
|
221
|
+
File is not a SimFCS REF, R64, or RE<n> file.
|
222
|
+
|
223
|
+
See Also
|
224
|
+
--------
|
225
|
+
phasorpy.io.phasor_to_simfcs_referenced
|
226
|
+
|
227
|
+
Notes
|
228
|
+
-----
|
229
|
+
The implementation for reading R64 files is based on the
|
230
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
231
|
+
|
232
|
+
Examples
|
233
|
+
--------
|
234
|
+
>>> phasor_to_simfcs_referenced(
|
235
|
+
... '_phasorpy.r64', *numpy.random.rand(3, 32, 32)
|
236
|
+
... )
|
237
|
+
>>> mean, real, imag, _ = phasor_from_simfcs_referenced('_phasorpy.r64')
|
238
|
+
>>> mean
|
239
|
+
array([[...]], dtype=float32)
|
240
|
+
|
241
|
+
"""
|
242
|
+
ext = os.path.splitext(filename)[-1].lower()
|
243
|
+
if ext == '.r64':
|
244
|
+
import lfdfiles
|
245
|
+
|
246
|
+
with lfdfiles.SimfcsR64(filename) as r64:
|
247
|
+
data = r64.asarray()
|
248
|
+
elif ext.startswith('.re') and len(ext) == 4:
|
249
|
+
if ext[-1] == 'f':
|
250
|
+
num_images = 5
|
251
|
+
elif ext[-1].isdigit():
|
252
|
+
# non-SimFCS referenced files containing other number of harmonics
|
253
|
+
num_images = int(ext[-1]) * 2 + 1
|
254
|
+
else:
|
255
|
+
raise ValueError(
|
256
|
+
f'file extension must be .ref, .r64, or .re<n>, not {ext!r}'
|
257
|
+
)
|
258
|
+
size = os.path.getsize(filename)
|
259
|
+
if (
|
260
|
+
size > 4294967295
|
261
|
+
or size % (num_images * 4)
|
262
|
+
or not math.sqrt(size // (num_images * 4)).is_integer()
|
263
|
+
):
|
264
|
+
raise ValueError(f'{filename!r} is not a valid referenced file')
|
265
|
+
size = int(math.sqrt(size // (num_images * 4)))
|
266
|
+
data = numpy.fromfile(filename, dtype='<f4').reshape((-1, size, size))
|
267
|
+
else:
|
268
|
+
raise ValueError(
|
269
|
+
f'file extension must be .ref, .r64, or .re<n>, not {ext!r}'
|
270
|
+
)
|
271
|
+
|
272
|
+
harmonic, keep_harmonic_dim = parse_harmonic(harmonic, data.shape[0] // 2)
|
273
|
+
|
274
|
+
mean = data[0].copy()
|
275
|
+
real = numpy.empty((len(harmonic),) + mean.shape, numpy.float32)
|
276
|
+
imag = numpy.empty_like(real)
|
277
|
+
for i, h in enumerate(harmonic):
|
278
|
+
h = (h - 1) * 2 + 1
|
279
|
+
re, im = phasor_from_polar(numpy.deg2rad(data[h]), data[h + 1])
|
280
|
+
real[i] = re
|
281
|
+
imag[i] = im
|
282
|
+
if not keep_harmonic_dim:
|
283
|
+
real = real.reshape(mean.shape)
|
284
|
+
imag = imag.reshape(mean.shape)
|
285
|
+
|
286
|
+
return mean, real, imag, {'dims': ('Y', 'X')}
|
287
|
+
|
288
|
+
|
289
|
+
def signal_from_fbd(
|
290
|
+
filename: str | PathLike[Any],
|
291
|
+
/,
|
292
|
+
*,
|
293
|
+
frame: int | None = None,
|
294
|
+
channel: int | None = 0,
|
295
|
+
keepdims: bool = False,
|
296
|
+
laser_factor: float = -1.0,
|
297
|
+
) -> DataArray:
|
298
|
+
"""Return phase histogram and metadata from FLIMbox FBD file.
|
299
|
+
|
300
|
+
FDB files contain encoded cross-correlation phase histograms from
|
301
|
+
digital frequency-domain measurements using a FLIMbox device.
|
302
|
+
The encoding scheme depends on the FLIMbox device's firmware.
|
303
|
+
The FBD file format is undocumented.
|
304
|
+
|
305
|
+
This function may fail to produce expected results when files use unknown
|
306
|
+
firmware, do not contain image scans, settings were recorded incorrectly,
|
307
|
+
scanner and FLIMbox frequencies were out of sync, or scanner settings were
|
308
|
+
changed during acquisition.
|
309
|
+
|
310
|
+
Parameters
|
311
|
+
----------
|
312
|
+
filename : str or Path
|
313
|
+
Name of FLIMbox FBD file to read.
|
314
|
+
frame : int, optional
|
315
|
+
If None (default), return all frames.
|
316
|
+
If < 0, integrate time axis, else return specified frame.
|
317
|
+
channel : int or None, optional
|
318
|
+
Index of channel to return.
|
319
|
+
By default, return the first channel.
|
320
|
+
If None, return all channels.
|
321
|
+
keepdims : bool, optional, default: False
|
322
|
+
If true, return reduced axes as size-one dimensions.
|
323
|
+
laser_factor : float, optional
|
324
|
+
Factor to correct dwell_time/laser_frequency.
|
325
|
+
|
326
|
+
Returns
|
327
|
+
-------
|
328
|
+
xarray.DataArray
|
329
|
+
Phase histogram with :ref:`axes codes <axes>` ``'TCYXH'`` and
|
330
|
+
type ``uint16``:
|
331
|
+
|
332
|
+
- ``coords['H']``: cross-correlation phases in radians.
|
333
|
+
- ``attrs['frequency']``: repetition frequency in MHz.
|
334
|
+
- ``attrs['harmonic']``: harmonic contained in phase histogram.
|
335
|
+
- ``attrs['flimbox_header']``: FBD binary header, if any.
|
336
|
+
- ``attrs['flimbox_firmware']``: FLIMbox firmware settings, if any.
|
337
|
+
- ``attrs['flimbox_settings']``: Settings from FBS XML, if any.
|
338
|
+
|
339
|
+
Raises
|
340
|
+
------
|
341
|
+
lfdfiles.LfdFileError
|
342
|
+
File is not a FLIMbox FBD file.
|
343
|
+
|
344
|
+
Notes
|
345
|
+
-----
|
346
|
+
The implementation is based on the
|
347
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
348
|
+
|
349
|
+
Examples
|
350
|
+
--------
|
351
|
+
>>> signal = signal_from_fbd(
|
352
|
+
... fetch('convallaria_000$EI0S.fbd')
|
353
|
+
... ) # doctest: +SKIP
|
354
|
+
>>> signal.values # doctest: +SKIP
|
355
|
+
array(...)
|
356
|
+
>>> signal.dtype # doctest: +SKIP
|
357
|
+
dtype('uint16')
|
358
|
+
>>> signal.shape # doctest: +SKIP
|
359
|
+
(9, 256, 256, 64)
|
360
|
+
>>> signal.dims # doctest: +SKIP
|
361
|
+
('T', 'Y', 'X', 'H')
|
362
|
+
>>> signal.coords['H'].data # doctest: +SKIP
|
363
|
+
array([0, ..., 6.185])
|
364
|
+
>>> signal.attrs['frequency'] # doctest: +SKIP
|
365
|
+
40.0
|
366
|
+
|
367
|
+
"""
|
368
|
+
import lfdfiles
|
369
|
+
|
370
|
+
integrate_frames = 0 if frame is None or frame >= 0 else 1
|
371
|
+
|
372
|
+
with lfdfiles.FlimboxFbd(filename, laser_factor=laser_factor) as fbd:
|
373
|
+
data = fbd.asimage(None, None, integrate_frames=integrate_frames)
|
374
|
+
if integrate_frames:
|
375
|
+
frame = None
|
376
|
+
copy = False
|
377
|
+
axes = 'TCYXH'
|
378
|
+
if channel is None:
|
379
|
+
if not keepdims and data.shape[1] == 1:
|
380
|
+
data = data[:, 0]
|
381
|
+
axes = 'TYXH'
|
382
|
+
else:
|
383
|
+
if channel < 0 or channel >= data.shape[1]:
|
384
|
+
raise IndexError(f'{channel=} out of bounds')
|
385
|
+
if keepdims:
|
386
|
+
data = data[:, channel : channel + 1]
|
387
|
+
else:
|
388
|
+
data = data[:, channel]
|
389
|
+
axes = 'TYXH'
|
390
|
+
copy = True
|
391
|
+
if frame is None:
|
392
|
+
if not keepdims and data.shape[0] == 1:
|
393
|
+
data = data[0]
|
394
|
+
axes = axes[1:]
|
395
|
+
else:
|
396
|
+
if frame < 0 or frame >= data.shape[0]:
|
397
|
+
raise IndexError(f'{frame=} out of bounds')
|
398
|
+
if keepdims:
|
399
|
+
data = data[frame : frame + 1]
|
400
|
+
else:
|
401
|
+
data = data[frame]
|
402
|
+
axes = axes[1:]
|
403
|
+
copy = True
|
404
|
+
if copy:
|
405
|
+
data = data.copy()
|
406
|
+
# TODO: return arrival window indices or micro-times as H coords?
|
407
|
+
phases = numpy.linspace(
|
408
|
+
0.0, numpy.pi * 2, data.shape[-1], endpoint=False
|
409
|
+
)
|
410
|
+
metadata = xarray_metadata(axes, data.shape, H=phases)
|
411
|
+
attrs = metadata['attrs']
|
412
|
+
attrs['frequency'] = fbd.laser_frequency * 1e-6
|
413
|
+
attrs['harmonic'] = fbd.harmonics
|
414
|
+
if fbd.header is not None:
|
415
|
+
attrs['flimbox_header'] = fbd.header
|
416
|
+
if fbd.fbf is not None:
|
417
|
+
attrs['flimbox_firmware'] = fbd.fbf
|
418
|
+
if fbd.fbs is not None:
|
419
|
+
attrs['flimbox_settings'] = fbd.fbs
|
420
|
+
|
421
|
+
from xarray import DataArray
|
422
|
+
|
423
|
+
return DataArray(data, **metadata)
|
424
|
+
|
425
|
+
|
426
|
+
def signal_from_b64(
|
427
|
+
filename: str | PathLike[Any],
|
428
|
+
/,
|
429
|
+
) -> DataArray:
|
430
|
+
"""Return intensity image and metadata from SimFCS B64 file.
|
431
|
+
|
432
|
+
B64 files contain one or more square intensity image(s), a carpet
|
433
|
+
of lines, or a stream of intensity data. B64 files contain no metadata.
|
434
|
+
|
435
|
+
Parameters
|
436
|
+
----------
|
437
|
+
filename : str or Path
|
438
|
+
Name of SimFCS B64 file to read.
|
439
|
+
|
440
|
+
Returns
|
441
|
+
-------
|
442
|
+
xarray.DataArray
|
443
|
+
Intensity image of type ``int16``.
|
444
|
+
|
445
|
+
Raises
|
446
|
+
------
|
447
|
+
lfdfiles.LfdFileError
|
448
|
+
File is not a SimFCS B64 file.
|
449
|
+
ValueError
|
450
|
+
File does not contain an image stack.
|
451
|
+
|
452
|
+
Notes
|
453
|
+
-----
|
454
|
+
The implementation is based on the
|
455
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
456
|
+
|
457
|
+
Examples
|
458
|
+
--------
|
459
|
+
>>> signal = signal_from_b64(fetch('simfcs.b64'))
|
460
|
+
>>> signal.values
|
461
|
+
array(...)
|
462
|
+
>>> signal.dtype
|
463
|
+
dtype('int16')
|
464
|
+
>>> signal.shape
|
465
|
+
(22, 1024, 1024)
|
466
|
+
>>> signal.dtype
|
467
|
+
dtype('int16')
|
468
|
+
>>> signal.dims
|
469
|
+
('I', 'Y', 'X')
|
470
|
+
|
471
|
+
"""
|
472
|
+
import lfdfiles
|
473
|
+
|
474
|
+
with lfdfiles.SimfcsB64(filename) as b64:
|
475
|
+
data = b64.asarray()
|
476
|
+
if data.ndim != 3:
|
477
|
+
raise ValueError(
|
478
|
+
f'{os.path.basename(filename)!r} '
|
479
|
+
'does not contain an image stack'
|
480
|
+
)
|
481
|
+
metadata = xarray_metadata(b64.axes, data.shape, filename)
|
482
|
+
|
483
|
+
from xarray import DataArray
|
484
|
+
|
485
|
+
return DataArray(data, **metadata)
|
486
|
+
|
487
|
+
|
488
|
+
def signal_from_z64(
|
489
|
+
filename: str | PathLike[Any],
|
490
|
+
/,
|
491
|
+
) -> DataArray:
|
492
|
+
"""Return image stack and metadata from SimFCS Z64 file.
|
493
|
+
|
494
|
+
Z64 files commonly contain stacks of square images, such as intensity
|
495
|
+
volumes or TCSPC histograms. Z64 files contain no metadata.
|
496
|
+
|
497
|
+
Parameters
|
498
|
+
----------
|
499
|
+
filename : str or Path
|
500
|
+
Name of SimFCS Z64 file to read.
|
501
|
+
|
502
|
+
Returns
|
503
|
+
-------
|
504
|
+
xarray.DataArray
|
505
|
+
Image stack of type ``float32``.
|
506
|
+
|
507
|
+
Raises
|
508
|
+
------
|
509
|
+
lfdfiles.LfdFileError
|
510
|
+
File is not a SimFCS Z64 file.
|
511
|
+
|
512
|
+
Notes
|
513
|
+
-----
|
514
|
+
The implementation is based on the
|
515
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
516
|
+
|
517
|
+
Examples
|
518
|
+
--------
|
519
|
+
>>> signal = signal_from_z64(fetch('simfcs.z64'))
|
520
|
+
>>> signal.values
|
521
|
+
array(...)
|
522
|
+
>>> signal.dtype
|
523
|
+
dtype('float32')
|
524
|
+
>>> signal.shape
|
525
|
+
(256, 256, 256)
|
526
|
+
>>> signal.dims
|
527
|
+
('Q', 'Y', 'X')
|
528
|
+
|
529
|
+
"""
|
530
|
+
import lfdfiles
|
531
|
+
|
532
|
+
with lfdfiles.SimfcsZ64(filename) as z64:
|
533
|
+
data = z64.asarray()
|
534
|
+
metadata = xarray_metadata(z64.axes, data.shape, filename)
|
535
|
+
|
536
|
+
from xarray import DataArray
|
537
|
+
|
538
|
+
return DataArray(data, **metadata)
|
539
|
+
|
540
|
+
|
541
|
+
def signal_from_bh(
|
542
|
+
filename: str | PathLike[Any],
|
543
|
+
/,
|
544
|
+
) -> DataArray:
|
545
|
+
"""Return TCSPC histogram and metadata from SimFCS B&H file.
|
546
|
+
|
547
|
+
B&H files contain TCSPC histograms acquired from Becker & Hickl
|
548
|
+
cards, or converted from other data sources. B&H files contain no metadata.
|
549
|
+
|
550
|
+
Parameters
|
551
|
+
----------
|
552
|
+
filename : str or Path
|
553
|
+
Name of SimFCS B&H file to read.
|
554
|
+
|
555
|
+
Returns
|
556
|
+
-------
|
557
|
+
xarray.DataArray
|
558
|
+
TCSPC histogram with :ref:`axes codes <axes>` ``'HYX'``,
|
559
|
+
shape ``(256, 256, 256)``, and type ``float32``.
|
560
|
+
|
561
|
+
Raises
|
562
|
+
------
|
563
|
+
lfdfiles.LfdFileError
|
564
|
+
File is not a SimFCS B&H file.
|
565
|
+
|
566
|
+
Notes
|
567
|
+
-----
|
568
|
+
The implementation is based on the
|
569
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
570
|
+
|
571
|
+
Examples
|
572
|
+
--------
|
573
|
+
>>> signal = signal_from_bh(fetch('simfcs.b&h'))
|
574
|
+
>>> signal.values
|
575
|
+
array(...)
|
576
|
+
>>> signal.dtype
|
577
|
+
dtype('float32')
|
578
|
+
>>> signal.shape
|
579
|
+
(256, 256, 256)
|
580
|
+
>>> signal.dims
|
581
|
+
('H', 'Y', 'X')
|
582
|
+
|
583
|
+
"""
|
584
|
+
import lfdfiles
|
585
|
+
|
586
|
+
with lfdfiles.SimfcsBh(filename) as bnh:
|
587
|
+
assert bnh.axes is not None
|
588
|
+
data = bnh.asarray()
|
589
|
+
metadata = xarray_metadata(
|
590
|
+
bnh.axes.replace('Q', 'H'), data.shape, filename
|
591
|
+
)
|
592
|
+
|
593
|
+
from xarray import DataArray
|
594
|
+
|
595
|
+
return DataArray(data, **metadata)
|
596
|
+
|
597
|
+
|
598
|
+
def signal_from_bhz(
|
599
|
+
filename: str | PathLike[Any],
|
600
|
+
/,
|
601
|
+
) -> DataArray:
|
602
|
+
"""Return TCSPC histogram and metadata from SimFCS BHZ file.
|
603
|
+
|
604
|
+
BHZ files contain TCSPC histograms acquired from Becker & Hickl
|
605
|
+
cards, or converted from other data sources. BHZ files contain no metadata.
|
606
|
+
|
607
|
+
Parameters
|
608
|
+
----------
|
609
|
+
filename : str or Path
|
610
|
+
Name of SimFCS BHZ file to read.
|
611
|
+
|
612
|
+
Returns
|
613
|
+
-------
|
614
|
+
xarray.DataArray
|
615
|
+
TCSPC histogram with :ref:`axes codes <axes>` ``'HYX'``,
|
616
|
+
shape ``(256, 256, 256)``, and type ``float32``.
|
617
|
+
|
618
|
+
Raises
|
619
|
+
------
|
620
|
+
lfdfiles.LfdFileError
|
621
|
+
File is not a SimFCS BHZ file.
|
622
|
+
|
623
|
+
Notes
|
624
|
+
-----
|
625
|
+
The implementation is based on the
|
626
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles/>`__ library.
|
627
|
+
|
628
|
+
Examples
|
629
|
+
--------
|
630
|
+
>>> signal = signal_from_bhz(fetch('simfcs.bhz'))
|
631
|
+
>>> signal.values
|
632
|
+
array(...)
|
633
|
+
>>> signal.dtype
|
634
|
+
dtype('float32')
|
635
|
+
>>> signal.shape
|
636
|
+
(256, 256, 256)
|
637
|
+
>>> signal.dims
|
638
|
+
('H', 'Y', 'X')
|
639
|
+
|
640
|
+
"""
|
641
|
+
import lfdfiles
|
642
|
+
|
643
|
+
with lfdfiles.SimfcsBhz(filename) as bhz:
|
644
|
+
assert bhz.axes is not None
|
645
|
+
data = bhz.asarray()
|
646
|
+
metadata = xarray_metadata(
|
647
|
+
bhz.axes.replace('Q', 'H'), data.shape, filename
|
648
|
+
)
|
649
|
+
|
650
|
+
from xarray import DataArray
|
651
|
+
|
652
|
+
return DataArray(data, **metadata)
|