Give a mask an edge treatment, and find out the watershed has none worth having
Two things, and the second is why the first matters more than expected. Mask layers gain a feather, a falloff curve and a morphology, all defined against a signed distance from the boundary rather than as separate features — one exact distance field answers "how soft" and "how far" at once, so dilation is a threshold at -r, erosion one at +r, and closing and opening are one of each in sequence. The compound pair costs a second distance field, which is why they are named rather than presented as a radius that happens to be signed. Types, defaults and sidecar round-trip only; the field itself is next. `edge-feather` and `edge-falloff`, not `feather` and `falloff`, because a radial mask already writes `feather` for the fraction of its radius it ramps over. Same word, different quantity, different units — sharing the key would have made an existing file ambiguous. The diagnostic that provoked this is committed as an ignored test, because "does the ladder land on things a person means" is the question S15 exists to answer and it should not depend on whoever still has the script. On bus.jpg it answers badly: 35,075 regions at blur 2 over an 810x1080 frame, and cutting that to 400 gives *one* region covering nearly the whole picture plus 399 noise specks. Not over-segmentation — collapse. Almost every saddle is near zero, so the merge order joins everything meaningful before it joins anything spurious, and a global cut spends its entire budget on grain. So the granularity ladder does not currently work on a photograph, and the region masks built on it inherit that. Recorded rather than worked around: the next commits move local masking onto the model's instances, where the edge treatment above is what makes a quarter-resolution mask usable.
This commit is contained in:
+14
-3
@@ -213,7 +213,7 @@ export component MaskPanel inherits Rectangle {
|
||||
HorizontalLayout {
|
||||
PanelHeading { text: "LOCAL"; }
|
||||
Rectangle { horizontal-stretch: 1; }
|
||||
if root.segmented: Value { text: root.region-count + " regions"; }
|
||||
if root.segmented: Value { text: root.level + " / " + root.region-count; }
|
||||
}
|
||||
|
||||
if !root.enabled: Caption { text: "No image"; }
|
||||
@@ -264,19 +264,30 @@ export component MaskPanel inherits Rectangle {
|
||||
// Granularity, labelled by what it does rather than by its number:
|
||||
// "detail" is what a photographer is choosing between, where "300
|
||||
// regions" is an implementation detail they would have to learn.
|
||||
// **The ceiling is the region count, not a constant.** It was 2000,
|
||||
// and a photograph that segments into more than that had the finest
|
||||
// part of its own ladder unreachable — the slider simply stopped
|
||||
// before the regions did.
|
||||
if root.enabled && root.segmented: SliderRow {
|
||||
label: "Detail";
|
||||
hint: "How finely a click divides the picture, out of "
|
||||
+ root.region-count + " regions the watershed found.";
|
||||
value: root.level;
|
||||
default-value: 300;
|
||||
minimum: 8;
|
||||
maximum: 2000;
|
||||
maximum: max(root.region-count, 8);
|
||||
changed(v) => { root.level-changed(v); }
|
||||
reset => { root.level-changed(300); }
|
||||
}
|
||||
|
||||
// --- what the model found ----------------------------------------
|
||||
if root.enabled && root.segmented && root.subjects.length > 0: Caption {
|
||||
text: "Recognised";
|
||||
// Says where these came from and how they differ from a region.
|
||||
// A list of four beside a count of three thousand invites exactly
|
||||
// one question, and the panel should answer it rather than
|
||||
// provoke it.
|
||||
text: "Subjects the model recognised. Click one to select the whole thing.";
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
if root.enabled && root.segmented: VerticalLayout {
|
||||
|
||||
Reference in New Issue
Block a user