Skip to content

Commit fc0e509

Browse files
committed
imageio backend and example data
1 parent 27e9c2c commit fc0e509

2 files changed

Lines changed: 102 additions & 0 deletions

File tree

advanced/backends/imageio_.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
from typing import TYPE_CHECKING
5+
6+
import numpy as np
7+
import xarray as xr
8+
9+
if TYPE_CHECKING:
10+
import os
11+
from typing import Any, Protocol
12+
13+
import numpy.typing as npt
14+
15+
class FileLike(Protocol): ...
16+
17+
class LockType(Protocol):
18+
def __enter__(self) -> Any: ...
19+
20+
def acquire(self, blocking: bool = True, timeout: int = -1) -> bool: ...
21+
def release(self) -> None: ...
22+
23+
IndexerType = int | slice | npt.NDArray[np.integer]
24+
FilenameOrObjectType = str | os.PathLike | bytes | FileLike
25+
26+
27+
@dataclass
28+
class ImageIOBackendArray(xr.backends.BackendArray):
29+
filename_or_obj: FilenameOrObjectType
30+
31+
shape: tuple[int]
32+
dtype: npt.DtypeLike
33+
34+
lock: LockType
35+
36+
def __getitem__(self, key: tuple[IndexerType]):
37+
return xr.core.indexing.explicit_indexing_adapter(
38+
key,
39+
self.shape,
40+
xr.core.indexing.IndexingSupport.BASIC,
41+
self.basic_indexing,
42+
)
43+
44+
def basic_indexing(self, key: tuple[IndexerType]) -> npt.NDArray:
45+
import imageio.v3 as iio
46+
47+
with self.lock, iio.imopen(self.filename_or_obj, io_mode='r') as f:
48+
if key == (slice(None),) * len(self.shape):
49+
return f.read()
50+
51+
first_indexer = key[0]
52+
if isinstance(first_indexer, int):
53+
data = f.read(index=first_indexer, mode='P', writable=True)
54+
55+
remaining_indexers = key[1:]
56+
else:
57+
if isinstance(first_indexer, slice):
58+
indices = range(*first_indexer.indices(self.shape[0]))
59+
else:
60+
indices = first_indexer
61+
62+
data = np.concatenate([f.read(index=index, mode='P') for index in indices], axis=0)
63+
64+
remaining_indexers = (..., *key[1:])
65+
66+
return data[remaining_indexers]
67+
68+
69+
class ImageIOBackend(xr.backends.BackendEntrypoint):
70+
def open_dataset(
71+
self, filename_or_obj: FilenameOrObjectType, *, drop_variables: bool | None = None
72+
) -> xr.Dataset:
73+
import imageio.v3 as iio
74+
75+
with iio.imopen(filename_or_obj, io_mode='r') as f:
76+
properties = f.properties()
77+
metadata = f.metadata()
78+
79+
dims = ['n_images', 'height', 'width', 'color']
80+
81+
background = metadata['background']
82+
duration = metadata['duration']
83+
84+
shape = properties.shape
85+
dtype = properties.dtype
86+
87+
backend_array = ImageIOBackendArray(
88+
filename_or_obj=filename_or_obj,
89+
shape=shape,
90+
dtype=dtype,
91+
lock=xr.backends.locks.SerializableLock(),
92+
)
93+
data = xr.core.indexing.LazilyIndexedArray(backend_array)
94+
95+
var = xr.Variable(
96+
dims=dims,
97+
data=data,
98+
attrs={'duration': duration, 'background': background},
99+
encoding={'preferred_chunks': dict(zip(dims, (1, *shape[1:])))},
100+
)
101+
102+
return xr.Dataset({'data': var}, coords={'color': ['red', 'green', 'blue']})

advanced/backends/ocean.gif

1.42 MB
Loading

0 commit comments

Comments
 (0)