Skip to content

block_meta ¤

Classes:

Name Description
BlockMeta

Block meta data of a sample member (a tensor in a sample)

LinearAxisTransform

Functions:

Name Description
split_multiple_shapes_into_blocks
split_shape_into_blocks

BlockMeta dataclass ¤

BlockMeta(sample_shape: PerAxis[int], inner_slice: PerAxis[SliceInfo], halo: PerAxis[Halo], block_index: BlockIndex, blocks_in_sample: TotalNumberOfBlocks)

Block meta data of a sample member (a tensor in a sample)

Figure for illustration: The first 2d block (dashed) of a sample member (bold). The inner slice (thin) is expanded by a halo in both dimensions on both sides. The outer slice reaches from the sample member origin (0, 0) to the right halo point.

first block (at the sample origin)
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┐
╷ halo(left)                         ╷
╷         padding outside the sample ╷
╷  (0, 0)┏━━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━➔
╷        ┃                 │         ╷  sample member
╷        ┃      inner      │  outer  ╷
╷        ┃      region     │  region ╷
╷        ┃      /slice     │  /slice ╷
╷        ┃                 │         ╷
╷        ┣─────────────────┘         ╷
╷        ┃   outer region/slice      ╷
╷        ┃               halo(right) ╷
└ ─ ─ ─ ─┃─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┘
         ⬇

Note: - Inner and outer slices are specified in sample member coordinates. - The outer_slice of a block at the sample edge may overlap by more than the halo with the neighboring block (the inner slices will not overlap though).

Methods:

Name Description
__post_init__
get_transformed

Attributes:

Name Type Description
block_index BlockIndex

the i-th block of the sample

blocks_in_sample TotalNumberOfBlocks

total number of blocks in the sample

dims Collection[AxisId]
halo PerAxis[Halo]

halo enlarging the inner region to the block's sizes

inner_shape PerAxis[int]

axis lengths of the inner region (without halo)

inner_slice PerAxis[SliceInfo]

inner region (without halo) wrt the sample

inner_slice_wo_overlap PerAxis[SliceInfo]

subslice of the inner slice, such that all inner_slice_wo_overlap can be

local_slice PerAxis[SliceInfo]

inner slice wrt the block, not the sample

outer_slice PerAxis[SliceInfo]

slice of the outer block (without padding) wrt the sample

padding PerAxis[PadWidth]

padding to realize the halo at the sample edge

sample_shape PerAxis[int]

the axis sizes of the whole (unblocked) sample

shape PerAxis[int]

axis lengths of the block

tagged_shape PerAxis[int]

alias for shape

block_index instance-attribute ¤

block_index: BlockIndex

the i-th block of the sample

blocks_in_sample instance-attribute ¤

blocks_in_sample: TotalNumberOfBlocks

total number of blocks in the sample

dims property ¤

dims: Collection[AxisId]

halo instance-attribute ¤

halo: PerAxis[Halo]

halo enlarging the inner region to the block's sizes

inner_shape cached property ¤

inner_shape: PerAxis[int]

axis lengths of the inner region (without halo)

inner_slice instance-attribute ¤

inner_slice: PerAxis[SliceInfo]

inner region (without halo) wrt the sample

inner_slice_wo_overlap property ¤

inner_slice_wo_overlap: PerAxis[SliceInfo]

subslice of the inner slice, such that all inner_slice_wo_overlap can be stiched together trivially to form the original sample.

This can also be used to calculate statistics without overrepresenting block edge regions.

local_slice cached property ¤

local_slice: PerAxis[SliceInfo]

inner slice wrt the block, not the sample

outer_slice cached property ¤

outer_slice: PerAxis[SliceInfo]

slice of the outer block (without padding) wrt the sample

padding cached property ¤

padding: PerAxis[PadWidth]

padding to realize the halo at the sample edge where we cannot simply enlarge the inner slice

sample_shape instance-attribute ¤

sample_shape: PerAxis[int]

