MibBigDataLabelsIndex¶
- class core.MibBigDataLabelsIndex¶
Bases:
core.@MibLabels.MibLabelsMIBBIGDATALABELSINDEX - read-only label overlay served from a pyramid the image does not share.
Subclass of
core.MibLabels. Attaches a remote or local OME-Zarr label pyramid to an already-open BigData image and serves it slice by slice as the view is read - nothing is downloaded in bulk andobj.datastays empty.Why a new class rather than a wider ``MibBigDataLabels``. Everything that forces a 63-material ceiling comes from the other branch of the hierarchy:
MibImage +-- MibLabels63 -- MibBigDataLabels -- MibBigDataLabelsZarr2 (packed byte, 63 max, editable store) +-- MibLabels -- MibBigDataLabelsIndex (separate layers, 65535+, read-only)MibLabels63packs six bits of material, bit 7 of mask and bit 8 of selection into one byte, so an instance segmentation’s object ids cannot survive it - 255 arrives as material 63 with the mask and selection bits set. This branch has no packing, so ids pass through untouched and the “no suitable class” problem dissolves. SubclassingMibLabels63instead would also fire everyisa(..., 'core.MibLabels63')branch in the codebase wrongly, starting withMibImage.getData:66.The one thing that must not be got wrong. The label pyramid does not start at the image’s resolution.
jrc_mus-kidney’snuchas 5 levels from 128 nm while the EM it segments has 12 from 8 nm, sonuc’s own level 0 is the EM’ss4.modelScaleFactorstherefore holds each level’s scale in the image’s level-0 voxels ([16 32 64 128 256]here), registered byio.loaders.OmeZarrMetadataUtils.registerLevelScalesat open time. Numbering the levels from the store’s own level 0 - which is whatMibBigDataLabelsZarr2.openStoredoes, correctly, for a store that shares the image’s resolution - would put every label at one-sixteenth of its true size with nothing to warn about it.Serving a fine view from a coarse level is then not a resize: the coarse block does not begin where the view begins.
getData()gathers one source voxel per screen pixel throughOmeZarrMetadataUtils.screenGridForRange/levelReadWindow; see those for the arithmetic and why a plainimresizeproduces plausible, misplaced labels.Read-only, and deliberately so. A remote label store belongs to another tool, its values are that tool’s labelling scheme, and MIB has no business writing into it.
setData()andsetDataFast()are blocked; to segment on the dataset, create a new model, which MIB writes to a store of its own.Read-only is not the same as un-exportable, though. “Save model as…” writes one chosen pyramid level to any of MIB’s model formats, streamed a slice at a time by
io.savers.MibImageSliceProviderthroughcore.MibLabels.save, so the volume is never gathered in memory. The level has to be chosen because these labels have no full-resolution level to default to - that is the whole reason this class exists - and writing the one the image is showing would mean upsampling the entire volume to a resolution the labels never had.Store access follows ``MibBigDataLabelsZarr2``, not ``MibVirtualImage``. Reads go through
io.zarr.ChunkCachekeyed on the level path plus the store version (io.zarr.ChunkCache.storeKey), so a store opened both as an image and as labels shares decoded chunks, nothing here writes, and a store replaced on disk is never served from the old one’s chunks.MibVirtualImage.getDataZarrwould have been the other candidate, but its defaults assumelevelImageSizes(1,:)is the full-resolution extent, which is untrue for an offset pyramid.- Constructor Summary
- MibBigDataLabelsIndex(img, meta)¶
MIBBIGDATALABELSINDEX - Construct an empty read-only label container.
- Syntax:
obj = core.MibBigDataLabelsIndex([], meta)
Same construction contract as
core.MibBigDataLabels: pass[]forimgand attach a store afterwards withopenStore(). Until thenobj.existsis false and reads return[]. There is nocreateStorecounterpart - a store MIB creates is always MIB’s own editable packed format, whichcore.MibBigDataLabelsowns.- Input Arguments:
img (optional) - [empty] pass
[]; pixel data is never held in memory for this class.meta (optional) - [dictionary] metadata dictionary used by the parent constructor chain to set dimensions. Default: empty MibImage info.
- Property Summary
- imageScaleFactors¶
[char] the store’s own declared C-order (e.g. ‘zyx’), from its multiscales.axes. A foreign store is read in whatever order it declared, so readLevel builds the bbox and permutes the result against this rather than assuming [y,x,z].
- modelArrayMeta¶
{1 x nLevels} io.zarr.Array handles, one per level, finest first.
- modelArrays¶
[string] zarr group root path or URL of the label pyramid.
- modelAxisOrder¶
[nLevels x 3] per-level [yScale, xScale, zScale] in the IMAGE’s level-0 voxels - NOT relative to this store’s own level 0. See the class note: this is the whole point of the class and the one field a wrong value in is invisible. Written only by openStore, from registerLevelScales.
- modelLevelNames¶
{1 x nLevels} full path or URL of each level array - the io.zarr.ChunkCache key, and the same one the image loaders use.
- modelLevelPaths¶
{1 x nLevels} io.zarr.Array.info() results, cached beside modelArrays so shape/chunkShape are not re-queried from the engine on every tile read.
- modelLevelSizes¶
{1 x nLevels} relative level paths within the group (‘s0’, ‘s1’, …).
- modelScaleFactors¶
[nLevels x 3] per-level [y, x, z] voxel counts, as the store holds them.
- modelStorePath¶
- readOnlyWarningShown¶
- renderPerObject¶
[nImageLevels x 3] the IMAGE pyramid’s own magnification axis, [y x z], level 0 being [1 1 1]. Needed because the overlay has to come back the size the image layer came back - which follows from the level the IMAGE is showing, not the one the labels are read from. getRGBimage composites the two with labeloverlay, where a one-row disagreement is an error.
- Method Summary
- countMaterials()¶
COUNTMATERIALS - Report the object count without reading the volume.
- Syntax:
result = obj.countMaterials()
Override of
core.MibLabels.countMaterials, which formaxMaterials >= 256scans every voxel of every time point for the highest index. Here that is a remote volume -jrc_mus-liver-6’sersegmentation is 510 GiB - so the count comes from the single small pyramid levelopenStore()probed instead, and this method only hands it back. The inherited version would in fact return 0 rather than reading anything, sinceobj.datais empty, but it would do so by accident; the number it should report is the one already in hand.- Output Arguments:
result - [numeric]
materialsCount, a lower bound on the object count when the store is an index map (see openStore), and 1 when the values are being collapsed to a single material
- getData(layerType, orient, colChannel, options)¶
GETDATA - Read a displayed label slice from the attached pyramid.
- Syntax:
dataset = obj.getData(layerType, orient, colChannel, options)
Override of
core.MibImage.getData, and load-bearing: that method dispatches togetData63only for acore.MibLabels63and otherwise reads the in-memoryobj.data, which for this class is permanently empty. Without this override the overlay is silently blank.The read is three steps, and the middle one is the whole feature:
Pick the level in the image’s scale space (
pickLevel()). AtmagFactor1 a pyramid starting at 128 nm has no level 1, so “nearest” returns its finest published level - which is the fallback, arrived at with no special case.Work out where the view’s pixels land on that level.
OmeZarrMetadataUtils.screenGridForRangesays where the image layer put each screen pixel in full-resolution space (and how many there are, which must match orlabeloverlayerrors);levelReadWindowturns that into one source voxel per pixel. Serving a fine view from a level 16x coarser is not a resize - resizing the covering block gives the wrong size AND a shifted origin. See those two methods for the arithmetic.Read and gather. One indexed read, no intermediate at full resolution.
- Input Arguments:
layerType (optional) - [char] layer to read:
'labels'- material indices, or a binary map whencolChannelis set'mask'/'selection'- correctly sized zeros; this class carries neither layer, and a caller asking for one wants an empty overlay, not an error'image'- treated as'labels'; the container IS the labels'everything'- errors. It means “the packed byte with all three layers”, which is structurally unavailable outside theMibLabels63family (getData3D.m:222makes the same point)
Default:
'labels'.orient (optional) - [numeric] viewing orientation,
1= XZ,2= YZ,3= YX. Default:3.colChannel (optional) - [numeric|empty] material index; when non-empty and reading labels, returns a binary
uint8map of that object - the store’s own ids, unaffected byrenderPerObject, which is a display setting rather than a property of the data.[]returns all indices, collapsed to 1 whenrenderPerObjectis false.options (optional) - [struct] with fields:
.magFactor- [numeric] display magnification. Default:1.pyramidLevel- [numeric] explicit 1-based label level; overridesmagFactorand returns that level’s own voxels with no display resampling, matchinggetData63’s meaning for the same field. This is the read contractio.savers.MibImageSliceProviderneeds, so it is also how a level of these labels is exported to a file.x/.y/.z- [1x2 numeric] screen ranges in full-resolution dataset coordinates. Default: the whole dataset on that axis
- Output Arguments:
dataset -
[ny, nx, nz]of classdataClass(uint8for a single material or a mask/selection request), at display resolution and the same size the image layer returns for the same request.[]when no store is attached.
- static imageReference(image)¶
IMAGEREFERENCE - Describe an open image in the absolute terms openStore needs.
- Syntax:
reference = core.MibBigDataLabelsIndex.imageReference(dataset.image)
Shared by the two places that have to answer “can this label pyramid be placed on the open image”:
MibModel.loadModel, which then attaches it, andcontrollers.SelectFromUrl.resolveLabelRoute, which only needs to know before Open is pressed. Keeping one implementation is what stops the panel promising a route the loader then refuses.The level voxel sizes come from ``pyramid.levelScaleFactors``, not from
pyramid.levelVoxelSizes, deliberately:levelScaleFactorsis the tablegetDataZarritself picks levels with, so registering against it guarantees the overlay’s idea of the image’s magnification axis is the one the image actually uses. Anisotropic voxels cancel in the division, so it holds for them too.- Input Arguments:
image - [core.MibImage] the dataset’s image layer; needs a
pyramidwithlevelScaleFactorsand aboundingBox
- Output Arguments:
reference - [struct] with
.ok/.reason, plus the three fieldsopenStore()reads:.shapeYXZ,.voxelSizesXYZ(micrometres) and.outerBoxUm(edge-based, micrometres)
- imageScaleForMagFactor(magFactor)¶
IMAGESCALEFORMAGFACTOR - Which image level the view is being served from.
- Syntax:
imageScaleYXZ = obj.imageScaleForMagFactor(magFactor)
Mirrors
getDataZarr:65-67exactly, because the overlay has to be the size the image came back and that size follows from the image’s level, not the label’s. Kept as its own method so the mirroring is one place that can be compared against the original.- Input Arguments:
magFactor - [numeric] current display magnification
- Output Arguments:
imageScaleYXZ - [1x3 numeric] that level’s
[y x z]scale factors;[1 1 1]when no image pyramid was registered
- openStore(storePath, imageReference)¶
OPENSTORE - Attach an existing label pyramid, registered against the open image.
- Syntax:
obj.openStore(storePath, imageReference)
Opens every level of an OME-Zarr label group as an
io.zarr.Arrayand places the pyramid inside the image’s scale space rather than its own. The store is never written to, and nothing is read here except metadata plus one small pyramid level - the finest that fits a fixed voxel budget - used to estimate the object count.The registration is the point. A label pyramid published from a coarse level down - which is what a whole-volume inference segmentation is - has its own level 0 somewhere in the middle of the image’s magnification axis:
jrc_mus-kidney’snucstarts at 128 nm, which is the EM’ss4.modelScaleFactorsis therefore[16 32 64 128 256]and not[1 2 4 8 16].io.loaders.OmeZarrMetadataUtils.registerLevelScalesderives that and refuses the pairing outright when the two pyramids do not cover the same volume, which is the only thing standing between a scale factor and labels placed at the origin at the wrong size.The dimensions come from the image, not the store.
height/width/depthare the image’s full-resolution extent, because that is the coordinate system every caller asks in -options.x/y/zare dataset coordinates, andgetData()maps them onto whichever level serves the request.pixSizeandboundingBoxare deliberately not set here; they belong to the dataset and are applied by the caller throughMibDataset.setPixSize, the same way every other model type receives them.- Input Arguments:
storePath - [char|string] path or URL of the OME-Zarr label group; v2 (
.zattrs) and v3 (zarr.json) are both detected, since the level arrays are opened throughio.zarr.Array, which reads eitherimageReference - [struct] describing the image this attaches to, with fields:
.shapeYXZ- [1x3 numeric] the image’s full-resolution[y x z]voxel counts.voxelSizesXYZ- [nImageLevels x 3 numeric] voxel size[x y z]per image level in micrometres, row 1 being full resolution; micrometres because the two stores may declare different units and an absolute one is the only one that cannot be misread.outerBoxUm- [1x6 numeric] the image’s outer physical extent[xmin xmax ymin ymax zmin zmax]in micrometres, edge-based asOmeZarrMetadataUtils.outerBoundingBoxreturns it
Raises an error rather than half-attaching: an unreadable store, a group with no multiscales, or a pyramid that does not register all throw, because the caller decided this route was available before getting here and a silent fallback would put the labels somewhere plausible and wrong.
- pickLevel(options)¶
PICKLEVEL - Choose which label level serves a read.
- Syntax:
levelIndex = obj.pickLevel(options)
Nearest level on the scale axis, the same rule
core.MibBigDataLabels.pickLevelandgetDataZarruse - but overmodelScaleFactors, which is in the image’s scale space, so “nearest to magFactor 1” is the finest level the labels HAVE rather than a level that does not exist. That is what makes the missing fine levels fall back to the finest published one, with no special case.- Input Arguments:
options - [struct] with optional
.pyramidLevel(explicit 1-based level, wins outright) and.magFactor
- Output Arguments:
levelIndex - [numeric] 1-based level, clamped to the pyramid
- readLevel(levelIndex, Ylim, Xlim, Zlim, keepObjectIds)¶
READLEVEL - Read a [ny x nx x nz] block of one level, in MIB’s axis order.
- Syntax:
block = obj.readLevel(levelIndex, Ylim, Xlim, Zlim) block = obj.readLevel(levelIndex, Ylim, Xlim, Zlim, keepObjectIds)
The store’s own values, with no bit-unpacking to undo - a foreign label array holds plain indices. Reads are served through
io.zarr.ChunkCacheon whole decoded chunks, exactly as the image loaders do; safe without invalidation precisely because this class never writes.The single-material collapse is applied here, after the cache, so the cache keeps holding raw chunks keyed by level path and stays shareable with the same store opened as an image.
keepObjectIdssuppresses that collapse.renderPerObjectis a display choice - “show this instance segmentation as one material” - and a caller naming a single object id is not displaying, it is asking about that object. Collapsing first makes the question unanswerable: every id has already become 1, so id 1 matches the union of every object in the volume and every other id matches nothing. That is one merged surface instead of 2165, and one merged volume out of “Save model as…” with a material index set.- Input Arguments:
levelIndex - [numeric] 1-based level
Ylim / Xlim / Zlim - [1x2 numeric] 1-based inclusive ranges in that level’s own voxels
keepObjectIds (optional) - [logical] return the store’s own ids even when
renderPerObjectis false. Default:false
- Output Arguments:
block - [ny x nx x nz] of class
dataClass
- setData(dataset, layerType, orient, colChannel, options)¶
#ok<INUSD> SETDATA - Blocked: an imported label pyramid is read-only.
- Syntax:
result = obj.setData(dataset, layerType, orient, colChannel, options)
Override of
core.MibImage.setData. Never touches the store or any in-memory state, and returnsfalseso a caller that checks gets an honest answer. The notice is shown once per session, on the first blocked attempt only: a single paint stroke firessetDataon every mouse-move.- Output Arguments:
result - [logical] always false
- setDataFast(dataset, z, colChannel, t)¶
#ok<INUSD> SETDATAFAST - Blocked: the in-place write path has no target here.
Override of
core.MibImage.setDataFast.MibDataset’s fast paths gate ondatasetType == 'Standard'and so never reach a BigData buffer, but the inherited version writes straight intoobj.data- which is empty here, so a stray call would silently grow a full-resolution array in memory rather than fail. Blocked for that reason rather than for symmetry.