phasorpy 0.3__cp312-cp312-win_amd64.whl → 0.4__cp312-cp312-win_amd64.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/_io.py +2431 -0
- phasorpy/_phasorpy.cp312-win_amd64.pyd +0 -0
- phasorpy/_phasorpy.pyx +69 -0
- phasorpy/_utils.py +77 -12
- phasorpy/color.py +1 -2
- phasorpy/datasets.py +81 -0
- phasorpy/io.py +4 -1810
- phasorpy/phasor.py +17 -5
- phasorpy/plot.py +25 -9
- phasorpy/version.py +2 -1
- {phasorpy-0.3.dist-info → phasorpy-0.4.dist-info}/LICENSE.txt +1 -1
- {phasorpy-0.3.dist-info → phasorpy-0.4.dist-info}/METADATA +4 -3
- phasorpy-0.4.dist-info/RECORD +25 -0
- {phasorpy-0.3.dist-info → phasorpy-0.4.dist-info}/WHEEL +1 -1
- phasorpy-0.3.dist-info/RECORD +0 -24
- {phasorpy-0.3.dist-info → phasorpy-0.4.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.3.dist-info → phasorpy-0.4.dist-info}/top_level.txt +0 -0
Binary file
|
phasorpy/_phasorpy.pyx
CHANGED
@@ -49,6 +49,12 @@ ctypedef fused float_t:
|
|
49
49
|
float
|
50
50
|
double
|
51
51
|
|
52
|
+
ctypedef fused uint_t:
|
53
|
+
uint8_t
|
54
|
+
uint16_t
|
55
|
+
uint32_t
|
56
|
+
uint64_t
|
57
|
+
|
52
58
|
ctypedef fused signal_t:
|
53
59
|
uint8_t
|
54
60
|
uint16_t
|
@@ -2159,3 +2165,66 @@ def _median_filter_2d(
|
|
2159
2165
|
image[i, j] = filtered_image[i, j]
|
2160
2166
|
|
2161
2167
|
free(kernel)
|
2168
|
+
|
2169
|
+
|
2170
|
+
###############################################################################
|
2171
|
+
# Decoder functions
|
2172
|
+
|
2173
|
+
|
2174
|
+
def _flimlabs_signal(
|
2175
|
+
uint_t[:, :, ::] signal, # channel, pixel, bin
|
2176
|
+
list data, # list[list[list[[int, int]]]]
|
2177
|
+
ssize_t channel = -1 # -1 == None
|
2178
|
+
):
|
2179
|
+
"""Return TCSPC histogram image from FLIM LABS JSON intensity data."""
|
2180
|
+
cdef:
|
2181
|
+
list channels, pixels
|
2182
|
+
ssize_t c, i, h, count
|
2183
|
+
|
2184
|
+
if channel < 0:
|
2185
|
+
c = 0
|
2186
|
+
for channels in data:
|
2187
|
+
i = 0
|
2188
|
+
for pixels in channels:
|
2189
|
+
for h, count in pixels:
|
2190
|
+
signal[c, i, h] = <uint_t> count
|
2191
|
+
i += 1
|
2192
|
+
c += 1
|
2193
|
+
else:
|
2194
|
+
i = 0
|
2195
|
+
for pixels in data[channel]:
|
2196
|
+
for h, count in pixels:
|
2197
|
+
signal[0, i, h] = <uint_t> count
|
2198
|
+
i += 1
|
2199
|
+
|
2200
|
+
|
2201
|
+
def _flimlabs_mean(
|
2202
|
+
float_t[:, ::] mean, # channel, pixel
|
2203
|
+
list data, # list[list[list[[int, int]]]]
|
2204
|
+
ssize_t channel = -1 # -1 == None
|
2205
|
+
):
|
2206
|
+
"""Return mean intensity image from FLIM LABS JSON intensity data."""
|
2207
|
+
cdef:
|
2208
|
+
list channels, pixels
|
2209
|
+
ssize_t c, i, h, count
|
2210
|
+
double sum
|
2211
|
+
|
2212
|
+
if channel < 0:
|
2213
|
+
c = 0
|
2214
|
+
for channels in data:
|
2215
|
+
i = 0
|
2216
|
+
for pixels in channels:
|
2217
|
+
sum = 0.0
|
2218
|
+
for h, count in pixels:
|
2219
|
+
sum += <double> count
|
2220
|
+
mean[c, i] = <float_t> (sum / 255.0)
|
2221
|
+
i += 1
|
2222
|
+
c += 1
|
2223
|
+
else:
|
2224
|
+
i = 0
|
2225
|
+
for pixels in data[channel]:
|
2226
|
+
sum = 0.0
|
2227
|
+
for h, count in pixels:
|
2228
|
+
sum += <double> count
|
2229
|
+
mean[0, i] = <float_t> (sum / 255.0)
|
2230
|
+
i += 1
|
phasorpy/_utils.py
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
"""Private auxiliary and convenience functions.
|
2
|
-
|
3
|
-
"""
|
1
|
+
"""Private auxiliary and convenience functions."""
|
4
2
|
|
5
3
|
from __future__ import annotations
|
6
4
|
|
@@ -10,6 +8,7 @@ __all__: list[str] = [
|
|
10
8
|
'kwargs_notnone',
|
11
9
|
'parse_harmonic',
|
12
10
|
'parse_kwargs',
|
11
|
+
'parse_signal_axis',
|
13
12
|
'phasor_from_polar_scalar',
|
14
13
|
'phasor_to_polar_scalar',
|
15
14
|
'scale_matrix',
|
@@ -247,6 +246,72 @@ def phasor_from_polar_scalar(
|
|
247
246
|
return real, imag
|
248
247
|
|
249
248
|
|
249
|
+
def parse_signal_axis(
|
250
|
+
signal: ArrayLike,
|
251
|
+
/,
|
252
|
+
axis: int | str | None = None,
|
253
|
+
) -> tuple[int, str]:
|
254
|
+
"""Return axis over which phasor coordinates are computed.
|
255
|
+
|
256
|
+
The axis parameter is not validated against the signal shape.
|
257
|
+
|
258
|
+
Parameters
|
259
|
+
----------
|
260
|
+
signal : array_like
|
261
|
+
Image stack.
|
262
|
+
axis : int or str, optional
|
263
|
+
Axis over which phasor coordinates are computed.
|
264
|
+
By default, the 'H' or 'C' axes if `signal` contains such
|
265
|
+
dimension names, else the last axis (-1).
|
266
|
+
|
267
|
+
Returns
|
268
|
+
-------
|
269
|
+
axis : int
|
270
|
+
Axis over which phasor coordinates are computed.
|
271
|
+
axis_label: str
|
272
|
+
Axis label from `signal.dims` if any.
|
273
|
+
|
274
|
+
Raises
|
275
|
+
------
|
276
|
+
ValueError
|
277
|
+
Axis not found in signal.dims or invalid for signal type.
|
278
|
+
|
279
|
+
Examples
|
280
|
+
--------
|
281
|
+
>>> parse_signal_axis([])
|
282
|
+
(-1, '')
|
283
|
+
>>> parse_signal_axis([], 1)
|
284
|
+
(1, '')
|
285
|
+
>>> class DataArray:
|
286
|
+
... dims = ('C', 'H', 'Y', 'X')
|
287
|
+
...
|
288
|
+
>>> parse_signal_axis(DataArray())
|
289
|
+
(1, 'H')
|
290
|
+
>>> parse_signal_axis(DataArray(), 'C')
|
291
|
+
(0, 'C')
|
292
|
+
>>> parse_signal_axis(DataArray(), 1)
|
293
|
+
(1, 'H')
|
294
|
+
|
295
|
+
"""
|
296
|
+
if hasattr(signal, 'dims'):
|
297
|
+
assert isinstance(signal.dims, tuple)
|
298
|
+
if axis is None:
|
299
|
+
for ax in 'HC':
|
300
|
+
if ax in signal.dims:
|
301
|
+
return signal.dims.index(ax), ax
|
302
|
+
return -1, signal.dims[-1]
|
303
|
+
if isinstance(axis, int):
|
304
|
+
return axis, signal.dims[axis]
|
305
|
+
if axis in signal.dims:
|
306
|
+
return signal.dims.index(axis), axis
|
307
|
+
raise ValueError(f'{axis=} not found in {signal.dims}')
|
308
|
+
if axis is None:
|
309
|
+
return -1, ''
|
310
|
+
if isinstance(axis, int):
|
311
|
+
return axis, ''
|
312
|
+
raise ValueError(f'{axis=} not valid for {type(signal)=}')
|
313
|
+
|
314
|
+
|
250
315
|
def parse_harmonic(
|
251
316
|
harmonic: int | Sequence[int] | Literal['all'] | str | None,
|
252
317
|
harmonic_max: int | None = None,
|
@@ -259,7 +324,7 @@ def parse_harmonic(
|
|
259
324
|
|
260
325
|
Parameters
|
261
326
|
----------
|
262
|
-
harmonic : int,
|
327
|
+
harmonic : int, sequence of int, 'all', or None
|
263
328
|
Harmonic parameter to parse.
|
264
329
|
harmonic_max : int, optional
|
265
330
|
Maximum value allowed in `hamonic`. Must be one or greater.
|
@@ -329,7 +394,7 @@ def chunk_iter(
|
|
329
394
|
shape: tuple[int, ...],
|
330
395
|
chunk_shape: tuple[int, ...],
|
331
396
|
/,
|
332
|
-
|
397
|
+
dims: Sequence[str] | None = None,
|
333
398
|
*,
|
334
399
|
pattern: str | None = None,
|
335
400
|
squeeze: bool = False,
|
@@ -343,11 +408,11 @@ def chunk_iter(
|
|
343
408
|
Shape of C-order ndarray to chunk.
|
344
409
|
chunk_shape : tuple of int
|
345
410
|
Shape of chunks in the most significant dimensions.
|
346
|
-
|
411
|
+
dims : sequence of str, optional
|
347
412
|
Labels for each axis in shape if `pattern` is None.
|
348
413
|
pattern : str, optional
|
349
414
|
String to format chunk indices.
|
350
|
-
If None, use ``_[{
|
415
|
+
If None, use ``_[{dims[index]}{chunk_index[index]}]`` for each axis.
|
351
416
|
squeeze : bool
|
352
417
|
If true, do not include length-1 chunked dimensions in label
|
353
418
|
unless dimensions are part of `chunk_shape`.
|
@@ -384,11 +449,11 @@ def chunk_iter(
|
|
384
449
|
ndim = len(shape)
|
385
450
|
|
386
451
|
sep = '_'
|
387
|
-
if
|
388
|
-
|
452
|
+
if dims is None:
|
453
|
+
dims = sep * ndim
|
389
454
|
sep = ''
|
390
|
-
elif ndim != len(
|
391
|
-
raise ValueError(f'{len(shape)=} != {len(
|
455
|
+
elif ndim != len(dims):
|
456
|
+
raise ValueError(f'{len(shape)=} != {len(dims)=}')
|
392
457
|
|
393
458
|
if pattern is not None:
|
394
459
|
try:
|
@@ -406,7 +471,7 @@ def chunk_iter(
|
|
406
471
|
|
407
472
|
chunked_shape = []
|
408
473
|
pattern_list = []
|
409
|
-
for i, (size, chunk_size, ax) in enumerate(zip(shape, chunk_shape,
|
474
|
+
for i, (size, chunk_size, ax) in enumerate(zip(shape, chunk_shape, dims)):
|
410
475
|
if size <= 0:
|
411
476
|
raise ValueError('shape must contain positive sizes')
|
412
477
|
if chunk_size <= 0:
|
phasorpy/color.py
CHANGED
phasorpy/datasets.py
CHANGED
@@ -305,6 +305,85 @@ CONVALLARIA_FBD = pooch.create(
|
|
305
305
|
},
|
306
306
|
)
|
307
307
|
|
308
|
+
FLIMLABS = pooch.create(
|
309
|
+
path=pooch.os_cache('phasorpy'),
|
310
|
+
base_url='https://github.com/phasorpy/phasorpy-data/raw/main/flimlabs',
|
311
|
+
env=ENV,
|
312
|
+
registry={
|
313
|
+
'calibrator_2_5_1737112045_imaging.json': (
|
314
|
+
'sha256:'
|
315
|
+
'a34c7077e88d1e7272953a46b2bb4e3ab8adf5a2f61c824dfc27032d952b920e'
|
316
|
+
),
|
317
|
+
'calibrator_2_5_1737112045_imaging.json.zip': (
|
318
|
+
'sha256:'
|
319
|
+
'fea791b28afd8365152018810cbbaaac1177cb72827578073587a1050d1af329'
|
320
|
+
),
|
321
|
+
'calibrator_2_5_1737112045_imaging_calibration.json': (
|
322
|
+
'sha256:'
|
323
|
+
'8f2ebe9b544fae9524dc13221c1a5ab1b57d9dfd40ec2eb06a7b1475fcd63057'
|
324
|
+
),
|
325
|
+
'calibrator_2_5_bis_1737112494_imaging.json': (
|
326
|
+
'sha256:'
|
327
|
+
'0509c5aba066419b03f83264eba58acbf4aae470aa1057c52f45e60225e033a4'
|
328
|
+
),
|
329
|
+
'calibrator_2_5_bis_1737112494_imaging.json.zip': (
|
330
|
+
'sha256:'
|
331
|
+
'bdc5df2a3f08a64ec7b7bb57b36e21546de142e67c59c052318252dbb66d8abf'
|
332
|
+
),
|
333
|
+
'calibrator_2_5_bis_1737112494_imaging_calibration.json': (
|
334
|
+
'sha256:'
|
335
|
+
'9bb0e21b1e7c04add672aa8a78048b09908c860fcaf907ca33c0c87d161f6ebf'
|
336
|
+
),
|
337
|
+
'convallaria_1_1737112980_phasor_ch1.json': (
|
338
|
+
'sha256:'
|
339
|
+
'4a296a0d7898dc660a388e1bba5cf98b43c35fe12d94b7aba48d00245e37242d'
|
340
|
+
),
|
341
|
+
'convallaria_1_1737112980_phasor_ch1.json.zip': (
|
342
|
+
'sha256:'
|
343
|
+
'79c416b9099c9f58d2092fe5b26ea6d0f695977b877784cf564d3ead896d9354'
|
344
|
+
),
|
345
|
+
'convallaria_2_1737113097_phasor_ch1.json': (
|
346
|
+
'sha256:'
|
347
|
+
'da549645ffd898238c26f7a1eac3aca4ffccec86653c0d241a6ece674dfce90d'
|
348
|
+
),
|
349
|
+
'convallaria_2_1737113097_phasor_ch1.json.zip': (
|
350
|
+
'sha256:'
|
351
|
+
'8801bb14b457dceaef42e8b3bf6af770a2e14264cd2b282ba7e3d70b91ea954c'
|
352
|
+
),
|
353
|
+
'data_2_calibrator_2_5_1737112409_phasor_ch1.json': (
|
354
|
+
'sha256:'
|
355
|
+
'ea8683892eb76f52231e5d6ceab64a3737454aa95fe73185366de8f758fd9b70'
|
356
|
+
),
|
357
|
+
'data_2_calibrator_2_5_1737112409_phasor_ch1.json.zip': (
|
358
|
+
'sha256:'
|
359
|
+
'40d2aa90b95fd8864a2392337c83a1a4f4931d7359cb30a486f65173f208de0a'
|
360
|
+
),
|
361
|
+
'data_calibrator_2_5_1737112133_phasor_ch1.json': (
|
362
|
+
'sha256:'
|
363
|
+
'6a8790212bc62014d597402ec5feb0e50ec6ae2aa62d63fae8cb62c6c5656268'
|
364
|
+
),
|
365
|
+
'data_calibrator_2_5_1737112133_phasor_ch1.json.zip': (
|
366
|
+
'sha256:'
|
367
|
+
'c9ef343bdbd7a51d23fdf4082e379dcdb1ce9f3e2ba065289bf2925d68ef55ba'
|
368
|
+
),
|
369
|
+
},
|
370
|
+
)
|
371
|
+
|
372
|
+
FIGSHARE_22336594 = pooch.create(
|
373
|
+
path=pooch.os_cache('phasorpy'),
|
374
|
+
base_url=(
|
375
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/figshare_22336594'
|
376
|
+
if DATA_ON_GITHUB
|
377
|
+
else 'doi:10.6084/m9.figshare.22336594.v1'
|
378
|
+
),
|
379
|
+
env=ENV,
|
380
|
+
registry={
|
381
|
+
'FLIM_testdata.lif': (
|
382
|
+
'sha256:'
|
383
|
+
'902d8fa6cd39da7cf062b32d43aab518fa2a851eab72b4bd8b8eca1bad591850'
|
384
|
+
),
|
385
|
+
},
|
386
|
+
)
|
308
387
|
|
309
388
|
REPOSITORIES: dict[str, pooch.Pooch] = {
|
310
389
|
'tests': TESTS,
|
@@ -313,6 +392,8 @@ REPOSITORIES: dict[str, pooch.Pooch] = {
|
|
313
392
|
'napari-flim-phasor-plotter': NAPARI_FLIM_PHASOR_PLOTTER,
|
314
393
|
'zenodo-13625087': ZENODO_13625087,
|
315
394
|
'convallaria-fbd': CONVALLARIA_FBD,
|
395
|
+
'flimlabs': FLIMLABS,
|
396
|
+
'figshare_22336594': FIGSHARE_22336594,
|
316
397
|
}
|
317
398
|
"""Pooch repositories."""
|
318
399
|
|