phasorpy 0.7__cp314-cp314-macosx_11_0_arm64.whl → 0.8__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.
- phasorpy/__init__.py +1 -1
- phasorpy/_phasorpy.cpython-314-darwin.so +0 -0
- phasorpy/_phasorpy.pyx +39 -1
- phasorpy/_utils.py +13 -5
- phasorpy/cluster.py +2 -2
- phasorpy/component.py +10 -6
- phasorpy/datasets.py +1 -1
- phasorpy/experimental.py +1 -163
- phasorpy/filter.py +966 -0
- phasorpy/io/__init__.py +2 -1
- phasorpy/io/_flimlabs.py +6 -6
- phasorpy/io/_leica.py +36 -34
- phasorpy/io/_ometiff.py +8 -6
- phasorpy/io/_other.py +3 -3
- phasorpy/io/_simfcs.py +11 -8
- phasorpy/lifetime.py +16 -16
- phasorpy/phasor.py +122 -642
- phasorpy/plot/_functions.py +6 -6
- phasorpy/plot/_lifetime_plots.py +1 -1
- phasorpy/plot/_phasorplot.py +17 -20
- phasorpy/plot/_phasorplot_fret.py +1 -1
- phasorpy/utils.py +1 -0
- {phasorpy-0.7.dist-info → phasorpy-0.8.dist-info}/METADATA +8 -7
- phasorpy-0.8.dist-info/RECORD +36 -0
- phasorpy-0.7.dist-info/RECORD +0 -35
- {phasorpy-0.7.dist-info → phasorpy-0.8.dist-info}/WHEEL +0 -0
- {phasorpy-0.7.dist-info → phasorpy-0.8.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.7.dist-info → phasorpy-0.8.dist-info}/licenses/LICENSE.txt +0 -0
- {phasorpy-0.7.dist-info → phasorpy-0.8.dist-info}/top_level.txt +0 -0
phasorpy/__init__.py
CHANGED
Binary file
|
phasorpy/_phasorpy.pyx
CHANGED
@@ -6,7 +6,13 @@
|
|
6
6
|
# cython: nonecheck = False
|
7
7
|
# cython: freethreading_compatible = True
|
8
8
|
|
9
|
-
"""
|
9
|
+
"""Private functions implemented in Cython for performance.
|
10
|
+
|
11
|
+
.. note::
|
12
|
+
This module and its functions are not part of the public interface.
|
13
|
+
They are intended to facilitate the development of the PhasorPy library.
|
14
|
+
|
15
|
+
"""
|
10
16
|
|
11
17
|
cimport cython
|
12
18
|
|
@@ -1005,6 +1011,38 @@ cdef (float_t, float_t) _phasor_divide(
|
|
1005
1011
|
)
|
1006
1012
|
|
1007
1013
|
|
1014
|
+
@cython.ufunc
|
1015
|
+
cdef (float_t, float_t, float_t) _phasor_combine(
|
1016
|
+
float_t int0,
|
1017
|
+
float_t real0,
|
1018
|
+
float_t imag0,
|
1019
|
+
float_t int1,
|
1020
|
+
float_t real1,
|
1021
|
+
float_t imag1,
|
1022
|
+
float_t fraction0,
|
1023
|
+
float_t fraction1,
|
1024
|
+
) noexcept nogil:
|
1025
|
+
"""Return linear combination of two phasor coordinates."""
|
1026
|
+
cdef:
|
1027
|
+
float_t intensity
|
1028
|
+
|
1029
|
+
fraction1 += fraction0
|
1030
|
+
if fraction1 == 0.0:
|
1031
|
+
return <float_t> 0.0, <float_t> NAN, <float_t> NAN
|
1032
|
+
fraction0 /= fraction1
|
1033
|
+
|
1034
|
+
int0 *= fraction0
|
1035
|
+
int1 *= <float_t> 1.0 - fraction0
|
1036
|
+
intensity = int0 + int1
|
1037
|
+
|
1038
|
+
if intensity == 0.0:
|
1039
|
+
return <float_t> 0.0, <float_t> NAN, <float_t> NAN
|
1040
|
+
|
1041
|
+
int0 /= intensity
|
1042
|
+
int1 /= intensity
|
1043
|
+
return intensity, int0 * real0 + int1 * real1, int0 * imag0 + int1 * imag1
|
1044
|
+
|
1045
|
+
|
1008
1046
|
###############################################################################
|
1009
1047
|
# Geometry ufuncs
|
1010
1048
|
|
phasorpy/_utils.py
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
"""Private auxiliary and convenience functions.
|
1
|
+
"""Private auxiliary and convenience functions.
|
2
|
+
|
3
|
+
.. note::
|
4
|
+
This module and its functions are not part of the public interface.
|
5
|
+
They are intended to facilitate the development of the PhasorPy library.
|
6
|
+
|
7
|
+
"""
|
2
8
|
|
3
9
|
from __future__ import annotations
|
4
10
|
|
@@ -667,7 +673,7 @@ def chunk_iter(
|
|
667
673
|
|
668
674
|
|
669
675
|
def init_module(globs: dict[str, Any], /) -> None:
|
670
|
-
"""Add names in module to ``__all__``
|
676
|
+
"""Add names in module to ``__all__`` attribute.
|
671
677
|
|
672
678
|
Parameters
|
673
679
|
----------
|
@@ -690,9 +696,11 @@ def init_module(globs: dict[str, Any], /) -> None:
|
|
690
696
|
}:
|
691
697
|
continue
|
692
698
|
names.append(name)
|
693
|
-
|
694
|
-
|
695
|
-
|
699
|
+
# do not change __module__ attributes because that may interfere
|
700
|
+
# with introspection and pickling
|
701
|
+
# obj = getattr(module, name)
|
702
|
+
# if hasattr(obj, '__module__'):
|
703
|
+
# obj.__module__ = module_name
|
696
704
|
globs['__all__'] = sorted(set(names))
|
697
705
|
|
698
706
|
|
phasorpy/cluster.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Cluster phasor coordinates.
|
2
2
|
|
3
|
-
The
|
3
|
+
The ``phasorpy.cluster`` module provides functions to:
|
4
4
|
|
5
5
|
- fit elliptic clusters to phasor coordinates using a
|
6
6
|
Gaussian Mixture Model (GMM):
|
@@ -66,7 +66,7 @@ def phasor_cluster_gmm(
|
|
66
66
|
- 'area': Sort by inverse area of ellipse (-major * minor).
|
67
67
|
|
68
68
|
**kwargs
|
69
|
-
|
69
|
+
Optional arguments passed to
|
70
70
|
:py:class:`sklearn.mixture.GaussianMixture`.
|
71
71
|
|
72
72
|
Common options include:
|
phasorpy/component.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Analyze components in phasor coordinates.
|
2
2
|
|
3
3
|
The ``phasorpy.component`` module provides functions to:
|
4
4
|
|
@@ -53,7 +53,7 @@ from ._phasorpy import (
|
|
53
53
|
_segment_direction_and_length,
|
54
54
|
)
|
55
55
|
from ._utils import sort_coordinates
|
56
|
-
from .
|
56
|
+
from .filter import phasor_threshold
|
57
57
|
from .utils import number_threads
|
58
58
|
|
59
59
|
|
@@ -95,6 +95,10 @@ def phasor_from_component(
|
|
95
95
|
imag : ndarray
|
96
96
|
Imaginary component of phasor coordinates.
|
97
97
|
|
98
|
+
See Also
|
99
|
+
--------
|
100
|
+
phasorpy.phasor.phasor_combine
|
101
|
+
|
98
102
|
Examples
|
99
103
|
--------
|
100
104
|
Calculate phasor coordinates from two components and their fractional
|
@@ -110,7 +114,7 @@ def phasor_from_component(
|
|
110
114
|
if dtype.char not in {'f', 'd'}:
|
111
115
|
raise ValueError(f'{dtype=} is not a floating point type')
|
112
116
|
|
113
|
-
fraction = numpy.
|
117
|
+
fraction = numpy.asarray(fraction, dtype=dtype, copy=True)
|
114
118
|
if fraction.ndim < 1:
|
115
119
|
raise ValueError(f'{fraction.ndim=} < 1')
|
116
120
|
if fraction.shape[axis] < 2:
|
@@ -362,7 +366,7 @@ def phasor_component_graphical(
|
|
362
366
|
|
363
367
|
dtype = numpy.min_scalar_type(real.size)
|
364
368
|
counts = numpy.empty(
|
365
|
-
(1 if num_components == 2 else 3, fractions.size), dtype
|
369
|
+
(1 if num_components == 2 else 3, fractions.size), dtype=dtype
|
366
370
|
)
|
367
371
|
|
368
372
|
c = 0
|
@@ -438,8 +442,8 @@ def phasor_component_fit(
|
|
438
442
|
component_imag : array_like
|
439
443
|
Imaginary coordinates of components.
|
440
444
|
Must be one or two-dimensional with harmonics in the first dimension.
|
441
|
-
**kwargs
|
442
|
-
|
445
|
+
**kwargs
|
446
|
+
Optional arguments passed to :py:func:`scipy.linalg.lstsq`.
|
443
447
|
|
444
448
|
Returns
|
445
449
|
-------
|
phasorpy/datasets.py
CHANGED
@@ -600,7 +600,7 @@ def fetch(
|
|
600
600
|
return_scalar : bool, optional
|
601
601
|
If true (default), return single path as string, else tuple of string.
|
602
602
|
**kwargs
|
603
|
-
|
603
|
+
Optional arguments passed to :py:func:`pooch.fetch`.
|
604
604
|
For example, ``progressbar=True``.
|
605
605
|
|
606
606
|
Returns
|
phasorpy/experimental.py
CHANGED
@@ -11,26 +11,18 @@ from __future__ import annotations
|
|
11
11
|
__all__ = [
|
12
12
|
'anscombe_transform',
|
13
13
|
'anscombe_transform_inverse',
|
14
|
-
'spectral_vector_denoise',
|
15
14
|
]
|
16
15
|
|
17
|
-
import math
|
18
16
|
from typing import TYPE_CHECKING
|
19
17
|
|
20
18
|
if TYPE_CHECKING:
|
21
|
-
from ._typing import Any, NDArray, ArrayLike
|
22
|
-
|
23
|
-
import numpy
|
19
|
+
from ._typing import Any, NDArray, ArrayLike
|
24
20
|
|
25
21
|
from ._phasorpy import (
|
26
22
|
_anscombe,
|
27
23
|
_anscombe_inverse,
|
28
24
|
_anscombe_inverse_approx,
|
29
|
-
_phasor_from_signal_vector,
|
30
|
-
_signal_denoise_vector,
|
31
25
|
)
|
32
|
-
from ._utils import parse_harmonic
|
33
|
-
from .utils import number_threads
|
34
26
|
|
35
27
|
|
36
28
|
def anscombe_transform(
|
@@ -154,157 +146,3 @@ def anscombe_transform_inverse(
|
|
154
146
|
data, **kwargs
|
155
147
|
)
|
156
148
|
return _anscombe_inverse(data, **kwargs) # type: ignore[no-any-return]
|
157
|
-
|
158
|
-
|
159
|
-
def spectral_vector_denoise(
|
160
|
-
signal: ArrayLike,
|
161
|
-
/,
|
162
|
-
spectral_vector: ArrayLike | None = None,
|
163
|
-
*,
|
164
|
-
axis: int = -1,
|
165
|
-
harmonic: int | Sequence[int] | Literal['all'] | str | None = None,
|
166
|
-
sigma: float = 0.05,
|
167
|
-
vmin: float | None = None,
|
168
|
-
dtype: DTypeLike | None = None,
|
169
|
-
num_threads: int | None = None,
|
170
|
-
) -> NDArray[Any]:
|
171
|
-
"""Return spectral-vector-denoised signal.
|
172
|
-
|
173
|
-
The spectral vector denoising algorithm is based on a Gaussian weighted
|
174
|
-
average calculation, with weights obtained in n-dimensional Chebyshev or
|
175
|
-
Fourier space [4]_.
|
176
|
-
|
177
|
-
Parameters
|
178
|
-
----------
|
179
|
-
signal : array_like
|
180
|
-
Hyperspectral data to be denoised.
|
181
|
-
A minimum of three samples are required along `axis`.
|
182
|
-
The samples must be uniformly spaced.
|
183
|
-
spectral_vector : array_like, optional
|
184
|
-
Spectral vector.
|
185
|
-
For example, phasor coordinates, PCA projected phasor coordinates,
|
186
|
-
or Chebyshev coefficients.
|
187
|
-
Must be of the same shape as `signal` with `axis` removed and an axis
|
188
|
-
containing spectral space appended.
|
189
|
-
If None (default), phasor coordinates are calculated at specified
|
190
|
-
`harmonic`.
|
191
|
-
axis : int, optional, default: -1
|
192
|
-
Axis over which `spectral_vector` is computed if not provided.
|
193
|
-
The default is the last axis (-1).
|
194
|
-
harmonic : int, sequence of int, or 'all', optional
|
195
|
-
Harmonics to include in calculating `spectral_vector`.
|
196
|
-
If `'all'`, include all harmonics for `signal` samples along `axis`.
|
197
|
-
Else, harmonics must be at least one and no larger than half the
|
198
|
-
number of `signal` samples along `axis`.
|
199
|
-
The default is the first harmonic (fundamental frequency).
|
200
|
-
A minimum of `harmonic * 2 + 1` samples are required along `axis`
|
201
|
-
to calculate correct phasor coordinates at `harmonic`.
|
202
|
-
sigma : float, default: 0.05
|
203
|
-
Width of Gaussian filter in spectral vector space.
|
204
|
-
Weighted averages are calculated using the spectra of signal items
|
205
|
-
within a spectral vector Euclidean distance of `3 * sigma` and
|
206
|
-
intensity above `vmin`.
|
207
|
-
vmin : float, optional
|
208
|
-
Signal intensity along `axis` below which spectra are excluded from
|
209
|
-
denoising.
|
210
|
-
dtype : dtype_like, optional
|
211
|
-
Data type of output arrays. Either float32 or float64.
|
212
|
-
The default is float64 unless the `signal` is float32.
|
213
|
-
num_threads : int, optional
|
214
|
-
Number of OpenMP threads to use for parallelization.
|
215
|
-
By default, multi-threading is disabled.
|
216
|
-
If zero, up to half of logical CPUs are used.
|
217
|
-
OpenMP may not be available on all platforms.
|
218
|
-
|
219
|
-
Returns
|
220
|
-
-------
|
221
|
-
ndarray
|
222
|
-
Denoised signal of `dtype`.
|
223
|
-
Spectra with integrated intensity below `vmin` are unchanged.
|
224
|
-
|
225
|
-
References
|
226
|
-
----------
|
227
|
-
.. [4] Harman RC, Lang RT, Kercher EM, Leven P, and Spring BQ.
|
228
|
-
`Denoising multiplexed microscopy images in n-dimensional spectral space
|
229
|
-
<https://doi.org/10.1364/BOE.463979>`_.
|
230
|
-
*Biomed Opt Express*, 13(8): 4298-4309 (2022)
|
231
|
-
|
232
|
-
Examples
|
233
|
-
--------
|
234
|
-
Denoise a hyperspectral image with a Gaussian filter width of 0.1 in
|
235
|
-
spectral vector space using first and second harmonic:
|
236
|
-
|
237
|
-
>>> signal = numpy.random.randint(0, 255, (8, 16, 16))
|
238
|
-
>>> spectral_vector_denoise(signal, axis=0, sigma=0.1, harmonic=[1, 2])
|
239
|
-
array([[[...]]])
|
240
|
-
|
241
|
-
"""
|
242
|
-
num_threads = number_threads(num_threads)
|
243
|
-
|
244
|
-
signal = numpy.asarray(signal)
|
245
|
-
if axis == -1 or axis == signal.ndim - 1:
|
246
|
-
axis = -1
|
247
|
-
else:
|
248
|
-
signal = numpy.moveaxis(signal, axis, -1)
|
249
|
-
shape = signal.shape
|
250
|
-
samples = shape[-1]
|
251
|
-
|
252
|
-
if harmonic is None:
|
253
|
-
harmonic = 1
|
254
|
-
harmonic, _ = parse_harmonic(harmonic, samples // 2)
|
255
|
-
num_harmonics = len(harmonic)
|
256
|
-
|
257
|
-
if vmin is None or vmin < 0.0:
|
258
|
-
vmin = 0.0
|
259
|
-
|
260
|
-
sincos = numpy.empty((num_harmonics, samples, 2))
|
261
|
-
for i, h in enumerate(harmonic):
|
262
|
-
phase = numpy.linspace(
|
263
|
-
0,
|
264
|
-
h * math.pi * 2.0,
|
265
|
-
samples,
|
266
|
-
endpoint=False,
|
267
|
-
dtype=numpy.float64,
|
268
|
-
)
|
269
|
-
sincos[i, :, 0] = numpy.cos(phase)
|
270
|
-
sincos[i, :, 1] = numpy.sin(phase)
|
271
|
-
|
272
|
-
signal = numpy.ascontiguousarray(signal).reshape(-1, samples)
|
273
|
-
size = signal.shape[0]
|
274
|
-
|
275
|
-
if dtype is None:
|
276
|
-
if signal.dtype.char == 'f':
|
277
|
-
dtype = signal.dtype
|
278
|
-
else:
|
279
|
-
dtype = numpy.float64
|
280
|
-
dtype = numpy.dtype(dtype)
|
281
|
-
if dtype.char not in {'d', 'f'}:
|
282
|
-
raise ValueError('dtype is not floating point')
|
283
|
-
|
284
|
-
if spectral_vector is None:
|
285
|
-
spectral_vector = numpy.zeros((size, num_harmonics * 2), dtype=dtype)
|
286
|
-
_phasor_from_signal_vector(
|
287
|
-
spectral_vector, signal, sincos, num_threads
|
288
|
-
)
|
289
|
-
else:
|
290
|
-
spectral_vector = numpy.ascontiguousarray(spectral_vector, dtype=dtype)
|
291
|
-
if spectral_vector.shape[:-1] != shape[:-1]:
|
292
|
-
raise ValueError('signal and spectral_vector shape mismatch')
|
293
|
-
spectral_vector = spectral_vector.reshape(
|
294
|
-
-1, spectral_vector.shape[-1]
|
295
|
-
)
|
296
|
-
|
297
|
-
if dtype == signal.dtype:
|
298
|
-
denoised = signal.copy()
|
299
|
-
else:
|
300
|
-
denoised = numpy.zeros(signal.shape, dtype=dtype)
|
301
|
-
denoised[:] = signal
|
302
|
-
integrated = numpy.zeros(size, dtype=dtype)
|
303
|
-
_signal_denoise_vector(
|
304
|
-
denoised, integrated, signal, spectral_vector, sigma, vmin, num_threads
|
305
|
-
)
|
306
|
-
|
307
|
-
denoised = denoised.reshape(shape) # type: ignore[assignment]
|
308
|
-
if axis != -1:
|
309
|
-
denoised = numpy.moveaxis(denoised, -1, axis)
|
310
|
-
return denoised
|