bioimageio.core.sample

  1from __future__ import annotations
  2
  3from dataclasses import dataclass
  4from math import ceil, floor
  5from typing import (
  6    Callable,
  7    Dict,
  8    Generic,
  9    Iterable,
 10    Optional,
 11    Tuple,
 12    TypeVar,
 13    Union,
 14)
 15
 16import numpy as np
 17from typing_extensions import Self
 18
 19from .axis import AxisId, PerAxis
 20from .block import Block
 21from .block_meta import (
 22    BlockMeta,
 23    LinearAxisTransform,
 24    split_multiple_shapes_into_blocks,
 25)
 26from .common import (
 27    BlockIndex,
 28    Halo,
 29    HaloLike,
 30    MemberId,
 31    PadMode,
 32    PerMember,
 33    SampleId,
 34    SliceInfo,
 35    TotalNumberOfBlocks,
 36)
 37from .stat_measures import Stat
 38from .tensor import Tensor
 39
 40# TODO: allow for lazy samples to read/write to disk
 41
 42
 43@dataclass
 44class Sample:
 45    """A dataset sample"""
 46
 47    members: Dict[MemberId, Tensor]
 48    """the sample's tensors"""
 49
 50    stat: Stat
 51    """sample and dataset statistics"""
 52
 53    id: SampleId
 54    """identifier within the sample's dataset"""
 55
 56    @property
 57    def shape(self) -> PerMember[PerAxis[int]]:
 58        return {tid: t.sizes for tid, t in self.members.items()}
 59
 60    def split_into_blocks(
 61        self,
 62        block_shapes: PerMember[PerAxis[int]],
 63        halo: PerMember[PerAxis[HaloLike]],
 64        pad_mode: PadMode,
 65        broadcast: bool = False,
 66    ) -> Tuple[TotalNumberOfBlocks, Iterable[SampleBlockWithOrigin]]:
 67        assert not (
 68            missing := [m for m in block_shapes if m not in self.members]
 69        ), f"`block_shapes` specified for unknown members: {missing}"
 70        assert not (
 71            missing := [m for m in halo if m not in block_shapes]
 72        ), f"`halo` specified for members without `block_shape`: {missing}"
 73
 74        n_blocks, blocks = split_multiple_shapes_into_blocks(
 75            shapes=self.shape,
 76            block_shapes=block_shapes,
 77            halo=halo,
 78            broadcast=broadcast,
 79        )
 80        return n_blocks, sample_block_generator(blocks, origin=self, pad_mode=pad_mode)
 81
 82    def as_single_block(self, halo: Optional[PerMember[PerAxis[Halo]]] = None):
 83        if halo is None:
 84            halo = {}
 85        return SampleBlockWithOrigin(
 86            sample_shape=self.shape,
 87            sample_id=self.id,
 88            blocks={
 89                m: Block(
 90                    sample_shape=self.shape[m],
 91                    data=data,
 92                    inner_slice={
 93                        a: SliceInfo(0, s) for a, s in data.tagged_shape.items()
 94                    },
 95                    halo=halo.get(m, {}),
 96                    block_index=0,
 97                    blocks_in_sample=1,
 98                )
 99                for m, data in self.members.items()
100            },
101            stat=self.stat,
102            origin=self,
103            block_index=0,
104            blocks_in_sample=1,
105        )
106
107    @classmethod
108    def from_blocks(
109        cls,
110        sample_blocks: Iterable[SampleBlock],
111        *,
112        fill_value: float = float("nan"),
113    ) -> Self:
114        members: PerMember[Tensor] = {}
115        stat: Stat = {}
116        sample_id = None
117        for sample_block in sample_blocks:
118            assert sample_id is None or sample_id == sample_block.sample_id
119            sample_id = sample_block.sample_id
120            stat = sample_block.stat
121            for m, block in sample_block.blocks.items():
122                if m not in members:
123                    if -1 in block.sample_shape.values():
124                        raise NotImplementedError(
125                            "merging blocks with data dependent axis not yet implemented"
126                        )
127
128                    members[m] = Tensor(
129                        np.full(
130                            tuple(block.sample_shape[a] for a in block.data.dims),
131                            fill_value,
132                            dtype=block.data.dtype,
133                        ),
134                        dims=block.data.dims,
135                    )
136
137                members[m][block.inner_slice] = block.inner_data
138
139        return cls(members=members, stat=stat, id=sample_id)
140
141
142BlockT = TypeVar("BlockT", Block, BlockMeta)
143
144
145@dataclass
146class SampleBlockBase(Generic[BlockT]):
147    """base class for `SampleBlockMeta` and `SampleBlock`"""
148
149    sample_shape: PerMember[PerAxis[int]]
150    """the sample shape this block represents a part of"""
151
152    sample_id: SampleId
153    """identifier for the sample within its dataset"""
154
155    blocks: Dict[MemberId, BlockT]
156    """Individual tensor blocks comprising this sample block"""
157
158    block_index: BlockIndex
159    """the n-th block of the sample"""
160
161    blocks_in_sample: TotalNumberOfBlocks
162    """total number of blocks in the sample"""
163
164    @property
165    def shape(self) -> PerMember[PerAxis[int]]:
166        return {mid: b.shape for mid, b in self.blocks.items()}
167
168    @property
169    def inner_shape(self) -> PerMember[PerAxis[int]]:
170        return {mid: b.inner_shape for mid, b in self.blocks.items()}
171
172
173@dataclass
174class LinearSampleAxisTransform(LinearAxisTransform):
175    member: MemberId
176
177
178@dataclass
179class SampleBlockMeta(SampleBlockBase[BlockMeta]):
180    """Meta data of a dataset sample block"""
181
182    def get_transformed(
183        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
184    ) -> Self:
185        sample_shape = {
186            m: {
187                a: (
188                    trf
189                    if isinstance(trf, int)
190                    else trf.compute(self.sample_shape[trf.member][trf.axis])
191                )
192                for a, trf in new_axes[m].items()
193            }
194            for m in new_axes
195        }
196
197        def get_member_halo(m: MemberId, round: Callable[[float], int]):
198            return {
199                a: (
200                    Halo(0, 0)
201                    if isinstance(trf, int)
202                    or trf.axis not in self.blocks[trf.member].halo
203                    else Halo(
204                        round(self.blocks[trf.member].halo[trf.axis].left * trf.scale),
205                        round(self.blocks[trf.member].halo[trf.axis].right * trf.scale),
206                    )
207                )
208                for a, trf in new_axes[m].items()
209            }
210
211        halo: Dict[MemberId, Dict[AxisId, Halo]] = {}
212        for m in new_axes:
213            halo[m] = get_member_halo(m, floor)
214            if halo[m] != get_member_halo(m, ceil):
215                raise ValueError(
216                    f"failed to unambiguously scale halo {halo[m]} with {new_axes[m]}"
217                    + f" for {m}."
218                )
219
220        inner_slice = {
221            m: {
222                a: (
223                    SliceInfo(0, trf)
224                    if isinstance(trf, int)
225                    else SliceInfo(
226                        trf.compute(
227                            self.blocks[trf.member].inner_slice[trf.axis].start
228                        ),
229                        trf.compute(self.blocks[trf.member].inner_slice[trf.axis].stop),
230                    )
231                )
232                for a, trf in new_axes[m].items()
233            }
234            for m in new_axes
235        }
236        return self.__class__(
237            blocks={
238                m: BlockMeta(
239                    sample_shape=sample_shape[m],
240                    inner_slice=inner_slice[m],
241                    halo=halo[m],
242                    block_index=self.block_index,
243                    blocks_in_sample=self.blocks_in_sample,
244                )
245                for m in new_axes
246            },
247            sample_shape=sample_shape,
248            sample_id=self.sample_id,
249            block_index=self.block_index,
250            blocks_in_sample=self.blocks_in_sample,
251        )
252
253    def with_data(self, data: PerMember[Tensor], *, stat: Stat) -> SampleBlock:
254        return SampleBlock(
255            sample_shape={
256                m: {
257                    a: data[m].tagged_shape[a] if s == -1 else s
258                    for a, s in member_shape.items()
259                }
260                for m, member_shape in self.sample_shape.items()
261            },
262            sample_id=self.sample_id,
263            blocks={
264                m: Block.from_meta(b, data=data[m]) for m, b in self.blocks.items()
265            },
266            stat=stat,
267            block_index=self.block_index,
268            blocks_in_sample=self.blocks_in_sample,
269        )
270
271
272@dataclass
273class SampleBlock(SampleBlockBase[Block]):
274    """A block of a dataset sample"""
275
276    stat: Stat
277    """computed statistics"""
278
279    @property
280    def members(self) -> PerMember[Tensor]:
281        """the sample block's tensors"""
282        return {m: b.data for m, b in self.blocks.items()}
283
284    def get_transformed_meta(
285        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
286    ) -> SampleBlockMeta:
287        return SampleBlockMeta(
288            sample_id=self.sample_id,
289            blocks=dict(self.blocks),
290            sample_shape=self.sample_shape,
291            block_index=self.block_index,
292            blocks_in_sample=self.blocks_in_sample,
293        ).get_transformed(new_axes)
294
295
296@dataclass
297class SampleBlockWithOrigin(SampleBlock):
298    """A `SampleBlock` with a reference (`origin`) to the whole `Sample`"""
299
300    origin: Sample
301    """the sample this sample block was taken from"""
302
303
304class _ConsolidatedMemberBlocks:
305    def __init__(self, blocks: PerMember[BlockMeta]):
306        super().__init__()
307        block_indices = {b.block_index for b in blocks.values()}
308        assert len(block_indices) == 1
309        self.block_index = block_indices.pop()
310        blocks_in_samples = {b.blocks_in_sample for b in blocks.values()}
311        assert len(blocks_in_samples) == 1
312        self.blocks_in_sample = blocks_in_samples.pop()
313
314
315def sample_block_meta_generator(
316    blocks: Iterable[PerMember[BlockMeta]],
317    *,
318    sample_shape: PerMember[PerAxis[int]],
319    sample_id: SampleId,
320):
321    for member_blocks in blocks:
322        cons = _ConsolidatedMemberBlocks(member_blocks)
323        yield SampleBlockMeta(
324            blocks=dict(member_blocks),
325            sample_shape=sample_shape,
326            sample_id=sample_id,
327            block_index=cons.block_index,
328            blocks_in_sample=cons.blocks_in_sample,
329        )
330
331
332def sample_block_generator(
333    blocks: Iterable[PerMember[BlockMeta]],
334    *,
335    origin: Sample,
336    pad_mode: PadMode,
337) -> Iterable[SampleBlockWithOrigin]:
338    for member_blocks in blocks:
339        cons = _ConsolidatedMemberBlocks(member_blocks)
340        yield SampleBlockWithOrigin(
341            blocks={
342                m: Block.from_sample_member(
343                    origin.members[m], block=member_blocks[m], pad_mode=pad_mode
344                )
345                for m in origin.members
346            },
347            sample_shape=origin.shape,
348            origin=origin,
349            stat=origin.stat,
350            sample_id=origin.id,
351            block_index=cons.block_index,
352            blocks_in_sample=cons.blocks_in_sample,
353        )
@dataclass
class Sample:
 44@dataclass
 45class Sample:
 46    """A dataset sample"""
 47
 48    members: Dict[MemberId, Tensor]
 49    """the sample's tensors"""
 50
 51    stat: Stat
 52    """sample and dataset statistics"""
 53
 54    id: SampleId
 55    """identifier within the sample's dataset"""
 56
 57    @property
 58    def shape(self) -> PerMember[PerAxis[int]]:
 59        return {tid: t.sizes for tid, t in self.members.items()}
 60
 61    def split_into_blocks(
 62        self,
 63        block_shapes: PerMember[PerAxis[int]],
 64        halo: PerMember[PerAxis[HaloLike]],
 65        pad_mode: PadMode,
 66        broadcast: bool = False,
 67    ) -> Tuple[TotalNumberOfBlocks, Iterable[SampleBlockWithOrigin]]:
 68        assert not (
 69            missing := [m for m in block_shapes if m not in self.members]
 70        ), f"`block_shapes` specified for unknown members: {missing}"
 71        assert not (
 72            missing := [m for m in halo if m not in block_shapes]
 73        ), f"`halo` specified for members without `block_shape`: {missing}"
 74
 75        n_blocks, blocks = split_multiple_shapes_into_blocks(
 76            shapes=self.shape,
 77            block_shapes=block_shapes,
 78            halo=halo,
 79            broadcast=broadcast,
 80        )
 81        return n_blocks, sample_block_generator(blocks, origin=self, pad_mode=pad_mode)
 82
 83    def as_single_block(self, halo: Optional[PerMember[PerAxis[Halo]]] = None):
 84        if halo is None:
 85            halo = {}
 86        return SampleBlockWithOrigin(
 87            sample_shape=self.shape,
 88            sample_id=self.id,
 89            blocks={
 90                m: Block(
 91                    sample_shape=self.shape[m],
 92                    data=data,
 93                    inner_slice={
 94                        a: SliceInfo(0, s) for a, s in data.tagged_shape.items()
 95                    },
 96                    halo=halo.get(m, {}),
 97                    block_index=0,
 98                    blocks_in_sample=1,
 99                )
100                for m, data in self.members.items()
101            },
102            stat=self.stat,
103            origin=self,
104            block_index=0,
105            blocks_in_sample=1,
106        )
107
108    @classmethod
109    def from_blocks(
110        cls,
111        sample_blocks: Iterable[SampleBlock],
112        *,
113        fill_value: float = float("nan"),
114    ) -> Self:
115        members: PerMember[Tensor] = {}
116        stat: Stat = {}
117        sample_id = None
118        for sample_block in sample_blocks:
119            assert sample_id is None or sample_id == sample_block.sample_id
120            sample_id = sample_block.sample_id
121            stat = sample_block.stat
122            for m, block in sample_block.blocks.items():
123                if m not in members:
124                    if -1 in block.sample_shape.values():
125                        raise NotImplementedError(
126                            "merging blocks with data dependent axis not yet implemented"
127                        )
128
129                    members[m] = Tensor(
130                        np.full(
131                            tuple(block.sample_shape[a] for a in block.data.dims),
132                            fill_value,
133                            dtype=block.data.dtype,
134                        ),
135                        dims=block.data.dims,
136                    )
137
138                members[m][block.inner_slice] = block.inner_data
139
140        return cls(members=members, stat=stat, id=sample_id)

