Skip to main content

SuperPoint

SuperPoint turns a detector-logit grid and a coarse descriptor grid into native keypoint, score and descriptor tensors. It executes on A65; requesting EV74 returns an unsupported-target error. It does not infer a profile from a model name and does not consume JSON or a serialized feature-point payload.

Contract​

PortShapeDtypeMeaning
Input 0[H,W,65]INT8, BF16, FP32Raw detector logits; channel 64 is dustbin
Input 1[H,W,D]INT8, BF16, FP32Coarse descriptors before L2 normalization
Output 0[K,2]FP32(x,y) coordinates in model pixels
Output 1[K]FP32Detection scores
Output 2[K,D]INT8, BF16, FP32Sampled, normalized descriptors
Output 3[1]INT32Number of valid entries

Input ranks 3–6 are accepted, with only unit leading dimensions: one image per invocation. D is 1–65535. K is Params::topk_capacity and must be positive. The detector and descriptor grids must have equal H/W. Their stride is eight model pixels, and the grids must cover the explicit model dimensions.

Dense input is contiguous HWC or explicit Strided HWC with scalar channel strides, per-pixel channel padding and tight rows. Singleton outer strides do not affect addressing. The required span ends after the last logical channel of the final pixel; trailing padding after that channel is not required. The prepared dense reader skips padding directly for INT8, BF16 and FP32; no repacking or widening tensor is needed. INT8/BF16 additionally support MlaCBlocked and MlaTiled with HWC axes, unit absent D axis and PadTo16 channels. Channel slices before the final slice end on C16 boundaries. Arbitrary strides, FP32 MLA storage, and compact MLA channel tails are rejected. Outputs are contiguous; all unused entries are zeroed. Each binding needs scalar alignment and its full physical capacity, including input tiles and channel padding. Outputs must not alias one another or either input; read-only inputs may share storage.

INT8 inputs require explicit quantization. With reciprocal=false, decoding is (q-zero_point)*scale; with reciprocal=true, it is (q-zero_point)/scale. The distinction preserves the arithmetic of existing compiled-model contracts. Floating inputs must not supply quantization.

Parameters​

profile selects the numerical contract. model_width and model_height are explicit model-pixel dimensions, not original-camera coordinates. topk_capacity fixes output capacity. detection_threshold is finite in [0,1]. nms_radius and border_margin use -1 for profile defaults; explicit values range from zero to the larger model dimension. Quantization is separately specified for each INT8 input; its scale is finite and positive and its zero point lies in [-128,127].

C++ call​

Place the includes and namespace aliases at file scope; the remaining statements belong in a simaai::kernels::Status-returning caller function. input_views and output_views are caller-supplied arrays of ConstHostBuffer and HostBuffer in port order, with accessible capacities and alignment from the checked plan. Keep their storage alive until synchronous execution returns. services is a caller-supplied HostServices with a valid parallel_for. Handle allocation exceptions during preparation/context creation at the caller boundary.

#include <utility>
#include <array>
#include <simaai/kernels/ops/superpoint.h>

namespace k = simaai::kernels;
namespace sp = k::ops::superpoint;
const std::array<k::TensorSpec, 2> inputs{
{{{2, 2, 65}, k::DType::Float32}, {{2, 2, 32}, k::DType::Float32}}};
const std::array<k::TensorSpec, 4> outputs{{{{4, 2}, k::DType::Float32},
{{4}, k::DType::Float32},
{{4, 32}, k::DType::Float32},
{{1}, k::DType::Int32}}};
sp::Params params;
params.model_width = params.model_height = 16;
params.topk_capacity = 4;
params.detection_threshold = 0.1F;
auto checked = sp::check_inference({inputs, outputs, params});
if (const auto *error = std::get_if<k::Error>(&checked)) return *error;
const auto plan = std::get<sp::Plan>(std::move(checked));

auto context = sp::create_context(plan, services); // Caller supplies HostServices.
if (!context) return k::Error{k::ErrorCode::UnsupportedEnvironment, "context",
"context preparation failed"};
return sp::execute(plan, *context, input_views, output_views);

See the correctness tests (A65/superpoint/tests/correctness.cpp) for concrete storage, independent expected results and failure cases.

Memory and lifecycle​

Typed request ── check_inference ── immutable Plan
│
HostServices ── create_context
│
Borrowed tensor views ────────────── execute ── four native outputs

check_inference owns metadata validation and storage requirements. It retains its own tensor descriptions. create_context builds lookup tables, indexing maps and bounded scratch before the first invocation. Each concurrent execution needs a distinct context created from that same plan; a context from a different plan is rejected. A successful execution allocates no heap memory.

make_executable(request, Target::A65) packages the plan for the generic runner. The runner provisions contexts through its resource recipe and retains the shared host task service. No operation-specific runner adapter is necessary.

The host service is explicit and borrowed. It synchronously executes task sets, retiring all started tasks on failure. SuperPoint partitions substantial phases into at most 16 indexed tasks, requesting at most four concurrent participants; small phases remain serial. It never creates a private thread pool. The service provider must outlive all contexts using it. The direct latency catalogue uses an explicitly serial service (workers1); it does not claim to measure the production parallel runner.

Numerical behavior​

