phasorpy 0.3__cp313-cp313-win_amd64.whl → 0.4__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.
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, list of int, 'all', or None
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
- axes: str | Sequence[str] | None = None,
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
- axes : str or sequence of str, optional
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 ``_[{axes[index]}{chunk_index[index]}]`` for each axis.
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 axes is None:
388
- axes = sep * ndim
452
+ if dims is None:
453
+ dims = sep * ndim
389
454
  sep = ''
390
- elif ndim != len(axes):
391
- raise ValueError(f'{len(shape)=} != {len(axes)=}')
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, axes)):
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
@@ -59,8 +59,7 @@ def wavelength2rgb(
59
59
  else:
60
60
  rgb = rgb.astype(dtype)
61
61
  if astuple:
62
- rgb_list = rgb.tolist()
63
- return (rgb_list[0], rgb_list[1], rgb_list[2])
62
+ return tuple(rgb.tolist()[:3])
64
63
  return rgb
65
64
 
66
65
 
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