VOXL OpenVINS Server 1.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 Zauberflote
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 if (!camera_queue.push(message))
179 {
180 if (true)
181 {
182 // TODO: DROP OLDEST FRAME, ADD NEW FRAME --> RIGHT NOW WE JUST DROP THE NEW FRAME, NOT KOSHER
183 std::cerr << "Camera queue full — dropping frame from cam " << get_channel() << std::endl;
184 vio_error_codes |= ERROR_CODE_DROPPED_CAM;
185 }
186 }
187 else
188 {
189 // Notify fusion system that camera data is ready
191 }
192 }
193
194 void StereoCamera::update_masks_if_needed(bool should_mask_left, bool should_mask_right)
195 {
196 // Check if we need to update the masks
197 const bool needs_update_left = !current_mask_state_left_.has_value() ||
198 current_mask_state_left_.value() != should_mask_left ||
199 mask_dimensions_changed_;
200
201 const bool needs_update_right = !current_mask_state_right_.has_value() ||
202 current_mask_state_right_.value() != should_mask_right ||
203 mask_dimensions_changed_;
204
205 if (!needs_update_left && !needs_update_right) {
206 return;
207 }
208
209 // Update mask state
210 current_mask_state_left_ = should_mask_left;
211 current_mask_state_right_ = should_mask_right;
212 mask_dimensions_changed_ = false;
213
214 // Create or update both masks efficiently
215 if (should_mask_left) {
216 // Use cv::Scalar constructor for better performance
217 use_mask_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(255));
218 } else {
219 // Use cv::Scalar constructor for better performance
220 use_mask_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(0));
221 }
222
223 if (should_mask_right) {
224 // Use cv::Scalar constructor for better performance
225 use_mask2_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(255));
226 } else {
227 // Use cv::Scalar constructor for better performance
228 use_mask2_ = cv::Mat(current_height, current_width, CV_8UC1, cv::Scalar(0));
229 }
230 }
231
232 /**
233 * @brief Check if system is in reset state
234 *
235 * Determines whether the VIO system is currently in a reset state,
236 * which affects how image processing should be handled.
237 *
238 * @return true if system is resetting, false otherwise
239 */
240 bool StereoCamera::is_system_resetting() const
241 {
242 return is_resetting;
243 }
244
245 /**
246 * @brief Check if system is ready to process images
247 *
248 * Determines whether the VIO system is ready to accept and process
249 * new image data. The system is considered ready when both the IMU
250 * is connected and the main process is running.
251 *
252 * @return true if system is ready, false otherwise
253 */
254 bool StereoCamera::is_system_ready() const
255 {
257 }
258
259} // namespace voxl
Stereo camera implementation for VOXL OpenVINS.
bool occlude_stereo_right
Occlude stereo right.
Definition VoxlVars.cpp:214
volatile int64_t last_cam_time
Timestamp of last camera data (nanoseconds)
Definition VoxlVars.cpp:199
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:56
bool occlude_stereo_left
Occlude stereo left.
Definition VoxlVars.cpp:211
std::mutex reset_mtx
Mutex used by reset thread.
Definition VoxlVars.cpp:59
float takeoff_alt_threshold
Takeoff altitude threshold.
Definition VoxlVars.cpp:208
std::condition_variable reset_cv
Reset conditional variable.
Definition VoxlVars.cpp:62
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.
static CameraQueueFusion & getInstance()
Get singleton instance.
void markCameraReady(size_t cam_id)
Mark a camera as ready with new data.
Base class for all camera implementations.
Definition CameraBase.h:57
cv::Mat use_mask_
Per-instance reusable mask for feature tracking.
Definition CameraBase.h:184
int get_channel() const
Get the camera pipe channel.
Definition CameraBase.h:113
size_t get_id() const
Get the camera identifier.
Definition CameraBase.h:119
img_ringbuf_packet curr_message_
Instance-local buffer for image processing.
Definition CameraBase.h:181
boost::lockfree::spsc_queue< ov_core::CameraData, boost::lockfree::capacity< 64 > > camera_queue
Lock-free SPSC queue for camera data.
Definition CameraBase.h:178
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:198
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