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