phasorpy 0.4__cp313-cp313-win_amd64.whl → 0.6__cp313-cp313-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/__init__.py +2 -3
- phasorpy/_phasorpy.cp313-win_amd64.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/datasets.py
CHANGED
@@ -14,8 +14,11 @@ Datasets from the following repositories are available:
|
|
14
14
|
<https://github.com/zoccoler/napari-flim-phasor-plotter/tree/0.0.6/src/napari_flim_phasor_plotter/data>`_
|
15
15
|
- `Phasor-based multi-harmonic unmixing for in-vivo hyperspectral imaging
|
16
16
|
<https://zenodo.org/records/13625087>`_
|
17
|
+
(`second record <https://zenodo.org/records/14860228>`_)
|
17
18
|
- `Convallaria slice acquired with time-resolved 2-photon microscope
|
18
19
|
<https://zenodo.org/records/14026720>`_
|
20
|
+
- `Convallaria FLIM dataset in FLIM LABS JSON format
|
21
|
+
<https://zenodo.org/records/15007900>`_
|
19
22
|
|
20
23
|
The implementation is based on the `Pooch <https://www.fatiando.org/pooch>`_
|
21
24
|
library.
|
@@ -266,14 +269,15 @@ ZENODO_13625087 = pooch.create(
|
|
266
269
|
base_url=(
|
267
270
|
'https://github.com/phasorpy/phasorpy-data/raw/main/zenodo_13625087'
|
268
271
|
# if DATA_ON_GITHUB
|
269
|
-
# else 'doi:10.1088/2050-6120/ac9ae9'
|
272
|
+
# else 'doi:10.1088/2050-6120/ac9ae9' # TODO: not working with Pooch
|
270
273
|
),
|
271
274
|
env=ENV,
|
272
275
|
registry={
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
276
|
+
# part of ZENODO_14860228
|
277
|
+
# '33_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
278
|
+
# 'sha256:'
|
279
|
+
# '68fcefcad4e750e9ec7068820e455258c986f6a9b724e66744a28bbbb689f986'
|
280
|
+
# ),
|
277
281
|
'34_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
278
282
|
'sha256:'
|
279
283
|
'5c0b7d76c274fd64891fca2507013b7c8c9979d8131ce282fac55fd24fbb38bd'
|
@@ -289,9 +293,65 @@ ZENODO_13625087 = pooch.create(
|
|
289
293
|
},
|
290
294
|
)
|
291
295
|
|
296
|
+
ZENODO_14860228 = pooch.create(
|
297
|
+
path=pooch.os_cache('phasorpy'),
|
298
|
+
base_url=(
|
299
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/zenodo_14860228'
|
300
|
+
if DATA_ON_GITHUB
|
301
|
+
else 'doi:10.5281/zenodo.14860228'
|
302
|
+
),
|
303
|
+
env=ENV,
|
304
|
+
registry={
|
305
|
+
'38_Hoechst_Golgi_Mito_Lyso_CellMAsk_404_488_561_633_SP.lsm': (
|
306
|
+
'sha256:'
|
307
|
+
'092ac050edf55e26dcda8cba10122408c6f1b81d19accf07214385d6eebfcf3e'
|
308
|
+
),
|
309
|
+
'spectral cell mask.lsm': (
|
310
|
+
'sha256:'
|
311
|
+
'c4c2c567bd99ef4930d7278794d4e3daebaad0375c0852a5ab86a2ea056f4fe3'
|
312
|
+
),
|
313
|
+
'spectral golgi.lsm': (
|
314
|
+
'sha256:'
|
315
|
+
'd0a5079d9ed18b1248434f3f6d4b2b240fb034891121262cfe9dfec64d8429cd'
|
316
|
+
),
|
317
|
+
'spectral hoehst.lsm': (
|
318
|
+
'sha256:'
|
319
|
+
'3ee44a7f9f125698bb5e34746d9723669f67c520ffbf21244757d7fc25dbbb88'
|
320
|
+
),
|
321
|
+
'spectral lyso tracker green.lsm': (
|
322
|
+
'sha256:'
|
323
|
+
'0964448649e2c73a57f5ca0c705c86511fb4625c0a2af0d7850dfa39698fcbb9'
|
324
|
+
),
|
325
|
+
'spectral mito tracker.lsm': (
|
326
|
+
'sha256:'
|
327
|
+
'99b9892b247256ebf8a9917c662bc7bb66a8daf3b5db950fbbb191de0cd35b37'
|
328
|
+
),
|
329
|
+
},
|
330
|
+
)
|
331
|
+
|
332
|
+
ZENODO_14976703 = pooch.create(
|
333
|
+
path=pooch.os_cache('phasorpy'),
|
334
|
+
base_url=(
|
335
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/zenodo_14976703'
|
336
|
+
if DATA_ON_GITHUB
|
337
|
+
else 'doi:10.5281/zenodo.14976703'
|
338
|
+
),
|
339
|
+
env=ENV,
|
340
|
+
registry={
|
341
|
+
'Convalaria_LambdaScan.lif': (
|
342
|
+
'sha256:'
|
343
|
+
'27f1282cf02f87e11f8c7d3064066a4517ad4c9c769c796b32e459774f18c62a'
|
344
|
+
),
|
345
|
+
},
|
346
|
+
)
|
347
|
+
|
292
348
|
CONVALLARIA_FBD = pooch.create(
|
293
349
|
path=pooch.os_cache('phasorpy'),
|
294
|
-
base_url=
|
350
|
+
base_url=(
|
351
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/zenodo_14026720'
|
352
|
+
if DATA_ON_GITHUB
|
353
|
+
else 'doi:10.5281/zenodo.14026719'
|
354
|
+
),
|
295
355
|
env=ENV,
|
296
356
|
registry={
|
297
357
|
'Convallaria_$EI0S.fbd': (
|
@@ -307,80 +367,94 @@ CONVALLARIA_FBD = pooch.create(
|
|
307
367
|
|
308
368
|
FLIMLABS = pooch.create(
|
309
369
|
path=pooch.os_cache('phasorpy'),
|
310
|
-
base_url=
|
370
|
+
base_url=(
|
371
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/flimlabs'
|
372
|
+
if DATA_ON_GITHUB
|
373
|
+
else 'doi:10.5281/zenodo.15007900'
|
374
|
+
),
|
311
375
|
env=ENV,
|
312
376
|
registry={
|
313
|
-
'
|
314
|
-
'sha256:'
|
315
|
-
'a34c7077e88d1e7272953a46b2bb4e3ab8adf5a2f61c824dfc27032d952b920e'
|
316
|
-
),
|
317
|
-
'calibrator_2_5_1737112045_imaging.json.zip': (
|
377
|
+
'Convallaria_m2_1740751781_phasor_ch1.json': (
|
318
378
|
'sha256:'
|
319
|
-
'
|
379
|
+
'a8bf0179f352ab2c6c78d0bd399545ab1fb6d5537f23dc06e5f12a7ef5af6615'
|
320
380
|
),
|
321
|
-
'
|
381
|
+
'Convallaria_m2_1740751781_phasor_ch1.json.zip': (
|
322
382
|
'sha256:'
|
323
|
-
'
|
383
|
+
'9c5691f55e85778717ace13607d573bcd00c29e357e063c8db4f173340f72984'
|
324
384
|
),
|
325
|
-
'
|
385
|
+
'Fluorescein_Calibration_m2_1740751189_imaging.json': (
|
326
386
|
'sha256:'
|
327
|
-
'
|
387
|
+
'aeebb074dbea6bff7578f409c7622b2f7f173bb23e5475d1436adedca7fc2eed'
|
328
388
|
),
|
329
|
-
'
|
389
|
+
'Fluorescein_Calibration_m2_1740751189_imaging.json.zip': (
|
330
390
|
'sha256:'
|
331
|
-
'
|
391
|
+
'32960bc1dec85fd16ffc7dd74a3cd63041fb3b69054ee6582f913129b0847086'
|
332
392
|
),
|
333
|
-
'
|
393
|
+
'Fluorescein_Calibration_m2_1740751189_imaging_calibration.json': (
|
334
394
|
'sha256:'
|
335
|
-
'
|
395
|
+
'7fd1b9749789bd139c132602a771a127ea0c76f403d1750e9636cd657cce017a'
|
336
396
|
),
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
397
|
+
},
|
398
|
+
)
|
399
|
+
|
400
|
+
FIGSHARE_22336594 = pooch.create(
|
401
|
+
path=pooch.os_cache('phasorpy'),
|
402
|
+
base_url=(
|
403
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/figshare_22336594'
|
404
|
+
if DATA_ON_GITHUB
|
405
|
+
else 'doi:10.6084/m9.figshare.22336594.v1'
|
406
|
+
),
|
407
|
+
env=ENV,
|
408
|
+
registry={
|
409
|
+
'FLIM_testdata.lif': (
|
342
410
|
'sha256:'
|
343
|
-
'
|
411
|
+
'902d8fa6cd39da7cf062b32d43aab518fa2a851eab72b4bd8b8eca1bad591850'
|
344
412
|
),
|
345
|
-
|
413
|
+
},
|
414
|
+
)
|
415
|
+
|
416
|
+
FIGSHARE_22336594_EXPORTED = pooch.create(
|
417
|
+
path=pooch.os_cache('phasorpy'),
|
418
|
+
base_url=(
|
419
|
+
'https://github.com/phasorpy/phasorpy-data/raw/main/figshare_22336594'
|
420
|
+
),
|
421
|
+
env=ENV,
|
422
|
+
registry={
|
423
|
+
'FLIM_testdata.lif.ptu': (
|
346
424
|
'sha256:'
|
347
|
-
'
|
425
|
+
'c85792b25d0b274f1484e490c99aa19052ab8e48e4e5022aabb1f218cd1123b6'
|
348
426
|
),
|
349
|
-
'
|
427
|
+
'FLIM_testdata.lif.ptu.zip': (
|
350
428
|
'sha256:'
|
351
|
-
'
|
429
|
+
'c5134c470f6a3e5cb21eabd538cbd5061d9911dad96d58e3a4040cfddadaef33'
|
352
430
|
),
|
353
|
-
'
|
431
|
+
'FLIM_testdata.xlef': (
|
354
432
|
'sha256:'
|
355
|
-
'
|
433
|
+
'7860ef0847dc9f5534895a9c11b979bb446f67382b577fe63fb166e281e5dc5e'
|
356
434
|
),
|
357
|
-
'
|
435
|
+
'FLIM_testdata.xlef.zip': (
|
358
436
|
'sha256:'
|
359
|
-
'
|
437
|
+
'ad0ad6389f38dcba6f9809b54934ef3f19da975d9dabeb4c3a248692b959b9cf'
|
360
438
|
),
|
361
|
-
'
|
439
|
+
'FLIM_testdata.lif.filtered.ptu': (
|
362
440
|
'sha256:'
|
363
|
-
'
|
441
|
+
'a00b84f626a39e79263322c60ae50b64163b36bb977ecc4dc54619097ba7f5b7'
|
364
442
|
),
|
365
|
-
'
|
443
|
+
'FLIM_testdata.lif.filtered.ptu.zip': (
|
366
444
|
'sha256:'
|
367
|
-
'
|
445
|
+
'717366b231213bfd6e295c3efb7bf1bcd90e720eb28aab3223376087172e93e5'
|
368
446
|
),
|
369
447
|
},
|
370
448
|
)
|
371
449
|
|
372
|
-
|
450
|
+
MISC = pooch.create(
|
373
451
|
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
|
-
),
|
452
|
+
base_url='https://github.com/phasorpy/phasorpy-data/raw/main/misc',
|
379
453
|
env=ENV,
|
380
454
|
registry={
|
381
|
-
'
|
455
|
+
'NADHandSHG.ifli': (
|
382
456
|
'sha256:'
|
383
|
-
'
|
457
|
+
'dfa65952850b8a222258776a8a14eb1ab7e70ff5f62b58aa2214797c5921b4a3'
|
384
458
|
),
|
385
459
|
},
|
386
460
|
)
|
@@ -391,9 +465,13 @@ REPOSITORIES: dict[str, pooch.Pooch] = {
|
|
391
465
|
'flute': FLUTE,
|
392
466
|
'napari-flim-phasor-plotter': NAPARI_FLIM_PHASOR_PLOTTER,
|
393
467
|
'zenodo-13625087': ZENODO_13625087,
|
468
|
+
'zenodo-14860228': ZENODO_14860228,
|
469
|
+
'zenodo-14976703': ZENODO_14976703,
|
394
470
|
'convallaria-fbd': CONVALLARIA_FBD,
|
395
471
|
'flimlabs': FLIMLABS,
|
396
472
|
'figshare_22336594': FIGSHARE_22336594,
|
473
|
+
'figshare_22336594_exported': FIGSHARE_22336594_EXPORTED,
|
474
|
+
'misc': MISC,
|
397
475
|
}
|
398
476
|
"""Pooch repositories."""
|
399
477
|
|
@@ -406,19 +484,19 @@ def fetch(
|
|
406
484
|
) -> Any: # str | tuple[str, ...]
|
407
485
|
"""Return absolute path(s) to sample file(s) in local storage.
|
408
486
|
|
409
|
-
The files are downloaded from a remote repository if
|
410
|
-
|
487
|
+
The files are downloaded from a remote repository if not present in local
|
488
|
+
storage.
|
411
489
|
|
412
490
|
Parameters
|
413
491
|
----------
|
414
|
-
*args: str or iterable of str, optional
|
492
|
+
*args : str or iterable of str, optional
|
415
493
|
Name(s) of file(s) or repositories to fetch from local storage.
|
416
494
|
If omitted, return files in all repositories.
|
417
495
|
extract_dir : str or None, optional
|
418
496
|
Path, relative to cache location, where ZIP files will be unpacked.
|
419
497
|
return_scalar : bool, optional
|
420
498
|
If true (default), return single path as string, else tuple of string.
|
421
|
-
**kwargs
|
499
|
+
**kwargs
|
422
500
|
Additional arguments passed to ``pooch.fetch()``.
|
423
501
|
For example, ``progressbar=True``.
|
424
502
|
|
phasorpy/experimental.py
ADDED
@@ -0,0 +1,312 @@
|
|
1
|
+
"""Experimental functions.
|
2
|
+
|
3
|
+
The ``phasorpy.experimental`` module provides functions related to phasor
|
4
|
+
analysis for evaluation.
|
5
|
+
The functions may be removed or moved to other modules in future releases.
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from __future__ import annotations
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
'anscombe_transform',
|
13
|
+
'anscombe_transform_inverse',
|
14
|
+
'spectral_vector_denoise',
|
15
|
+
]
|
16
|
+
|
17
|
+
import math
|
18
|
+
from typing import TYPE_CHECKING
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from ._typing import Any, NDArray, ArrayLike, DTypeLike, Literal, Sequence
|
22
|
+
|
23
|
+
import numpy
|
24
|
+
|
25
|
+
from ._phasorpy import (
|
26
|
+
_anscombe,
|
27
|
+
_anscombe_inverse,
|
28
|
+
_anscombe_inverse_approx,
|
29
|
+
_phasor_from_signal_vector,
|
30
|
+
_signal_denoise_vector,
|
31
|
+
)
|
32
|
+
from ._utils import parse_harmonic
|
33
|
+
from .utils import number_threads
|
34
|
+
|
35
|
+
|
36
|
+
def anscombe_transform(
|
37
|
+
data: ArrayLike,
|
38
|
+
/,
|
39
|
+
**kwargs: Any,
|
40
|
+
) -> NDArray[Any]:
|
41
|
+
r"""Return Anscombe variance-stabilizing transformation.
|
42
|
+
|
43
|
+
The Anscombe transformation normalizes the standard deviation of noisy,
|
44
|
+
Poisson-distributed data.
|
45
|
+
It can be used to transform un-normalized phasor coordinates to
|
46
|
+
approximate standard Gaussian distributions.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
data : array_like
|
51
|
+
Noisy Poisson-distributed data to be transformed.
|
52
|
+
**kwargs
|
53
|
+
Optional `arguments passed to numpy universal functions
|
54
|
+
<https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
55
|
+
|
56
|
+
Returns
|
57
|
+
-------
|
58
|
+
ndarray
|
59
|
+
Anscombe-transformed data with variance of approximately 1.
|
60
|
+
|
61
|
+
Notes
|
62
|
+
-----
|
63
|
+
The Anscombe transformation according to [1]_:
|
64
|
+
|
65
|
+
.. math::
|
66
|
+
|
67
|
+
z = 2 \cdot \sqrt{x + 3 / 8}
|
68
|
+
|
69
|
+
References
|
70
|
+
----------
|
71
|
+
|
72
|
+
.. [1] Anscombe FJ.
|
73
|
+
`The transformation of Poisson, binomial and negative-binomial data
|
74
|
+
<https://doi.org/10.2307/2332343>`_.
|
75
|
+
*Biometrika*, 35(3-4): 246-254 (1948)
|
76
|
+
|
77
|
+
Examples
|
78
|
+
--------
|
79
|
+
|
80
|
+
>>> z = anscombe_transform(numpy.random.poisson(10, 10000))
|
81
|
+
>>> numpy.allclose(numpy.std(z), 1.0, atol=0.1)
|
82
|
+
True
|
83
|
+
|
84
|
+
"""
|
85
|
+
return _anscombe(data, **kwargs) # type: ignore[no-any-return]
|
86
|
+
|
87
|
+
|
88
|
+
def anscombe_transform_inverse(
|
89
|
+
data: ArrayLike,
|
90
|
+
/,
|
91
|
+
*,
|
92
|
+
approx: bool = False,
|
93
|
+
**kwargs: Any,
|
94
|
+
) -> NDArray[Any]:
|
95
|
+
r"""Return inverse Anscombe transformation.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
data : array_like
|
100
|
+
Anscombe-transformed data.
|
101
|
+
approx : bool, default: False
|
102
|
+
If true, return approximation of exact unbiased inverse.
|
103
|
+
**kwargs
|
104
|
+
Optional `arguments passed to numpy universal functions
|
105
|
+
<https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
106
|
+
|
107
|
+
Returns
|
108
|
+
-------
|
109
|
+
ndarray
|
110
|
+
Inverse Anscombe-transformed data.
|
111
|
+
|
112
|
+
Notes
|
113
|
+
-----
|
114
|
+
The inverse Anscombe transformation according to [1]_:
|
115
|
+
|
116
|
+
.. math::
|
117
|
+
|
118
|
+
x = (z / 2.0)^2 - 3 / 8
|
119
|
+
|
120
|
+
The approximate inverse Anscombe transformation according to [2]_ and [3]_:
|
121
|
+
|
122
|
+
.. math::
|
123
|
+
|
124
|
+
x = 1/4 \cdot z^2
|
125
|
+
+ 1/4 \cdot \sqrt{3/2} \cdot z^{-1}
|
126
|
+
- 11/8 \cdot z^{-2}
|
127
|
+
+ 5/8 \cdot \sqrt(3/2) \cdot z^{-3}
|
128
|
+
- 1/8
|
129
|
+
|
130
|
+
References
|
131
|
+
----------
|
132
|
+
|
133
|
+
.. [2] Makitalo M, and Foi A.
|
134
|
+
`A closed-form approximation of the exact unbiased inverse of the
|
135
|
+
Anscombe variance-stabilizing transformation
|
136
|
+
<https://doi.org/10.1109/TIP.2011.2121085>`_.
|
137
|
+
*IEEE Trans Image Process*, 20(9): 2697-8 (2011).
|
138
|
+
|
139
|
+
.. [3] Makitalo M, and Foi A
|
140
|
+
`Optimal inversion of the generalized Anscombe transformation for
|
141
|
+
Poisson-Gaussian noise
|
142
|
+
<https://doi.org/10.1109/TIP.2012.2202675>`_,
|
143
|
+
*IEEE Trans Image Process*, 22(1): 91-103 (2013)
|
144
|
+
|
145
|
+
Examples
|
146
|
+
--------
|
147
|
+
|
148
|
+
>>> x = numpy.random.poisson(10, 100)
|
149
|
+
>>> x2 = anscombe_transform_inverse(anscombe_transform(x))
|
150
|
+
>>> numpy.allclose(x, x2, atol=1e-3)
|
151
|
+
True
|
152
|
+
|
153
|
+
"""
|
154
|
+
if approx:
|
155
|
+
return _anscombe_inverse_approx( # type: ignore[no-any-return]
|
156
|
+
data, **kwargs
|
157
|
+
)
|
158
|
+
return _anscombe_inverse(data, **kwargs) # type: ignore[no-any-return]
|
159
|
+
|
160
|
+
|
161
|
+
def spectral_vector_denoise(
|
162
|
+
signal: ArrayLike,
|
163
|
+
/,
|
164
|
+
spectral_vector: ArrayLike | None = None,
|
165
|
+
*,
|
166
|
+
axis: int = -1,
|
167
|
+
harmonic: int | Sequence[int] | Literal['all'] | str | None = None,
|
168
|
+
sigma: float = 0.05,
|
169
|
+
vmin: float | None = None,
|
170
|
+
dtype: DTypeLike | None = None,
|
171
|
+
num_threads: int | None = None,
|
172
|
+
) -> NDArray[Any]:
|
173
|
+
"""Return spectral-vector-denoised signal.
|
174
|
+
|
175
|
+
The spectral vector denoising algorithm is based on a Gaussian weighted
|
176
|
+
average calculation, with weights obtained in n-dimensional Chebyshev or
|
177
|
+
Fourier space [4]_.
|
178
|
+
|
179
|
+
Parameters
|
180
|
+
----------
|
181
|
+
signal : array_like
|
182
|
+
Hyperspectral data to be denoised.
|
183
|
+
A minimum of three samples are required along `axis`.
|
184
|
+
The samples must be uniformly spaced.
|
185
|
+
spectral_vector : array_like, optional
|
186
|
+
Spectral vector.
|
187
|
+
For example, phasor coordinates, PCA projected phasor coordinates,
|
188
|
+
or Chebyshev coefficients.
|
189
|
+
Must be of same shape as `signal` with `axis` removed and axis
|
190
|
+
containing spectral space appended.
|
191
|
+
If None (default), phasor coordinates are calculated at specified
|
192
|
+
`harmonic`.
|
193
|
+
axis : int, optional, default: -1
|
194
|
+
Axis over which `spectral_vector` is computed if not provided.
|
195
|
+
The default is the last axis (-1).
|
196
|
+
harmonic : int, sequence of int, or 'all', optional
|
197
|
+
Harmonics to include in calculating `spectral_vector`.
|
198
|
+
If `'all'`, include all harmonics for `signal` samples along `axis`.
|
199
|
+
Else, harmonics must be at least one and no larger than half the
|
200
|
+
number of `signal` samples along `axis`.
|
201
|
+
The default is the first harmonic (fundamental frequency).
|
202
|
+
A minimum of `harmonic * 2 + 1` samples are required along `axis`
|
203
|
+
to calculate correct phasor coordinates at `harmonic`.
|
204
|
+
sigma : float, default: 0.05
|
205
|
+
Width of Gaussian filter in spectral vector space.
|
206
|
+
Weighted averages are calculated using the spectra of signal items
|
207
|
+
within an spectral vector Euclidean distance of `3 * sigma` and
|
208
|
+
intensity above `vmin`.
|
209
|
+
vmin : float, optional
|
210
|
+
Signal intensity along `axis` below which not to include in denoising.
|
211
|
+
dtype : dtype_like, optional
|
212
|
+
Data type of output arrays. Either float32 or float64.
|
213
|
+
The default is float64 unless the `signal` is float32.
|
214
|
+
num_threads : int, optional
|
215
|
+
Number of OpenMP threads to use for parallelization.
|
216
|
+
By default, multi-threading is disabled.
|
217
|
+
If zero, up to half of logical CPUs are used.
|
218
|
+
OpenMP may not be available on all platforms.
|
219
|
+
|
220
|
+
Returns
|
221
|
+
-------
|
222
|
+
ndarray
|
223
|
+
Denoised signal of `dtype`.
|
224
|
+
Spectra with integrated intensity below `vmin` are unchanged.
|
225
|
+
|
226
|
+
References
|
227
|
+
----------
|
228
|
+
|
229
|
+
.. [4] Harman RC, Lang RT, Kercher EM, Leven P, and Spring BQ.
|
230
|
+
`Denoising multiplexed microscopy images in n-dimensional spectral space
|
231
|
+
<https://doi.org/10.1364/BOE.463979>`_.
|
232
|
+
*Biomed Opt Express*, 13(8): 4298-4309 (2022)
|
233
|
+
|
234
|
+
Examples
|
235
|
+
--------
|
236
|
+
Denoise a hyperspectral image with a Gaussian filter width of 0.1 in
|
237
|
+
spectral vector space using first and second harmonic:
|
238
|
+
|
239
|
+
>>> signal = numpy.random.randint(0, 255, (8, 16, 16))
|
240
|
+
>>> spectral_vector_denoise(signal, axis=0, sigma=0.1, harmonic=[1, 2])
|
241
|
+
array([[[...]]])
|
242
|
+
|
243
|
+
"""
|
244
|
+
num_threads = number_threads(num_threads)
|
245
|
+
|
246
|
+
signal = numpy.asarray(signal)
|
247
|
+
if axis == -1 or axis == signal.ndim - 1:
|
248
|
+
axis = -1
|
249
|
+
else:
|
250
|
+
signal = numpy.moveaxis(signal, axis, -1)
|
251
|
+
shape = signal.shape
|
252
|
+
samples = shape[-1]
|
253
|
+
|
254
|
+
if harmonic is None:
|
255
|
+
harmonic = 1
|
256
|
+
harmonic, _ = parse_harmonic(harmonic, samples // 2)
|
257
|
+
num_harmonics = len(harmonic)
|
258
|
+
|
259
|
+
if vmin is None or vmin < 0.0:
|
260
|
+
vmin = 0.0
|
261
|
+
|
262
|
+
sincos = numpy.empty((num_harmonics, samples, 2))
|
263
|
+
for i, h in enumerate(harmonic):
|
264
|
+
phase = numpy.linspace(
|
265
|
+
0,
|
266
|
+
h * math.pi * 2.0,
|
267
|
+
samples,
|
268
|
+
endpoint=False,
|
269
|
+
dtype=numpy.float64,
|
270
|
+
)
|
271
|
+
sincos[i, :, 0] = numpy.cos(phase)
|
272
|
+
sincos[i, :, 1] = numpy.sin(phase)
|
273
|
+
|
274
|
+
signal = numpy.ascontiguousarray(signal).reshape(-1, samples)
|
275
|
+
size = signal.shape[0]
|
276
|
+
|
277
|
+
if dtype is None:
|
278
|
+
if signal.dtype.char == 'f':
|
279
|
+
dtype = signal.dtype
|
280
|
+
else:
|
281
|
+
dtype = numpy.float64
|
282
|
+
dtype = numpy.dtype(dtype)
|
283
|
+
if dtype.char not in {'d', 'f'}:
|
284
|
+
raise ValueError('dtype is not floating point')
|
285
|
+
|
286
|
+
if spectral_vector is None:
|
287
|
+
spectral_vector = numpy.zeros((size, num_harmonics * 2), dtype=dtype)
|
288
|
+
_phasor_from_signal_vector(
|
289
|
+
spectral_vector, signal, sincos, num_threads
|
290
|
+
)
|
291
|
+
else:
|
292
|
+
spectral_vector = numpy.ascontiguousarray(spectral_vector, dtype=dtype)
|
293
|
+
if spectral_vector.shape[:-1] != shape[:-1]:
|
294
|
+
raise ValueError('signal and spectral_vector shape mismatch')
|
295
|
+
spectral_vector = spectral_vector.reshape(
|
296
|
+
-1, spectral_vector.shape[-1]
|
297
|
+
)
|
298
|
+
|
299
|
+
if dtype == signal.dtype:
|
300
|
+
denoised = signal.copy()
|
301
|
+
else:
|
302
|
+
denoised = numpy.zeros(signal.shape, dtype=dtype)
|
303
|
+
denoised[:] = signal
|
304
|
+
integrated = numpy.zeros(size, dtype=dtype)
|
305
|
+
_signal_denoise_vector(
|
306
|
+
denoised, integrated, signal, spectral_vector, sigma, vmin, num_threads
|
307
|
+
)
|
308
|
+
|
309
|
+
denoised = denoised.reshape(shape)
|
310
|
+
if axis != -1:
|
311
|
+
denoised = numpy.moveaxis(denoised, -1, axis)
|
312
|
+
return denoised
|