Segmentation¶
Write masks, let the encoding be chosen for you, and read them back in the shape a loss wants.
Write the masks¶
Hand add_segmentation a mapping of class to boolean volume. You do not choose
an encoding:
w.add_segmentation("organs", grid="ct",
masks={"liver": liver, "lesion": lesion},
annotated_classes=["liver", "spleen", "lesion"])
It measures the class overlap graph and picks an encoding that can represent it, returning which one and the statistics behind the choice:
liver and lesion overlap — a lesion is inside the liver — so a single
labelmap cannot hold both, and the writer picks among the encodings that can:
instances for sparse localized objects, layers while the classes pack into
few planes, bitmask beyond that. Disjoint classes get a labelmap, which is
smaller. This is a storage decision and nothing you read later depends on it.
annotated_classes is the argument to think about, not the encoding. Above
it names the spleen although there is no spleen mask, recording "we looked and
found none" — a usable negative. See
Partial labels and coverage for which form to use.
Read them back¶
One API, whatever the encoding:
organs = s.annotations["organs"]
organs.kind # "layers" — informational
organs.dense(["liver", "lesion"]) # (2, *shape) bool, one plane per class
organs.labelmap() # (*shape) of class ids
organs.voxel_counts() # {1: 75000, 2: 0, 3: 600}
dense() is the one a loss wants: (C, *spatial), in the class order you asked
for, so the channel index is yours and not the file's. Ask for a class the
annotation does not contain and you get a zero plane — which is correct when the
class was examined, and is why coverage is a separate question.
Read a patch rather than a volume by passing an ROI:
Overlap you did not expect¶
That prints per-class counts, the overlap graph and what each encoding would cost. It is the fastest way to find out that two classes you thought were disjoint are not — usually a rater including a lesion in the organ it sits in.
Change the encoding later¶
Losslessly, without touching the annotation's meaning:
Not every conversion is possible: an encoding that cannot represent overlap
refuses a transcode from one that does, rather than dropping voxels. --dry-run
says what would happen.
Check it¶
Related¶
- Partial labels and coverage —
annotated_classesin full. - Annotation kinds — the five encodings and the transcode table.
- Tune performance — making
dense()on a patch fast.