phasorpy 0.4__cp311-cp311-win_arm64.whl → 0.6__cp311-cp311-win_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.
- phasorpy/__init__.py +2 -3
- phasorpy/_phasorpy.cp311-win_arm64.pyd +0 -0
- phasorpy/_phasorpy.pyx +237 -51
- phasorpy/_utils.py +201 -7
- phasorpy/cli.py +58 -3
- phasorpy/cluster.py +206 -0
- phasorpy/color.py +16 -11
- phasorpy/components.py +240 -69
- phasorpy/conftest.py +2 -0
- phasorpy/cursors.py +9 -9
- phasorpy/datasets.py +129 -51
- phasorpy/experimental.py +312 -0
- phasorpy/io/__init__.py +137 -0
- phasorpy/io/_flimlabs.py +350 -0
- phasorpy/io/_leica.py +329 -0
- phasorpy/io/_ometiff.py +445 -0
- phasorpy/io/_other.py +782 -0
- phasorpy/io/_simfcs.py +627 -0
- phasorpy/phasor.py +572 -97
- phasorpy/plot/__init__.py +27 -0
- phasorpy/plot/_functions.py +717 -0
- phasorpy/plot/_lifetime_plots.py +553 -0
- phasorpy/plot/_phasorplot.py +1119 -0
- phasorpy/plot/_phasorplot_fret.py +559 -0
- phasorpy/utils.py +90 -297
- {phasorpy-0.4.dist-info → phasorpy-0.6.dist-info}/METADATA +11 -16
- phasorpy-0.6.dist-info/RECORD +34 -0
- {phasorpy-0.4.dist-info → phasorpy-0.6.dist-info}/WHEEL +1 -1
- phasorpy/_io.py +0 -2431
- phasorpy/io.py +0 -5
- phasorpy/plot.py +0 -2094
- phasorpy/version.py +0 -72
- phasorpy-0.4.dist-info/RECORD +0 -25
- {phasorpy-0.4.dist-info → phasorpy-0.6.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.4.dist-info → phasorpy-0.6.dist-info/licenses}/LICENSE.txt +0 -0
- {phasorpy-0.4.dist-info → phasorpy-0.6.dist-info}/top_level.txt +0 -0
phasorpy/io/__init__.py
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
"""Read and write time-resolved and hyperspectral image file formats.
|
2
|
+
|
3
|
+
The ``phasorpy.io`` module provides functions to:
|
4
|
+
|
5
|
+
- read time-resolved and hyperspectral signals, as well as metadata from
|
6
|
+
many file formats used in bio-imaging:
|
7
|
+
|
8
|
+
- :py:func:`signal_from_lif` - Leica LIF and XLEF
|
9
|
+
- :py:func:`signal_from_lsm` - Zeiss LSM
|
10
|
+
- :py:func:`signal_from_ptu` - PicoQuant PTU
|
11
|
+
- :py:func:`signal_from_sdt` - Becker & Hickl SDT
|
12
|
+
- :py:func:`signal_from_fbd` - FLIMbox FBD
|
13
|
+
- :py:func:`signal_from_flimlabs_json` - FLIM LABS JSON
|
14
|
+
- :py:func:`signal_from_imspector_tiff` - ImSpector FLIM TIFF
|
15
|
+
- :py:func:`signal_from_flif` - FlimFast FLIF
|
16
|
+
- :py:func:`signal_from_b64` - SimFCS B64
|
17
|
+
- :py:func:`signal_from_z64` - SimFCS Z64
|
18
|
+
- :py:func:`signal_from_bhz` - SimFCS BHZ
|
19
|
+
- :py:func:`signal_from_bh` - SimFCS B&H
|
20
|
+
|
21
|
+
- read phasor coordinates, lifetime images, and metadata from
|
22
|
+
specialized file formats:
|
23
|
+
|
24
|
+
- :py:func:`phasor_from_ometiff` - PhasorPy OME-TIFF
|
25
|
+
- :py:func:`phasor_from_ifli` - ISS IFLI
|
26
|
+
- :py:func:`phasor_from_lif` - Leica LIF and XLEF
|
27
|
+
- :py:func:`phasor_from_flimlabs_json` - FLIM LABS JSON
|
28
|
+
- :py:func:`phasor_from_simfcs_referenced` - SimFCS REF and R64
|
29
|
+
- :py:func:`lifetime_from_lif` - Leica LIF and XLEF
|
30
|
+
|
31
|
+
- write phasor coordinate images to OME-TIFF and SimFCS file formats:
|
32
|
+
|
33
|
+
- :py:func:`phasor_to_ometiff`
|
34
|
+
- :py:func:`phasor_to_simfcs_referenced`
|
35
|
+
|
36
|
+
Support for other file formats is being considered:
|
37
|
+
|
38
|
+
- OME-TIFF
|
39
|
+
- Zeiss CZI
|
40
|
+
- Nikon ND2
|
41
|
+
- Olympus OIB/OIF
|
42
|
+
- Olympus OIR
|
43
|
+
|
44
|
+
The functions are implemented as minimal wrappers around specialized
|
45
|
+
third-party file reader libraries, currently
|
46
|
+
`tifffile <https://github.com/cgohlke/tifffile>`_,
|
47
|
+
`ptufile <https://github.com/cgohlke/ptufile>`_,
|
48
|
+
`liffile <https://github.com/cgohlke/liffile>`_,
|
49
|
+
`sdtfile <https://github.com/cgohlke/sdtfile>`_, and
|
50
|
+
`lfdfiles <https://github.com/cgohlke/lfdfiles>`_.
|
51
|
+
For advanced or unsupported use cases, consider using these libraries directly.
|
52
|
+
|
53
|
+
The signal-reading functions typically have the following signature::
|
54
|
+
|
55
|
+
signal_from_ext(
|
56
|
+
filename: str | PathLike,
|
57
|
+
/,
|
58
|
+
**kwargs
|
59
|
+
): -> xarray.DataArray
|
60
|
+
|
61
|
+
where ``ext`` indicates the file format and ``kwargs`` are optional arguments
|
62
|
+
passed to the underlying file reader library or used to select which data is
|
63
|
+
returned. The returned `xarray.DataArray
|
64
|
+
<https://docs.xarray.dev/en/stable/user-guide/data-structures.html>`_
|
65
|
+
contains an N-dimensional array with labeled coordinates, dimensions, and
|
66
|
+
attributes:
|
67
|
+
|
68
|
+
- ``data`` or ``values`` (*array_like*)
|
69
|
+
|
70
|
+
Numpy array or array-like holding the array's values.
|
71
|
+
|
72
|
+
- ``dims`` (*tuple of str*)
|
73
|
+
|
74
|
+
:ref:`Axes character codes <axes>` for each dimension in ``data``.
|
75
|
+
For example, ``('T', 'C', 'Y', 'X')`` defines the dimension order in a
|
76
|
+
4-dimensional array of a time-series of multi-channel images.
|
77
|
+
|
78
|
+
- ``coords`` (*dict_like[str, array_like]*)
|
79
|
+
|
80
|
+
Coordinate arrays labelling each point in the data array.
|
81
|
+
The keys are :ref:`axes character codes <axes>`.
|
82
|
+
Values are 1-dimensional arrays of numbers or strings.
|
83
|
+
For example, ``coords['C']`` could be an array of emission wavelengths.
|
84
|
+
|
85
|
+
- ``attrs`` (*dict[str, Any]*)
|
86
|
+
|
87
|
+
Arbitrary metadata such as measurement or calibration parameters required to
|
88
|
+
interpret the data values.
|
89
|
+
For example, the laser repetition frequency of a time-resolved measurement.
|
90
|
+
|
91
|
+
.. _axes:
|
92
|
+
|
93
|
+
Axes character codes from the OME model and tifffile library are used as
|
94
|
+
``dims`` items and ``coords`` keys:
|
95
|
+
|
96
|
+
- ``'X'`` : width (OME)
|
97
|
+
- ``'Y'`` : height (OME)
|
98
|
+
- ``'Z'`` : depth (OME)
|
99
|
+
- ``'S'`` : sample (color components or phasor coordinates)
|
100
|
+
- ``'I'`` : sequence (of images, frames, or planes)
|
101
|
+
- ``'T'`` : time (OME)
|
102
|
+
- ``'C'`` : channel (OME. Acquisition path or emission wavelength)
|
103
|
+
- ``'A'`` : angle (OME)
|
104
|
+
- ``'P'`` : phase (OME. In LSM, ``'P'`` maps to position)
|
105
|
+
- ``'R'`` : tile (OME. Region, position, or mosaic)
|
106
|
+
- ``'H'`` : lifetime histogram (OME)
|
107
|
+
- ``'E'`` : lambda (OME. Excitation wavelength)
|
108
|
+
- ``'F'`` : frequency (ISS)
|
109
|
+
- ``'Q'`` : other (OME. Harmonics in PhasorPy TIFF)
|
110
|
+
- ``'L'`` : exposure (FluoView)
|
111
|
+
- ``'V'`` : event (FluoView)
|
112
|
+
- ``'M'`` : mosaic (LSM 6)
|
113
|
+
- ``'J'`` : column (NDTiff)
|
114
|
+
- ``'K'`` : row (NDTiff)
|
115
|
+
|
116
|
+
"""
|
117
|
+
|
118
|
+
from __future__ import annotations
|
119
|
+
|
120
|
+
__all__: list[str] = []
|
121
|
+
|
122
|
+
from .._utils import init_module
|
123
|
+
from ._flimlabs import *
|
124
|
+
from ._leica import *
|
125
|
+
from ._ometiff import *
|
126
|
+
from ._other import *
|
127
|
+
from ._simfcs import *
|
128
|
+
|
129
|
+
# The `init_module()` function dynamically populates the `__all__` list with
|
130
|
+
# all public symbols imported from submodules or defined in this module.
|
131
|
+
# Any name not starting with an underscore will be automatically exported
|
132
|
+
# when using "from phasorpy.io import *"
|
133
|
+
|
134
|
+
init_module(globals())
|
135
|
+
del init_module
|
136
|
+
|
137
|
+
# flake8: noqa: F401, F403
|
phasorpy/io/_flimlabs.py
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
"""Read FLIM LABS file formats."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
__all__ = ['phasor_from_flimlabs_json', 'signal_from_flimlabs_json']
|
6
|
+
|
7
|
+
import json
|
8
|
+
from typing import TYPE_CHECKING
|
9
|
+
|
10
|
+
from .._utils import parse_harmonic, xarray_metadata
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from .._typing import (
|
14
|
+
Any,
|
15
|
+
DataArray,
|
16
|
+
DTypeLike,
|
17
|
+
Literal,
|
18
|
+
NDArray,
|
19
|
+
PathLike,
|
20
|
+
Sequence,
|
21
|
+
)
|
22
|
+
|
23
|
+
import numpy
|
24
|
+
|
25
|
+
|
26
|
+
def phasor_from_flimlabs_json(
|
27
|
+
filename: str | PathLike[Any],
|
28
|
+
/,
|
29
|
+
channel: int | None = 0,
|
30
|
+
harmonic: int | Sequence[int] | Literal['all'] | str | None = None,
|
31
|
+
) -> tuple[NDArray[Any], NDArray[Any], NDArray[Any], dict[str, Any]]:
|
32
|
+
"""Return phasor coordinates and metadata from FLIM LABS JSON phasor file.
|
33
|
+
|
34
|
+
FLIM LABS JSON files may contain calibrated phasor coordinates
|
35
|
+
(possibly for multiple channels and harmonics) and metadata from
|
36
|
+
digital frequency-domain measurements.
|
37
|
+
|
38
|
+
Parameters
|
39
|
+
----------
|
40
|
+
filename : str or Path
|
41
|
+
Name of FLIM LABS JSON phasor file to read.
|
42
|
+
The file name usually contains the string "_phasor".
|
43
|
+
channel : int, optional
|
44
|
+
Index of channel to return.
|
45
|
+
By default, return the first channel.
|
46
|
+
If None, return all channels.
|
47
|
+
harmonic : int, sequence of int, or 'all', optional
|
48
|
+
Harmonic(s) to return from file.
|
49
|
+
If None (default), return the first harmonic stored in the file.
|
50
|
+
If `'all'`, return all harmonics as stored in file.
|
51
|
+
If a list, the first axes of the returned `real` and `imag` arrays
|
52
|
+
contain specified harmonic(s).
|
53
|
+
If an integer, the returned `real` and `imag` arrays are single
|
54
|
+
harmonic and have the same shape as `mean`.
|
55
|
+
|
56
|
+
Returns
|
57
|
+
-------
|
58
|
+
mean : ndarray
|
59
|
+
Average intensity image.
|
60
|
+
Zeroed if an intensity image is not present in file.
|
61
|
+
real : ndarray
|
62
|
+
Image of real component of phasor coordinates.
|
63
|
+
imag : ndarray
|
64
|
+
Image of imaginary component of phasor coordinates.
|
65
|
+
attrs : dict
|
66
|
+
Select metadata:
|
67
|
+
|
68
|
+
- ``'dims'`` (tuple of str):
|
69
|
+
:ref:`Axes codes <axes>` for `mean` image dimensions.
|
70
|
+
- ``'harmonic'`` (int):
|
71
|
+
Harmonic of `real` and `imag`.
|
72
|
+
- ``'frequency'`` (float):
|
73
|
+
Fundamental frequency of time-resolved phasor coordinates in MHz.
|
74
|
+
- ``'flimlabs_header'`` (dict):
|
75
|
+
FLIM LABS file header.
|
76
|
+
|
77
|
+
Raises
|
78
|
+
------
|
79
|
+
ValueError
|
80
|
+
File is not a FLIM LABS JSON file containing phasor coordinates.
|
81
|
+
IndexError
|
82
|
+
Harmonic or channel not found in file.
|
83
|
+
|
84
|
+
See Also
|
85
|
+
--------
|
86
|
+
phasorpy.io.signal_from_flimlabs_json
|
87
|
+
|
88
|
+
Examples
|
89
|
+
--------
|
90
|
+
>>> mean, real, imag, attrs = phasor_from_flimlabs_json(
|
91
|
+
... fetch('Convallaria_m2_1740751781_phasor_ch1.json'), harmonic='all'
|
92
|
+
... )
|
93
|
+
>>> real.shape
|
94
|
+
(3, 256, 256)
|
95
|
+
>>> attrs['dims']
|
96
|
+
('Y', 'X')
|
97
|
+
>>> attrs['harmonic']
|
98
|
+
[1, 2, 3]
|
99
|
+
>>> attrs['frequency'] # doctest: +NUMBER
|
100
|
+
40.00
|
101
|
+
|
102
|
+
"""
|
103
|
+
with open(filename, 'rb') as fh:
|
104
|
+
try:
|
105
|
+
data = json.load(fh)
|
106
|
+
except Exception as exc:
|
107
|
+
raise ValueError('not a valid JSON file') from exc
|
108
|
+
|
109
|
+
if (
|
110
|
+
'header' not in data
|
111
|
+
or 'phasors_data' not in data
|
112
|
+
or 'laser_period_ns' not in data['header']
|
113
|
+
or 'file_id' not in data['header']
|
114
|
+
# or data['header']['file_id'] != [73, 80, 71, 49] # 'IPG1'
|
115
|
+
):
|
116
|
+
raise ValueError(
|
117
|
+
'not a FLIM LABS JSON file containing phasor coordinates'
|
118
|
+
)
|
119
|
+
|
120
|
+
header = data['header']
|
121
|
+
phasor_data = data['phasors_data']
|
122
|
+
|
123
|
+
harmonics = []
|
124
|
+
channels = [] # 1-based
|
125
|
+
for d in phasor_data:
|
126
|
+
h = d['harmonic']
|
127
|
+
if h not in harmonics:
|
128
|
+
harmonics.append(h)
|
129
|
+
c = d['channel']
|
130
|
+
if c not in channels:
|
131
|
+
channels.append(c)
|
132
|
+
harmonics = sorted(harmonics)
|
133
|
+
channels = sorted(channels)
|
134
|
+
|
135
|
+
if channel is not None:
|
136
|
+
if channel + 1 not in channels:
|
137
|
+
raise IndexError(f'{channel=}')
|
138
|
+
channel += 1 # 1-based index
|
139
|
+
|
140
|
+
if isinstance(harmonic, str) and harmonic == 'all':
|
141
|
+
harmonic = harmonics
|
142
|
+
keep_harmonic_axis = True
|
143
|
+
else:
|
144
|
+
harmonic, keep_harmonic_axis = parse_harmonic(harmonic, harmonics[-1])
|
145
|
+
if any(h not in harmonics for h in harmonic):
|
146
|
+
raise IndexError(f'{harmonic=} not in {harmonics!r}')
|
147
|
+
harmonic_index = {h: i for i, h in enumerate(harmonic)}
|
148
|
+
|
149
|
+
nharmonics = len(harmonic)
|
150
|
+
nchannels = len(channels) if channel is None else 1
|
151
|
+
height = header['image_height']
|
152
|
+
width = header['image_width']
|
153
|
+
dtype = numpy.float32
|
154
|
+
|
155
|
+
shape: tuple[int, ...] = nharmonics, nchannels, height, width
|
156
|
+
axes: str = 'CYX'
|
157
|
+
mean = numpy.zeros(shape[1:], dtype)
|
158
|
+
real = numpy.zeros(shape, dtype)
|
159
|
+
imag = numpy.zeros(shape, dtype)
|
160
|
+
|
161
|
+
for d in phasor_data:
|
162
|
+
h = d['harmonic']
|
163
|
+
if h not in harmonic_index:
|
164
|
+
continue
|
165
|
+
h = harmonic_index[h]
|
166
|
+
if channel is not None:
|
167
|
+
if d['channel'] != channel:
|
168
|
+
continue
|
169
|
+
c = 0
|
170
|
+
else:
|
171
|
+
c = channels.index(d['channel'])
|
172
|
+
|
173
|
+
real[h, c] = numpy.asarray(d['g_data'], dtype)
|
174
|
+
imag[h, c] = numpy.asarray(d['s_data'], dtype)
|
175
|
+
|
176
|
+
if 'intensities_data' in data:
|
177
|
+
from .._phasorpy import _flimlabs_mean
|
178
|
+
|
179
|
+
mean.shape = nchannels, height * width
|
180
|
+
_flimlabs_mean(
|
181
|
+
mean,
|
182
|
+
data['intensities_data'],
|
183
|
+
-1 if channel is None else channels.index(channel),
|
184
|
+
)
|
185
|
+
mean.shape = shape[1:]
|
186
|
+
# JSON cannot store NaN values
|
187
|
+
nan_mask = mean == 0
|
188
|
+
real[:, nan_mask] = numpy.nan
|
189
|
+
imag[:, nan_mask] = numpy.nan
|
190
|
+
del nan_mask
|
191
|
+
|
192
|
+
if nchannels == 1:
|
193
|
+
axes = axes[1:]
|
194
|
+
mean = mean[0]
|
195
|
+
real = real[:, 0]
|
196
|
+
imag = imag[:, 0]
|
197
|
+
|
198
|
+
if not keep_harmonic_axis:
|
199
|
+
real = real[0]
|
200
|
+
imag = imag[0]
|
201
|
+
|
202
|
+
attrs = {
|
203
|
+
'dims': tuple(axes),
|
204
|
+
'samples': 256,
|
205
|
+
'harmonic': harmonic if keep_harmonic_axis else harmonic[0],
|
206
|
+
'frequency': 1000.0 / header['laser_period_ns'],
|
207
|
+
'flimlabs_header': header,
|
208
|
+
}
|
209
|
+
|
210
|
+
return mean, real, imag, attrs
|
211
|
+
|
212
|
+
|
213
|
+
def signal_from_flimlabs_json(
|
214
|
+
filename: str | PathLike[Any],
|
215
|
+
/,
|
216
|
+
*,
|
217
|
+
channel: int | None = 0,
|
218
|
+
dtype: DTypeLike | None = None,
|
219
|
+
) -> DataArray:
|
220
|
+
"""Return TCSPC histogram and metadata from FLIM LABS JSON imaging file.
|
221
|
+
|
222
|
+
FLIM LABS JSON imaging files contain encoded, multi-channel TCSPC
|
223
|
+
histograms and metadata from digital frequency-domain measurements.
|
224
|
+
|
225
|
+
Parameters
|
226
|
+
----------
|
227
|
+
filename : str or Path
|
228
|
+
Name of FLIM LABS JSON imaging file to read.
|
229
|
+
The file name usually contains the string "_imaging" or "_phasor".
|
230
|
+
channel : int, optional
|
231
|
+
Index of channel to return.
|
232
|
+
By default, return the first channel.
|
233
|
+
If None, return all channels.
|
234
|
+
dtype : dtype-like, optional, default: uint16
|
235
|
+
Unsigned integer type of TCSPC histogram.
|
236
|
+
Increase the bit-depth for high photon counts.
|
237
|
+
|
238
|
+
Returns
|
239
|
+
-------
|
240
|
+
xarray.DataArray
|
241
|
+
TCSPC histogram with :ref:`axes codes <axes>` ``'CYXH'`` and
|
242
|
+
type specified in ``dtype``:
|
243
|
+
|
244
|
+
- ``coords['H']``: delay-times of histogram bins in ns.
|
245
|
+
- ``attrs['frequency']``: laser repetition frequency in MHz.
|
246
|
+
- ``attrs['flimlabs_header']``: FLIM LABS file header.
|
247
|
+
|
248
|
+
Raises
|
249
|
+
------
|
250
|
+
ValueError
|
251
|
+
File is not a FLIM LABS JSON file containing TCSPC histogram.
|
252
|
+
`dtype` is not an unsigned integer.
|
253
|
+
IndexError
|
254
|
+
Channel out of range.
|
255
|
+
|
256
|
+
See Also
|
257
|
+
--------
|
258
|
+
phasorpy.io.phasor_from_flimlabs_json
|
259
|
+
|
260
|
+
Examples
|
261
|
+
--------
|
262
|
+
>>> signal = signal_from_flimlabs_json(
|
263
|
+
... fetch('Convallaria_m2_1740751781_phasor_ch1.json')
|
264
|
+
... )
|
265
|
+
>>> signal.values
|
266
|
+
array(...)
|
267
|
+
>>> signal.shape
|
268
|
+
(256, 256, 256)
|
269
|
+
>>> signal.dims
|
270
|
+
('Y', 'X', 'H')
|
271
|
+
>>> signal.coords['H'].data
|
272
|
+
array(...)
|
273
|
+
>>> signal.attrs['frequency'] # doctest: +NUMBER
|
274
|
+
40.00
|
275
|
+
|
276
|
+
"""
|
277
|
+
with open(filename, 'rb') as fh:
|
278
|
+
try:
|
279
|
+
data = json.load(fh)
|
280
|
+
except Exception as exc:
|
281
|
+
raise ValueError('not a valid JSON file') from exc
|
282
|
+
|
283
|
+
if (
|
284
|
+
'header' not in data
|
285
|
+
or 'laser_period_ns' not in data['header']
|
286
|
+
or 'file_id' not in data['header']
|
287
|
+
or ('data' not in data and 'intensities_data' not in data)
|
288
|
+
):
|
289
|
+
raise ValueError(
|
290
|
+
'not a FLIM LABS JSON file containing TCSPC histogram'
|
291
|
+
)
|
292
|
+
|
293
|
+
if dtype is None:
|
294
|
+
dtype = numpy.uint16
|
295
|
+
else:
|
296
|
+
dtype = numpy.dtype(dtype)
|
297
|
+
if dtype.kind != 'u':
|
298
|
+
raise ValueError(f'{dtype=} is not an unsigned integer type')
|
299
|
+
|
300
|
+
header = data['header']
|
301
|
+
nchannels = len([c for c in header['channels'] if c])
|
302
|
+
height = header['image_height']
|
303
|
+
width = header['image_width']
|
304
|
+
frequency = 1000.0 / header['laser_period_ns']
|
305
|
+
|
306
|
+
if channel is not None:
|
307
|
+
if channel >= nchannels or channel < 0:
|
308
|
+
raise IndexError(f'{channel=} out of range[0, {nchannels=}]')
|
309
|
+
nchannels = 1
|
310
|
+
|
311
|
+
if 'data' in data:
|
312
|
+
# file_id = [73, 77, 71, 49] # 'IMG1'
|
313
|
+
intensities_data = data['data']
|
314
|
+
else:
|
315
|
+
# file_id = [73, 80, 71, 49] # 'IPG1'
|
316
|
+
intensities_data = data['intensities_data']
|
317
|
+
|
318
|
+
from .._phasorpy import _flimlabs_signal
|
319
|
+
|
320
|
+
signal = numpy.zeros((nchannels, height * width, 256), dtype)
|
321
|
+
_flimlabs_signal(
|
322
|
+
signal,
|
323
|
+
intensities_data,
|
324
|
+
-1 if channel is None else channel,
|
325
|
+
)
|
326
|
+
|
327
|
+
if channel is None and nchannels > 1:
|
328
|
+
signal.shape = (nchannels, height, width, 256)
|
329
|
+
axes = 'CYXH'
|
330
|
+
else:
|
331
|
+
signal.shape = (height, width, 256)
|
332
|
+
axes = 'YXH'
|
333
|
+
|
334
|
+
coords: dict[str, Any] = {}
|
335
|
+
coords['H'] = numpy.linspace(
|
336
|
+
0.0, header['laser_period_ns'], 256, endpoint=False
|
337
|
+
)
|
338
|
+
if channel is None and nchannels > 1:
|
339
|
+
coords['C'] = numpy.asarray(
|
340
|
+
[i for i, c in enumerate(header['channels']) if c]
|
341
|
+
)
|
342
|
+
|
343
|
+
metadata = xarray_metadata(axes, signal.shape, filename, **coords)
|
344
|
+
attrs = metadata['attrs']
|
345
|
+
attrs['frequency'] = frequency
|
346
|
+
attrs['flimlabs_header'] = header
|
347
|
+
|
348
|
+
from xarray import DataArray
|
349
|
+
|
350
|
+
return DataArray(signal, **metadata)
|