Expand description
Global grid-direction recovery from per-feature dual-axis estimates.
Given a set of features that each carry two undirected local
lattice-axis estimates (e.g. chess-corner x-junctions whose two
tanh-fit ridges approximate the local grid directions), this module
recovers the two global grid-direction centres (Θ₀, Θ₁) and
labels every feature by which of its two axes matches Θ₀ vs Θ₁.
It is the orientation-prior stage shared by every grid pipeline that ingests dual-oriented features. Target-specific glue (which features are eligible, how to map the canonical/swapped assignment onto a caller’s own label type, parity-coherence repair) stays caller-side; this module is the pure direction-clustering math.
Tier: stable facade — cluster_axes and its
AxisClusterCenters / AxisAssignment types are re-exported at the
crate root and follow normal semver intent.
§Inputs / outputs
- Input: a slice of
AxisFeature, each carrying its twoAxisObservations(angle, sigma)and a detectorstrength. Axes whose sigma is the no-info sentinel (≥π) or non-finite are skipped. Callers pre-filter to the features they want to vote (e.g. chessboard passes only itsStrongcorners). - Output:
AxisClusterCenters{ theta0, theta1 }in[0, π)withtheta0 ≤ theta1.- A per-feature
AxisAssignment.
§Algorithm
- Build a smoothed circular histogram on
[0, π)withnum_binsbins. For every feature and every axisk ∈ {0, 1}, add a vote atwrap_pi(axes[k].angle)with weightstrength / (1 + axes[k].sigma). - Smooth with a
[1, 4, 6, 4, 1] / 16circular kernel. - Find local maxima. Keep peaks with total weight ≥
min_peak_weight_fraction × total. Pick the two strongest peaks separated by at leastpeak_min_separation_rad. - Refine centres via double-angle 2-means over per-axis
votes. Each axis vote
θis mapped to2θbefore averaging; the average is halved back — this is the correct undirected- angle (mod π) circular mean. Iterate up tomax_iters_2means. - Per-feature label: for each feature compute the two possible axis assignments (canonical vs swapped) and pick the cheaper. Require the LARGER distance in the winning assignment to be within the per-feature tolerance; otherwise the feature is unassigned.
Structs§
- Angle
Vote - A single weighted vote over the mod-π circle.
- Axis
Cluster Centers - The two recovered global grid-direction centres in
[0, π)withtheta0 ≤ theta1. - Axis
Cluster Debug - Stage introspection captured during a single
cluster_axesrun. - Axis
Feature - A feature with two undirected local lattice-axis estimates plus a detector strength used to weight its histogram votes.
- Axis
Observation - One undirected local lattice-axis estimate feeding the clustering stage: an angle in radians and its 1σ angular uncertainty.
- Cluster
Params - Tuning for
cluster_axes. - Peak
Pick Options - Options for
pick_two_peaks.
Enums§
- Axis
Assignment - Per-feature assignment produced by
cluster_axes.
Functions§
- angle_
to_ bin - Map an angle in
[0, π)to the bin index in a histogram ofnequal-width bins over that range. Idempotent under priorwrap_pi; inputs outside[0, π)are wrapped first. - angular_
dist_ pi - Smallest angular distance on the circle with period π. Result in
[0, π/2]. - assign_
axes - Assign one feature’s two axes to a slot ordering given known centres.
- bin_
to_ angle - Inverse of
angle_to_bin: bin center angle in[0, π). - cluster_
axes - Recover the two global grid-direction centres from a slice of dual-axis features and assign each feature to a slot ordering.
- effective_
tol_ rad - Per-feature cluster admission threshold in radians.
- pick_
two_ peaks - Pick the two strongest plateau-aware peaks from a smoothed circular histogram, subject to a minimum-weight floor and minimum angular separation.
- refine_
2means_ double_ angle - Refine two cluster centers via weighted 2-means on mod-π-circular
vote angles using the double-angle trick: accumulate
(w·cos 2θ, w·sin 2θ)per cluster and halve the resulting atan2. This is the correct circular mean for undirected angles (mod π); accumulating raw(cos θ, sin θ)silently returns garbage near the 0°/180° seam. - refit_
centers - Refit cluster centres from a labelled subset’s axes only.
- smooth_
circular_ 5 - Smooth a circular histogram with a one-pass
[1, 4, 6, 4, 1] / 16kernel. Handles the wrap boundary withrem_euclid. Empty input returns empty output. - wrap_pi
- Wrap an angle to
[0, π). Works for any finite input.