#pragma once #include #include #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; 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