Body Blob Centroid (BodyBlobNav)
Overview
BodyBlobNav recovers a single translation
from one or more body brightness centroids. For each offered BODY_BLOB feature the technique
computes a brightness-weighted moment inside the predicted bounding box, compares the
observed centroid to the predicted lit-weighted centroid carried on the feature, and runs an
inverse-variance-weighted joint fit across every consumed body to recover the per-image
translation. With N >= 2 blobs the fit is over-determined and the joint solution is
robust to centroid errors on any single body.
Feasibility passes when at least one offered BODY_BLOB carries a non-zero predicted
diameter; feasibility fails when every blob has degenerate geometry (a sub-pixel body that
collapses the brightness-weighted moment).
The technique reports a confidence intrinsically capped at 0.4 via the spec’s
hard_cap — a brightness-weighted
centroid is much weaker evidence than a limb fit, so even an ideal blob match cannot dominate
the ensemble combine.
Theory
The technique fits a per-image translation by minimising the inverse-variance-weighted squared residual between the per-blob observed and predicted centroids.
Coarse acquisition (lit-shape matched filter)
A brightness-weighted moment only sees the body when it already sits inside the predicted bounding box, so the bare centroid’s capture range is just the box – a few pixels of per-body slop. Once the SPICE pointing error exceeds that slop the body drifts out of the box, the moment is taken over a clipped fragment, and the technique reports a silently biased centroid (no spurious or at-edge flag fires). To extend the capture range to the full extended-FOV search window, each blob first runs a coarse acquisition that re-centres its bounding box on the body before the centroid is taken:
If a pass-1 prior offset is installed on the context (another technique already located the body), the box is shifted by the rounded prior. The prior is a measured offset, so it applies regardless of phase.
Otherwise the technique correlates a matched-filter template of the predicted lit silhouette against the lit-signal image (background subtracted, clipped at zero, sky-masked) over
predicted_center +/- margin. The response peaks where a body of that shape is best centred; the integer peak offset re-centres the box. The template depends on phase (_COARSE_CORRELATION_MAX_PHASE_DEG, 90 deg):At or below half phase the lit silhouette is a near-full disc, so the kernel is a filled disc of the predicted body radius.
Above half phase the sunlit region is a thin crescent whose bright pixels sit a fraction of a radius off the body center; a disc kernel would lock onto the crescent arc rather than the center. The kernel is instead a synthesised crescent – a Lambertian
max(0, cos(incidence))rendering of a sphere of the predicted radius at the body’s phase, lit from the sub-solar direction theBODY_BLOBfeature carries (sub_solar_dir_vu, the projected body-to-Sun direction; see Body Navigation Model). Correlating the crescent puts the template center on the body center instead of the bright arc.
The kernel is flipped before the FFT so the operation is a cross-correlation, and the template’s own brightness-centroid offset is added back to the peak: the feature carries the body’s lit centroid (which on a crescent sits off the geometric center), so the recovered shift is expressed in lit-centroid terms and matches the residual the centroid step forms.
The crescent template needs the sub-solar direction. It is undefined near full phase (the lit
and geometric centroids coincide), where the disc kernel is used anyway, and is reported as
(0, 0) then. If a body is past half phase yet carries no direction (its illumination
geometry was not populated), the coarse stage makes no relocation and keeps the predicted box
(an installed prior still applies). The coarse offset is integer; the sub-pixel precision
comes entirely from the brightness-weighted moment below, computed inside the re-centred box,
so the recovered observed - predicted residual already includes the coarse shift.
Per-blob centroid
For each consumed body, the technique computes the brightness-weighted moment over the (coarse-re-centred) predicted bounding box:
The predicted centroid carried on the feature is the lit-weighted moment of the rendered model (see Body Navigation Model); the per-blob residual is
Per-blob covariance
The per-blob centroid uncertainty follows the standard CRLB scaling for a uniform-brightness disc:
where \(D_{\mathrm{px}}\) is the predicted disc diameter in pixels, \(N_{\mathrm{lit}}\) is the number of lit pixels inside the predicted bounding box, and SNR is the per-pixel signal-to-noise ratio. Two additional contributions are added in quadrature: a shape-irregularity sigma scaling with the body’s phase-and-irregularity coupling \(\kappa\) (see Body Navigation Model) times half the predicted disc diameter, and the photon-noise floor. The total per-blob sigma populates a 2x2 isotropic covariance.
Joint translation fit
The joint translation minimises
The closed-form minimum is the inverse-variance-weighted mean of the per-blob residuals:
The reported translation covariance is \(\sigma^{2} I\) with \(\sigma^{2} = 1 / \sum_{i} w_{i}\) — the standard precision-weighted-mean variance.
Restrictions and assumptions
Per-blob centroids assume the (coarse-re-centred) bounding box truly contains the body’s flux. When a cosmic-ray hit, an in-band stellar source, or a neighbouring body’s halo lands inside the box, the moment skews and the technique reports a wrong centroid. The upstream
BODY_BLOBemission gates filter pathological cases (see Body Navigation Model).The coarse lit-shape acquisition extends the capture range from the bounding box to the full search window at any phase: a disc template at or below half phase, a synthesised crescent above it. The only residual gap is a body past half phase whose illumination geometry was not populated (no
sub_solar_dir_vu), where the crescent cannot be oriented; such a body is then recovered only via the bounding-box centroid (small offsets) or an installed prior.A vanishing total flux (an entirely-in-shadow body whose predicted bounding box happens to cover the right part of the FOV) collapses the moment; the technique drops such blobs before the joint fit and reports a no-signal failure when every blob is dropped.
Very small bodies are deliberately gated out. The
BODY_BLOBfeature’s reliability carries ablob_extent_pxterm that drives reliability below the keep threshold for a body only a handful of pixels across (on the simulated catalog a 20 px body passes but a ~24 px-or-smaller body sits just under the gate). This is intentional: on a real frame a body a few pixels wide is dominated by the point-spread function, cosmic rays, and background structure, so its brightness-weighted centroid is not trustworthy even though the arithmetic still produces a number. Navigating bodies below that floor is therefore held back pending calibration against the operator-curated real-image library; the floor is a config-tunable gate, not a hard algorithmic limit.The technique carries no rotation evidence — a brightness-weighted centroid is rotation- invariant about itself. When the per-instrument
fit_camera_rotationis true, the technique returns the rank-deficient 3x3 covariance fromembed_rotation_unobservable()and reportsrotation_radas zero with the rotation-unobservable sentinel sigma.
Sources of uncertainty
The reported covariance is the precision-weighted-mean variance described above. It does
not capture systematic biases from a body whose true rotational orientation differs from the
rendered ellipsoid (the
phase_irregularity_factor term tracks this so the
confidence formula can down-weight the technique on irregular high-phase scenes, but the
centroid itself remains biased). When the converged offset sits within
\(\mathtt{at\_edge\_tolerance\_px}\) of any axis bound, the result is flagged
at_edge and the hard-zero
gate forces confidence to zero.
Configuration
All numeric tunables for this technique live in techniques.BodyBlobNav.tuning in
src/nav/config_files/config_510_techniques.yaml.
at_edge_tolerance_px— float, default1.0px. A converged offset whose absolute distance from any search-window axis bound falls within this tolerance is flaggedat_edge.
The remaining numeric thresholds (the per-blob CRLB scaling constants, the noise-floor
detection threshold) are derived from the per-image
image_noise_sigma and the per-blob
geometry; no YAML knob is exposed.
Per-instrument overrides
The at_edge_tolerance_px knob is global; per-instrument YAML files in
src/nav/config_files/config_4N0_inst_*.yaml do not override it. The
search-window margin used by the at-edge test comes from the per-instrument
InstrumentSettings.
Confidence formula
The technique reports a calibrated confidence in \([0, 1]\) produced by the shared
sigmoid combination; see Confidence Calibration (Shared Sigmoid-of-Linear Combination) for the per-term arithmetic.
The formula spec is techniques.BodyBlobNav in the same YAML file and consumes attributes
off BodyBlobDiagnostics plus
at_edge.
body_snr_inside_predicted_bbox— alpha = 0.5, offset = 0.0, divisor = 4.0, cap at 1.0. Per-image SNR inside the predicted bounding box. Brightness-weighted centroid uncertainty shrinks with SNR.body_extent_px— alpha = 1.0, offset = 8.0, divisor = 8.0, cap at 1.0. Predicted body’s longer-axis extent in pixels. Larger blobs carry more centroid signal up to a 16-pixel saturation point.blob_count— alpha = 0.4, offset = 0.0, divisor = 3.0, cap at 1.0. Number ofBODY_BLOBfeatures fused. Multi-body geometry over-determines the joint translation up to a 3-blob saturation.max_phase_irregularity_factor— alpha = 0.0, offset = 0.0, divisor = 0.15, cap at 1.0. Maximum phase-and-irregularity factor across the consumed blobs (see Body Navigation Model for the formula). The term carries no weight in the current confidence formula; the wiring is in place so a downstream recalibration can tune the alpha without code changes.
Hard-zero gate: at_edge
firing forces confidence to zero before the sigmoid evaluates. The constant baseline is
\(\alpha_{0} = -1.0\). A post-sigmoid hard_cap of 0.4 clamps the result: a
brightness-weighted centroid cannot drive the ensemble past 0.4 confidence even when every
term saturates.
Implementation
Source files:
src/nav/nav_technique/nav_technique_body_blob.py—BodyBlobNav, the per-blob residual collector, and the joint-translation helper.src/nav/nav_technique/confidence.py— shared sigmoid-combination evaluator; documented at Confidence Calibration (Shared Sigmoid-of-Linear Combination).src/nav/nav_technique/diagnostics.py—BodyBlobDiagnostics; documented at Per-Technique Diagnostics (Shared Dataclass Family).
Public class BodyBlobNav, base
NavTechnique. Self-registers via
__init_subclass__ so NavTechnique._registry discovers it.
Class attributes:
name—'BodyBlobNav'.accepts_feature_types—frozenset({BODY_BLOB}).requires_prior—False. Runs in pass 1 of the orchestrator’s two-pass pipeline.confidence_attributes—{'at_edge', 'body_snr_inside_predicted_bbox', 'body_extent_px', 'blob_count', 'residual_px', 'max_phase_angle_deg', 'max_phase_irregularity_factor'}.
Public methods (autodocumented at nav.nav_technique):
is_feasible() and
navigate().
Diagnostics
body_snr_inside_predicted_bbox— per-image SNR inside the predicted bounding box. Consumed by the confidence formula.body_extent_px— predicted body’s longer-axis extent in pixels. Consumed by the confidence formula.blob_count— number ofBODY_BLOBfeatures fused. Consumed by the confidence formula.residual_px— joint-fit RMS residual after solving the precision-weighted mean. Diagnostic only; not in the formula.max_phase_angle_deg— maximum raw phase angle across consumed blobs. Diagnostic only; the formula consumesmax_phase_irregularity_factorinstead because raw phase understates the centroid uncertainty for an irregular body.max_phase_irregularity_factor— maximum \(\sin(\phi/2) \cdot \sigma_{\mathrm{ellipsoid}} / R_{\mathrm{body}}\) across consumed blobs. Consumed by the confidence formula (alpha=0.0 — the wiring is in place so the formula picks up a recalibrated alpha without code changes).
Call path
Call path traced through
navigate():
Open a logged section. Filter the offered features down to
BODY_BLOBentries with a non-zero predicted diameter via the private eligibility helper.Read the search-window margin off the observation via
search_window_for_obs(), the extfov image offimage_ext, and the per-image noise sigma offimage_noise_sigma(clamped at a tiny floor so the noise-floor test stays well-defined on near-blank inputs).For each eligible blob, the private residual collector slices the predicted bounding box from the extfov image, evaluates the brightness-weighted-moment centroid, drops the blob when total flux falls below the noise floor, computes the per-blob residual against the predicted lit-weighted centroid, and accumulates the per-blob inverse-variance weight.
No surviving blobs. The technique returns a spurious zero-confidence result via the private fail helper, with the corresponding
BodyBlobDiagnostics.
The private joint-fit helper computes the inverse-variance-weighted-mean translation and the precision-weighted-mean covariance.
Apply the at-edge test against the search-window axis bounds.
Result-shape branches on
fit_camera_rotation:No rotation fit.
covariance_px2is the (2, 2) translation block.rotation_radandsigma_rotation_radareNone.Rotation fit. The technique embeds the (2, 2) translation block in a (3, 3) covariance via
embed_rotation_unobservable(), setsrotation_radto0.0, and reportsrotation_unobservable_sigma_rad()as thesigma_rotation_rad. A brightness-weighted centroid is rotation-invariant about itself, so the technique carries no rotation evidence.
Build a
BodyBlobDiagnosticsfrom the per-blob residuals (max-SNR, max-extent, blob-count, RMS residual, max raw phase, max phase-irregularity factor), evaluate the confidence spec viaevaluate_sigmoid_combination(), log the per-term breakdown vialog_confidence_breakdown(), and assemble theNavTechniqueResult.
The feature_ids field
preserves every consumed
feature_id so the orchestrator’s curator can
attribute each contribution at audit time.
Examples
below_resolution_body(Cassini ISS NAC, imageN1777325846_1)Mimas approximately 20 px in diameter in the lower left, at phase angle 72 degrees. The body model emits a single
BODY_BLOBfeature (the per-pixel ellipsoid uncertainty exceedsLIMB_ARC_MAX_UNCERTAINTY_PXsoLIMB_ARCis suppressed in favour of the centroid path).BodyBlobNavconsumes the blob and converges within ~1 px of the operator-verified offset \((\Delta v, \Delta u) = (6.08, -1.53)\) px. The post-sigmoid hard cap of 0.4 keeps the technique from outranking a hypothetical limb fit on a similar but well-resolved scene.multi_body(Cassini ISS NAC, imageN1487595731_1)Dione and Rhea both visible at phase angle approximately 90 degrees. When the body model emits
BODY_BLOBfeatures for both bodies (or one body’s limb fails the uncertainty gate),BodyBlobNavfuses the two centroids into a joint translation. The 3-blob saturation in the confidence formula is not reached, but the multi-bodyblob_countterm still contributes a positive offset. Operator-verified offset is \((\Delta v, \Delta u) = (7.03, -18.42)\) px.