A dataset sample

Sample( members: Dict[bioimageio.spec.model.v0_5.TensorId, bioimageio.core.Tensor], stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer>, return_type=PydanticUndefined, when_used='always')]]], id: Hashable)

the sample's tensors

stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator at 0x7f9a7099e840>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer at 0x7f9a7099ea20>, return_type=PydanticUndefined, when_used='always')]]]

sample and dataset statistics

id: Hashable

identifier within the sample's dataset

shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]]
57    @property
58    def shape(self) -> PerMember[PerAxis[int]]:
59        return {tid: t.sizes for tid, t in self.members.items()}
def split_into_blocks( self, block_shapes: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], halo: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, Union[int, Tuple[int, int], bioimageio.core.common.Halo]]], pad_mode: Literal['edge', 'reflect', 'symmetric'], broadcast: bool = False) -> Tuple[int, Iterable[SampleBlockWithOrigin]]:
61    def split_into_blocks(
62        self,
63        block_shapes: PerMember[PerAxis[int]],
64        halo: PerMember[PerAxis[HaloLike]],
65        pad_mode: PadMode,
66        broadcast: bool = False,
67    ) -> Tuple[TotalNumberOfBlocks, Iterable[SampleBlockWithOrigin]]:
68        assert not (
69            missing := [m for m in block_shapes if m not in self.members]
70        ), f"`block_shapes` specified for unknown members: {missing}"
71        assert not (
72            missing := [m for m in halo if m not in block_shapes]
73        ), f"`halo` specified for members without `block_shape`: {missing}"
74
75        n_blocks, blocks = split_multiple_shapes_into_blocks(
76            shapes=self.shape,
77            block_shapes=block_shapes,
78            halo=halo,
79            broadcast=broadcast,
80        )
81        return n_blocks, sample_block_generator(blocks, origin=self, pad_mode=pad_mode)
def as_single_block( self, halo: Optional[Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, bioimageio.core.common.Halo]]] = None):
 83    def as_single_block(self, halo: Optional[PerMember[PerAxis[Halo]]] = None):
 84        if halo is None:
 85            halo = {}
 86        return SampleBlockWithOrigin(
 87            sample_shape=self.shape,
 88            sample_id=self.id,
 89            blocks={
 90                m: Block(
 91                    sample_shape=self.shape[m],
 92                    data=data,
 93                    inner_slice={
 94                        a: SliceInfo(0, s) for a, s in data.tagged_shape.items()
 95                    },
 96                    halo=halo.get(m, {}),
 97                    block_index=0,
 98                    blocks_in_sample=1,
 99                )
100                for m, data in self.members.items()
101            },
102            stat=self.stat,
103            origin=self,
104            block_index=0,
105            blocks_in_sample=1,
106        )
@classmethod
def from_blocks( cls, sample_blocks: Iterable[SampleBlock], *, fill_value: float = nan) -> Self:
108    @classmethod
109    def from_blocks(
110        cls,
111        sample_blocks: Iterable[SampleBlock],
112        *,
113        fill_value: float = float("nan"),
114    ) -> Self:
115        members: PerMember[Tensor] = {}
116        stat: Stat = {}
117        sample_id = None
118        for sample_block in sample_blocks:
119            assert sample_id is None or sample_id == sample_block.sample_id
120            sample_id = sample_block.sample_id
121            stat = sample_block.stat
122            for m, block in sample_block.blocks.items():
123                if m not in members:
124                    if -1 in block.sample_shape.values():
125                        raise NotImplementedError(
126                            "merging blocks with data dependent axis not yet implemented"
127                        )
128
129                    members[m] = Tensor(
130                        np.full(
131                            tuple(block.sample_shape[a] for a in block.data.dims),
132                            fill_value,
133                            dtype=block.data.dtype,
134                        ),
135                        dims=block.data.dims,
136                    )
137
138                members[m][block.inner_slice] = block.inner_data
139
140        return cls(members=members, stat=stat, id=sample_id)
@dataclass
class SampleBlockBase(typing.Generic[~BlockT]):
146@dataclass
147class SampleBlockBase(Generic[BlockT]):
148    """base class for `SampleBlockMeta` and `SampleBlock`"""
149
150    sample_shape: PerMember[PerAxis[int]]
151    """the sample shape this block represents a part of"""
152
153    sample_id: SampleId
154    """identifier for the sample within its dataset"""
155
156    blocks: Dict[MemberId, BlockT]
157    """Individual tensor blocks comprising this sample block"""
158
159    block_index: BlockIndex
160    """the n-th block of the sample"""
161
162    blocks_in_sample: TotalNumberOfBlocks
163    """total number of blocks in the sample"""
164
165    @property
166    def shape(self) -> PerMember[PerAxis[int]]:
167        return {mid: b.shape for mid, b in self.blocks.items()}
168
169    @property
170    def inner_shape(self) -> PerMember[PerAxis[int]]:
171        return {mid: b.inner_shape for mid, b in self.blocks.items()}

