Local masking needs to know where an image's regions are. The watershed spike (S15 arm A) found the boundaries but had no idea what any of them enclosed; its coarse levels were geometric accidents. This adds the other half and the thing that joins them. `core/dr-segment` is where region reasoning now lives — the hierarchy moves out of `dr-gpu`, which keeps only the pixel passes that are genuinely shaders. The new crate is device-free and, without its default features, model-free too: 20 of its tests need neither an adapter nor 11 MB of weights. Arm B runs YOLO26n-seg through `ort`. D13 framed inference as a choice between `ort`'s C++ runtime and the pure-Rust dependency policy; that was a false choice. `ort`'s `alternative-backend` feature unlinks the C entirely and `ort-tract` supplies the API from tract, which is pure Rust. Measured before committing to it: zero unsupported operators, 420 ms for 640x640, and correct masks on bus.jpg. No NDK problem to solve, so D13's largest tolerated exception is not needed. Arm C is `prior.rs`, and it ships because the two arms fail in opposite directions. Instance membership re-weights the merge saddles, so region pairs the model believes share an object merge early and pairs straddling its edge merge late. No boundary moves — only the order in which they dissolve — which is how the result stays pixel-accurate at every level while its coarse levels become named things. Two things the spec assumed that turned out to be false, both recorded in models/LICENCE.md: there is no usable ADE20K-trained YOLO, so the shipped vocabulary is COCO's 80 subjects and *stuff* like sky and foliage must come from arm A; and tract cannot parse a dynamic-shape export, so the graph's input is fixed and tiling is the only route to more semantic resolution. Weights are AGPL-3.0, which GPLv3 §13 permits and which makes the combined work effectively AGPL. Deliberate, not accidental. They live in Git LFS, and a build script fails with an instruction rather than embedding a pointer file when the clone lacks them.
47 lines
1.7 KiB
TOML
47 lines
1.7 KiB
TOML
[package]
|
|
name = "dr-segment"
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
rust-version.workspace = true
|
|
license.workspace = true
|
|
# Guards against a Git LFS pointer being embedded in place of the weights.
|
|
build = "build.rs"
|
|
|
|
[dependencies]
|
|
thiserror.workspace = true
|
|
log.workspace = true
|
|
|
|
# Inference. `ort` is the API; **tract is the engine** — see the workspace
|
|
# manifest for why the C++ ONNX Runtime is not linked here.
|
|
ort = { workspace = true, optional = true }
|
|
ort-tract = { workspace = true, optional = true }
|
|
ndarray = { workspace = true, optional = true }
|
|
|
|
[dev-dependencies]
|
|
# The example reads an ordinary JPEG, because the thing worth looking at is
|
|
# whether detections land on a real photograph. Pure Rust, and already in the
|
|
# tree for embedded previews.
|
|
zune-jpeg.workspace = true
|
|
env_logger.workspace = true
|
|
|
|
[features]
|
|
# On by default: a local adjustment that cannot select a subject is half the
|
|
# feature, and the whole point of the tract backend is that enabling this costs
|
|
# no C dependency on any platform.
|
|
default = ["semantic", "embedded-model"]
|
|
|
|
# Arm B — the ONNX runtime and the instance decoder.
|
|
#
|
|
# Separable because the watershed half is genuinely independent of it: with
|
|
# this off, `dr-segment` is a pure-CPU graph algorithm crate with no model to
|
|
# carry, which is what the headless hierarchy tests want.
|
|
semantic = ["dep:ort", "dep:ort-tract", "dep:ndarray"]
|
|
|
|
# Compile the weights into the binary.
|
|
#
|
|
# Separate from `semantic` because the two answer different questions. Android
|
|
# hands the app no filesystem path to read a model from (ARCH §6.9), so there
|
|
# it must be embedded; a desktop packager pointing at a system model directory,
|
|
# or a test that only needs the decoder, wants the runtime without the 11 MB.
|
|
embedded-model = ["semantic"]
|