317 lines
13 KiB
C
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
|