ngio 0.1.5__py3-none-any.whl → 0.2.0a1__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.
Files changed (84) hide show
  1. ngio/__init__.py +31 -5
  2. ngio/common/__init__.py +44 -0
  3. ngio/common/_array_pipe.py +160 -0
  4. ngio/common/_axes_transforms.py +63 -0
  5. ngio/common/_common_types.py +5 -0
  6. ngio/common/_dimensions.py +113 -0
  7. ngio/common/_pyramid.py +222 -0
  8. ngio/{core/roi.py → common/_roi.py} +22 -23
  9. ngio/common/_slicer.py +97 -0
  10. ngio/{pipes/_zoom_utils.py → common/_zoom.py} +46 -80
  11. ngio/hcs/__init__.py +60 -0
  12. ngio/images/__init__.py +23 -0
  13. ngio/images/abstract_image.py +240 -0
  14. ngio/images/create.py +251 -0
  15. ngio/images/image.py +383 -0
  16. ngio/images/label.py +96 -0
  17. ngio/images/omezarr_container.py +512 -0
  18. ngio/ome_zarr_meta/__init__.py +35 -0
  19. ngio/ome_zarr_meta/_generic_handlers.py +320 -0
  20. ngio/ome_zarr_meta/_meta_handlers.py +142 -0
  21. ngio/ome_zarr_meta/ngio_specs/__init__.py +63 -0
  22. ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
  23. ngio/ome_zarr_meta/ngio_specs/_channels.py +378 -0
  24. ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
  25. ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +5 -0
  26. ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +434 -0
  27. ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +84 -0
  28. ngio/ome_zarr_meta/v04/__init__.py +11 -0
  29. ngio/ome_zarr_meta/v04/_meta_handlers.py +54 -0
  30. ngio/ome_zarr_meta/v04/_v04_spec_utils.py +412 -0
  31. ngio/tables/__init__.py +21 -5
  32. ngio/tables/_validators.py +192 -0
  33. ngio/tables/backends/__init__.py +8 -0
  34. ngio/tables/backends/_abstract_backend.py +71 -0
  35. ngio/tables/backends/_anndata_utils.py +194 -0
  36. ngio/tables/backends/_anndata_v1.py +75 -0
  37. ngio/tables/backends/_json_v1.py +56 -0
  38. ngio/tables/backends/_table_backends.py +102 -0
  39. ngio/tables/tables_container.py +300 -0
  40. ngio/tables/v1/__init__.py +6 -5
  41. ngio/tables/v1/_feature_table.py +161 -0
  42. ngio/tables/v1/_generic_table.py +99 -182
  43. ngio/tables/v1/_masking_roi_table.py +175 -0
  44. ngio/tables/v1/_roi_table.py +226 -0
  45. ngio/utils/__init__.py +23 -10
  46. ngio/utils/_datasets.py +51 -0
  47. ngio/utils/_errors.py +10 -4
  48. ngio/utils/_zarr_utils.py +378 -0
  49. {ngio-0.1.5.dist-info → ngio-0.2.0a1.dist-info}/METADATA +19 -39
  50. ngio-0.2.0a1.dist-info/RECORD +53 -0
  51. ngio/core/__init__.py +0 -7
  52. ngio/core/dimensions.py +0 -122
  53. ngio/core/image_handler.py +0 -228
  54. ngio/core/image_like_handler.py +0 -549
  55. ngio/core/label_handler.py +0 -410
  56. ngio/core/ngff_image.py +0 -387
  57. ngio/core/utils.py +0 -287
  58. ngio/io/__init__.py +0 -19
  59. ngio/io/_zarr.py +0 -88
  60. ngio/io/_zarr_array_utils.py +0 -0
  61. ngio/io/_zarr_group_utils.py +0 -61
  62. ngio/iterators/__init__.py +0 -1
  63. ngio/ngff_meta/__init__.py +0 -27
  64. ngio/ngff_meta/fractal_image_meta.py +0 -1267
  65. ngio/ngff_meta/meta_handler.py +0 -92
  66. ngio/ngff_meta/utils.py +0 -235
  67. ngio/ngff_meta/v04/__init__.py +0 -6
  68. ngio/ngff_meta/v04/specs.py +0 -158
  69. ngio/ngff_meta/v04/zarr_utils.py +0 -376
  70. ngio/pipes/__init__.py +0 -7
  71. ngio/pipes/_slicer_transforms.py +0 -176
  72. ngio/pipes/_transforms.py +0 -33
  73. ngio/pipes/data_pipe.py +0 -52
  74. ngio/tables/_ad_reader.py +0 -80
  75. ngio/tables/_utils.py +0 -301
  76. ngio/tables/tables_group.py +0 -252
  77. ngio/tables/v1/feature_tables.py +0 -182
  78. ngio/tables/v1/masking_roi_tables.py +0 -243
  79. ngio/tables/v1/roi_tables.py +0 -285
  80. ngio/utils/_common_types.py +0 -5
  81. ngio/utils/_pydantic_utils.py +0 -52
  82. ngio-0.1.5.dist-info/RECORD +0 -44
  83. {ngio-0.1.5.dist-info → ngio-0.2.0a1.dist-info}/WHEEL +0 -0
  84. {ngio-0.1.5.dist-info → ngio-0.2.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,549 +0,0 @@
1
- """Generic class to handle Image-like data in a OME-NGFF file."""
2
-
3
- from pathlib import Path
4
- from typing import Any, Literal
5
-
6
- import dask.array as da
7
- import numpy as np
8
- import zarr
9
- from dask.delayed import Delayed
10
-
11
- from ngio.core.dimensions import Dimensions
12
- from ngio.core.roi import WorldCooROI
13
- from ngio.io import AccessModeLiteral, StoreOrGroup, open_group_wrapper
14
- from ngio.ngff_meta import (
15
- Dataset,
16
- ImageLabelMeta,
17
- PixelSize,
18
- SpaceUnits,
19
- get_ngff_image_meta_handler,
20
- )
21
- from ngio.pipes import DataTransformPipe, NaiveSlicer, RoiSlicer, on_disk_zoom
22
- from ngio.utils import ngio_logger
23
- from ngio.utils._common_types import ArrayLike
24
-
25
- try:
26
- from dask.distributed import Lock
27
- except ImportError:
28
- Lock = None # type: ignore
29
-
30
-
31
- class ImageLike:
32
- """A class to handle OME-NGFF images stored in Zarr format.
33
-
34
- This class provides methods to access image data and ROI tables.
35
- """
36
-
37
- _virtual_pixel_size: PixelSize | None
38
-
39
- def __init__(
40
- self,
41
- store: StoreOrGroup,
42
- path: str | None = None,
43
- idx: int | None = None,
44
- pixel_size: PixelSize | None = None,
45
- highest_resolution: bool = False,
46
- strict: bool = True,
47
- meta_mode: Literal["image", "label"] = "image",
48
- cache: bool = True,
49
- mode: AccessModeLiteral = "r+",
50
- _label_group: Any = None,
51
- ) -> None:
52
- """Initialize the MultiscaleHandler in read mode.
53
-
54
- Note: Only one of `path`, `idx`, 'pixel_size' or 'highest_resolution'
55
- should be provided.
56
-
57
- store (StoreOrGroup): The Zarr store or group containing the image data.
58
- path (str | None): The path to the level.
59
- idx (int | None): The index of the level.
60
- pixel_size (PixelSize | None): The pixel size of the level.
61
- highest_resolution (bool): Whether to get the highest resolution level.
62
- strict (bool): Whether to raise an error where a pixel size is not found
63
- to match the requested "pixel_size".
64
- meta_mode (str): The mode of the metadata handler.
65
- cache (bool): Whether to cache the metadata.
66
- _label_group (LabelGroup): The group containing the label data (internal use).
67
- """
68
- if not strict:
69
- ngio_logger.warning("Strict mode is not fully supported yet.")
70
-
71
- self._mode = mode
72
- if not isinstance(store, zarr.Group):
73
- store = open_group_wrapper(store=store, mode=self._mode)
74
-
75
- self._group = store
76
-
77
- self._metadata_handler = get_ngff_image_meta_handler(
78
- store=store, meta_mode=meta_mode, cache=cache
79
- )
80
-
81
- # Find the level / resolution index
82
- metadata = self._metadata_handler.load_meta()
83
- dataset = metadata.get_dataset(
84
- path=path,
85
- idx=idx,
86
- pixel_size=pixel_size,
87
- highest_resolution=highest_resolution,
88
- strict=strict,
89
- )
90
-
91
- if pixel_size is not None:
92
- pixel_size.virtual = True
93
- self._virtual_pixel_size = pixel_size
94
- else:
95
- self._virtual_pixel_size = None
96
-
97
- self._init_dataset(dataset)
98
- self._dask_lock = None
99
-
100
- self._label_group = _label_group
101
-
102
- def _init_dataset(self, dataset: Dataset) -> None:
103
- """Set the dataset of the image.
104
-
105
- This method is for internal use only.
106
- """
107
- self._dataset = dataset
108
- self._array = self._group.get(self._dataset.path, None)
109
-
110
- if self._array is None:
111
- raise ValueError(f"Dataset {self._dataset.path} not found in the group.")
112
-
113
- self._diminesions = Dimensions(
114
- on_disk_shape=self._array.shape,
115
- axes_names=self._dataset.axes_names,
116
- axes_order=self._dataset.axes_order,
117
- )
118
-
119
- def _debug_set_new_dataset(
120
- self,
121
- new_dataset: Dataset,
122
- ) -> None:
123
- """Debug method to change the the dataset metadata.
124
-
125
- This methods allow to change dataset after initialization.
126
- This allow to skip the OME-NGFF metadata validation.
127
- This method is for testing/debug purposes only.
128
-
129
- DO NOT USE THIS METHOD IN PRODUCTION CODE.
130
- """
131
- self._init_dataset(new_dataset)
132
-
133
- # Method to get the metadata of the image
134
- @property
135
- def group(self) -> zarr.Group:
136
- """Return the Zarr group containing the image data."""
137
- return self._group
138
-
139
- @property
140
- def root_path(self) -> str:
141
- """Return the path to the root group."""
142
- return str(self._group.store.path)
143
-
144
- @property
145
- def group_path(self) -> str:
146
- """Return the path to the group."""
147
- root = self.root_path
148
- if root.endswith("/"):
149
- root = root[:-1]
150
- return f"{root}/{self._group.path}"
151
-
152
- @property
153
- def array_path(self) -> str:
154
- """Return the path to the array."""
155
- group = self.group_path
156
- if group.endswith("/"):
157
- group = group[:-1]
158
- return f"{group}/{self.path}"
159
-
160
- @property
161
- def metadata(self) -> ImageLabelMeta:
162
- """Return the metadata of the image."""
163
- return self._metadata_handler.load_meta()
164
-
165
- @property
166
- def dataset(self) -> Dataset:
167
- """Return the dataset of the image."""
168
- return self._dataset
169
-
170
- @property
171
- def path(self) -> str:
172
- """Return the path of the dataset (relative to the root group)."""
173
- return self.dataset.path
174
-
175
- @property
176
- def axes_names(self) -> list[str]:
177
- """Return the names of the axes in the image."""
178
- return self.dataset.axes_names
179
-
180
- @property
181
- def space_axes_names(self) -> list[str]:
182
- """Return the names of the space axes in the image."""
183
- return self.dataset.space_axes_names
184
-
185
- @property
186
- def space_axes_unit(self) -> SpaceUnits:
187
- """Return the units of the space axes in the image."""
188
- return self.dataset.space_axes_unit
189
-
190
- @property
191
- def pixel_size(self) -> PixelSize:
192
- """Return the pixel resolution of the image."""
193
- if self._virtual_pixel_size is not None:
194
- return self._virtual_pixel_size
195
-
196
- return self.dataset.pixel_size
197
-
198
- # Utility methods to get the image dimensionality
199
- @property
200
- def dimensions(self) -> Dimensions:
201
- """Return an object representation the dimensions of the image."""
202
- return self._diminesions
203
-
204
- @property
205
- def shape(self) -> tuple[int, ...]:
206
- """Return the shape of the image in canonical order (TCZYX)."""
207
- return self.dimensions.shape
208
-
209
- @property
210
- def is_time_series(self) -> bool:
211
- """Return whether the image is a time series."""
212
- return self.dimensions.is_time_series
213
-
214
- @property
215
- def is_2d(self) -> bool:
216
- """Return whether the image is 2D."""
217
- return self.dimensions.is_2d
218
-
219
- @property
220
- def is_2d_time_series(self) -> bool:
221
- """Return whether the image is a 2D time series."""
222
- return self.dimensions.is_2d_time_series
223
-
224
- @property
225
- def is_3d(self) -> bool:
226
- """Return whether the image is 3D."""
227
- return self.dimensions.is_3d
228
-
229
- @property
230
- def is_3d_time_series(self) -> bool:
231
- """Return whether the image is a 3D time series."""
232
- return self.dimensions.is_3d_time_series
233
-
234
- @property
235
- def is_multi_channels(self) -> bool:
236
- """Return whether the image has multiple channels."""
237
- return self.dimensions.is_multi_channels
238
-
239
- # Methods to get the image data as is on disk
240
- @property
241
- def on_disk_array(self) -> zarr.Array:
242
- """Return the image data as a Zarr array."""
243
- return self._array
244
-
245
- @property
246
- def on_disk_dask_array(self) -> da.core.Array:
247
- """Return the image data as a Dask array."""
248
- return da.from_zarr(self.on_disk_array) # type: ignore
249
-
250
- @property
251
- def on_disk_shape(self) -> tuple[int, ...]:
252
- """Return the shape of the image."""
253
- return self.dimensions.on_disk_shape
254
-
255
- def find_axis(self, axis_name: str) -> int | None:
256
- """Return the index of the given axis name."""
257
- return self.dimensions.find_axis(axis_name)
258
-
259
- # Methods to get the image data in the canonical order
260
- def init_lock(self, lock_id: str | None = None) -> None:
261
- """Set the lock for the Dask array."""
262
- if Lock is None:
263
- raise ImportError(
264
- "Lock is not available. Please install dask[distributed]."
265
- )
266
- # Unique zarr array identifier
267
- array_path = (
268
- Path(self._group.store.path) / self._group.path / self._dataset.path
269
- )
270
- lock_id = f"Zarr_IO_Lock_{array_path}" if lock_id is None else lock_id
271
- self._dask_lock = Lock(lock_id)
272
-
273
- def _get_pipe(
274
- self,
275
- data_pipe: DataTransformPipe,
276
- mode: Literal["numpy", "dask"] = "numpy",
277
- ) -> ArrayLike:
278
- """Return the data transform pipe."""
279
- if mode == "numpy":
280
- return data_pipe.get(data=self.on_disk_array)
281
- elif mode == "dask":
282
- return data_pipe.get(data=self.on_disk_dask_array)
283
- else:
284
- raise ValueError(f"Invalid mode {mode}")
285
-
286
- def _set_pipe(
287
- self,
288
- data_pipe: DataTransformPipe,
289
- patch: ArrayLike,
290
- ) -> None:
291
- """Set the data transform pipe."""
292
- if isinstance(patch, np.ndarray):
293
- data_pipe.set(data=self.on_disk_array, patch=patch)
294
-
295
- elif isinstance(patch, (da.core.Array, Delayed)): # noqa: UP038
296
- if self._dask_lock is None:
297
- return data_pipe.set(data=self.on_disk_array, patch=patch)
298
-
299
- array = self.on_disk_array
300
- with self._dask_lock:
301
- data_pipe.set(data=array, patch=patch)
302
- else:
303
- raise ValueError(
304
- f"Invalid patch type {type(patch)}. "
305
- "Supported types are np.ndarray and da.core.Array"
306
- )
307
-
308
- def _build_roi_pipe(
309
- self,
310
- roi: WorldCooROI,
311
- t: int | slice | None = None,
312
- c: int | slice | None = None,
313
- preserve_dimensions: bool = False,
314
- ) -> DataTransformPipe:
315
- """Build the data transform pipe for a region of interest (ROI)."""
316
- roi_coo = roi.to_raster_coo(
317
- pixel_size=self.dataset.pixel_size, dimensions=self.dimensions
318
- )
319
-
320
- slicer = RoiSlicer(
321
- on_disk_axes_name=self.dataset.on_disk_axes_names,
322
- axes_order=self.dataset.axes_order,
323
- roi=roi_coo,
324
- t=t,
325
- c=c,
326
- preserve_dimensions=preserve_dimensions,
327
- )
328
- return DataTransformPipe(slicer=slicer)
329
-
330
- def _build_naive_pipe(
331
- self,
332
- x: int | slice | None = None,
333
- y: int | slice | None = None,
334
- z: int | slice | None = None,
335
- t: int | slice | None = None,
336
- c: int | slice | None = None,
337
- preserve_dimensions: bool = False,
338
- ) -> DataTransformPipe:
339
- """Build the data transform pipe for a naive slice."""
340
- slicer = NaiveSlicer(
341
- on_disk_axes_name=self.dataset.on_disk_axes_names,
342
- axes_order=self.dataset.axes_order,
343
- x=x,
344
- y=y,
345
- z=z,
346
- t=t,
347
- c=c,
348
- preserve_dimensions=preserve_dimensions,
349
- )
350
- return DataTransformPipe(slicer=slicer)
351
-
352
- def _get_array_from_roi(
353
- self,
354
- roi: WorldCooROI,
355
- t: int | slice | None = None,
356
- c: int | slice | None = None,
357
- mode: Literal["numpy", "dask"] = "numpy",
358
- preserve_dimensions: bool = False,
359
- ) -> ArrayLike | tuple[ArrayLike, DataTransformPipe]:
360
- """Return the image data from a region of interest (ROI).
361
-
362
- Args:
363
- roi (WorldCooROI): The region of interest.
364
- t (int | slice | None): The time index or slice.
365
- c (int | slice | None): The channel index or slice.
366
- mode (str): The mode to return the data.
367
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
368
- """
369
- data_pipe = self._build_roi_pipe(
370
- roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions
371
- )
372
- return_array = self._get_pipe(data_pipe=data_pipe, mode=mode)
373
- return return_array
374
-
375
- def _set_array_from_roi(
376
- self,
377
- patch: ArrayLike,
378
- roi: WorldCooROI,
379
- t: int | slice | None = None,
380
- c: int | slice | None = None,
381
- preserve_dimensions: bool = False,
382
- ) -> None:
383
- """Set the image data from a region of interest (ROI).
384
-
385
- Args:
386
- patch (ArrayLike): The patch to set.
387
- roi (WorldCooROI): The region of interest.
388
- t (int | slice | None): The time index or slice.
389
- c (int | slice | None): The channel index or slice.
390
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
391
- """
392
- data_pipe = self._build_roi_pipe(
393
- roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions
394
- )
395
- self._set_pipe(data_pipe=data_pipe, patch=patch)
396
-
397
- def _get_array(
398
- self,
399
- x: int | slice | None = None,
400
- y: int | slice | None = None,
401
- z: int | slice | None = None,
402
- t: int | slice | None = None,
403
- c: int | slice | None = None,
404
- mode: Literal["numpy", "dask"] = "numpy",
405
- preserve_dimensions: bool = False,
406
- ) -> ArrayLike:
407
- """Return the image data.
408
-
409
- Args:
410
- x (int | slice | None): The x index or slice.
411
- y (int | slice | None): The y index or slice.
412
- z (int | slice | None): The z index or slice.
413
- t (int | slice | None): The time index or slice.
414
- c (int | slice | None): The channel index or slice.
415
- mode (str): The mode to return the data.
416
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
417
- """
418
- data_pipe = self._build_naive_pipe(
419
- x=x, y=y, z=z, t=t, c=c, preserve_dimensions=preserve_dimensions
420
- )
421
- return self._get_pipe(data_pipe=data_pipe, mode=mode)
422
-
423
- def _set_array(
424
- self,
425
- patch: ArrayLike,
426
- x: int | slice | None = None,
427
- y: int | slice | None = None,
428
- z: int | slice | None = None,
429
- t: int | slice | None = None,
430
- c: int | slice | None = None,
431
- preserve_dimensions: bool = False,
432
- ) -> None:
433
- """Set the image data in the zarr array.
434
-
435
- Args:
436
- patch (ArrayLike): The patch to set.
437
- x (int | slice | None): The x index or slice.
438
- y (int | slice | None): The y index or slice.
439
- z (int | slice | None): The z index or slice.
440
- t (int | slice | None): The time index or slice.
441
- c (int | slice | None): The channel index or slice.
442
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
443
- """
444
- data_pipe = self._build_naive_pipe(
445
- x=x, y=y, z=z, t=t, c=c, preserve_dimensions=preserve_dimensions
446
- )
447
- self._set_pipe(data_pipe=data_pipe, patch=patch)
448
-
449
- def _get_array_masked(
450
- self,
451
- roi: WorldCooROI,
452
- t: int | slice | None = None,
453
- c: int | slice | None = None,
454
- mask_mode: Literal["bbox", "mask"] = "bbox",
455
- mode: Literal["numpy"] = "numpy",
456
- preserve_dimensions: bool = False,
457
- ) -> ArrayLike:
458
- """Return the image data from a region of interest (ROI).
459
-
460
- Args:
461
- roi (WorldCooROI): The region of interest.
462
- t (int | slice | None): The time index or slice.
463
- c (int | slice | None): The channel index or slice.
464
- mask_mode (str): Masking mode
465
- mode (str): The mode to return the data.
466
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
467
- """
468
- label_name = roi.infos.get("label_name", None)
469
- if label_name is None:
470
- raise ValueError("The label name must be provided in the ROI infos.")
471
-
472
- data_pipe = self._build_roi_pipe(
473
- roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions
474
- )
475
-
476
- if mask_mode == "bbox":
477
- return self._get_pipe(data_pipe=data_pipe, mode=mode)
478
-
479
- label = self._label_group.get_label(label_name, pixel_size=self.pixel_size)
480
-
481
- mask = label.mask(
482
- roi,
483
- t=t,
484
- mode=mode,
485
- )
486
- array = self._get_pipe(data_pipe=data_pipe, mode=mode)
487
- where_func = np.where if mode == "numpy" else da.where
488
- return where_func(mask, array, 0)
489
-
490
- def _set_array_masked(
491
- self,
492
- patch: ArrayLike,
493
- roi: WorldCooROI,
494
- t: int | slice | None = None,
495
- c: int | slice | None = None,
496
- preserve_dimensions: bool = False,
497
- ) -> None:
498
- """Set the image data from a region of interest (ROI).
499
-
500
- Args:
501
- patch (ArrayLike): The patch to set.
502
- roi (WorldCooROI): The region of interest.
503
- t (int | slice | None): The time index or slice.
504
- c (int | slice | None): The channel index or slice.
505
- preserve_dimensions (bool): Whether to preserve the dimensions of the data.
506
- """
507
- data_pipe = self._build_roi_pipe(
508
- roi=roi, t=t, c=c, preserve_dimensions=preserve_dimensions
509
- )
510
- self._set_pipe(data_pipe=data_pipe, patch=patch)
511
-
512
- def _consolidate(self, order: Literal[0, 1, 2] = 1) -> None:
513
- """Consolidate the Zarr array."""
514
- processed_paths = [self]
515
-
516
- todo_image = [
517
- ImageLike(store=self.group, path=_path)
518
- for _path in self.metadata.levels_paths
519
- if _path != self.path
520
- ]
521
-
522
- while todo_image:
523
- dist_matrix = np.zeros((len(processed_paths), len(todo_image)))
524
- for i, image in enumerate(todo_image):
525
- for j, processed_image in enumerate(processed_paths):
526
- dist_matrix[j, i] = np.sqrt(
527
- np.sum(
528
- [
529
- (s1 - s2) ** 2
530
- for s1, s2 in zip(
531
- image.shape, processed_image.shape, strict=False
532
- )
533
- ]
534
- )
535
- )
536
-
537
- source, target = np.unravel_index(dist_matrix.argmin(), dist_matrix.shape)
538
-
539
- source_image = processed_paths[source]
540
- target_image = todo_image.pop(target)
541
-
542
- on_disk_zoom(
543
- source=source_image.on_disk_array,
544
- target=target_image.on_disk_array,
545
- order=order,
546
- )
547
-
548
- # compute the transformation
549
- processed_paths.append(target_image)