VOXL OpenVINS Server 0.7.0
Visual Inertial Odometry Server for VOXL Platform
Loading...
Searching...
No Matches
StereoCameraMinimal.cpp
Go to the documentation of this file.
1/**
2 * @file StereoCameraMinimal.cpp
3 * @brief Stereo camera implementation for VOXL OpenVINS
4 * @author Joao Leonardo Silva Cotta (@zauberflote1)
5 * @date 2025
6 * @version 1.0
7 *
8 * This file implements the StereoCamera class, which specializes the CameraBase
9 * for stereo camera configurations. It handles single camera image processing
10 * and integration with the VIO system.
11 *
12 * The implementation provides:
13 * - Single camera image processing for RAW8 format
14 * - ION buffer processing for efficient memory management
15 * - Queue-based data management for VIO integration
16 * - System state awareness and validation
17 * - Camera fusion system integration
18 * - Thread-safe data handling
19 */
20
21#include "StereoCameraMinimal.h"
22
23namespace voxl
24{
25
26 /**
27 * @brief Constructor for StereoCamera
28 *
29 * Initializes a stereo camera instance by calling the base class
30 * constructor with the provided camera configuration information.
31 *
32 * @param camera_info Camera configuration and calibration information
33 */
35 : CameraBase(camera_info)
36 {
37 }
38
39 /**
40 * @brief Process incoming image data
41 *
42 * Overrides the base class method to handle stereo camera-specific
43 * image processing. This method is called by the pipe callback when
44 * new image data arrives.
45 *
46 * The processing includes:
47 * - Updating camera connection status and timestamps
48 * - Checking system readiness before processing
49 * - Routing to appropriate format-specific processing
50 * - Updating current image dimensions
51 *
52 * Currently supports RAW8 format with fast-path processing.
53 *
54 * @param meta Image metadata containing timestamp and format information
55 * @param frame Pointer to image data buffer
56 */
57 void StereoCamera::process_image(const camera_image_metadata_t &meta, voxl::ImageType type, void *frame)
58 {
59
60 // Update flags quickly
61 is_cam_connected = true;
62 last_cam_time = _apps_time_monotonic_ns();
63
64 // Early return if system not ready
65 if (!is_system_ready())
66 return;
67
68 // if we are resetting, just return
69 if (is_resetting.load(std::memory_order_relaxed)) return;
70
71 // indicate that we are processing IMU data
72 active_callbacks.fetch_add(1, std::memory_order_acquire);
73 if (is_resetting.load(std::memory_order_relaxed))
74 {
75 active_callbacks.fetch_sub(1, std::memory_order_release);
76 return;
77 }
78
79 // Update dimensions
80 current_height = meta.height;
81 current_width = meta.width;
82
83 // Process only supported formats with fast path
84 if (type == voxl::ImageType::CV_MAT)
85 {
86 if (meta.format == IMAGE_FORMAT_STEREO_RAW8)
87 {
88 process_raw8(meta, (char*)frame);
89 }
90 else
91 {
92 // Rare case, can be slower
93 fprintf(stderr, "Unsupported image format: %d\n", meta.format);
94 vio_error_codes |= ERROR_CODE_CAM_BAD_FORMAT;
95 }
96 }
97 if (type == voxl::ImageType::CL_MEM)
98 {
99 fprintf(stderr, "Using CL_MEM is currently unsupported for stereo images\n");
100 }
101
102 // check if in flight processing count reaches zero and if a reset is requested
103 if (active_callbacks.fetch_sub(1, std::memory_order_release) == 1)
104 {
105 // Notify the reset thread to continue processing
106 std::lock_guard<std::mutex> lk(reset_mtx);
107 reset_cv.notify_one();
108 }
109 }
110
111 /**
112 * @brief Process RAW8 image format
113 *
114 * Handles the processing of RAW8 format images, which is a common
115 * format for monochrome cameras used in VIO systems.
116 *
117 * The processing includes:
118 * - Setting up image ring buffer packet with metadata
119 * - Copying image data to internal buffer
120 * - Creating OpenCV Mat view of the image data
121 * - Setting up mask for feature tracking regions
122 * - Creating CameraData message for VIO processing
123 * - Pushing data to camera queue
124 * - Notifying fusion system of data availability
125 *
126 * @param meta Image metadata containing timestamp and dimensions
127 * @param frame Pointer to image data
128 */
129 void StereoCamera::process_raw8(const camera_image_metadata_t &meta, char *frame)
130 {
131 // TODO: FIX INTERCHANCHABLE META AND CURR_MESSAGE USAGE, KEEP IT CONSISTENT, RESPECT HALACHAH
132 // Use static buffer to avoid allocations in the hot path
134 curr_message_.metadata = meta;
135
136 // SPLIT THE IMAGE HERE
137 const int full_w = meta.width;
138 const int full_h = meta.height; // height of one camera
139 const size_t bytes_per_row = full_w; // CV_8UC1: one byte per pixel
140 const size_t bytes_one_img = bytes_per_row * full_h;
141
142 memcpy(curr_message_.image_pixels, reinterpret_cast<uint8_t *>(frame), meta.size_bytes);
143
144 // OpenCV view (no copy)
145 cv::Mat imgL(current_height, current_width, CV_8UC1, curr_message_.image_pixels); // cam-0
146 cv::Mat imgR(current_height, current_width, CV_8UC1, curr_message_.image_pixels + bytes_one_img); // cam-1
147
148 // Check if dimensions changed and update masks efficiently
149 const bool dimensions_changed = (use_mask_.rows != current_height || use_mask_.cols != current_width);
150 if (dimensions_changed) {
151 mask_dimensions_changed_ = true;
152 }
153
154 // Determine if masks should be active based on occlusion and altitude for each camera independently
155 // For stereo cameras, each camera might have different occlusion characteristics
156 const bool should_mask_left = occlude_stereo_left &&
157 std::abs(alt_z.load(std::memory_order_relaxed)) < takeoff_alt_threshold;
158
159 // Right camera might have different occlusion logic - for now using same logic
160 // but this can be extended based on individual camera characteristics
161 const bool should_mask_right = occlude_stereo_right &&
162 std::abs(alt_z.load(std::memory_order_relaxed)) < takeoff_alt_threshold;
163
164 // Update masks only when necessary with independent logic
165 update_masks_if_needed(should_mask_left, should_mask_right);
166
167 ov_core::CameraData message;
168 message.timestamp = (meta.timestamp_ns) * 1e-09; // TODO: check if we should consider adding exposure time/2 --> NAIVELY ADDING BRING CHAOS (Multi-cam) DO IT AT YOUR OWN RISK
169
170 message.sensor_ids.push_back(get_id() + 0);
171 message.images.emplace_back(imgL.clone()); // clone might be optional --> depends on consumer thread ownership guarantees TODO: CHECK THIS LATER ON
172 message.masks.emplace_back(use_mask_);
173
174 message.sensor_ids.push_back(get_id() + 1);
175 message.images.emplace_back(imgR.clone());
176 message.masks.emplace_back(use_mask2_);
177
178 // Push the paired two-sensor message straight into the estimator's lock-free ring (this
179 // stereo pipe thread is the single producer for its stream); ordering happens at release
180 if (vio_manager && frame_transform.is_initialized)
181 {
182 vio_manager->feed_measurement_camera(message);
183 }
184 }
185
186 void StereoCamera::update_masks_if_needed(bool should_mask_left, bool should_mask_right)
187 {
188 // Check if we need to update the masks
189 const bool needs_update_left = !current_mask_state_left_.has_value() ||
190 current_mask_state_left_.value() != should_mask_left ||
191 mask_dimensions_changed_;
192
193 const bool needs_update_right = !current_mask_state_right_.has_value() ||
194 current_mask_state_right_.value() != should_mask_right ||
195 mask_dimensions_changed_;
196
197 if (!needs_update_left && !needs_update_right) {
198 return;
199 }
200
201 // Update mask state
202 current_mask_state_left_ = should_mask_left;
203 current_mask_state_right_ = should_mask_right;
204 mask_dimensions_changed_ = false;
205
206 // Create or update both masks efficiently
207 if (should_mask_left) {
208 // Use cv::Scalar constructor for better performance
209 use_mask_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(255));
210 } else {
211 // Use cv::Scalar constructor for better performance
212 use_mask_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(0));
213 }
214
215 if (should_mask_right) {
216 // Use cv::Scalar constructor for better performance
217 use_mask2_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(255));
218 } else {
219 // Use cv::Scalar constructor for better performance
220 use_mask2_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(0));
221 }
222 }
223
224
225 /**
226 * @brief Check if system is ready to process images
227 *
228 * Determines whether the VIO system is ready to accept and process
229 * new image data. The system is considered ready when both the IMU
230 * is connected and the main process is running.
231 *
232 * @return true if system is ready, false otherwise
233 */
234 bool StereoCamera::is_system_ready() const
235 {
237 }
238
239} // namespace voxl
Stereo camera implementation for VOXL OpenVINS.
bool occlude_stereo_right
Occlude stereo right.
Definition VoxlVars.cpp:203
volatile int64_t last_cam_time
Timestamp of last camera data (nanoseconds)
Definition VoxlVars.cpp:188
volatile int main_running
Main process running flag.
Definition VoxlVars.cpp:38
std::atomic< uint32_t > active_callbacks
Number of callbacks inside the system.
Definition VoxlVars.cpp:63
voxl::FrameTransform frame_transform
Global frame transform instance.
Definition VoxlVars.cpp:181
bool occlude_stereo_left
Occlude stereo left.
Definition VoxlVars.cpp:200
std::mutex reset_mtx
Mutex used by reset thread.
Definition VoxlVars.cpp:66
float takeoff_alt_threshold
Takeoff altitude threshold.
Definition VoxlVars.cpp:197
std::unique_ptr< ov_msckf::VioManager > vio_manager
Main VIO manager instance.
Definition VoxlVars.cpp:31
std::condition_variable reset_cv
Reset conditional variable.
Definition VoxlVars.cpp:69
std::atomic< bool > is_resetting
VIO reset state flag.
std::atomic< float > alt_z
Altitude z.
std::atomic< uint32_t > vio_error_codes
VIO error codes.
std::atomic< bool > is_imu_connected
IMU connection state.
std::atomic< bool > is_cam_connected
Camera connection state.
Base class for all camera implementations.
Definition CameraBase.h:57
cv::Mat use_mask_
Per-instance reusable mask for feature tracking.
Definition CameraBase.h:182
int get_channel() const
Pop camera data from the queue.
Definition CameraBase.h:112
size_t get_id() const
Get the camera identifier.
Definition CameraBase.h:118
img_ringbuf_packet curr_message_
Lock-free SPSC queue for camera data.
Definition CameraBase.h:179
StereoCamera(const cam_info &camera_info)
Constructor.
void process_image(const camera_image_metadata_t &meta, voxl::ImageType type, void *frame) override
Process incoming image data.
Main namespace for VOXL OpenVINS server components.
Camera information and calibration data.
Definition VoxlCommon.h:146
uint8_t image_pixels[MAX_IMAGE_SIZE]
Raw image pixel data.
Definition VoxlVars.h:64
camera_image_metadata_t metadata
Image metadata (timestamp, format, etc.)
Definition VoxlVars.h:63
int camid
Camera identifier.
Definition VoxlVars.h:62