swcgeom 0.18.3__py3-none-any.whl → 0.19.0__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 +80 -121
  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.0.dist-info}/METADATA +5 -4
  58. swcgeom-0.19.0.dist-info/RECORD +67 -0
  59. {swcgeom-0.18.3.dist-info → swcgeom-0.19.0.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.0.dist-info/licenses}/LICENSE +0 -0
  62. {swcgeom-0.18.3.dist-info → swcgeom-0.19.0.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
@@ -21,7 +21,7 @@ import warnings
21
21
  from abc import ABC, abstractmethod
22
22
  from collections.abc import Callable, Iterable
23
23
  from functools import cache, lru_cache
24
- from typing import Any, Generic, Literal, Optional, TypeVar, cast, overload
24
+ from typing import Any, Generic, Literal, TypeVar, cast, overload
25
25
 
26
26
  import nrrd
27
27
  import numpy as np
@@ -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
 
@@ -284,27 +266,23 @@ class NrrdImageStack(NDArrayImageStack[ScalarType]):
284
266
  class V3dImageStack(NDArrayImageStack[ScalarType]):
285
267
  """v3d image stack."""
286
268
 
287
- def __init__(
288
- self, fname: str, loader: Raw | PBD, *, dtype: ScalarType, **kwargs
289
- ) -> None:
290
- r = loader()
269
+ def __init_subclass__(cls, loader: Raw | PBD) -> None:
270
+ super().__init_subclass__()
271
+ cls._loader = loader
272
+
273
+ def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
274
+ r = self._loader()
291
275
  imgs = r.load(fname)
292
276
  super().__init__(imgs, dtype=dtype, **kwargs)
293
277
 
294
278
 
295
- class V3drawImageStack(V3dImageStack[ScalarType]):
279
+ class V3drawImageStack(V3dImageStack[ScalarType], loader=Raw):
296
280
  """v3draw image stack."""
297
281
 
298
- def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
299
- super().__init__(fname, loader=Raw, dtype=dtype, **kwargs)
300
-
301
282
 
302
- class V3dpbdImageStack(V3dImageStack[ScalarType]):
283
+ class V3dpbdImageStack(V3dImageStack[ScalarType], loader=PBD):
303
284
  """v3dpbd image stack."""
304
285
 
305
- def __init__(self, fname: str, *, dtype: ScalarType, **kwargs) -> None:
306
- super().__init__(fname, loader=PBD, dtype=dtype, **kwargs)
307
-
308
286
 
309
287
  class TeraflyImageStack(ImageStack[ScalarType]):
310
288
  """TeraFly image stack.
@@ -312,21 +290,17 @@ class TeraflyImageStack(ImageStack[ScalarType]):
312
290
  TeraFly is a terabytes of multidimensional volumetric images file
313
291
  format as described in [1]_.
314
292
 
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
293
+ NOTE: Terafly and Vaa3d use a especial right-handed coordinate system
326
294
  (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
295
+ flip y-axis to makes it a left-handed coordinate system (with origin
328
296
  point in the left-bottom and z-axis points front). If you need to
329
297
  use its coordinate system, remember to FLIP Y-AXIS BACK.
298
+
299
+ References:
300
+ .. [1] Bria, Alessandro, Giulio Iannello, Leonardo Onofri, and Hanchuan Peng.
301
+ “TeraFly: Real-Time Three-Dimensional Visualization and Annotation of Terabytes
302
+ of Multidimensional Volumetric Images.” Nature Methods 13, no. 3 (March 2016):
303
+ 192-94. https://doi.org/10.1038/nmeth.3767.
330
304
  """
331
305
 
332
306
  _listdir: Callable[[str], list[str]]
@@ -336,17 +310,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
336
310
  self, root: str, *, dtype: ScalarType, lru_maxsize: int | None = 128
337
311
  ) -> None:
338
312
  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.
313
+ Args:
314
+ root: The root of terafly which contains directories named as `RES(YxXxZ)`.
315
+ dtype: np.dtype
316
+ lru_maxsize: Forwarding to `functools.lru_cache`.
317
+ A decompressed array size of (256, 256, 256, 1), which is the typical
318
+ size of terafly image stack, takes about 256 * 256 * 256 * 1 * 4B = 64MB.
319
+ A cache size of 128 requires about 8GB memory.
350
320
  """
351
321
 
352
322
  super().__init__()
@@ -372,11 +342,8 @@ class TeraflyImageStack(ImageStack[ScalarType]):
372
342
  def __getitem__(self, key):
373
343
  """Get images in max resolution.
374
344
 
375
- Examples
376
- --------
377
- ```python
378
- imgs[0, 0, 0, 0] # get value
379
- imgs[0:64, 0:64, 0:64, :] # get patch
345
+ >>> imgs[0, 0, 0, 0] # get value # doctest: +SKIP
346
+ >>> imgs[0:64, 0:64, 0:64, :] # get patch # doctest: +SKIP
380
347
  ```