the axis sizes of the whole (unblocked) sample

shape cached property ¤

shape: PerAxis[int]

axis lengths of the block

tagged_shape property ¤

tagged_shape: PerAxis[int]

alias for shape

__post_init__ ¤

__post_init__()
Source code in src/bioimageio/core/block_meta.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
def __post_init__(self):
    # freeze mutable inputs
    if not isinstance(self.sample_shape, Frozen):
        object.__setattr__(self, "sample_shape", Frozen(self.sample_shape))

    if not isinstance(self.inner_slice, Frozen):
        object.__setattr__(self, "inner_slice", Frozen(self.inner_slice))

    if not isinstance(self.halo, Frozen):
        object.__setattr__(self, "halo", Frozen(self.halo))

    assert all(a in self.sample_shape for a in self.inner_slice), (
        "block has axes not present in sample"
    )

    assert all(a in self.inner_slice for a in self.halo), (
        "halo has axes not present in block"
    )

    if any(s > self.sample_shape[a] for a, s in self.shape.items()):
        logger.warning(
            "block {} larger than sample {}", self.shape, self.sample_shape
        )

get_transformed ¤

get_transformed(new_axes: PerAxis[Union[LinearAxisTransform, int]]) -> Self
Source code in src/bioimageio/core/block_meta.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
def get_transformed(
    self, new_axes: PerAxis[Union[LinearAxisTransform, int]]
) -> Self:
    return self.__class__(
        sample_shape={
            a: (
                trf
                if isinstance(trf, int)
                else trf.compute(self.sample_shape[trf.axis])
            )
            for a, trf in new_axes.items()
        },
        inner_slice={
            a: (
                SliceInfo(0, trf)
                if isinstance(trf, int)
                else SliceInfo(
                    trf.compute(self.inner_slice[trf.axis].start),
                    trf.compute(self.inner_slice[trf.axis].stop),
                )
            )
            for a, trf in new_axes.items()
        },
        halo={
            a: (
                Halo(0, 0)
                if isinstance(trf, int)
                else Halo(self.halo[trf.axis].left, self.halo[trf.axis].right)
            )
            for a, trf in new_axes.items()
        },
        block_index=self.block_index,
        blocks_in_sample=self.blocks_in_sample,
    )

LinearAxisTransform dataclass ¤

LinearAxisTransform(axis: AxisId, scale: float, offset: int)

Methods:

Name Description
compute

Attributes:

Name Type Description
axis AxisId
offset int
scale float

axis instance-attribute ¤

axis: AxisId

offset instance-attribute ¤

offset: int

scale instance-attribute ¤

scale: float

compute ¤

compute(s: int, round: Callable[[float], int] = floor) -> int
Source code in src/bioimageio/core/block_meta.py
41
42
def compute(self, s: int, round: Callable[[float], int] = floor) -> int:
    return round(s * self.scale) + self.offset

split_multiple_shapes_into_blocks ¤

