ngio 0.4.0a3__py3-none-any.whl → 0.4.0a4__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.
- ngio/__init__.py +1 -2
- ngio/common/__init__.py +2 -51
- ngio/common/_dimensions.py +223 -64
- ngio/common/_pyramid.py +42 -23
- ngio/common/_roi.py +47 -411
- ngio/common/_zoom.py +32 -7
- ngio/experimental/iterators/_abstract_iterator.py +2 -2
- ngio/experimental/iterators/_feature.py +9 -14
- ngio/experimental/iterators/_image_processing.py +17 -27
- ngio/experimental/iterators/_rois_utils.py +4 -4
- ngio/experimental/iterators/_segmentation.py +37 -53
- ngio/images/_abstract_image.py +135 -93
- ngio/images/_create.py +16 -0
- ngio/images/_create_synt_container.py +10 -0
- ngio/images/_image.py +33 -9
- ngio/images/_label.py +24 -3
- ngio/images/_masked_image.py +60 -81
- ngio/images/_ome_zarr_container.py +33 -0
- ngio/io_pipes/__init__.py +49 -0
- ngio/io_pipes/_io_pipes.py +286 -0
- ngio/io_pipes/_io_pipes_masked.py +481 -0
- ngio/io_pipes/_io_pipes_roi.py +143 -0
- ngio/io_pipes/_io_pipes_utils.py +299 -0
- ngio/io_pipes/_match_shape.py +376 -0
- ngio/io_pipes/_ops_axes.py +146 -0
- ngio/io_pipes/_ops_slices.py +218 -0
- ngio/io_pipes/_ops_transforms.py +104 -0
- ngio/io_pipes/_zoom_transform.py +175 -0
- ngio/ome_zarr_meta/__init__.py +6 -2
- ngio/ome_zarr_meta/ngio_specs/__init__.py +6 -4
- ngio/ome_zarr_meta/ngio_specs/_axes.py +182 -70
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +47 -121
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +30 -22
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +17 -1
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +33 -30
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
- ngio/resources/__init__.py +1 -0
- ngio/resources/resource_model.py +1 -0
- ngio/{common/transforms → transforms}/__init__.py +1 -1
- ngio/transforms/_zoom.py +19 -0
- ngio/utils/_zarr_utils.py +5 -1
- {ngio-0.4.0a3.dist-info → ngio-0.4.0a4.dist-info}/METADATA +1 -1
- ngio-0.4.0a4.dist-info/RECORD +83 -0
- ngio/common/_array_io_pipes.py +0 -554
- ngio/common/_array_io_utils.py +0 -508
- ngio/common/transforms/_label.py +0 -12
- ngio/common/transforms/_zoom.py +0 -109
- ngio-0.4.0a3.dist-info/RECORD +0 -76
- {ngio-0.4.0a3.dist-info → ngio-0.4.0a4.dist-info}/WHEEL +0 -0
- {ngio-0.4.0a3.dist-info → ngio-0.4.0a4.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,18 +3,14 @@ from collections.abc import Callable, Generator, Sequence
|
|
|
3
3
|
import dask.array as da
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
-
from ngio.common import
|
|
7
|
-
Roi,
|
|
8
|
-
TransformProtocol,
|
|
9
|
-
build_roi_dask_getter,
|
|
10
|
-
build_roi_numpy_getter,
|
|
11
|
-
)
|
|
6
|
+
from ngio.common import Roi
|
|
12
7
|
from ngio.experimental.iterators._abstract_iterator import AbstractIteratorBuilder
|
|
13
8
|
from ngio.images import Image, Label
|
|
14
9
|
from ngio.images._image import (
|
|
15
10
|
ChannelSlicingInputType,
|
|
16
11
|
add_channel_selection_to_slicing_dict,
|
|
17
12
|
)
|
|
13
|
+
from ngio.io_pipes import DaskRoiGetter, NumpyRoiGetter, TransformProtocol
|
|
18
14
|
|
|
19
15
|
|
|
20
16
|
class FeatureExtractorIterator(AbstractIteratorBuilder):
|
|
@@ -58,6 +54,9 @@ class FeatureExtractorIterator(AbstractIteratorBuilder):
|
|
|
58
54
|
self._input_transforms = input_transforms
|
|
59
55
|
self._label_transforms = label_transforms
|
|
60
56
|
|
|
57
|
+
self._input.require_axes_match(self._input_label)
|
|
58
|
+
self._input.require_can_be_rescaled(self._input_label)
|
|
59
|
+
|
|
61
60
|
def get_init_kwargs(self) -> dict:
|
|
62
61
|
"""Return the initialization arguments for the iterator."""
|
|
63
62
|
return {
|
|
@@ -70,21 +69,19 @@ class FeatureExtractorIterator(AbstractIteratorBuilder):
|
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
def build_numpy_getter(self, roi: Roi):
|
|
73
|
-
data_getter =
|
|
72
|
+
data_getter = NumpyRoiGetter(
|
|
74
73
|
zarr_array=self._input.zarr_array,
|
|
75
74
|
dimensions=self._input.dimensions,
|
|
76
75
|
axes_order=self._axes_order,
|
|
77
76
|
transforms=self._input_transforms,
|
|
78
|
-
pixel_size=self._input.pixel_size,
|
|
79
77
|
roi=roi,
|
|
80
78
|
slicing_dict=self._input_slicing_kwargs,
|
|
81
79
|
)
|
|
82
|
-
label_getter =
|
|
80
|
+
label_getter = NumpyRoiGetter(
|
|
83
81
|
zarr_array=self._input_label.zarr_array,
|
|
84
82
|
dimensions=self._input_label.dimensions,
|
|
85
83
|
axes_order=self._axes_order,
|
|
86
84
|
transforms=self._label_transforms,
|
|
87
|
-
pixel_size=self._input_label.pixel_size,
|
|
88
85
|
roi=roi,
|
|
89
86
|
remove_channel_selection=True,
|
|
90
87
|
)
|
|
@@ -94,21 +91,19 @@ class FeatureExtractorIterator(AbstractIteratorBuilder):
|
|
|
94
91
|
return None
|
|
95
92
|
|
|
96
93
|
def build_dask_getter(self, roi: Roi):
|
|
97
|
-
data_getter =
|
|
94
|
+
data_getter = DaskRoiGetter(
|
|
98
95
|
zarr_array=self._input.zarr_array,
|
|
99
96
|
dimensions=self._input.dimensions,
|
|
100
97
|
axes_order=self._axes_order,
|
|
101
98
|
transforms=self._input_transforms,
|
|
102
|
-
pixel_size=self._input.pixel_size,
|
|
103
99
|
roi=roi,
|
|
104
100
|
slicing_dict=self._input_slicing_kwargs,
|
|
105
101
|
)
|
|
106
|
-
label_getter =
|
|
102
|
+
label_getter = DaskRoiGetter(
|
|
107
103
|
zarr_array=self._input_label.zarr_array,
|
|
108
104
|
dimensions=self._input_label.dimensions,
|
|
109
105
|
axes_order=self._axes_order,
|
|
110
106
|
transforms=self._label_transforms,
|
|
111
|
-
pixel_size=self._input_label.pixel_size,
|
|
112
107
|
roi=roi,
|
|
113
108
|
remove_channel_selection=True,
|
|
114
109
|
)
|
|
@@ -3,21 +3,20 @@ from collections.abc import Callable, Generator, Sequence
|
|
|
3
3
|
import dask.array as da
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
-
from ngio.common import
|
|
7
|
-
Roi,
|
|
8
|
-
TransformProtocol,
|
|
9
|
-
build_roi_dask_getter,
|
|
10
|
-
build_roi_dask_setter,
|
|
11
|
-
build_roi_numpy_getter,
|
|
12
|
-
build_roi_numpy_setter,
|
|
13
|
-
)
|
|
6
|
+
from ngio.common import Roi
|
|
14
7
|
from ngio.experimental.iterators._abstract_iterator import AbstractIteratorBuilder
|
|
15
8
|
from ngio.images import Image
|
|
16
9
|
from ngio.images._image import (
|
|
17
10
|
ChannelSlicingInputType,
|
|
18
11
|
add_channel_selection_to_slicing_dict,
|
|
19
12
|
)
|
|
20
|
-
from ngio.
|
|
13
|
+
from ngio.io_pipes import (
|
|
14
|
+
DaskRoiGetter,
|
|
15
|
+
DaskRoiSetter,
|
|
16
|
+
NumpyRoiGetter,
|
|
17
|
+
NumpyRoiSetter,
|
|
18
|
+
TransformProtocol,
|
|
19
|
+
)
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class ImageProcessingIterator(AbstractIteratorBuilder):
|
|
@@ -72,12 +71,7 @@ class ImageProcessingIterator(AbstractIteratorBuilder):
|
|
|
72
71
|
self._input_transforms = input_transforms
|
|
73
72
|
self._output_transforms = output_transforms
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
77
|
-
raise NgioValidationError(
|
|
78
|
-
"Input image and output label have incompatible dimensions. "
|
|
79
|
-
f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
80
|
-
)
|
|
74
|
+
self._input.require_dimensions_match(self._output, allow_singleton=True)
|
|
81
75
|
|
|
82
76
|
def get_init_kwargs(self) -> dict:
|
|
83
77
|
"""Return the initialization arguments for the iterator."""
|
|
@@ -92,46 +86,42 @@ class ImageProcessingIterator(AbstractIteratorBuilder):
|
|
|
92
86
|
}
|
|
93
87
|
|
|
94
88
|
def build_numpy_getter(self, roi: Roi):
|
|
95
|
-
return
|
|
89
|
+
return NumpyRoiGetter(
|
|
96
90
|
zarr_array=self._input.zarr_array,
|
|
97
91
|
dimensions=self._input.dimensions,
|
|
92
|
+
roi=roi,
|
|
98
93
|
axes_order=self._axes_order,
|
|
99
94
|
transforms=self._input_transforms,
|
|
100
|
-
pixel_size=self._input.pixel_size,
|
|
101
|
-
roi=roi,
|
|
102
95
|
slicing_dict=self._input_slicing_kwargs,
|
|
103
96
|
)
|
|
104
97
|
|
|
105
98
|
def build_numpy_setter(self, roi: Roi):
|
|
106
|
-
return
|
|
99
|
+
return NumpyRoiSetter(
|
|
107
100
|
zarr_array=self._output.zarr_array,
|
|
108
101
|
dimensions=self._output.dimensions,
|
|
102
|
+
roi=roi,
|
|
109
103
|
axes_order=self._axes_order,
|
|
110
104
|
transforms=self._output_transforms,
|
|
111
|
-
pixel_size=self._output.pixel_size,
|
|
112
|
-
roi=roi,
|
|
113
105
|
slicing_dict=self._output_slicing_kwargs,
|
|
114
106
|
)
|
|
115
107
|
|
|
116
108
|
def build_dask_getter(self, roi: Roi):
|
|
117
|
-
return
|
|
109
|
+
return DaskRoiGetter(
|
|
118
110
|
zarr_array=self._input.zarr_array,
|
|
119
111
|
dimensions=self._input.dimensions,
|
|
112
|
+
roi=roi,
|
|
120
113
|
axes_order=self._axes_order,
|
|
121
114
|
transforms=self._input_transforms,
|
|
122
|
-
pixel_size=self._input.pixel_size,
|
|
123
|
-
roi=roi,
|
|
124
115
|
slicing_dict=self._input_slicing_kwargs,
|
|
125
116
|
)
|
|
126
117
|
|
|
127
118
|
def build_dask_setter(self, roi: Roi):
|
|
128
|
-
return
|
|
119
|
+
return DaskRoiSetter(
|
|
129
120
|
zarr_array=self._output.zarr_array,
|
|
130
121
|
dimensions=self._output.dimensions,
|
|
122
|
+
roi=roi,
|
|
131
123
|
axes_order=self._axes_order,
|
|
132
124
|
transforms=self._output_transforms,
|
|
133
|
-
pixel_size=self._output.pixel_size,
|
|
134
|
-
roi=roi,
|
|
135
125
|
slicing_dict=self._output_slicing_kwargs,
|
|
136
126
|
)
|
|
137
127
|
|
|
@@ -100,10 +100,10 @@ def by_chunks(
|
|
|
100
100
|
) -> list[Roi]:
|
|
101
101
|
"""This method is a placeholder for chunked processing."""
|
|
102
102
|
chunk_size = ref_image.chunks
|
|
103
|
-
t_axis = ref_image.
|
|
104
|
-
z_axis = ref_image.
|
|
105
|
-
y_axis = ref_image.
|
|
106
|
-
x_axis = ref_image.
|
|
103
|
+
t_axis = ref_image.axes_handler.get_index("t")
|
|
104
|
+
z_axis = ref_image.axes_handler.get_index("z")
|
|
105
|
+
y_axis = ref_image.axes_handler.get_index("y")
|
|
106
|
+
x_axis = ref_image.axes_handler.get_index("x")
|
|
107
107
|
|
|
108
108
|
size_x = chunk_size[x_axis] if x_axis is not None else None
|
|
109
109
|
size_y = chunk_size[y_axis] if y_axis is not None else None
|
|
@@ -3,18 +3,7 @@ from collections.abc import Callable, Generator, Sequence
|
|
|
3
3
|
import dask.array as da
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
|
-
from ngio.common import
|
|
7
|
-
Roi,
|
|
8
|
-
TransformProtocol,
|
|
9
|
-
build_roi_dask_getter,
|
|
10
|
-
build_roi_dask_setter,
|
|
11
|
-
build_roi_masked_dask_getter,
|
|
12
|
-
build_roi_masked_dask_setter,
|
|
13
|
-
build_roi_masked_numpy_getter,
|
|
14
|
-
build_roi_masked_numpy_setter,
|
|
15
|
-
build_roi_numpy_getter,
|
|
16
|
-
build_roi_numpy_setter,
|
|
17
|
-
)
|
|
6
|
+
from ngio.common import Roi
|
|
18
7
|
from ngio.experimental.iterators._abstract_iterator import AbstractIteratorBuilder
|
|
19
8
|
from ngio.images import Image, Label
|
|
20
9
|
from ngio.images._image import (
|
|
@@ -22,7 +11,19 @@ from ngio.images._image import (
|
|
|
22
11
|
add_channel_selection_to_slicing_dict,
|
|
23
12
|
)
|
|
24
13
|
from ngio.images._masked_image import MaskedImage
|
|
25
|
-
from ngio.
|
|
14
|
+
from ngio.io_pipes import (
|
|
15
|
+
DaskGetterMasked,
|
|
16
|
+
DaskRoiGetter,
|
|
17
|
+
DaskRoiSetter,
|
|
18
|
+
DaskSetterMasked,
|
|
19
|
+
DataGetter,
|
|
20
|
+
DataSetter,
|
|
21
|
+
NumpyGetterMasked,
|
|
22
|
+
NumpyRoiGetter,
|
|
23
|
+
NumpyRoiSetter,
|
|
24
|
+
NumpySetterMasked,
|
|
25
|
+
TransformProtocol,
|
|
26
|
+
)
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class SegmentationIterator(AbstractIteratorBuilder):
|
|
@@ -66,12 +67,7 @@ class SegmentationIterator(AbstractIteratorBuilder):
|
|
|
66
67
|
self._input_transforms = input_transforms
|
|
67
68
|
self._output_transforms = output_transforms
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
71
|
-
raise NgioValidationError(
|
|
72
|
-
"Input image and output label have incompatible dimensions. "
|
|
73
|
-
f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
74
|
-
)
|
|
70
|
+
self._input.require_dimensions_match(self._output, allow_singleton=False)
|
|
75
71
|
|
|
76
72
|
def get_init_kwargs(self) -> dict:
|
|
77
73
|
"""Return the initialization arguments for the iterator."""
|
|
@@ -84,47 +80,43 @@ class SegmentationIterator(AbstractIteratorBuilder):
|
|
|
84
80
|
"output_transforms": self._output_transforms,
|
|
85
81
|
}
|
|
86
82
|
|
|
87
|
-
def build_numpy_getter(self, roi: Roi):
|
|
88
|
-
return
|
|
83
|
+
def build_numpy_getter(self, roi: Roi) -> DataGetter[np.ndarray]:
|
|
84
|
+
return NumpyRoiGetter(
|
|
89
85
|
zarr_array=self._input.zarr_array,
|
|
90
86
|
dimensions=self._input.dimensions,
|
|
87
|
+
roi=roi,
|
|
91
88
|
axes_order=self._axes_order,
|
|
92
89
|
transforms=self._input_transforms,
|
|
93
|
-
pixel_size=self._input.pixel_size,
|
|
94
|
-
roi=roi,
|
|
95
90
|
slicing_dict=self._input_slicing_kwargs,
|
|
96
91
|
)
|
|
97
92
|
|
|
98
|
-
def build_numpy_setter(self, roi: Roi):
|
|
99
|
-
return
|
|
93
|
+
def build_numpy_setter(self, roi: Roi) -> DataSetter[np.ndarray]:
|
|
94
|
+
return NumpyRoiSetter(
|
|
100
95
|
zarr_array=self._output.zarr_array,
|
|
101
96
|
dimensions=self._output.dimensions,
|
|
97
|
+
roi=roi,
|
|
102
98
|
axes_order=self._axes_order,
|
|
103
99
|
transforms=self._output_transforms,
|
|
104
|
-
pixel_size=self._output.pixel_size,
|
|
105
|
-
roi=roi,
|
|
106
100
|
remove_channel_selection=True,
|
|
107
101
|
)
|
|
108
102
|
|
|
109
|
-
def build_dask_getter(self, roi: Roi):
|
|
110
|
-
return
|
|
103
|
+
def build_dask_getter(self, roi: Roi) -> DataGetter[da.Array]:
|
|
104
|
+
return DaskRoiGetter(
|
|
111
105
|
zarr_array=self._input.zarr_array,
|
|
112
106
|
dimensions=self._input.dimensions,
|
|
107
|
+
roi=roi,
|
|
113
108
|
axes_order=self._axes_order,
|
|
114
109
|
transforms=self._input_transforms,
|
|
115
|
-
pixel_size=self._input.pixel_size,
|
|
116
|
-
roi=roi,
|
|
117
110
|
slicing_dict=self._input_slicing_kwargs,
|
|
118
111
|
)
|
|
119
112
|
|
|
120
|
-
def build_dask_setter(self, roi: Roi):
|
|
121
|
-
return
|
|
113
|
+
def build_dask_setter(self, roi: Roi) -> DataSetter[da.Array]:
|
|
114
|
+
return DaskRoiSetter(
|
|
122
115
|
zarr_array=self._output.zarr_array,
|
|
123
116
|
dimensions=self._output.dimensions,
|
|
117
|
+
roi=roi,
|
|
124
118
|
axes_order=self._axes_order,
|
|
125
119
|
transforms=self._output_transforms,
|
|
126
|
-
pixel_size=self._output.pixel_size,
|
|
127
|
-
roi=roi,
|
|
128
120
|
remove_channel_selection=True,
|
|
129
121
|
)
|
|
130
122
|
|
|
@@ -205,11 +197,11 @@ class MaskedSegmentationIterator(SegmentationIterator):
|
|
|
205
197
|
self._output_transforms = output_transforms
|
|
206
198
|
|
|
207
199
|
# Check compatibility between input and output images
|
|
208
|
-
if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
200
|
+
# if not self._input.dimensions.is_compatible_with(self._output.dimensions):
|
|
201
|
+
# raise NgioValidationError(
|
|
202
|
+
# "Input image and output label have incompatible dimensions. "
|
|
203
|
+
# f"Input: {self._input.dimensions}, Output: {self._output.dimensions}."
|
|
204
|
+
# )
|
|
213
205
|
|
|
214
206
|
def get_init_kwargs(self) -> dict:
|
|
215
207
|
"""Return the initialization arguments for the iterator."""
|
|
@@ -223,58 +215,50 @@ class MaskedSegmentationIterator(SegmentationIterator):
|
|
|
223
215
|
}
|
|
224
216
|
|
|
225
217
|
def build_numpy_getter(self, roi: Roi):
|
|
226
|
-
return
|
|
227
|
-
roi=roi,
|
|
218
|
+
return NumpyGetterMasked(
|
|
228
219
|
zarr_array=self._input.zarr_array,
|
|
229
220
|
dimensions=self._input.dimensions,
|
|
221
|
+
roi=roi,
|
|
230
222
|
label_zarr_array=self._input._label.zarr_array,
|
|
231
223
|
label_dimensions=self._input._label.dimensions,
|
|
232
|
-
label_pixel_size=self._input._label.pixel_size,
|
|
233
224
|
axes_order=self._axes_order,
|
|
234
225
|
transforms=self._input_transforms,
|
|
235
|
-
pixel_size=self._input.pixel_size,
|
|
236
226
|
slicing_dict=self._input_slicing_kwargs,
|
|
237
227
|
)
|
|
238
228
|
|
|
239
229
|
def build_numpy_setter(self, roi: Roi):
|
|
240
|
-
return
|
|
230
|
+
return NumpySetterMasked(
|
|
241
231
|
roi=roi,
|
|
242
232
|
zarr_array=self._output.zarr_array,
|
|
243
233
|
dimensions=self._output.dimensions,
|
|
244
234
|
label_zarr_array=self._input._label.zarr_array,
|
|
245
235
|
label_dimensions=self._input._label.dimensions,
|
|
246
|
-
label_pixel_size=self._input._label.pixel_size,
|
|
247
236
|
axes_order=self._axes_order,
|
|
248
237
|
transforms=self._output_transforms,
|
|
249
|
-
pixel_size=self._output.pixel_size,
|
|
250
238
|
remove_channel_selection=True,
|
|
251
239
|
)
|
|
252
240
|
|
|
253
241
|
def build_dask_getter(self, roi: Roi):
|
|
254
|
-
return
|
|
242
|
+
return DaskGetterMasked(
|
|
255
243
|
roi=roi,
|
|
256
244
|
zarr_array=self._input.zarr_array,
|
|
257
245
|
dimensions=self._input.dimensions,
|
|
258
246
|
label_zarr_array=self._input._label.zarr_array,
|
|
259
247
|
label_dimensions=self._input._label.dimensions,
|
|
260
|
-
label_pixel_size=self._input._label.pixel_size,
|
|
261
248
|
axes_order=self._axes_order,
|
|
262
249
|
transforms=self._input_transforms,
|
|
263
|
-
pixel_size=self._input.pixel_size,
|
|
264
250
|
slicing_dict=self._input_slicing_kwargs,
|
|
265
251
|
)
|
|
266
252
|
|
|
267
253
|
def build_dask_setter(self, roi: Roi):
|
|
268
|
-
return
|
|
254
|
+
return DaskSetterMasked(
|
|
269
255
|
roi=roi,
|
|
270
256
|
zarr_array=self._output.zarr_array,
|
|
271
257
|
dimensions=self._output.dimensions,
|
|
272
258
|
label_zarr_array=self._input._label.zarr_array,
|
|
273
259
|
label_dimensions=self._input._label.dimensions,
|
|
274
|
-
label_pixel_size=self._input._label.pixel_size,
|
|
275
260
|
axes_order=self._axes_order,
|
|
276
261
|
transforms=self._output_transforms,
|
|
277
|
-
pixel_size=self._output.pixel_size,
|
|
278
262
|
remove_channel_selection=True,
|
|
279
263
|
)
|
|
280
264
|
|