neu_lib.piece

An array and the frame that says where it is: Piece.

piece.shape # (364, 244, 244) piece.bbox # BBox in the frame’s voxels: where it sits in its parent piece.bounds_nm # the same box in nm, as floats piece.crop(box) # a sub-piece, its origin shifted to match piece.kind # “segmentation”: what the voxels mean, if the source said piece.name # “gt/vol_03700”: what it is called, if the source said piece.to_numpy() # the voxels in CPU memory

Keeping the array and its origin in one object is the point: carried separately, the origin is easy to drop, and the data then lands at nm zero with no error.

Coordinates are the frame’s voxels, not array indices: a piece read from voxel 7819 of a level has bbox.lo == 7819, the number needed to place it back or compare it with a sibling. bbox is an integer BBox; bounds_nm is floats and deliberately not a BBox, since rounding a physical box to integers would move it.

Attributes

Classes

Piece

A 3-D zyx array, optionally with a leading channel axis, and its Frame.

Module Contents

class neu_lib.piece.Piece

A 3-D zyx array, optionally with a leading channel axis, and its Frame.

frame.origin_nm is where the array’s (0, 0, 0) sits in nm, so a piece cropped out of a volume stays in that volume’s coordinates.

kind (one of KINDS, or None if unknown) is never guessed from the dtype: a uint8 label array looks exactly like an image. name is a label for display, not an identity; neu_vol.read_piece() fills both in from the source.

The array may be any array type with shape, dtype and ndim, including cupy, dask, zarr and h5py. The piece is frozen but the array is not copied or made read-only: piece.array[...] = 0 mutates it, and whatever it is a view of. Use copy() before editing in place.

apply(fn: Any, *args, frame: neu_lib.frame.Frame | None = None, kind: Any = _UNSET, name: Any = _UNSET, **kwargs) → Piece

fn(array, *args, **kwargs) as a new piece, keeping everything else.

piece.apply(lambda a: a > 0.5, kind=”segmentation”) piece.apply(scipy.ndimage.gaussian_filter, sigma=2) piece.apply(dilate).apply(label, kind=”segmentation”)

The frame, kind and name carry over, with two guards:

  • A change of spatial shape needs ``frame=``. A 2x downsample halves the shape and doubles the voxel size; keeping the old frame would place the result at half its real size. No factor is guessed, since real pyramids are anisotropic.

  • A change of dtype needs ``kind=``: a member of KINDS, "same", or None for unknown. A dtype change is the visible sign that the meaning may have changed (a probability map thresholded to a mask, a mask labelled), and carrying "probability" through it would later allow averaging label ids.

name carries over unless given; name=None clears it.

copy(**changes) → Piece

A copy, with any field replaced — and the array copied too.

out = piece.copy(array=gaussian(piece.array)) # a transform, same frame out = piece.copy(kind=”probability”) # relabel what it is out = piece.copy(); out.array[mask] = 0 # then edit in place, safely

Unlike dataclasses.replace, which pairs the same array with new metadata, so that editing the result edits the original. Passing array= skips the copy. The frame is shared, which is safe because it is frozen. Fields are validated as usual.

crop(box: Any) → Piece

The sub-piece inside box (a BBox or (lo, hi)), given in the frame’s voxels like bbox, not in array indices. The result’s origin is shifted to match, so it stays in the parent’s coordinates.

to_cpu() → Piece

The same piece with its array in CPU memory; unchanged if it already is.

to_nm(points_zyx: Sequence | numpy.ndarray) → numpy.ndarray

Voxel coordinates of this piece’s array, zyx, to nm.

to_numpy() → numpy.ndarray

The voxels as a CPU numpy array; for a cupy array, a copy off the GPU.

plt.imshow(piece.to_numpy()[32])

to_cpu() does the same but keeps the piece. There is deliberately no to_gpu: it would need cupy, and this package depends on numpy alone. Use neu_proc.ops.backend.to_gpu.

with_frame(frame: neu_lib.frame.Frame) → Piece

The same array in a different frame — for a source that recorded none.

with_kind(kind: str | None) → Piece

The same array and frame, saying what the voxels mean.

with_name(name: str | None) → Piece

The same piece under a different name.

array: numpy.ndarray
property bbox: neu_lib.grid.BBox

Where the piece sits, as a half-open box in the frame’s voxels (not array indices).

property bounds_nm: tuple[tuple[float, float, float], tuple[float, float, float]]

(lo, hi) in nm, as floats. Not a BBox, whose bounds are integers.

property channel_axis: bool

Whether there is a leading channel axis (the array is 4-D).

property channels: int
property dtype: numpy.dtype
frame: neu_lib.frame.Frame
property is_segmentation: bool

Whether coarsening this must take a mode rather than a mean. False when kind is unknown.

kind: str | None = None
name: str | None = None
property origin_nm: tuple[float, float, float]
property origin_voxel: tuple[int, int, int]

Where the array starts, in the frame’s voxels (what HDF5 pieces record as voxel_offset). Raises if the origin is not on the voxel grid, rather than rounding and shifting the piece by up to half a voxel.

property shape: tuple[int, ...]

The array’s full shape, channel axis included.

property spatial_shape: tuple[int, int, int]

The three spatial extents, zyx, whether or not there is a channel axis.

property voxel_size_nm: tuple[float, float, float]
neu_lib.piece.KINDS = ('image', 'probability', 'segmentation')