base class for SampleBlockMeta and SampleBlock

SampleBlockBase( sample_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], sample_id: Hashable, blocks: Dict[bioimageio.spec.model.v0_5.TensorId, ~BlockT], block_index: int, blocks_in_sample: int)

the sample shape this block represents a part of

sample_id: Hashable

identifier for the sample within its dataset

blocks: Dict[bioimageio.spec.model.v0_5.TensorId, ~BlockT]

Individual tensor blocks comprising this sample block

block_index: int

the n-th block of the sample

blocks_in_sample: int

total number of blocks in the sample

shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]]
165    @property
166    def shape(self) -> PerMember[PerAxis[int]]:
167        return {mid: b.shape for mid, b in self.blocks.items()}
inner_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]]
169    @property
170    def inner_shape(self) -> PerMember[PerAxis[int]]:
171        return {mid: b.inner_shape for mid, b in self.blocks.items()}
@dataclass
class LinearSampleAxisTransform(bioimageio.core.block_meta.LinearAxisTransform):
174@dataclass
175class LinearSampleAxisTransform(LinearAxisTransform):
176    member: MemberId
LinearSampleAxisTransform( axis: bioimageio.spec.model.v0_5.AxisId, scale: float, offset: int, member: bioimageio.spec.model.v0_5.TensorId)
179@dataclass
180class SampleBlockMeta(SampleBlockBase[BlockMeta]):
181    """Meta data of a dataset sample block"""
182
183    def get_transformed(
184        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
185    ) -> Self:
186        sample_shape = {
187            m: {
188                a: (
189                    trf
190                    if isinstance(trf, int)
191                    else trf.compute(self.sample_shape[trf.member][trf.axis])
192                )
193                for a, trf in new_axes[m].items()
194            }
195            for m in new_axes
196        }
197
198        def get_member_halo(m: MemberId, round: Callable[[float], int]):
199            return {
200                a: (
201                    Halo(0, 0)
202                    if isinstance(trf, int)
203                    or trf.axis not in self.blocks[trf.member].halo
204                    else Halo(
205                        round(self.blocks[trf.member].halo[trf.axis].left * trf.scale),
206                        round(self.blocks[trf.member].halo[trf.axis].right * trf.scale),
207                    )
208                )
209                for a, trf in new_axes[m].items()
210            }
211
212        halo: Dict[MemberId, Dict[AxisId, Halo]] = {}
213        for m in new_axes:
214            halo[m] = get_member_halo(m, floor)
215            if halo[m] != get_member_halo(m, ceil):
216                raise ValueError(
217                    f"failed to unambiguously scale halo {halo[m]} with {new_axes[m]}"
218                    + f" for {m}."
219                )
220
221        inner_slice = {
222            m: {
223                a: (
224                    SliceInfo(0, trf)
225                    if isinstance(trf, int)
226                    else SliceInfo(
227                        trf.compute(
228                            self.blocks[trf.member].inner_slice[trf.axis].start
229                        ),
230                        trf.compute(self.blocks[trf.member].inner_slice[trf.axis].stop),
231                    )
232                )
233                for a, trf in new_axes[m].items()
234            }
235            for m in new_axes
236        }
237        return self.__class__(
238            blocks={
239                m: BlockMeta(
240                    sample_shape=sample_shape[m],
241                    inner_slice=inner_slice[m],
242                    halo=halo[m],
243                    block_index=self.block_index,
244                    blocks_in_sample=self.blocks_in_sample,
245                )
246                for m in new_axes
247            },
248            sample_shape=sample_shape,
249            sample_id=self.sample_id,
250            block_index=self.block_index,
251            blocks_in_sample=self.blocks_in_sample,
252        )
253
254    def with_data(self, data: PerMember[Tensor], *, stat: Stat) -> SampleBlock:
255        return SampleBlock(
256            sample_shape={
257                m: {
258                    a: data[m].tagged_shape[a] if s == -1 else s
259                    for a, s in member_shape.items()
260                }
261                for m, member_shape in self.sample_shape.items()
262            },
263            sample_id=self.sample_id,
264            blocks={
265                m: Block.from_meta(b, data=data[m]) for m, b in self.blocks.items()
266            },
267            stat=stat,
268            block_index=self.block_index,
269            blocks_in_sample=self.blocks_in_sample,
270        )