381
348
  """
382
349
  if not isinstance(key, tuple):
@@ -399,9 +366,8 @@ class TeraflyImageStack(ImageStack[ScalarType]):
399
366
  ) -> npt.NDArray[ScalarType]:
400
367
  """Get patch of image stack.
401
368
 
402
- Returns
403
- -------
404
- patch : array of shape (X, Y, Z, C)
369
+ Returns:
370
+ patch: array of shape (X, Y, Z, C)
405
371
  """
406
372
  if isinstance(strides, int):
407
373
  strides = (strides, strides, strides)
@@ -421,10 +387,9 @@ class TeraflyImageStack(ImageStack[ScalarType]):
421
387
  def find_correspond_imgs(self, p, res_level=-1):
422
388
  """Find the image which contain this point.
423
389
 
424
- Returns
425
- -------
426
- patch : array of shape (X, Y, Z, C)
427
- patch_offset : (int, int, int)
390
+ Returns:
391
+ patch: array of shape (X, Y, Z, C)
392
+ patch_offset: (int, int, int)
428
393
  """
429
394
  p = np.array(p)
430
395
  self._check_params(res_level, p)
@@ -442,14 +407,10 @@ class TeraflyImageStack(ImageStack[ScalarType]):
442
407
  def get_resolutions(cls, root: str) -> tuple[list[Vec3i], list[str], list[Vec3i]]:
443
408
  """Get all resolutions.
444
409
 
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.
410
+ Returns:
411
+ resolutions: Sequence of sorted resolutions (from small to large).
412
+ roots: Sequence of root of resolutions respectively.
413
+ patch_sizes: Sequence of patch size of resolutions respectively.
453
414
  """
454
415
 
455
416
  roots = list(cls.get_resolution_dirs(root))
@@ -497,13 +458,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
497
458
 
498
459
  res = self.res[res_level]
499
460
  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)"
461
+ assert np.less([0, 0, 0], p).all(), (
462
+ f"indices ({p[0]}, {p[1]}, {p[2]}) out of range (0, 0, 0)"
463
+ )
503
464
 
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]})"
465
+ assert np.greater(res, p).all(), (
466
+ f"indices ({p[0]}, {p[1]}, {p[2]}) out of range ({res[0]}, {res[1]}, {res[2]})"
467
+ )
507
468
 
508
469
  def _get_range(self, starts, ends, res_level, out):
509
470
  # pylint: disable=too-many-locals
@@ -529,13 +490,13 @@ class TeraflyImageStack(ImageStack[ScalarType]):
529
490
  if shape[1] > lens[1]:
530
491
  starts_y = starts + [0, lens[1], 0]
531
492
  ends_y = np.array([starts[0], ends[1], ends[2]])
532
- ends_y += [min(shape[0], lens[0]), 0, 0] # type: ignore
493
+ ends_y += [min(shape[0], lens[0]), 0, 0]
533
494
  self._get_range(starts_y, ends_y, res_level, out[:, lens[1] :, :])
534
495
 
535
496
  if shape[2] > lens[2]:
536
497
  starts_z = starts + [0, 0, lens[2]]
537
498
  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
499
+ ends_z += [min(shape[0], lens[0]), min(shape[1], lens[1]), 0]
539
500
  self._get_range(starts_z, ends_z, res_level, out[:, :, lens[2] :])
540
501
 
541
502
  def _find_correspond_imgs(self, p, res_level):
@@ -580,14 +541,14 @@ class GrayImageStack:
580
541
  def __init__(self, imgs: ImageStack) -> None:
581
542
  self.imgs = imgs
582
543
 
583
- # fmt: off
584
544
  @overload
585
545
  def __getitem__(self, key: Vec3i) -> np.float32: ...
586
546
  @overload
587
547
  def __getitem__(self, key: npt.NDArray[np.integer[Any]]) -> np.float32: ...
588
548
  @overload
589
- def __getitem__(self, key: slice | tuple[slice, slice] | tuple[slice, slice, slice]) -> npt.NDArray[np.float32]: ...
590
- # fmt: on
549
+ def __getitem__(
550
+ self, key: slice | tuple[slice, slice] | tuple[slice, slice, slice]
551
+ ) -> npt.NDArray[np.float32]: ...
591
552
  def __getitem__(self, key):
592
553
  """Get pixel/patch of image stack."""
593
554
  v = self[key]
@@ -601,14 +562,12 @@ class GrayImageStack:
601
562
  return v[:, 0]
602
563
  if v.ndim == 1:
603
564
  return v[0]
604
- raise ValueError("unsupport key")
565
+ raise ValueError("unsupported key")
605
566
 
606
567
  def get_full(self) -> npt.NDArray[np.float32]:
607
568
  """Get full image stack.
608
569
 
609
- Notes
610
- -----
611
- this will load the full image stack into memory.
570
+ NOTE: this will load the full image stack into memory.
612
571
  """
613
572
  return self.imgs.get_full()[:, :, :, 0]
614
573