2026-07-15 10:35:58 +08:00

317 lines
13 KiB
C

#pragma once
#include <stdint.h>
#include <stddef.h>
#if defined(_WIN32) && defined(STEREO_BOLT_SHARED)
# if defined(STEREO_BOLT_BUILDING_LIBRARY)
# define STEREO_BOLT_API __declspec(dllexport)
# else
# define STEREO_BOLT_API __declspec(dllimport)
# endif
#else
# define STEREO_BOLT_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct StereoBoltCtx StereoBoltCtx;
typedef enum {
SB_OK = 0,
SB_ERR_INVALID_CONFIG = -1,
SB_ERR_IO = -2,
SB_ERR_ALGORITHM_REJECTED = -3,
SB_ERR_INTERNAL = -99
} sb_status_t;
typedef struct {
double x;
double y;
double z;
} StereoBoltPoint3d;
typedef struct {
int idx;
int x;
int y;
int width;
int height;
double score;
int class_id;
} StereoBoltModuleRoiC;
typedef struct {
int pair_id;
int idx_L;
int idx_R;
char status[32];
double disparity_center;
double ncc_score;
} StereoBoltModulePairC;
typedef struct {
StereoBoltPoint3d point;
StereoBoltPoint3d direction;
} StereoBoltModuleLine3dC;
typedef struct {
int bolt_id;
int pair_id;
int idx_L;
int idx_R;
StereoBoltModuleRoiC left_roi;
StereoBoltModuleRoiC right_roi;
double height_mm;
/* Physical height endpoint. In support-plane height mode this is the
* centerline/datum-plane intersection, so |top_xyz-foot_xyz| equals the
* reported axial height. */
StereoBoltPoint3d foot_xyz;
StereoBoltPoint3d top_xyz;
StereoBoltModuleLine3dC centerline;
/* Per-bolt confidence so the consumer can filter (the fragile reproj/consistency
* cross-checks are advisory and only lower confidence, they no longer drop the bolt):
* confidence: 0 = trusted, 1 = suspect (use trusted directly; gate suspect yourself).
* status: short reason string, "ok" when no intrinsic issue. */
int confidence;
char status[32];
} StereoBoltModuleBoltC;
typedef struct {
int bolt_id_i;
int bolt_id_j;
double distance_mm;
} StereoBoltModuleDistanceC;
typedef struct {
double model[4]; /* ax + by + cz + d = 0 */
double rms_mm;
int inlier_count;
} StereoBoltModulePlaneC;
typedef struct {
int n_bolts;
StereoBoltModuleBoltC* bolts;
int n_adjacent_distances;
StereoBoltModuleDistanceC* adjacent_distances;
StereoBoltModulePlaneC ground_plane;
} StereoBoltModuleSuccessC;
typedef struct {
char reason[128];
int n_left_rois;
StereoBoltModuleRoiC* left_rois;
int n_right_rois;
StereoBoltModuleRoiC* right_rois;
int n_pairs;
StereoBoltModulePairC* pairs;
} StereoBoltModuleFailureC;
typedef struct {
int success; /* 1 == all `expected` bolts measured ok */
/* Partial-result contract: `data.bolts` holds complete right-paired bolts that
* measured cleanly (each with its own height/status/confidence) EVEN when
* success == 0. Left-only fallback heights (idx_R < 0) are not exported as
* product measurements; `failure` is always populated too (reason + full
* left/right ROIs + per-pair status) so you can see which bolts are missing
* and why. When success == 1, failure.reason is empty. */
StereoBoltModuleSuccessC data;
StereoBoltModuleFailureC failure;
} StereoBoltModuleResultC;
STEREO_BOLT_API StereoBoltCtx* sb_create(const char* config_yaml_path);
STEREO_BOLT_API void sb_destroy(StereoBoltCtx* ctx);
STEREO_BOLT_API const char* sb_last_error(StereoBoltCtx* ctx);
/* ------------------------------------------------------------------------- *
* In-memory construction (sb_create_ex): every input is passed by value or
* buffer through the ABI -- no config.yaml, no calibration.xml, no weights
* file on disk. The library never touches the filesystem and makes no
* assumption about the working directory or install layout, so it is immune
* to the relative-path coupling and install-dir read-permission problems of
* the file-based sb_create(). The host owns where the values come from
* (registry, database, its own config system).
*
* Given equivalent values, sb_create_ex() produces identical results to
* sb_create(). Destroy the returned context with sb_destroy() as usual.
* ------------------------------------------------------------------------- */
/* Stereo calibration -- replaces the calibration XML. All matrices are
* row-major; distortion is the first 5 OpenCV coefficients (k1,k2,p1,p2,k3). */
typedef struct {
int image_width; /* calibrated size; input frames MUST match this */
int image_height;
double left_K[9]; /* 3x3 left camera matrix, row-major */
double left_D[5]; /* k1, k2, p1, p2, k3 */
double right_K[9]; /* 3x3 right camera matrix, row-major */
double right_D[5];
double R[9]; /* 3x3 rotation, left -> right, row-major */
double T[3]; /* translation, left -> right, in millimetres */
double baseline_mm; /* <= 0 => derived from |T| */
} StereoBoltCalibC;
/* YOLO detector backend selector (replaces the yolo.backend string). */
typedef enum {
SB_YOLO_BACKEND_AUTO = 0, /* opencv_dnn for ONNX bytes, rknn for RKNN bytes */
SB_YOLO_BACKEND_OPENCV = 1, /* cv::dnn; `data` must be ONNX bytes */
SB_YOLO_BACKEND_RKNN = 2 /* RK3588 NPU; `data` must be RKNN bytes */
} sb_yolo_backend_t;
/* YOLO model -- replaces the weights file path. `data` points at the raw
* .onnx or .rknn bytes; it is only read during sb_create_ex() (the host keeps
* ownership and may free it once the call returns). */
typedef struct {
const uint8_t* data;
size_t size;
int backend; /* sb_yolo_backend_t */
int imgsz; /* network input size, e.g. 1280 */
float conf; /* confidence threshold, e.g. 0.5f */
float iou; /* NMS IoU; <= 0 => default 0.45f */
} StereoBoltModelC;
/* Algorithm parameters -- replaces the numeric config.yaml keys that actually
* affect the result (the dense-route residue keys are dropped). Fill with
* sb_default_params() then override what you need. New fields may be appended
* at the end in future versions; `struct_size` lets the library stay
* compatible with callers built against an older layout. */
typedef struct {
uint32_t struct_size; /* set to sizeof(StereoBoltParamsC) */
/* --- precise path (sb_process_bolt_module_*) --- */
int interpolation; /* 0 = linear, 1 = cubic */
double rect_alpha; /* stereoRectify alpha, e.g. 1.0 */
/* Disparity search band. Provide EITHER a working-distance band (preferred,
* rig-portable: derived as f*B/Z) OR the fixed pixel band; at least one. */
double wd_z_min_mm; /* working-distance band; <= 0 => unused */
double wd_z_max_mm;
double sgbm_min_disparity; /* fixed pixel band (used when wd_* unset) */
double sgbm_num_disparities;
int foot_rim_correction; /* -1 = built-in default (off), 0 = off, 1 = on */
/* --- binned ranging path (sb_range_bolt_binned) --- */
double rng_z_min_mm;
double rng_z_max_mm;
int rng_ncc_half;
double rng_ncc_half_frac; /* <= 0 => use the fixed rng_ncc_half */
int rng_ncc_vslack;
double rng_min_ncc;
double rng_lrc_max_px;
double rng_lrc_trusted_px;
double rng_ncc_trusted;
double rng_lrc_max_mm; /* <= 0 => use the px gate */
double rng_lrc_trusted_mm; /* <= 0 => use the px gate */
/* --- precise support-plane height mode (appended in selected4 v1) ---
* These fields are deliberately appended: callers compiled against the
* previous, shorter struct remain compatible through struct_size. */
int plane_use_sgbm; /* 0 = legacy BM support, 1 = SGBM support */
int height_from_plane; /* 0 = visible top-to-foot, 1 = top-to-plane */
int plane_height_along_axis;/* 0 = normal distance, 1 = axis/plane distance */
double plane_anchor_max_foot_gap_std_mm;
double plane_sgbm_scale; /* e.g. 0.25; <= 0 => built-in default */
int plane_max_samples; /* e.g. 40000; <= 0 => built-in default */
} StereoBoltParamsC;
/* Return a StereoBoltParamsC pre-filled with the same defaults the YAML path
* uses, with struct_size set. Override fields, then pass to sb_create_ex().
* (The disparity band has no scene-default: set wd_z_min/max_mm or the
* sgbm_* pair before creating, or sb_create_ex returns SB_ERR_INVALID_CONFIG.) */
STEREO_BOLT_API StereoBoltParamsC sb_default_params(void);
/* In-memory context creation. All three pointers are read-only and need not
* outlive the call. Returns NULL on error (query sb_last_error(NULL)). */
STEREO_BOLT_API StereoBoltCtx* sb_create_ex(const StereoBoltCalibC* calib,
const StereoBoltModelC* model,
const StereoBoltParamsC* params);
STEREO_BOLT_API sb_status_t sb_process_bolt_module_files(
StereoBoltCtx* ctx,
const char* left_path,
const char* right_path,
int expected_bolt_count,
StereoBoltModuleResultC* out);
/* In-memory variant: feed raw camera frames directly, no disk round-trip.
* Use this in production (the *_files variant is for offline/testing).
*
* - left_data / right_data : packed 8-bit pixels, row-major, top row first.
* - channels : 1 = grayscale (Mono8), 3 = BGR (converted to
* gray internally to match the file path).
* - *_stride_bytes : bytes per row; pass 0 when rows are tightly
* packed (i.e. width * channels).
* - width / height : MUST equal the calibrated size (config
* image.width / image.height) or the call is
* rejected with SB_ERR_IO.
*
* Buffers are only read during the call; the caller keeps ownership and may
* reuse/free them once the call returns. Output/lifetime identical to the
* *_files variant (free with sb_free_bolt_module_result). */
STEREO_BOLT_API sb_status_t sb_process_bolt_module_buffers(
StereoBoltCtx* ctx,
const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes,
const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes,
int channels,
int expected_bolt_count,
StereoBoltModuleResultC* out);
STEREO_BOLT_API void sb_free_bolt_module_result(StereoBoltModuleResultC* out);
/* ------------------------------------------------------------------------- *
* Lightweight binned ranging (independent of sb_process_bolt_module_*).
*
* For a drone / real-time use case where the camera runs 2x2 binning (e.g. a
* 12 MP sensor outputting 3 MP). It answers ONE question fast: "how far is the
* left camera from the bolts?" -- and is deliberately tolerant of partial
* visibility: it NEVER requires a bolt count and returns a distance from
* whatever bolts are visible (>= 1). Use this for hover/positioning feedback;
* use sb_process_bolt_module_* for the precise per-bolt measurement.
* ------------------------------------------------------------------------- */
typedef struct {
double distance_mm; /* straight-line distance, left optical centre -> bolt */
int left_x; /* bbox centre in the rectified-left (binned) image */
int left_y;
int right_x; /* matched point in the rectified-right (binned) image */
int right_y; /* OBB-axis route: right OBB axis point; NCC fallback: correspondence */
double ncc_score; /* detector/match score; ABI name kept for compatibility */
double lrc_px; /* OBB disparity stddev or NCC LRC residual; smaller is better */
int confidence; /* 0 = trusted (strong + self-consistent), 1 = suspect */
} StereoBoltRangeBoltC;
typedef struct {
int found; /* 1 if >= 1 bolt was detected and paired/matched */
double distance_mm; /* representative straight-line distance over visible bolts */
int bolt_count; /* total visible bolts (may exceed the number written to bolts[]) */
char status[32]; /* "ok" | "no_detection" | "no_valid_match" (why found==0) */
} StereoBoltRangeSummaryC;
/* Range the bolts in a binned stereo pair.
*
* - left_data / right_data : binned frames (e.g. 3 MP), same size; Mono8
* (channels=1, zero-copy) or BGR (channels=3).
* - *_stride_bytes : bytes per row; 0 when tightly packed.
* - The binned size may be ANY clean downscale of the calibrated size
* (config image.width/height); the binned intrinsics are derived internally
* (no separate binned calibration needed). 2x2 binning is the intended case.
* - out : summary (found / representative distance / visible count). Required.
* - bolts : optional caller-allocated array (capacity max_bolts); filled with
* per-bolt detail (distance + left/right match points), near -> far.
* Pass NULL / max_bolts=0 to skip. The caller owns it; nothing to free.
*
* Returns SB_OK on a successfully processed frame REGARDLESS of found (found==0
* just means no bolt in view -- a normal frame, poll out->found each call).
* SB_ERR_IO / SB_ERR_INVALID_CONFIG / SB_ERR_INTERNAL on bad input/state. */
STEREO_BOLT_API sb_status_t sb_range_bolt_binned(
StereoBoltCtx* ctx,
const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes,
const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes,
int channels,
StereoBoltRangeSummaryC* out,
StereoBoltRangeBoltC* bolts, int max_bolts);
#ifdef __cplusplus
}
#endif