Meta data of a dataset sample block

SampleBlockMeta( sample_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], sample_id: Hashable, blocks: Dict[bioimageio.spec.model.v0_5.TensorId, ~BlockT], block_index: int, blocks_in_sample: int)
def get_transformed( self, new_axes: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, Union[LinearSampleAxisTransform, int]]]) -> Self:
183    def get_transformed(
184        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
185    ) -> Self:
186        sample_shape = {
187            m: {
188                a: (
189                    trf
190                    if isinstance(trf, int)
191                    else trf.compute(self.sample_shape[trf.member][trf.axis])
192                )
193                for a, trf in new_axes[m].items()
194            }
195            for m in new_axes
196        }
197
198        def get_member_halo(m: MemberId, round: Callable[[float], int]):
199            return {
200                a: (
201                    Halo(0, 0)
202                    if isinstance(trf, int)
203                    or trf.axis not in self.blocks[trf.member].halo
204                    else Halo(
205                        round(self.blocks[trf.member].halo[trf.axis].left * trf.scale),
206                        round(self.blocks[trf.member].halo[trf.axis].right * trf.scale),
207                    )
208                )
209                for a, trf in new_axes[m].items()
210            }
211
212        halo: Dict[MemberId, Dict[AxisId, Halo]] = {}
213        for m in new_axes:
214            halo[m] = get_member_halo(m, floor)
215            if halo[m] != get_member_halo(m, ceil):
216                raise ValueError(
217                    f"failed to unambiguously scale halo {halo[m]} with {new_axes[m]}"
218                    + f" for {m}."
219                )
220
221        inner_slice = {
222            m: {
223                a: (
224                    SliceInfo(0, trf)
225                    if isinstance(trf, int)
226                    else SliceInfo(
227                        trf.compute(
228                            self.blocks[trf.member].inner_slice[trf.axis].start
229                        ),
230                        trf.compute(self.blocks[trf.member].inner_slice[trf.axis].stop),
231                    )
232                )
233                for a, trf in new_axes[m].items()
234            }
235            for m in new_axes
236        }
237        return self.__class__(
238            blocks={
239                m: BlockMeta(
240                    sample_shape=sample_shape[m],
241                    inner_slice=inner_slice[m],
242                    halo=halo[m],
243                    block_index=self.block_index,
244                    blocks_in_sample=self.blocks_in_sample,
245                )
246                for m in new_axes
247            },
248            sample_shape=sample_shape,
249            sample_id=self.sample_id,
250            block_index=self.block_index,
251            blocks_in_sample=self.blocks_in_sample,
252        )
def with_data( self, data: Mapping[bioimageio.spec.model.v0_5.TensorId, bioimageio.core.Tensor], *, stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer>, return_type=PydanticUndefined, when_used='always')]]]) -> SampleBlock:
254    def with_data(self, data: PerMember[Tensor], *, stat: Stat) -> SampleBlock:
255        return SampleBlock(
256            sample_shape={
257                m: {
258                    a: data[m].tagged_shape[a] if s == -1 else s
259                    for a, s in member_shape.items()
260                }
261                for m, member_shape in self.sample_shape.items()
262            },
263            sample_id=self.sample_id,
264            blocks={
265                m: Block.from_meta(b, data=data[m]) for m, b in self.blocks.items()
266            },
267            stat=stat,
268            block_index=self.block_index,
269            blocks_in_sample=self.blocks_in_sample,
270        )
@dataclass
class SampleBlock(bioimageio.core.sample.SampleBlockBase[bioimageio.core.block.Block]):
273@dataclass
274class SampleBlock(SampleBlockBase[Block]):
275    """A block of a dataset sample"""
276
277    stat: Stat
278    """computed statistics"""
279
280    @property
281    def members(self) -> PerMember[Tensor]:
282        """the sample block's tensors"""
283        return {m: b.data for m, b in self.blocks.items()}
284
285    def get_transformed_meta(
286        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
287    ) -> SampleBlockMeta:
288        return SampleBlockMeta(
289            sample_id=self.sample_id,
290            blocks=dict(self.blocks),
291            sample_shape=self.sample_shape,
292            block_index=self.block_index,
293            blocks_in_sample=self.blocks_in_sample,
294        ).get_transformed(new_axes)