split_multiple_shapes_into_blocks(shapes: PerMember[PerAxis[int]], block_shapes: PerMember[PerAxis[int]], *, halo: PerMember[PerAxis[HaloLike]], strides: Optional[PerMember[PerAxis[int]]] = None, broadcast: bool = False) -> Tuple[TotalNumberOfBlocks, Iterable[PerMember[BlockMeta]]]
Source code in src/bioimageio/core/block_meta.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
def split_multiple_shapes_into_blocks(
    shapes: PerMember[PerAxis[int]],
    block_shapes: PerMember[PerAxis[int]],
    *,
    halo: PerMember[PerAxis[HaloLike]],
    strides: Optional[PerMember[PerAxis[int]]] = None,
    broadcast: bool = False,
) -> Tuple[TotalNumberOfBlocks, Iterable[PerMember[BlockMeta]]]:
    if unknown_blocks := [t for t in block_shapes if t not in shapes]:
        raise ValueError(
            f"block shape specified for unknown tensors: {unknown_blocks}."
        )

    if not block_shapes:
        block_shapes = shapes

    if not broadcast and (
        missing_blocks := [t for t in shapes if t not in block_shapes]
    ):
        raise ValueError(
            f"no block shape specified for {missing_blocks}."
            + " Set `broadcast` to True if these tensors should be repeated"
            + " as a whole for each block."
        )

    if extra_halo := [t for t in halo if t not in block_shapes]:
        raise ValueError(
            f"`halo` specified for tensors without block shape: {extra_halo}."
        )

    if strides is None:
        strides = {}

    assert not (unknown_block := [t for t in strides if t not in block_shapes]), (
        f"`stride` specified for tensors without block shape: {unknown_block}"
    )

    blocks: Dict[MemberId, Iterable[BlockMeta]] = {}
    n_blocks: Dict[MemberId, TotalNumberOfBlocks] = {}
    for t in block_shapes:
        n_blocks[t], blocks[t] = split_shape_into_blocks(
            shape=shapes[t],
            block_shape=block_shapes[t],
            halo=halo.get(t, {}),
            stride=strides.get(t),
        )
        assert n_blocks[t] > 0, n_blocks

    assert len(blocks) > 0, blocks
    assert len(n_blocks) > 0, n_blocks
    unique_n_blocks = set(n_blocks.values())
    n = max(unique_n_blocks)
    if len(unique_n_blocks) == 2 and 1 in unique_n_blocks:
        if not broadcast:
            raise ValueError(
                "Mismatch for total number of blocks due to unsplit (single block)"
                + f" tensors: {n_blocks}. Set `broadcast` to True if you want to"
                + " repeat unsplit (single block) tensors."
            )

        blocks = {
            t: _repeat_single_block(block_gen, n) if n_blocks[t] == 1 else block_gen
            for t, block_gen in blocks.items()
        }
    elif len(unique_n_blocks) != 1:
        raise ValueError(f"Mismatch for total number of blocks: {n_blocks}")

    return n, _aligned_blocks_generator(n, blocks)

split_shape_into_blocks ¤

split_shape_into_blocks(shape: PerAxis[int], block_shape: PerAxis[int], halo: PerAxis[HaloLike], stride: Optional[PerAxis[int]] = None) -> Tuple[TotalNumberOfBlocks, Generator[BlockMeta, Any, None]]
Source code in src/bioimageio/core/block_meta.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
def split_shape_into_blocks(
    shape: PerAxis[int],
    block_shape: PerAxis[int],
    halo: PerAxis[HaloLike],
    stride: Optional[PerAxis[int]] = None,
) -> Tuple[TotalNumberOfBlocks, Generator[BlockMeta, Any, None]]:
    assert all(a in shape for a in block_shape), (
        tuple(shape),
        set(block_shape),
    )
    if any(shape[a] < block_shape[a] for a in block_shape):
        # TODO: allow larger blockshape
        raise ValueError(f"shape {shape} is smaller than block shape {block_shape}")

    assert all(a in shape for a in halo), (tuple(shape), set(halo))

    # fill in default halo (0) and block axis length (from tensor shape)
    halo = {a: Halo.create(halo.get(a, 0)) for a in shape}
    block_shape = {a: block_shape.get(a, s) for a, s in shape.items()}
    if stride is None:
        stride = {}

    inner_1d_slices: Dict[AxisId, List[SliceInfo]] = {}
    for a, s in shape.items():
        inner_size = block_shape[a] - sum(halo[a])
        stride_1d = stride.get(a, inner_size)
        inner_1d_slices[a] = [
            SliceInfo(min(p, s - inner_size), min(p + inner_size, s))
            for p in range(0, s, stride_1d)
        ]

    n_blocks = prod(map(len, inner_1d_slices.values()))

    return n_blocks, _block_meta_generator(
        shape,
        blocks_in_sample=n_blocks,
        inner_1d_slices=inner_1d_slices,
        halo=halo,
    )