The caller selects A65V1, LightGlueV1 or MagicLeapDemoV1. Unknown or reserved profile values are rejected.

  1. Read logical channels directly from dense or MLA storage; reconstruct BF16 byte planes where required. Padding is not a detector class.
  2. Compute profile-specific detector scores and expand coarse cells to model coordinates.
  3. Apply the profile's threshold, suppression, border and deterministic Top-K rules. Ties order by score descending, then y, x and source bin ascending.
  4. Prepare unique descriptor-corner work, normalize/cache eligible corners, interpolate descriptors and normalize the selected result.
  5. Write native tensors directly, zero unused capacity, and publish count only after descriptor computation succeeds.

A65V1​

For each cell, select the first maximum among channels 0–63 and compute

p=σ(lbest−ldustbin). p = \sigma(l_{best}-l_{dustbin}).

The stable sigmoid branch avoids unnecessary overflow. INT8 uses a prepared 511-entry difference table. The profile uses strict threshold comparison and greedy Euclidean suppression (default radius 4, border 0).

The difference tables scale each integer delta in double precision before converting it to FP32. Reciprocal quantization divides the delta by the supplied scale; it does not multiply by a precomputed reciprocal. Thus equal logits keep a zero difference even for the smallest admitted positive scale.

Descriptor coordinates use the established Q4 mapping. INT8 descriptors retain centered integer interpolation with integer rounding before final normalization; this path does not normalize coarse corners first. Floating descriptors use normalized coarse corners and the same profile-specific coordinates.

LightGlueV1​

The detector uses a 65-way stabilized softmax, drops dustbin, applies the LightGlue suppression/refinement policy and uses its descriptor coordinate mapping (default radius/border 4). INT8 exponentials come from a prepared max-minus-value table with the same delta-first scaling described above. The INT8 path accumulates its denominator and rounds its quotient in FP32. Its multiplication-based prefilter admits equality because the rounded product can equal a numerator whose quotient rounds above the threshold. The final comparison remains strictly score > detection_threshold; equal scores are still rejected. The prefilter boundary is at least the smallest positive FP32 value, rejecting zero numerators without division while retaining positive subnormals. The A65 floating SIMD exponential path retains a scalar recheck near threshold boundaries. Its evaluation order is not interchangeable with a blanket rewrite to a different softmax implementation.

At threshold zero, a zero approximate foreground exponential also triggers the scalar recheck: its exact FP32 value may be a positive subnormal that must pass the strict threshold. Truly underflowed zero scores remain excluded. Positive exponentials do not trigger this zero-threshold fallback.

MagicLeapDemoV1​

The detector evaluates

pi=exp⁡(li)10−5+∑j=064exp⁡(lj) p_i = \frac{\exp(l_i)}{10^{-5}+\sum_{j=0}^{64}\exp(l_j)}

with double-precision exponentials/denominator. The quotient is rounded to FP32 before the inclusive comparison score >= detection_threshold. A prepared prefilter uses the threshold's preceding FP32 value, so it can skip divisions for clearly rejected candidates without discarding scores that round up to equality. The final comparison uses the original threshold, including at zero. Greedy Chebyshev suppression defaults to radius/border 4. This formula is deliberately not substituted by the LightGlue stabilized formula.

Descriptor output​

Floating interpolation is followed by L2 normalization, with a 1e-12 norm floor where the floating path applies. A65 INT8 uses its dedicated integer interpolation/normalization path. FP32 writes the resulting values. BF16 rounds to nearest, ties to even. INT8 performs normalization, multiplication by 127, nearest-even rounding and saturation to [-127,127] in that order.

When the A65 FP32 sum of squares overflows, normalization falls back to a double-precision norm and reciprocal, narrowing only the reciprocal to FP32. This keeps large finite descriptors normalizable even when their norm exceeds the FP32 range. The portable path uses the same double-precision calculation.

INT8 floating readers use NEON only when the prepared FP32 scaling multiplier is normal (finite, nonzero and not subnormal) and the full centered INT8 range stays within FP32's finite range under both the original scale and the rounded multiplier. Preparation checks these bounds once: even a normal multiplier can round upward enough to overflow a product that double-precision scaling keeps finite. Otherwise, dense and tiled reads use the prepared lookup table, computed with double-precision multiplication or division before conversion to FP32. In particular, a zero-point sample remains zero even when a reciprocal multiplier would overflow FP32.

Nearest rounding and gradual underflow are required. Execution checks the calling thread and numerical task workers rather than silently changing their FP state. Nonfinite detector/descriptor data is handled by the retained profile-specific numerical checks; content failures are not transactional rollback. Invalid metadata and buffer bindings are rejected before output writes.

Implementation notes​

Logical input reads use prepared tile indexing rather than materializing dense intermediates. Native descriptor output writes directly to its requested tensor: no serialized header, interleaved detection record, or packing copy is involved. Detector candidate buckets, unique descriptor-corner work and task-local scratch are provisioned before execution. Preserve per-profile arithmetic and tie order when changing these implementation choices.

Tests and qualification​

tests/correctness.cpp covers admitted ranks, three profiles, input/output precisions, tiled channel tails, explicit independent physical packing, normalization, zeroed capacity, guards, reuse and binding rejection. tests/latency.cpp measures the public checked execution path and reports preparation separately. Baseline-free reports are measurements, not performance qualification. Target SIMD and parallel-runner evidence must come from A65 runs; a successful host build is not target qualification.