A block of a dataset sample

SampleBlock( sample_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], sample_id: Hashable, blocks: Dict[bioimageio.spec.model.v0_5.TensorId, ~BlockT], block_index: int, blocks_in_sample: int, stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer>, return_type=PydanticUndefined, when_used='always')]]])
stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator at 0x7f9a7099e840>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer at 0x7f9a7099ea20>, return_type=PydanticUndefined, when_used='always')]]]

computed statistics

280    @property
281    def members(self) -> PerMember[Tensor]:
282        """the sample block's tensors"""
283        return {m: b.data for m, b in self.blocks.items()}

the sample block's tensors

def get_transformed_meta( self, new_axes: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, Union[LinearSampleAxisTransform, int]]]) -> SampleBlockMeta:
285    def get_transformed_meta(
286        self, new_axes: PerMember[PerAxis[Union[LinearSampleAxisTransform, int]]]
287    ) -> SampleBlockMeta:
288        return SampleBlockMeta(
289            sample_id=self.sample_id,
290            blocks=dict(self.blocks),
291            sample_shape=self.sample_shape,
292            block_index=self.block_index,
293            blocks_in_sample=self.blocks_in_sample,
294        ).get_transformed(new_axes)
@dataclass
class SampleBlockWithOrigin(bioimageio.core.sample.SampleBlockBase[bioimageio.core.block.Block]):
297@dataclass
298class SampleBlockWithOrigin(SampleBlock):
299    """A `SampleBlock` with a reference (`origin`) to the whole `Sample`"""
300
301    origin: Sample
302    """the sample this sample block was taken from"""

