neuralib-imaging 0.7.0__tar.gz
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.
- neuralib_imaging-0.7.0/LICENSE +28 -0
- neuralib_imaging-0.7.0/PKG-INFO +40 -0
- neuralib_imaging-0.7.0/pyproject.toml +51 -0
- neuralib_imaging-0.7.0/setup.cfg +4 -0
- neuralib_imaging-0.7.0/src/neuralib/registration/__init__.py +1 -0
- neuralib_imaging-0.7.0/src/neuralib/registration/coordinates.py +242 -0
- neuralib_imaging-0.7.0/src/neuralib/spikes/__init__.py +2 -0
- neuralib_imaging-0.7.0/src/neuralib/spikes/cascade.py +572 -0
- neuralib_imaging-0.7.0/src/neuralib/spikes/oasis.py +115 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/__init__.py +3 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/align.py +53 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/fft.py +103 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/fft_view.py +158 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/main_app.py +30 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/plot.py +50 -0
- neuralib_imaging-0.7.0/src/neuralib/widefield/svd.py +70 -0
- neuralib_imaging-0.7.0/src/neuralib_imaging.egg-info/PKG-INFO +40 -0
- neuralib_imaging-0.7.0/src/neuralib_imaging.egg-info/SOURCES.txt +22 -0
- neuralib_imaging-0.7.0/src/neuralib_imaging.egg-info/dependency_links.txt +1 -0
- neuralib_imaging-0.7.0/src/neuralib_imaging.egg-info/requires.txt +21 -0
- neuralib_imaging-0.7.0/src/neuralib_imaging.egg-info/top_level.txt +1 -0
- neuralib_imaging-0.7.0/test/test_registration.py +49 -0
- neuralib_imaging-0.7.0/test/test_spikes.py +26 -0
- neuralib_imaging-0.7.0/test/test_wfield.py +23 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, YT.WEI
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: neuralib-imaging
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Tools for cellular imaging and widefield imaging
|
|
5
|
+
Author-email: Yu-Ting Wei <ytsimon2004@gmail.com>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/ytsimon2004/neuralib2
|
|
8
|
+
Project-URL: Documentation, https://neuralib2.readthedocs.io/en/latest/index.html
|
|
9
|
+
Project-URL: Repository, https://github.com/ytsimon2004/neuralib2
|
|
10
|
+
Project-URL: Issues, https://github.com/ytsimon2004/neuralib2/issues
|
|
11
|
+
Keywords: Parser,Segmentation,Morphology
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: <3.13,>=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: neuralib-utils[all]>=0.7.0
|
|
24
|
+
Requires-Dist: numpy
|
|
25
|
+
Requires-Dist: numba
|
|
26
|
+
Requires-Dist: matplotlib
|
|
27
|
+
Provides-Extra: cascade
|
|
28
|
+
Requires-Dist: ruamel.yaml; extra == "cascade"
|
|
29
|
+
Requires-Dist: tensorflow; extra == "cascade"
|
|
30
|
+
Provides-Extra: widefield
|
|
31
|
+
Requires-Dist: napari; extra == "widefield"
|
|
32
|
+
Requires-Dist: bokeh; extra == "widefield"
|
|
33
|
+
Requires-Dist: scikit-learn; extra == "widefield"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: neuralib-imaging[cascade]; extra == "all"
|
|
36
|
+
Requires-Dist: neuralib-imaging[widefield]; extra == "all"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: neuralib-imaging[all]; extra == "test"
|
|
39
|
+
Requires-Dist: neuralib-parser[all]; extra == "test"
|
|
40
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "neuralib-imaging"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
requires-python = ">=3.11,<3.13"
|
|
5
|
+
description = "Tools for cellular imaging and widefield imaging"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Yu-Ting Wei", email = "ytsimon2004@gmail.com" },
|
|
8
|
+
]
|
|
9
|
+
license = { text = "BSD-3-Clause" }
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
keywords = ["Parser", "Segmentation", "Morphology"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3.11",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
"License :: OSI Approved :: BSD License",
|
|
16
|
+
"Natural Language :: English",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Scientific/Engineering",
|
|
19
|
+
'Intended Audience :: Science/Research',
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
dependencies = [
|
|
24
|
+
"neuralib-utils[all]>=0.7.0",
|
|
25
|
+
"numpy",
|
|
26
|
+
"numba",
|
|
27
|
+
"matplotlib",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
|
|
32
|
+
cascade = ["ruamel.yaml", "tensorflow"]
|
|
33
|
+
widefield = ["napari", "bokeh", "scikit-learn"]
|
|
34
|
+
|
|
35
|
+
all = ["neuralib-imaging[cascade]", "neuralib-imaging[widefield]"]
|
|
36
|
+
|
|
37
|
+
test = ["neuralib-imaging[all]", "neuralib-parser[all]"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/ytsimon2004/neuralib2"
|
|
42
|
+
Documentation = "https://neuralib2.readthedocs.io/en/latest/index.html"
|
|
43
|
+
Repository = "https://github.com/ytsimon2004/neuralib2"
|
|
44
|
+
Issues = "https://github.com/ytsimon2004/neuralib2/issues"
|
|
45
|
+
|
|
46
|
+
[build-system]
|
|
47
|
+
build-backend = "setuptools.build_meta"
|
|
48
|
+
requires = ["setuptools>=61.0"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .coordinates import *
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import attrs
|
|
4
|
+
import numpy as np
|
|
5
|
+
from matplotlib.patches import Polygon
|
|
6
|
+
from typing import Sequence, Literal, Self
|
|
7
|
+
|
|
8
|
+
from neuralib.util.unstable import unstable
|
|
9
|
+
|
|
10
|
+
__all__ = ['get_field_of_view',
|
|
11
|
+
'get_cellular_coordinate',
|
|
12
|
+
'FieldOfView',
|
|
13
|
+
'CellularCoordinates']
|
|
14
|
+
|
|
15
|
+
UNIT = Literal['mm', 'um']
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_field_of_view(am: Sequence[float],
|
|
19
|
+
pm: Sequence[float],
|
|
20
|
+
pl: Sequence[float],
|
|
21
|
+
al: Sequence[float],
|
|
22
|
+
*,
|
|
23
|
+
rotation_ml: float = 0,
|
|
24
|
+
rotation_ap: float = 0,
|
|
25
|
+
unit: UNIT = 'mm',
|
|
26
|
+
perpendicular: bool = True,
|
|
27
|
+
region_name: str | None = None) -> FieldOfView:
|
|
28
|
+
"""
|
|
29
|
+
Construct a :class:`~FieldOfView` from four corner coordinates.
|
|
30
|
+
|
|
31
|
+
:param am: anteromedial corner coordinate [x, y] in mm or µm.
|
|
32
|
+
:param pm: posteromedial corner coordinate [x, y] in mm or µm.
|
|
33
|
+
:param pl: posterolateral corner coordinate [x, y] in mm or µm.
|
|
34
|
+
:param al: anterolateral corner coordinate [x, y] in mm or µm.
|
|
35
|
+
:param rotation_ml: in-plane rotation (CCW) around the ML axis, in degrees.
|
|
36
|
+
:param rotation_ap: tilt around the AP axis (foreshortening), in degrees.
|
|
37
|
+
:param unit: unit of the corners value
|
|
38
|
+
:param perpendicular: imaging objective is perpendicular to the cranial windows. if True, skip rotation and tilt transforms.
|
|
39
|
+
:param region_name: optional identifier for this FOV region.
|
|
40
|
+
:returns: FieldOfView instance with corners stacked and optionally transformed.
|
|
41
|
+
"""
|
|
42
|
+
corners = np.vstack([am, pm, pl, al])
|
|
43
|
+
return FieldOfView(corners, rotation_ml, rotation_ap, unit,
|
|
44
|
+
perpendicular=perpendicular,
|
|
45
|
+
region_name=region_name)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _validator_corners(instance, attribute, value: np.ndarray):
|
|
49
|
+
if not isinstance(value, np.ndarray):
|
|
50
|
+
raise TypeError(f'{attribute} should be a numpy array')
|
|
51
|
+
if value.shape != (4, 2):
|
|
52
|
+
raise ValueError('')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@unstable()
|
|
56
|
+
def _rotate_and_tilt(pts: np.ndarray, ml: float, ap: float) -> np.ndarray:
|
|
57
|
+
"""telecentric / orthographic imaging transformation"""
|
|
58
|
+
pivot = pts.mean(axis=0)
|
|
59
|
+
theta_x = np.deg2rad(ml)
|
|
60
|
+
c, s = np.cos(theta_x), np.sin(theta_x)
|
|
61
|
+
R = np.array([[c, -s], [s, c]])
|
|
62
|
+
pts0 = (pts - pivot) @ R.T + pivot
|
|
63
|
+
theta_y = np.deg2rad(ap)
|
|
64
|
+
scale_x = np.cos(theta_y)
|
|
65
|
+
pts1 = (pts0 - pivot) * np.array([scale_x, 1.0]) + pivot
|
|
66
|
+
|
|
67
|
+
return pts1
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@attrs.frozen
|
|
71
|
+
class FieldOfView:
|
|
72
|
+
"""Field‑Of‑View defined by its four XY corners"""
|
|
73
|
+
|
|
74
|
+
corners: np.ndarray = attrs.field(validator=_validator_corners)
|
|
75
|
+
"""corner coordinates in order [AM, PM, PL, AL]. `Array[float, [4, 2]]`"""
|
|
76
|
+
rotation_ml: float = attrs.field(default=0.0)
|
|
77
|
+
"""in-plane rotation (CCW) around the ML axis, in degrees"""
|
|
78
|
+
rotation_ap: float = attrs.field(default=0.0)
|
|
79
|
+
"""tilt around the AP axis (foreshortening), in degrees"""
|
|
80
|
+
unit: UNIT = attrs.field(default='mm', validator=attrs.validators.in_(['mm', 'um']))
|
|
81
|
+
"""unit of the corners value"""
|
|
82
|
+
perpendicular: bool = attrs.field(default=True, kw_only=True)
|
|
83
|
+
"""imaging objective is perpendicular to the cranial windows. if True, skip rotation and tilt transforms"""
|
|
84
|
+
region_name: str | None = attrs.field(default=None, kw_only=True)
|
|
85
|
+
"""optional identifier for this FOV region"""
|
|
86
|
+
|
|
87
|
+
def __attrs_post_init__(self):
|
|
88
|
+
if not self.perpendicular:
|
|
89
|
+
new_corners = _rotate_and_tilt(
|
|
90
|
+
self.corners,
|
|
91
|
+
self.rotation_ml,
|
|
92
|
+
self.rotation_ap,
|
|
93
|
+
)
|
|
94
|
+
object.__setattr__(self, 'corners', new_corners)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def am(self) -> np.ndarray:
|
|
98
|
+
"""anteromedial corner coordinate [x, y] in mm or µm."""
|
|
99
|
+
return self.corners[0]
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def pm(self) -> np.ndarray:
|
|
103
|
+
"""posteromedial corner coordinate [x, y] in mm or µm"""
|
|
104
|
+
return self.corners[1]
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def pl(self) -> np.ndarray:
|
|
108
|
+
"""posterolateral corner coordinate [x, y] in mm or µm"""
|
|
109
|
+
return self.corners[2]
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def al(self) -> np.ndarray:
|
|
113
|
+
"""anterolateral corner coordinate [x, y] in mm or µm"""
|
|
114
|
+
return self.corners[3]
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def ap_distance(self) -> float:
|
|
118
|
+
"""span along the AP axis"""
|
|
119
|
+
return float(np.ptp(self.corners[:, 1]))
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def ml_distance(self) -> float:
|
|
123
|
+
"""span along the ML axis"""
|
|
124
|
+
return float(np.ptp(self.corners[:, 0]))
|
|
125
|
+
|
|
126
|
+
def invert_axes(self, ap: bool = True, ml: bool = True) -> Self:
|
|
127
|
+
"""Return a new FOV with specified axes inverted
|
|
128
|
+
|
|
129
|
+
:param ap: invert anterior-posterior (Y) axis if True.
|
|
130
|
+
:param ml: invert medial-lateral (X) axis if True.
|
|
131
|
+
"""
|
|
132
|
+
factors = np.array([-1. if ml else 1., -1. if ap else 1.])
|
|
133
|
+
return attrs.evolve(self, corners=self.corners * factors)
|
|
134
|
+
|
|
135
|
+
def to_um(self) -> Self:
|
|
136
|
+
if self.unit == 'um':
|
|
137
|
+
raise RuntimeError('unit already in um')
|
|
138
|
+
return attrs.evolve(self, corners=self.corners * 1000, unit='um')
|
|
139
|
+
|
|
140
|
+
def to_polygon(self, **kwargs) -> Polygon:
|
|
141
|
+
"""convert corners to a matplotlib Polygon patch"""
|
|
142
|
+
idx = [0, 1, 3, 2]
|
|
143
|
+
reorder = self.corners[idx]
|
|
144
|
+
return Polygon(reorder, closed=True, edgecolor='r', facecolor='none', **kwargs)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_cellular_coordinate(neuron_idx: np.ndarray,
|
|
148
|
+
ap: np.ndarray,
|
|
149
|
+
ml: np.ndarray, *,
|
|
150
|
+
unit: UNIT = 'mm',
|
|
151
|
+
plane_index: np.ndarray | None = None) -> CellularCoordinates:
|
|
152
|
+
"""
|
|
153
|
+
Get cellular coordinates container for doing brain mapping / topographical analysis
|
|
154
|
+
|
|
155
|
+
:param neuron_idx: neuron index. `Array[float, N]`
|
|
156
|
+
:param ap: anterior posterior coordinates. `Array[float, N]`
|
|
157
|
+
:param ml: medial lateral coordinates. `Array[float, N]`
|
|
158
|
+
:param unit: unit of the ap/ml value. default in `mm`
|
|
159
|
+
:param plane_index: neuron's corresponding image plane. `Array[float, N]`. If None then full_zero
|
|
160
|
+
:return: :class:`~CellularCoordinates`
|
|
161
|
+
"""
|
|
162
|
+
return CellularCoordinates(neuron_idx, ap, ml, unit, plane_index)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _validator_shape(instance: CellularCoordinates, attribute, value: np.ndarray | None):
|
|
166
|
+
if value is None:
|
|
167
|
+
return
|
|
168
|
+
else:
|
|
169
|
+
assert instance.neuron_idx.shape == value.shape
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@attrs.define
|
|
173
|
+
class CellularCoordinates:
|
|
174
|
+
"""Cellular Coordinates container"""
|
|
175
|
+
|
|
176
|
+
neuron_idx: np.ndarray
|
|
177
|
+
"""neuron index. `Array[float, N]`"""
|
|
178
|
+
ap: np.ndarray
|
|
179
|
+
"""anterior posterior coordinates (default in mm). `Array[float, N]`"""
|
|
180
|
+
ml: np.ndarray
|
|
181
|
+
"""medial lateral coordinates (default in mm). `Array[float, N]`"""
|
|
182
|
+
unit: UNIT = attrs.field(default='mm', validator=attrs.validators.in_(['mm', 'um']))
|
|
183
|
+
"""unit of the ap/ml value"""
|
|
184
|
+
plane_index: np.ndarray = attrs.field(default=None, validator=_validator_shape)
|
|
185
|
+
"""neuron's corresponding image plane. `Array[float, N]`"""
|
|
186
|
+
value: np.ndarray | None = attrs.field(default=None, validator=_validator_shape)
|
|
187
|
+
"""metric (i.e., used in topographical analysis). `Array[float, N]`"""
|
|
188
|
+
|
|
189
|
+
def __attrs_post_init__(self):
|
|
190
|
+
if self.plane_index is None:
|
|
191
|
+
self.plane_index = np.full_like(self.neuron_idx, 0, dtype=int)
|
|
192
|
+
|
|
193
|
+
assert self.neuron_idx.shape == self.ap.shape == self.ml.shape == self.plane_index.shape
|
|
194
|
+
|
|
195
|
+
def relative_origin(self, fov: FieldOfView,
|
|
196
|
+
origin: Literal['am', 'pm', 'al', 'pl'] = 'am') -> Self:
|
|
197
|
+
"""
|
|
198
|
+
coordinates relative to :class:`~FieldOfView` origin point
|
|
199
|
+
|
|
200
|
+
:param fov: :class:`~FieldOfView`
|
|
201
|
+
:param origin: relative origin point
|
|
202
|
+
:return:
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
factor = 1000 if self.unit == 'mm' else 1
|
|
206
|
+
ap_um = self.ap * factor
|
|
207
|
+
ml_um = self.ml * factor
|
|
208
|
+
if fov.unit != 'um':
|
|
209
|
+
fov = fov.to_um()
|
|
210
|
+
|
|
211
|
+
orig_pt = getattr(fov, origin)
|
|
212
|
+
pts = np.vstack([ml_um, ap_um]).T
|
|
213
|
+
|
|
214
|
+
if fov.perpendicular:
|
|
215
|
+
delta = orig_pt - pts # posterior to origin
|
|
216
|
+
else:
|
|
217
|
+
delta_pts = orig_pt - _rotate_and_tilt(
|
|
218
|
+
pts,
|
|
219
|
+
ml=fov.rotation_ml,
|
|
220
|
+
ap=fov.rotation_ap,
|
|
221
|
+
)
|
|
222
|
+
delta = delta_pts
|
|
223
|
+
|
|
224
|
+
ml_new, ap_new = delta[:, 0], delta[:, 1]
|
|
225
|
+
return attrs.evolve(self, ml=ml_new, ap=ap_new, unit='um')
|
|
226
|
+
|
|
227
|
+
def with_value(self, value: np.ndarray) -> Self:
|
|
228
|
+
"""assign value foreach neuron"""
|
|
229
|
+
return attrs.evolve(self, value=value)
|
|
230
|
+
|
|
231
|
+
def with_masking(self, mask: np.ndarray) -> Self:
|
|
232
|
+
"""do neuronal selection by bool masking
|
|
233
|
+
:param mask: `Array[bool, N]`
|
|
234
|
+
"""
|
|
235
|
+
return attrs.evolve(
|
|
236
|
+
self,
|
|
237
|
+
neuron_idx=self.neuron_idx[mask],
|
|
238
|
+
ap=self.ap[mask],
|
|
239
|
+
ml=self.ml[mask],
|
|
240
|
+
plane_index=self.plane_index[mask],
|
|
241
|
+
value=None if self.value is None else self.value[mask]
|
|
242
|
+
)
|