cellmap_flow.portable.fill
Labelling a box of an annotation volume: the decisions behind the Finetune
tab’s Seed, All Background and Split Objects and an accepted AI annotation
(finetune/session/fill.py says what each is for), on arrays alone, so the
server makes them over MinIO’s zarr and the browser over its paint store’s
chunks (portable/fill_calls.py for the Label it tools,
portable/ai_annotate_calls.py for an accept).
box_voxels: the annotation voxels a box in world nm covers.seed_labels: the ids a box gets from a segmentation: the id the user painted on an object kept, fresh ones for the rest (fresh_ids).relabel_objects: the box’s foreground relabelled by connected component.paint,rewriteandrestore: which voxels a write changes and an undo puts back, with the counts the routes answer.
Nothing here reads or writes a volume: the callers read the box, hand it in
and write back what comes out (finetune/session/fill.py through zarr
over MinIO, web/src/backend/routes/view-labels.ts and
web/src/backend/ai-annotate-job.ts through the paint store’s
web/src/ai-annotate/fill.ts).
Functions
|
The world position of an annotation volume's voxel-0 lower corner, in nm. |
|
|
|
|
|
Whether new objects' ids count up from the box's largest ( |
|
Labels for a box from a segmentation: an id per object inside, 1 outside. |
|
An id per connected object of |
|
The box's foreground (labels 2 and up) relabelled by connected component. |
|
The box after |
|
The box after |
|
The box put back to |
Module Contents
- cellmap_flow.portable.fill.volume_corner_nm(dataset_offset_nm, output_voxel_size) numpy.ndarray
The world position of an annotation volume’s voxel-0 lower corner, in nm.
dataset_offset_nmis voxel 0’s centre, the OME translation, asfinetune.session.volume.volume_corner_nmreads it (its docstring says why), so the corner is half a voxel before it; voxel 0’s corner is at minus half a voxel when the volume has no offset.
- cellmap_flow.portable.fill.box_voxels(volume, offset_nm, shape_nm, volume_shape)
(lo, hi): the annotation voxels whose centres lie in a box, clipped to the volume.offset_nmandshape_nmare the box’s lower corner and size in world nm, as a good region records them;volumeis the volume’s record, for its grid (output_voxel_size,dataset_offset_nm). None when no voxel of the volume is in the box.
- cellmap_flow.portable.fill.fresh_ids(existing, count, count_up)
countids for new objects in a box that holdsexisting.count_up: from one past the box’s largest label, so an affinity target’s objects stay apart from every object nearby (ids elsewhere in the volume are not looked at: affinities only compare nearby voxels). Else the lowest ids the box does not use, so a uint8 volume’s 254 ids are never run through: a repeat in another box is only a repeated colour, which a binary or distance target does not see. Raises ValueError when the dtype has no room.
- cellmap_flow.portable.fill.counts_up(instance_target, dtype) bool
Whether new objects’ ids count up from the box’s largest (
fresh_ids): for a model trained on instances (instance_target: affinities, or Cellpose’s flows) whose volume holds more than 8-bit ids. A uint8 volume reuses the ids its box leaves free, whatever the model, or its 254 would soon run out.
- cellmap_flow.portable.fill.seed_labels(objects, existing, count_up=False)
Labels for a box from a segmentation: an id per object inside, 1 outside.
objectsis a segmenter’s output over the box (0 background, objects 1..n),existingwhat the box holds now. Each object gets an id of its own (fresh_ids;count_upfor an instance target), so neighbouring objects are told apart, and a merge the model made shows as one colour to split; but an object the user has already painted part of takes the id painted there most, or the unpainted rest of it would be taught as a different object from its painted part. A binary or distance target reads every id as foreground, so the ids cost it nothing.
- cellmap_flow.portable.fill.connected_components(mask, connectivity=1, per_slice=False)
An id per connected object of
mask(its nonzero voxels), 1..n, 0 elsewhere.scipy.ndimage.labelwith the structure ofconnectivity(1 face neighbours, 2 and edges, 3 every neighbour, at most the array’s dimensions), numbered as it numbers them: by the raster order of each object’s first voxel.per_slice: each z slice of a (z, y, x) mask is labelled on its own, in 2D, its ids following the slice before’s. Aspost.segment.connected_componentslabels a seed’s objects, without its minimum size.
- cellmap_flow.portable.fill.relabel_objects(existing, count_up=False, connectivity=1, per_slice=False)
The box’s foreground (labels 2 and up) relabelled by connected component.
Each component takes the id most of its voxels hold, so a stroke that joins two objects merges them into the id of the bigger. An id left on several components, an object a background wall cut in two, stays on the largest and the others get fresh ids (
fresh_ids). Background (1) and unpainted (0) voxels are left alone. Returns(labels, counts):countshasobjects,split(components given a fresh id) andmerged(components that held more than one id).A cut has to go through every slice the object spans: the brush paints one slice, and an object cut in one slice is still one object above and below it.
splitis 0 then, which the page says. Withper_sliceeach z slice’s pieces are components on their own, so a wall in one slice splits that slice, and every slice of an object gets its own id.connectivity: as forconnected_components.
- cellmap_flow.portable.fill.paint(existing, labels, overwrite=False)
The box after
labelsare written overexisting, and what that wrote.A label of 0 leaves its voxel as it is. Without
overwriteonly unannotated voxels (0) are written, as for a seed: what the user painted stays. With it every voxel the labels cover is written, painted or not, for a result the user has looked at and prefers to what is there. Returns(after, (n_foreground, n_background, n_overwritten)): the box as it is then, ofexisting’s dtype, or None when nothing would change; how many voxels were written with foreground (2 and up) and with background, and how many of those held a label before.
- cellmap_flow.portable.fill.rewrite(existing, labels)
The box after
labelsare written over its foreground voxels, and how many changed.Only foreground voxels (2 and up) whose label changed are written, so a stroke painted meanwhile elsewhere in the box is kept (Split Objects relabels the box it read). Returns
(after, n_changed),afterNone when none changed.
- cellmap_flow.portable.fill.restore(current, before, after)
The box put back to
beforewhere it still holdsafter: an undo.Only voxels the action changed and nobody has touched since go back, so a stroke painted in the box after the action is kept. Returns
(restored, n): the box then, or None when nothing goes back, and how many voxels did.