|
9 | 9 | Dict, |
10 | 10 | Iterable, |
11 | 11 | Iterator, |
| 12 | + List, |
12 | 13 | Optional, |
13 | 14 | Tuple, |
14 | 15 | Type, |
@@ -203,6 +204,26 @@ def write( |
203 | 204 | This parameter used to be relative for `View` and absolute for `MagView`, |
204 | 205 | and specified in the mag of the respective view. |
205 | 206 |
|
| 207 | + Writing data to a segmentation layer manually does not automatically update the largest_segment_id. To set |
| 208 | + the largest segment id properly run the `refresh_largest_segment_id` method on your layer or set the |
| 209 | + `largest_segment_id` property manually.. |
| 210 | +
|
| 211 | + Example: |
| 212 | +
|
| 213 | + ```python |
| 214 | + ds = Dataset(DS_PATH, voxel_size=(1, 1, 1)) |
| 215 | +
|
| 216 | + segmentation_layer = cast( |
| 217 | + SegmentationLayer, |
| 218 | + ds.add_layer("segmentation", SEGMENTATION_CATEGORY), |
| 219 | + ) |
| 220 | + mag = segmentation_layer.add_mag(Mag(1)) |
| 221 | +
|
| 222 | + mag.write(data=MY_NP_ARRAY) |
| 223 | +
|
| 224 | + segmentation_layer.refresh_largest_segment_id() |
| 225 | + ``` |
| 226 | +
|
206 | 227 | Note that writing compressed data which is not aligned with the blocks on disk may result in |
207 | 228 | diminished performance, as full blocks will automatically be read to pad the write actions. |
208 | 229 | """ |
@@ -809,6 +830,64 @@ def some_work(args: Tuple[View, int], some_parameter: int) -> None: |
809 | 830 | executor.map_to_futures(func_per_chunk, job_args), progress_desc |
810 | 831 | ) |
811 | 832 |
|
| 833 | + def map_chunk( |
| 834 | + self, |
| 835 | + func_per_chunk: Callable[["View"], Any], |
| 836 | + chunk_shape: Optional[Vec3IntLike] = None, # in Mag(1) |
| 837 | + executor: Optional[Executor] = None, |
| 838 | + progress_desc: Optional[str] = None, |
| 839 | + ) -> List[Any]: |
| 840 | + """ |
| 841 | + The view is chunked into multiple sub-views of size `chunk_shape` (in Mag(1)), |
| 842 | + by default one chunk per file. |
| 843 | + Then, `func_per_chunk` is performed on each sub-view and the results are collected |
| 844 | + in a list. |
| 845 | + Additional parameters for `func_per_chunk` can be specified using `functools.partial`. |
| 846 | + The computation of each chunk has to be independent of each other. |
| 847 | + Therefore, the work can be parallelized with `executor`. |
| 848 | +
|
| 849 | + If the `View` is of type `MagView` only the bounding box from the properties is chunked. |
| 850 | +
|
| 851 | + Example: |
| 852 | + ```python |
| 853 | + from webknossos.utils import named_partial |
| 854 | +
|
| 855 | + def some_work(view: View, some_parameter: int) -> None: |
| 856 | + # perform operations on the view |
| 857 | + ... |
| 858 | +
|
| 859 | + # ... |
| 860 | + # let 'mag1' be a `MagView` |
| 861 | + func = named_partial(some_work, some_parameter=42) |
| 862 | + results = mag1.map_chunk( |
| 863 | + func, |
| 864 | + ) |
| 865 | + ``` |
| 866 | + """ |
| 867 | + |
| 868 | + if chunk_shape is None: |
| 869 | + chunk_shape = self._get_file_dimensions_mag1() |
| 870 | + else: |
| 871 | + chunk_shape = Vec3Int(chunk_shape) |
| 872 | + self._check_chunk_shape(chunk_shape, read_only=self.read_only) |
| 873 | + |
| 874 | + job_args = [] |
| 875 | + for chunk in self.bounding_box.chunk(chunk_shape, chunk_shape): |
| 876 | + chunk_view = self.get_view( |
| 877 | + absolute_offset=chunk.topleft, |
| 878 | + size=chunk.size, |
| 879 | + ) |
| 880 | + job_args.append(chunk_view) |
| 881 | + |
| 882 | + # execute the work for each chunk |
| 883 | + with get_executor_for_args(None, executor) as executor: |
| 884 | + results = wait_and_ensure_success( |
| 885 | + executor.map_to_futures(func_per_chunk, job_args), |
| 886 | + progress_desc=progress_desc, |
| 887 | + ) |
| 888 | + |
| 889 | + return results |
| 890 | + |
812 | 891 | def for_zipped_chunks( |
813 | 892 | self, |
814 | 893 | func_per_chunk: Callable[[Tuple["View", "View", int]], None], |
|
0 commit comments