A SampleBlock with a reference (origin) to the whole Sample

SampleBlockWithOrigin( sample_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], sample_id: Hashable, blocks: Dict[bioimageio.spec.model.v0_5.TensorId, ~BlockT], block_index: int, blocks_in_sample: int, stat: Dict[Annotated[Union[Annotated[Union[bioimageio.core.stat_measures.SampleMean, bioimageio.core.stat_measures.SampleStd, bioimageio.core.stat_measures.SampleVar, bioimageio.core.stat_measures.SampleQuantile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Annotated[Union[bioimageio.core.stat_measures.DatasetMean, bioimageio.core.stat_measures.DatasetStd, bioimageio.core.stat_measures.DatasetVar, bioimageio.core.stat_measures.DatasetPercentile], Discriminator(discriminator='name', custom_error_type=None, custom_error_message=None, custom_error_context=None)]], Discriminator(discriminator='scope', custom_error_type=None, custom_error_message=None, custom_error_context=None)], Union[float, Annotated[bioimageio.core.Tensor, BeforeValidator(func=<function tensor_custom_before_validator>, json_schema_input_type=PydanticUndefined), PlainSerializer(func=<function tensor_custom_serializer>, return_type=PydanticUndefined, when_used='always')]]], origin: Sample)
origin: Sample

the sample this sample block was taken from

def sample_block_meta_generator( blocks: Iterable[Mapping[bioimageio.spec.model.v0_5.TensorId, bioimageio.core.BlockMeta]], *, sample_shape: Mapping[bioimageio.spec.model.v0_5.TensorId, Mapping[bioimageio.spec.model.v0_5.AxisId, int]], sample_id: Hashable):
316def sample_block_meta_generator(
317    blocks: Iterable[PerMember[BlockMeta]],
318    *,
319    sample_shape: PerMember[PerAxis[int]],
320    sample_id: SampleId,
321):
322    for member_blocks in blocks:
323        cons = _ConsolidatedMemberBlocks(member_blocks)
324        yield SampleBlockMeta(
325            blocks=dict(member_blocks),
326            sample_shape=sample_shape,
327            sample_id=sample_id,
328            block_index=cons.block_index,
329            blocks_in_sample=cons.blocks_in_sample,
330        )
def sample_block_generator( blocks: Iterable[Mapping[bioimageio.spec.model.v0_5.TensorId, bioimageio.core.BlockMeta]], *, origin: Sample, pad_mode: Literal['edge', 'reflect', 'symmetric']) -> Iterable[SampleBlockWithOrigin]:
333def sample_block_generator(
334    blocks: Iterable[PerMember[BlockMeta]],
335    *,
336    origin: Sample,
337    pad_mode: PadMode,
338) -> Iterable[SampleBlockWithOrigin]:
339    for member_blocks in blocks:
340        cons = _ConsolidatedMemberBlocks(member_blocks)
341        yield SampleBlockWithOrigin(
342            blocks={
343                m: Block.from_sample_member(
344                    origin.members[m], block=member_blocks[m], pad_mode=pad_mode
345                )
346                for m in origin.members
347            },
348            sample_shape=origin.shape,
349            origin=origin,
350            stat=origin.stat,
351            sample_id=origin.id,
352            block_index=cons.block_index,
353            blocks_in_sample=cons.blocks_in_sample,
354        )