swcgeom 0.18.3__py3-none-any.whl → 0.19.1__py3-none-any.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.

Potentially problematic release.


This version of swcgeom might be problematic. Click here for more details.

Files changed (62) hide show
  1. swcgeom/analysis/feature_extractor.py +22 -24
  2. swcgeom/analysis/features.py +18 -40
  3. swcgeom/analysis/lmeasure.py +227 -323
  4. swcgeom/analysis/sholl.py +17 -23
  5. swcgeom/analysis/trunk.py +23 -28
  6. swcgeom/analysis/visualization.py +37 -44
  7. swcgeom/analysis/visualization3d.py +16 -25
  8. swcgeom/analysis/volume.py +33 -47
  9. swcgeom/core/__init__.py +1 -6
  10. swcgeom/core/branch.py +10 -17
  11. swcgeom/core/branch_tree.py +3 -2
  12. swcgeom/core/compartment.py +1 -1
  13. swcgeom/core/node.py +3 -6
  14. swcgeom/core/path.py +11 -16
  15. swcgeom/core/population.py +32 -51
  16. swcgeom/core/swc.py +25 -16
  17. swcgeom/core/swc_utils/__init__.py +4 -6
  18. swcgeom/core/swc_utils/assembler.py +5 -12
  19. swcgeom/core/swc_utils/base.py +40 -31
  20. swcgeom/core/swc_utils/checker.py +3 -8
  21. swcgeom/core/swc_utils/io.py +32 -47
  22. swcgeom/core/swc_utils/normalizer.py +17 -23
  23. swcgeom/core/swc_utils/subtree.py +13 -20
  24. swcgeom/core/tree.py +61 -51
  25. swcgeom/core/tree_utils.py +36 -49
  26. swcgeom/core/tree_utils_impl.py +4 -6
  27. swcgeom/images/augmentation.py +23 -39
  28. swcgeom/images/contrast.py +22 -46
  29. swcgeom/images/folder.py +32 -34
  30. swcgeom/images/io.py +108 -126
  31. swcgeom/transforms/base.py +28 -19
  32. swcgeom/transforms/branch.py +31 -41
  33. swcgeom/transforms/branch_tree.py +3 -1
  34. swcgeom/transforms/geometry.py +13 -4
  35. swcgeom/transforms/image_preprocess.py +2 -0
  36. swcgeom/transforms/image_stack.py +40 -35
  37. swcgeom/transforms/images.py +31 -24
  38. swcgeom/transforms/mst.py +27 -40
  39. swcgeom/transforms/neurolucida_asc.py +13 -13
  40. swcgeom/transforms/path.py +4 -0
  41. swcgeom/transforms/population.py +4 -0
  42. swcgeom/transforms/tree.py +16 -11
  43. swcgeom/transforms/tree_assembler.py +37 -54
  44. swcgeom/utils/download.py +7 -14
  45. swcgeom/utils/dsu.py +12 -0
  46. swcgeom/utils/ellipse.py +26 -14
  47. swcgeom/utils/file.py +8 -13
  48. swcgeom/utils/neuromorpho.py +78 -92
  49. swcgeom/utils/numpy_helper.py +15 -12
  50. swcgeom/utils/plotter_2d.py +10 -16
  51. swcgeom/utils/plotter_3d.py +7 -9
  52. swcgeom/utils/renderer.py +16 -8
  53. swcgeom/utils/sdf.py +12 -23
  54. swcgeom/utils/solid_geometry.py +58 -2
  55. swcgeom/utils/transforms.py +164 -100
  56. swcgeom/utils/volumetric_object.py +29 -53
  57. {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/METADATA +6 -5
  58. swcgeom-0.19.1.dist-info/RECORD +67 -0
  59. {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/WHEEL +1 -1
  60. swcgeom-0.18.3.dist-info/RECORD +0 -67
  61. {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info/licenses}/LICENSE +0 -0
  62. {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/top_level.txt +0 -0
@@ -15,12 +15,10 @@
15
15
 
16
16
  """The contrast of an image.
17
17
 
18
- Notes
19
- -----
20
- This is expremental code, and the API is subject to change.
18
+ NOTE: This is expremental code, and the API is subject to change.
21
19
  """
22
20
 
23
- from typing import Optional, overload
21
+ from typing import overload
24
22
 
25
23
  import numpy as np
26
24
  import numpy.typing as npt
@@ -34,13 +32,11 @@ Array3D = npt.NDArray[np.float32]
34
32
  def contrast_std(image: Array3D) -> float:
35
33
  """Get the std contrast of an image stack.
36
34
 
37
- Parameters
38
- ----------
39
- imgs : ndarray
35
+ Args:
36
+ imgs: ndarray
40
37
 
41
- Returns
42
- -------
43
- contrast : float
38
+ Returns:
39
+ contrast
44
40
  """
45
41
  ...
46
42
 
@@ -49,21 +45,17 @@ def contrast_std(image: Array3D) -> float:
49
45
  def contrast_std(image: Array3D, contrast: float) -> Array3D:
50
46
  """Adjust the contrast of an image stack.
51
47
 
52
- Parameters
53
- ----------
54
- imgs : ndarray
55
- constrast : float
56
- The contrast adjustment factor. 1.0 leaves the image unchanged.
48
+ Args:
49
+ imgs: ndarray
50
+ contrast: The contrast adjustment factor. 1.0 leaves the image unchanged.
57
51
 
58
- Returns
59
- -------
60
- imgs : ndarray
61
- The adjusted image.
52
+ Returns:
53
+ imgs: The adjusted image.
62
54
  """
63
55
  ...
64
56
 
65
57
 
66
- def contrast_std(image: Array3D, contrast: Optional[float] = None):
58
+ def contrast_std(image: Array3D, contrast: float | None = None):
67
59
  if contrast is None:
68
60
  return np.std(image).item()
69
61
  else:
@@ -73,15 +65,9 @@ def contrast_std(image: Array3D, contrast: Optional[float] = None):
73
65
  def contrast_michelson(image: Array3D) -> float:
74
66
  """Get the Michelson contrast of an image stack.
75
67
 
76
- Parameters
77
- ----------
78
- imgs : ndarray
79
-
80
- Returns
81
- -------
82
- contrast : float
68
+ Returns:
69
+ contrast: float
83
70
  """
84
-
85
71
  vmax = np.max(image)
86
72
  vmin = np.min(image)
87
73
  return ((vmax - vmin) / (vmax + vmin)).item()
@@ -90,33 +76,23 @@ def contrast_michelson(image: Array3D) -> float:
90
76
  def contrast_rms(imgs: npt.NDArray[np.float32]) -> float:
91
77
  """Get the RMS contrast of an image stack.
92
78
 
93
- Parameters
94
- ----------
95
- imgs : ndarray
96
-
97
- Returns
98
- -------
99
- contrast : float
79
+ Returns:
80
+ contrast
100
81
  """
101
-
102
82
  return np.sqrt(np.mean(imgs**2)).item()
103
83
 
104
84
 
105
85
  def contrast_weber(imgs: Array3D, mask: npt.NDArray[np.bool_]) -> float:
106
86
  """Get the Weber contrast of an image stack.
107
87
 
108
- Parameters
109
- ----------
110
- imgs : ndarray
111
- mask : ndarray of bool
112
- The mask to segment the foreground and background. 1 for
113
- foreground, 0 for background.
88
+ Args:
89
+ imgs: ndarray
90
+ mask: The mask to segment the foreground and background.
91
+ 1 for foreground, 0 for background.
114
92
 
115
- Returns
116
- -------
117
- contrast : float
93
+ Returns:
94
+ contrast
118
95
  """
119
-
120
96
  l_foreground = np.mean(imgs, where=mask)
121
97
  l_background = np.mean(imgs, where=np.logical_not(mask))
122
98
  return ((l_foreground - l_background) / l_background).item()
swcgeom/images/folder.py CHANGED
@@ -20,7 +20,7 @@ import os
20
20
  import re
21
21
  from collections.abc import Callable, Iterable
22
22
  from dataclasses import dataclass
23
- from typing import Generic, Literal, Optional, TypeVar, overload
23
+ from typing import Generic, Literal, TypeVar, overload
24
24
 
25
25
  import numpy as np
26
26
  import numpy.typing as npt
@@ -41,13 +41,21 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
41
41
  files: list[str]
42
42
  transform: Transform[npt.NDArray[ScalarType], T]
43
43
 
44
- # fmt: off
45
44
  @overload
46
- def __init__(self, files: Iterable[str], *, dtype: None = ..., transform: Optional[Transform[npt.NDArray[np.float32], T]] = None) -> None: ...
45
+ def __init__(
46
+ self,
47
+ files: Iterable[str],
48
+ *,
49
+ transform: Transform[npt.NDArray[np.float32], T] | None = ...,
50
+ ) -> None: ...
47
51
  @overload
48
- def __init__(self, files: Iterable[str], *, dtype: ScalarType, transform: Optional[Transform[npt.NDArray[ScalarType], T]] = None) -> None: ...
49
- # fmt: on
50
-
52
+ def __init__(
53
+ self,
54
+ files: Iterable[str],
55
+ *,
56
+ dtype: ScalarType,
57
+ transform: Transform[npt.NDArray[ScalarType], T] | None = ...,
58
+ ) -> None: ...
51
59
  def __init__(self, files: Iterable[str], *, dtype=None, transform=None) -> None:
52
60
  super().__init__()
53
61
  self.files = list(files)
@@ -66,7 +74,7 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
66
74
  return read_imgs(fname, dtype=self.dtype).get_full() # type: ignore
67
75
 
68
76
  @staticmethod
69
- def scan(root: str, *, pattern: Optional[str] = None) -> list[str]:
77
+ def scan(root: str, *, pattern: str | None = None) -> list[str]:
70
78
  if not os.path.isdir(root):
71
79
  raise NotADirectoryError(f"not a directory: {root}")
72
80
 
@@ -108,16 +116,12 @@ class ImageStackFolder(ImageStackFolderBase[ScalarType, T]):
108
116
  def stat(self, *, transform: bool = False, verbose: bool = False) -> Statistics:
109
117
  """Statistics of folder.
110
118
 
111
- Parameters
112
- ----------
113
- transform : bool, default to False
114
- Apply transform to the images. If True, you need to make
115
- sure the transformed data is a ndarray.
116
- verbose : bool, optional
119
+ NOTE: We are asserting that the images are of the same shape.
117
120
 
118
- Notes
119
- -----
120
- We are asserting that the images are of the same shape.
121
+ Args:
122
+ transform: Apply transform to the images.
123
+ If True, you need to make sure the transformed data is a ndarray.
124
+ verbose: Show progress bar.
121
125
  """
122
126
 
123
127
  vmin, vmax = math.inf, -math.inf
@@ -134,7 +138,7 @@ class ImageStackFolder(ImageStackFolderBase[ScalarType, T]):
134
138
  M2 = np.zeros_like(imgs)
135
139
 
136
140
  n += 1
137
- delta = imgs - mean # type: ignore
141
+ delta = imgs - mean
138
142
  mean += delta / n
139
143
  delta2 = imgs - mean
140
144
  M2 += delta * delta2
@@ -152,15 +156,12 @@ class ImageStackFolder(ImageStackFolderBase[ScalarType, T]):
152
156
  )
153
157
 
154
158
  @classmethod
155
- def from_dir(cls, root: str, *, pattern: Optional[str] = None, **kwargs) -> Self:
159
+ def from_dir(cls, root: str, *, pattern: str | None = None, **kwargs) -> Self:
156
160
  """
157
- Parameters
158
- ----------
159
- root : str
160
- pattern : str, optional
161
- Filter files by pattern.
162
- **kwargs
163
- Pass to `cls.__init__`
161
+ Args:
162
+ root: str
163
+ pattern: Filter files by pattern.
164
+ **kwargs: Pass to `cls.__init__`
164
165
  """
165
166
 
166
167
  return cls(cls.scan(root, pattern=pattern), **kwargs)
@@ -184,7 +185,7 @@ class LabeledImageStackFolder(ImageStackFolderBase[ScalarType, T]):
184
185
  root: str,
185
186
  label: int | Callable[[str], int],
186
187
  *,
187
- pattern: Optional[str] = None,
188
+ pattern: str | None = None,
188
189
  **kwargs,
189
190
  ) -> Self:
190
191
  files = cls.scan(root, pattern=pattern)
@@ -211,15 +212,12 @@ class PathImageStackFolder(ImageStackFolderBase[ScalarType, T]):
211
212
  return self._get(self.files[idx]), relpath
212
213
 
213
214
  @classmethod
214
- def from_dir(cls, root: str, *, pattern: Optional[str] = None, **kwargs) -> Self:
215
+ def from_dir(cls, root: str, *, pattern: str | None = None, **kwargs) -> Self:
215
216
  """
216
- Parameters
217
- ----------
218
- root : str
219
- pattern : str, optional
220
- Filter files by pattern.
221
- **kwargs
222
- Pass to `cls.__init__`
217
+ Args:
218
+ root: str
219
+ pattern: Filter files by pattern.
220
+ **kwargs: Pass to `cls.__init__`
223
221
  """
224
222
 
225
223
  return cls(cls.scan(root, pattern=pattern), root=root, **kwargs)
swcgeom/images/io.py CHANGED
@@ -17,18 +17,18 @@
17
17
 
18
18
  import os
19
19
  import re
20
+ import sys
20
21
  import warnings
21
22
  from abc import ABC, abstractmethod
22
23
  from collections.abc import Callable, Iterable
23
24
  from functools import cache, lru_cache
24
- from typing import Any, Generic, Literal, Optional, TypeVar, cast, overload
25
+ from typing import Any, Generic, Literal, TypeVar, cast, overload
25
26
 
26
27
  import nrrd
27
28
  import numpy as np
28
29
  import numpy.typing as npt
29
30
  import tifffile
30
31
  from typing_extensions import deprecated
31
- from v3dpy.loaders import PBD, Raw
32
32
 
33
33
  __all__ = ["read_imgs", "save_tiff", "read_images"]
34
34
 
@@ -39,10 +39,10 @@ RE_TERAFLY_ROOT = re.compile(r"^RES\((\d+)x(\d+)x(\d+)\)$")
39
39
  RE_TERAFLY_NAME = re.compile(r"^\d+(_\d+)?(_\d+)?")
40
40
 
41
41
  UINT_MAX = {
42
- np.dtype(np.uint8): (2**8) - 1, # type: ignore
43
- np.dtype(np.uint16): (2**16) - 1, # type: ignore
44
- np.dtype(np.uint32): (2**32) - 1, # type: ignore
45
- np.dtype(np.uint64): (2**64) - 1, # type: ignore
42
+ np.dtype(np.uint8): (2**8) - 1,
43
+ np.dtype(np.uint16): (2**16) - 1,
44
+ np.dtype(np.uint32): (2**32) - 1,
45
+ np.dtype(np.uint64): (2**64) - 1,
46
46
  }
47
47
 
48
48
  AXES_ORDER = {
@@ -83,19 +83,15 @@ class ImageStack(ABC, Generic[ScalarType]):
83
83
  def __getitem__(self, key):
84
84
  """Get pixel/patch of image stack.
85
85
 
86
- Returns
87
- -------
88
- value : ndarray of f32
89
- NDArray which shape depends on key. If key is tuple of ints,
86
+ Returns:
87
+ value: NDArray which shape depends on key. If key is tuple of ints,
90
88
  """
91
89
  raise NotImplementedError()
92
90
 
93
91
  def get_full(self) -> npt.NDArray[ScalarType]:
94
92
  """Get full image stack.
95
93
 
96
- Notes
97
- -----
98
- this will load the full image stack into memory.
94
+ NOTE: this will load the full image stack into memory.
99
95
  """
100
96
  return self[:, :, :, :]
101
97
 
@@ -104,29 +100,20 @@ class ImageStack(ABC, Generic[ScalarType]):
104
100
  raise NotImplementedError()
105
101
 
106
102
 
107
- # fmt:off
108
103
  @overload
109
104
  def read_imgs(fname: str, *, dtype: ScalarType, **kwargs) -> ImageStack[ScalarType]: ...
110
105
  @overload
111
- def read_imgs(fname: str, *, dtype: None =..., **kwargs) -> ImageStack[np.float32]: ...
112
- # fmt:on
113
-
114
-
106
+ def read_imgs(fname: str, *, dtype: None = ..., **kwargs) -> ImageStack[np.float32]: ...
115
107
  def read_imgs(fname: str, **kwargs): # type: ignore
116
108
  """Read image stack.
117
109
 
118
- Parameters
119
- ----------
120
- fname : str
121
- The path of image stack.
122
- dtype : np.dtype, default to `np.float32`
123
- Casting data to specified dtype. If integer and float
124
- conversions occur, they will be scaled (assuming floats are
125
- between 0 and 1).
126
- **kwargs : dict[str, Any]
127
- Forwarding to the corresponding reader.
110
+ Args:
111
+ fname: The path of image stack.
112
+ dtype: Casting data to specified dtype.
113
+ If integer and float conversions occur, they will be scaled (assuming floats
114
+ are between 0 and 1). Default to `np.float32`.
115
+ **kwargs: Forwarding to the corresponding reader.
128
116
  """
129
-
130
117
  kwargs.setdefault("dtype", np.float32)
131
118
  if not os.path.exists(fname):
132
119
  raise ValueError(f"image stack not exists: {fname}")
@@ -155,27 +142,22 @@ def save_tiff(
155
142
  data: npt.NDArray | ImageStack,
156
143
  fname: str,
157
144
  *,
158
- dtype: Optional[np.unsignedinteger | np.floating] = None,
145
+ dtype: np.unsignedinteger | np.floating | None = None,
159
146
  compression: str | Literal[False] = "zlib",
160
147
  **kwargs,
161
148
  ) -> None:
162
149
  """Save image stack as tiff.
163
150
 
164
- Parameters
165
- ----------
166
- data : array
167
- The image stack.
168
- fname : str
169
- dtype : np.dtype, optional
170
- Casting data to specified dtype. If integer and float
171
- conversions occur, they will be scaled (assuming floats are
172
- between 0 and 1).
173
- compression : str | False, default `zlib`
174
- Compression algorithm, forwarding to `tifffile.imwrite`. If no
175
- algorithnm is specify specified, we will use the zlib algorithm
176
- with compression level 6 by default.
177
- **kwargs : dict[str, Any]
178
- Forwarding to `tifffile.imwrite`
151
+ Args:
152
+ data: The image stack.
153
+ fname: str
154
+ dtype: Casting data to specified dtype.
155
+ If integer and float conversions occur, they will be scaled (assuming
156
+ floats are between 0 and 1).
157
+ compression: Compression algorithm, forwarding to `tifffile.imwrite`.
158
+ If no algorithnm is specify specified, we will use the zlib algorithm with
159
+ compression level 6 by default.
160
+ **kwargs: Forwarding to `tifffile.imwrite`
179
161
  """
180
162
  if isinstance(data, ImageStack):
181
163
  data = data.get_full() # TODO: avoid load full imgs to memory
@@ -191,11 +173,11 @@ def save_tiff(
191
173
  if np.issubdtype(data.dtype, np.floating) and np.issubdtype(
192
174
  dtype, np.unsignedinteger
193
175
  ):
194
- scaler_factor = UINT_MAX[np.dtype(dtype)] # type: ignore
176
+ scaler_factor = UINT_MAX[np.dtype(dtype)]
195
177
  elif np.issubdtype(data.dtype, np.unsignedinteger) and np.issubdtype(
196
178
  dtype, np.floating
197
179
  ):
198
- scaler_factor = 1 / UINT_MAX[np.dtype(data.dtype)] # type: ignore
180
+ scaler_factor = 1 / UINT_MAX[np.dtype(data.dtype)]
199
181
  else:
200
182
  scaler_factor = 1
201
183
 
@@ -218,7 +200,7 @@ class NDArrayImageStack(ImageStack[ScalarType]):
218
200
  """NDArray image stack."""
219
201
 
220
202
  def __init__(
221
- self, imgs: npt.NDArray[Any], *, dtype: Optional[ScalarType] = None
203
+ self, imgs: npt.NDArray[Any], *, dtype: ScalarType | None = None
222
204
  ) -> None:
223
205
  super().__init__()
224
206
 
@@ -231,13 +213,13 @@ class NDArrayImageStack(ImageStack[ScalarType]):
231
213
  if np.issubdtype(dtype, np.floating) and np.issubdtype(
232
214
  dtype_raw, np.unsignedinteger
233
215
  ):
234
- sclar_factor = 1.0 / UINT_MAX[dtype_raw]
235
- imgs = sclar_factor * imgs.astype(dtype)
216
+ scalar_factor = 1.0 / UINT_MAX[dtype_raw]
217
+ imgs = scalar_factor * imgs.astype(dtype)
236
218
  elif np.issubdtype(dtype, np.unsignedinteger) and np.issubdtype(
237
219
  dtype_raw, np.floating
238
220
  ):
239
- sclar_factor = UINT_MAX[dtype] # type: ignore
240
- imgs *= (sclar_factor * imgs).astype(dtype)
221
+ scalar_factor = UINT_MAX[dtype]
222
+ imgs *= (scalar_factor * imgs).astype(dtype)
241
223
  else:
242
224
  imgs = imgs.astype(dtype)
243
225
 
@@ -281,29 +263,48 @@ class NrrdImageStack(NDArrayImageStack[ScalarType]):
281
263
  self.header = header
282
264
 
283
265
 
284
- class V3dImageStack(NDArrayImageStack[ScalarType]):
285
- """v3d image stack."""
266
+ if sys.version_info < (3, 13):
267
+ from v3dpy.loaders import PBD, Raw
286
268
 
287
- def __init__(
288
- self, fname: str, loader: Raw | PBD, *, dtype: ScalarType, **kwargs
289
- ) -> None:
290
- r = loader()
291
- imgs = r.load(fname)
292
- super().__init__(imgs, dtype=dtype, **kwargs)
269
+ class V3dImageStack(NDArrayImageStack[ScalarType]):
270
+ """v3d image stack."""
293
271
 
272
+ def __init_subclass__(cls, loader: Raw | PBD) -> None:
273
+ super().__init_subclass__()
274
+ cls._loader = loader
294
275
 
295
- class V3drawImageStack(V3dImageStack[ScalarType]):
296
- """v3draw image stack."""
276
+ def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
277
+ r = self._loader()
278
+ imgs = r.load(fname)
279
+ super().__init__(imgs, dtype=dtype, **kwargs)
297
280
 
298
- def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
299
- super().__init__(fname, loader=Raw, dtype=dtype, **kwargs)
281
+ class V3drawImageStack(V3dImageStack[ScalarType], loader=Raw):
282
+ """v3draw image stack."""
300
283
 
284
+ class V3dpbdImageStack(V3dImageStack[ScalarType], loader=PBD):
285
+ """v3dpbd image stack."""
301
286
 
302
- class V3dpbdImageStack(V3dImageStack[ScalarType]):
303
- """v3dpbd image stack."""
287
+ else:
304
288
 
305
- def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
306
- super().__init__(fname, loader=PBD, dtype=dtype, **kwargs)
289
+ def _v3d_not_supported(*args, **kwargs):
290
+ raise RuntimeError(
291
+ "v3d-py-helper is not supported in Python 3.13 or newer. See #33"
292
+ )
293
+
294
+ class V3dImageStack:
295
+ """v3d image stack (disabled due to unsupported Python version)."""
296
+
297
+ __init__ = _v3d_not_supported
298
+
299
+ class V3drawImageStack:
300
+ """v3draw image stack (disabled due to unsupported Python version)."""
301
+
302
+ __init__ = _v3d_not_supported
303
+
304
+ class V3dpbdImageStack:
305
+ """v3dpbd image stack (disabled due to unsupported Python version)."""
306
+
307
+ __init__ = _v3d_not_supported
307
308
 
308
309
 
309
310
  class TeraflyImageStack(ImageStack[ScalarType]):
@@ -312,21 +313,17 @@ class TeraflyImageStack(ImageStack[ScalarType]):
312
313
  TeraFly is a terabytes of multidimensional volumetric images file
313
314
  format as described in [1]_.
314
315
 
315
- References
316
- ----------
317
- .. [1] Bria, Alessandro, Giulio Iannello, Leonardo Onofri, and
318
- Hanchuan Peng. “TeraFly: Real-Time Three-Dimensional
319
- Visualization and Annotation of Terabytes of Multidimensional
320
- Volumetric Images.” Nature Methods 13,
321
- no. 3 (March 2016): 192-94. https://doi.org/10.1038/nmeth.3767.
322
-
323
- Notes
324
- -----
325
- Terafly and Vaa3d use a especial right-handed coordinate system
316
+ NOTE: Terafly and Vaa3d use a especial right-handed coordinate system
326
317
  (with origin point in the left-top and z-axis points front), but we
327
- flip y-axis to makes it a left-handed coordinate system (with orgin
318
+ flip y-axis to makes it a left-handed coordinate system (with origin
328
319
  point in the left-bottom and z-axis points front). If you need to
329
320
  use its coordinate system, remember to FLIP Y-AXIS BACK.
321
+
322
+ References:
323
+ .. [1] Bria, Alessandro, Giulio Iannello, Leonardo Onofri, and Hanchuan Peng.
324
+ “TeraFly: Real-Time Three-Dimensional Visualization and Annotation of Terabytes
325
+ of Multidimensional Volumetric Images.” Nature Methods 13, no. 3 (March 2016):
326
+ 192-94. https://doi.org/10.1038/nmeth.3767.
330
327
  """
331
328
 
332
329
  _listdir: Callable[[str], list[str]]
@@ -336,17 +333,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
336
333
  self, root: str, *, dtype: ScalarType, lru_maxsize: int | None = 128
337
334
  ) -> None:
338
335
  r"""
339
- Parameters
340
- ----------
341
- root : str
342
- The root of terafly which contains directories named as
343
- `RES(YxXxZ)`.
344
- dtype : np.dtype
345
- lru_maxsize : int or None, default to 128
346
- Forwarding to `functools.lru_cache`. A decompressed array
347
- size of (256, 256, 256, 1), which is the typical size of
348
- terafly image stack, takes about 256 * 256 * 256 * 1 *
349
- 4B = 64MB. A cache size of 128 requires about 8GB memeory.
336
+ Args:
337
+ root: The root of terafly which contains directories named as `RES(YxXxZ)`.
338
+ dtype: np.dtype
339
+ lru_maxsize: Forwarding to `functools.lru_cache`.
340
+ A decompressed array size of (256, 256, 256, 1), which is the typical
341
+ size of terafly image stack, takes about 256 * 256 * 256 * 1 * 4B = 64MB.
342
+ A cache size of 128 requires about 8GB memory.
350
343
  """
351
344
 
352
345
  super().__init__()
@@ -372,11 +365,8 @@ class TeraflyImageStack(ImageStack[ScalarType]):
372
365
  def __getitem__(self, key):
373
366
  """Get images in max resolution.
374
367
 
375
- Examples
376
- --------
377
- ```python
378
- imgs[0, 0, 0, 0] # get value
379
- imgs[0:64, 0:64, 0:64, :] # get patch
368
+ >>> imgs[0, 0, 0, 0] # get value # doctest: +SKIP
369
+ >>> imgs[0:64, 0:64, 0:64, :] # get patch # doctest: +SKIP
380
370
  ```
381
371
  """
382
372
  if not isinstance(key, tuple):
@@ -399,9 +389,8 @@ class TeraflyImageStack(ImageStack[ScalarType]):
399
389
  ) -> npt.NDArray[ScalarType]:
400
390
  """Get patch of image stack.
401
391
 
402
- Returns
403
- -------
404
- patch : array of shape (X, Y, Z, C)
392
+ Returns:
393
+ patch: array of shape (X, Y, Z, C)
405
394
  """
406
395
  if isinstance(strides, int):
407
396
  strides = (strides, strides, strides)
@@ -421,10 +410,9 @@ class TeraflyImageStack(ImageStack[ScalarType]):
421
410
  def find_correspond_imgs(self, p, res_level=-1):
422
411
  """Find the image which contain this point.
423
412
 
424
- Returns
425
- -------
426
- patch : array of shape (X, Y, Z, C)
427
- patch_offset : (int, int, int)
413
+ Returns:
414
+ patch: array of shape (X, Y, Z, C)
415
+ patch_offset: (int, int, int)
428
416
  """
429
417
  p = np.array(p)
430
418
  self._check_params(res_level, p)
@@ -442,14 +430,10 @@ class TeraflyImageStack(ImageStack[ScalarType]):
442
430
  def get_resolutions(cls, root: str) -> tuple[list[Vec3i], list[str], list[Vec3i]]:
443
431
  """Get all resolutions.
444
432
 
445
- Returns
446
- -------
447
- resolutions : List of (int, int, int)
448
- Sequence of sorted resolutions (from small to large).
449
- roots : list[str]
450
- Sequence of root of resolutions respectively.
451
- patch_sizes : List of (int, int, int)
452
- Sequence of patch size of resolutions respectively.
433
+ Returns:
434
+ resolutions: Sequence of sorted resolutions (from small to large).
435
+ roots: Sequence of root of resolutions respectively.
436
+ patch_sizes: Sequence of patch size of resolutions respectively.
453
437
  """
454
438
 
455
439
  roots = list(cls.get_resolution_dirs(root))
@@ -497,13 +481,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
497
481
 
498
482
  res = self.res[res_level]
499
483
  for p in coords:
500
- assert np.less(
501
- [0, 0, 0], p
502
- ).all(), f"indices ({p[0]}, {p[1]}, {p[2]}) out of range (0, 0, 0)"
484
+ assert np.less([0, 0, 0], p).all(), (
485
+ f"indices ({p[0]}, {p[1]}, {p[2]}) out of range (0, 0, 0)"
486
+ )
503
487
 
504
- assert np.greater(
505
- res, p
506
- ).all(), f"indices ({p[0]}, {p[1]}, {p[2]}) out of range ({res[0]}, {res[1]}, {res[2]})"
488
+ assert np.greater(res, p).all(), (
489
+ f"indices ({p[0]}, {p[1]}, {p[2]}) out of range ({res[0]}, {res[1]}, {res[2]})"
490
+ )
507
491
 
508
492
  def _get_range(self, starts, ends, res_level, out):
509
493
  # pylint: disable=too-many-locals
@@ -529,13 +513,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
529
513
  if shape[1] > lens[1]:
530
514
  starts_y = starts + [0, lens[1], 0]
531
515
  ends_y = np.array([starts[0], ends[1], ends[2]])
532
- ends_y += [min(shape[0], lens[0]), 0, 0] # type: ignore
516
+ ends_y += [min(shape[0], lens[0]), 0, 0]
533
517
  self._get_range(starts_y, ends_y, res_level, out[:, lens[1] :, :])
534
518
 
535
519
  if shape[2] > lens[2]:
536
520
  starts_z = starts + [0, 0, lens[2]]
537
521
  ends_z = np.array([starts[0], starts[1], ends[2]])
538
- ends_z += [min(shape[0], lens[0]), min(shape[1], lens[1]), 0] # type: ignore
522
+ ends_z += [min(shape[0], lens[0]), min(shape[1], lens[1]), 0]
539
523
  self._get_range(starts_z, ends_z, res_level, out[:, :, lens[2] :])
540
524
 
541
525
  def _find_correspond_imgs(self, p, res_level):
@@ -580,14 +564,14 @@ class GrayImageStack:
580
564
  def __init__(self, imgs: ImageStack) -> None:
581
565
  self.imgs = imgs
582
566
 
583
- # fmt: off
584
567
  @overload
585
568
  def __getitem__(self, key: Vec3i) -> np.float32: ...
586
569
  @overload
587
570
  def __getitem__(self, key: npt.NDArray[np.integer[Any]]) -> np.float32: ...
588
571
  @overload
589
- def __getitem__(self, key: slice | tuple[slice, slice] | tuple[slice, slice, slice]) -> npt.NDArray[np.float32]: ...
590
- # fmt: on
572
+ def __getitem__(
573
+ self, key: slice | tuple[slice, slice] | tuple[slice, slice, slice]
574
+ ) -> npt.NDArray[np.float32]: ...
591
575
  def __getitem__(self, key):
592
576
  """Get pixel/patch of image stack."""
593
577
  v = self[key]
@@ -601,14 +585,12 @@ class GrayImageStack:
601
585
  return v[:, 0]
602
586
  if v.ndim == 1:
603
587
  return v[0]
604
- raise ValueError("unsupport key")
588
+ raise ValueError("unsupported key")
605
589
 
606
590
  def get_full(self) -> npt.NDArray[np.float32]:
607
591
  """Get full image stack.
608
592
 
609
- Notes
610
- -----
611
- this will load the full image stack into memory.
593
+ NOTE: this will load the full image stack into memory.
612
594
  """
613
595
  return self.imgs.get_full()[:, :, :, 0]
614
596