Auterion App SDK
Auterion SDK is a library that can be used by AuterionOS apps to communicate with the system.
Loading...
Searching...
No Matches
camera.hpp
1/****************************************************************************
2 *
3 * Copyright 2023-2026 Auterion AG. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 ****************************************************************************/
32
33#pragma once
34
35#ifdef ROS_DISTRO_IS_ROLLING
36#include <tf2_ros/transform_listener.hpp>
37#else
38#include <tf2_ros/transform_listener.h>
39#endif
40
41#include <Eigen/Eigen>
42#include <auterion_core_msgs/msg/camera_info.hpp>
43#include <auterion_core_msgs/msg/image8m.hpp>
44#include <chrono>
45#include <functional>
46#include <opencv2/core/mat.hpp>
47#include <rclcpp/rclcpp.hpp>
48#include <sensor_msgs/msg/camera_info.hpp>
49#include <string>
50#include <utility>
51
52namespace auterion {
56using namespace std::chrono_literals;
57
58class SDK;
59class CameraMonitorImpl;
60class CameraImpl;
61
67 ImageHeader() = default;
68
70 enum class Encoding {
71 Unknown,
72 Y800,
73 Y16,
74 UYVY,
75 YUY2,
76 I420,
77 NV12,
78 RGB8,
79 BGR8,
81 BA81,
82 GBRG,
83 GRBG,
84 RGGB,
85 RGBA
86 };
87
88 ImageHeader(int width_, int height_, Encoding encoding_, int step_ = 0);
89
90 template <typename ImageT = auterion_core_msgs::msg::Image8m>
91 explicit ImageHeader(const ImageT& image)
92 : width(image.width), height(image.height), step(image.step), timestamp(image.stamp) {
93 const int encoding_len =
94 strnlen(reinterpret_cast<const char*>(image.encoding.data()), image.encoding.size());
95 const std::string encoding_str(reinterpret_cast<const char*>(image.encoding.data()),
96 encoding_len);
97 if (encoding_str == "mono8") {
98 encoding = Encoding::Y800;
99 } else if (encoding_str == "mono16") {
100 encoding = Encoding::Y16;
101 } else if (encoding_str == "uyvy") {
102 encoding = Encoding::UYVY;
103 } else if (encoding_str == "yuyv") {
104 encoding = Encoding::YUY2;
105 } else if (encoding_str == "i420") {
106 // (This does not exist in ROS:
107 // https://github.com/ros2/common_interfaces/blob/master/sensor_msgs/include/sensor_msgs/image_encodings.hpp)
108 encoding = Encoding::I420;
109 } else if (encoding_str == "nv12") {
110 // Does not exist in ROS 2
111 encoding = Encoding::NV12;
112 } else if (encoding_str == "rgb8") {
113 encoding = Encoding::RGB8;
114 } else if (encoding_str == "rgba8") {
115 encoding = Encoding::RGBA;
116 } else if (encoding_str == "bgr8") {
117 encoding = Encoding::BGR8;
118 } else if (encoding_str == "bayer_bggr8") {
119 encoding = Encoding::BA81;
120 } else if (encoding_str == "bayer_gbrg8") {
121 encoding = Encoding::GBRG;
122 } else if (encoding_str == "bayer_grbg8") {
123 encoding = Encoding::GRBG;
124 } else if (encoding_str == "bayer_rggb8") {
125 encoding = Encoding::RGGB;
126 }
127 }
128
129 template <typename ImageT = auterion_core_msgs::msg::Image8m>
130 void toImage(ImageT& image) {
131 image.width = width;
132 image.height = height;
133 image.step = step;
134 image.stamp = timestamp;
135 switch (encoding) {
136 case Encoding::Unknown:
137 strncpy(reinterpret_cast<char*>(image.encoding.data()), "unknown",
138 image.encoding.size());
139 break;
140 case Encoding::Y800:
141 strncpy(reinterpret_cast<char*>(image.encoding.data()), "mono8",
142 image.encoding.size());
143 break;
144 case Encoding::Y16:
145 strncpy(reinterpret_cast<char*>(image.encoding.data()), "mono16",
146 image.encoding.size());
147 break;
148 case Encoding::UYVY:
149 strncpy(reinterpret_cast<char*>(image.encoding.data()), "uyvy",
150 image.encoding.size());
151 break;
152 case Encoding::YUY2:
153 strncpy(reinterpret_cast<char*>(image.encoding.data()), "yuyv",
154 image.encoding.size());
155 break;
156 case Encoding::I420:
157 strncpy(reinterpret_cast<char*>(image.encoding.data()), "i420",
158 image.encoding.size());
159 break;
160 case Encoding::NV12:
161 strncpy(reinterpret_cast<char*>(image.encoding.data()), "nv12",
162 image.encoding.size());
163 break;
164 case Encoding::RGB8:
165 strncpy(reinterpret_cast<char*>(image.encoding.data()), "rgb8",
166 image.encoding.size());
167 break;
168 case Encoding::BGR8:
169 strncpy(reinterpret_cast<char*>(image.encoding.data()), "bgr8",
170 image.encoding.size());
171 break;
172 case Encoding::RGBA:
173 strncpy(reinterpret_cast<char*>(image.encoding.data()), "rgba8",
174 image.encoding.size());
175 break;
176 case Encoding::BA81:
177 strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_bggr8",
178 image.encoding.size());
179 break;
180 case Encoding::GBRG:
181 strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_gbrg8",
182 image.encoding.size());
183 break;
184 case Encoding::GRBG:
185 strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_grbg8",
186 image.encoding.size());
187 break;
188 case Encoding::RGGB:
189 strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_rggb8",
190 image.encoding.size());
191 break;
192 }
193 }
194
195 size_t planeOffset(int plane) const;
196 size_t planeSizeBytes(int plane) const;
197 size_t imageSizeBytes() const;
198
199 int width{};
200 int height{};
201 int step{};
202 Encoding encoding{Encoding::Unknown};
203
204 rclcpp::Time timestamp{};
205 std::optional<uint64_t> monotonic_timestamp_us{std::nullopt};
206
207 float framerate{0.f};
208
211 uint16_t frame_id{0};
212
213 sensor_msgs::msg::CameraInfo camera_info{};
214};
215
216std::ostream& operator<<(std::ostream& os, const ImageHeader::Encoding& encoding);
217
222class Image {
223 public:
224 Image(ImageHeader header, uint8_t* data) : _header(std::move(header)), _data(data) {}
225
226 const ImageHeader& header() const { return _header; }
227
228 uint8_t* data() { return _data; }
229 const uint8_t* data() const { return _data; }
230
231 int width() const { return _header.width; }
232 int height() const { return _header.height; }
233
238 cv::Mat asOpenCVMat(int plane = -1) const;
239
240 private:
241 ImageHeader _header{};
242 uint8_t* _data{nullptr};
243};
244
252class ImageFD {
253 public:
254 ImageFD(ImageHeader header, int fd, uint64_t buffer_size,
255 std::function<void()> release_function)
256 : _header(std::move(header)),
257 _fd(fd),
258 _buffer_size(buffer_size),
259 _release_function(std::move(release_function)) {}
260
261 ~ImageFD() { release(); }
262
263 // Delete copy operations
264 // Copying an ImageFD is not allowed since it contains an FD that should be uniquely owned.
265 ImageFD(const ImageFD&) = delete;
266 ImageFD& operator=(const ImageFD&) = delete;
267
268 // Move constructor
269 ImageFD(ImageFD&& other) noexcept
270 : _header(std::move(other._header)),
271 _fd(other._fd),
272 _buffer_size(other._buffer_size),
273 _release_function(std::move(other._release_function)),
274 _was_released(other._was_released) {
275 other._fd = -1;
276 other._buffer_size = 0;
277 other._release_function = nullptr;
278 other._was_released = true;
279 }
280
281 // Move assignment operator
282 ImageFD& operator=(ImageFD&& other) noexcept {
283 if (this != &other) {
284 release();
285 _header = std::move(other._header);
286 _fd = other._fd;
287 _buffer_size = other._buffer_size;
288 _release_function = std::move(other._release_function);
289 _was_released = other._was_released;
290 other._fd = -1;
291 other._buffer_size = 0;
292 other._release_function = nullptr;
293 other._was_released = true;
294 }
295 return *this;
296 }
297
298 const ImageHeader& header() const { return _header; }
299
300 int fd() const { return _fd; }
301
302 uint64_t bufferSize() const { return _buffer_size; }
303
304 /*
305 * Release the FD, signalling that the buffer is no longer in use.
306 */
307 void release() {
308 if (_release_function) {
309 _release_function();
310 _release_function = nullptr;
311 }
312 _fd = -1;
313 _was_released = true;
314 }
315
316 bool wasReleased() const { return _was_released; }
317
318 private:
319 ImageHeader _header{};
320 int _fd{-1};
321 uint64_t _buffer_size{0};
322
329 std::function<void()> _release_function{nullptr};
330
331 bool _was_released{false};
332};
333
339 enum class SensorType {
340 Unknown = 0,
341 Monochrome = 1,
342 RGB = 2,
343 Depth = 3,
344 RGBD = 4,
345 IR = 5,
346 };
347 enum class MountOrientation {
348 Unknown = 0,
349 Dynamic = 1,
350 Forward = 2,
351 Backward = 3,
352 Upward = 4,
353 Downward = 5,
354 Leftward = 6,
355 Rightward = 7,
356 };
357 enum class MountOrientationDetail {
358 Unknown = 0,
359 Left = 1,
360 Right = 2,
361 Upper = 3,
362 Lower = 4,
363 };
364 enum class PrimaryPurpose {
365 Unknown = 0,
366 Generic = 1,
367 OpticalFlow = 2,
368 VIO = 3,
369 FPV = 4,
370 Survey = 5,
371 Tracking = 6,
372 };
374 Eigen::Quaterniond rotation{-0.5, 0.5, -0.5, 0.5}; // {q, x, y, z} default front facing
375 Eigen::Vector3d translation{0.0, 0.0, 0.0};
376 };
377
378 CameraDescriptor() = default;
379 explicit CameraDescriptor(const auterion_core_msgs::msg::CameraInfo& camera_info);
380
381 operator auterion_core_msgs::msg::CameraInfo() const;
382
383 std::string device_driver;
384 std::string device_identifier;
385
386 std::string unique_hash;
387 std::string unique_name;
388
389 std::string camera_model;
390
391 SensorType sensor_type{SensorType::Unknown};
392 MountOrientation mount_orientation{MountOrientation::Unknown};
393 MountOrientationDetail mount_orientation_detail{MountOrientationDetail::Unknown};
394 PrimaryPurpose primary_purpose{PrimaryPurpose::Unknown};
395 ExtrinsicCalibration extrinsic_calibration;
396};
397
404 public:
405 using NotificationCallback = std::function<void(const CameraDescriptor&)>;
406
407 CameraMonitor(SDK& sdk, const NotificationCallback& on_camera_added,
408 const NotificationCallback& on_camera_removed = nullptr);
409
410 ~CameraMonitor() = default;
411
412 private:
413 std::shared_ptr<CameraMonitorImpl> _impl;
414};
415
420class Camera {
421 public:
422 Camera(SDK& sdk, const CameraDescriptor& camera_descriptor);
423 ~Camera() = default;
424
429 const std::function<bool(const CameraDescriptor&)>& find_callback);
430
431 [[deprecated(
432 "waitForCamera with 'camera_descriptor' not used anymore. This will be removed in some "
433 "later release.")]] static Camera
434 waitForCamera(SDK& sdk,
435 std::optional<auterion::CameraDescriptor> camera_descriptor = std::nullopt);
436
437 static std::optional<Camera> open(SDK& sdk, const std::string& unique_name,
438 const std::chrono::seconds& timeout = 30s);
439 static std::optional<Camera> open(
440 SDK& sdk, const std::function<bool(const CameraDescriptor&)>& find_callback,
441 const std::chrono::seconds& timeout = 30s);
442 static std::optional<Camera> openFirst(SDK& sdk, const std::chrono::seconds& timeout = 30s);
443
444 using ImageCallback = std::function<void(const Image& image)>;
445 using ImageFDCallback = std::function<void(const std::shared_ptr<ImageFD>& image_fd)>;
446 using CameraInfoAfterSwitchCallback =
447 std::function<void(const sensor_msgs::msg::CameraInfo::SharedPtr& camera_info)>;
448
454 void subscribeImage(const ImageCallback& callback);
455
463 void subscribeImageFD(const ImageFDCallback& callback);
464
465 [[deprecated(
466 "subscribeCameraInfo() is deprecated and will be removed in a future release. The "
467 "CameraInfo is instead included in the image header obtained with subscribeImage(), and "
468 "can also be queried with getCameraInfo().")]] void
469 subscribeCameraInfo(
470 std::function<void(const sensor_msgs::msg::CameraInfo::SharedPtr)> callback);
471
477 void subscribeCameraInfoAfterSwitch(const CameraInfoAfterSwitchCallback& callback);
478
484 bool cameraInfoValid() const;
485
494 std::optional<cv::Size> getLastImageSize() const;
495
502 Eigen::Vector3d unprojectImagePoint(const cv::Point& image_point) const;
503
518 bool getPoseBodyCamera(Eigen::Quaterniond& q_body_camera, Eigen::Vector3d& t_body_camera) const;
519
538 bool getPoseWorldCamera(Eigen::Quaterniond& q_enu_camera, Eigen::Vector3d& t_enu_camera,
539 const tf2::TimePoint& time = tf2::TimePointZero) const;
540
570 bool getDeltaRotationWorldCamera(Eigen::Quaterniond& q_delta_world_camera,
571 tf2::TimePoint& time_start, tf2::TimePoint& time_end);
572
579 bool getCameraInfo(sensor_msgs::msg::CameraInfo& camera_info) const;
580
590 bool switchTo(const std::function<bool(const CameraDescriptor&)>& find_callback);
591
596
597 private:
598 std::shared_ptr<CameraImpl> _impl;
599};
600
602} // namespace auterion
Monitor for new or removed cameras. This will also list all existing cameras upon creating the object...
Definition camera.hpp:403
Camera client to subscribe to a camera image stream.
Definition camera.hpp:420
const CameraDescriptor & descriptor() const
Get the descriptor of the active camera source.
bool cameraInfoValid() const
Determine whether a camera info message contains valid data.
void subscribeCameraInfoAfterSwitch(const CameraInfoAfterSwitchCallback &callback)
Subscribe to camera info updates emitted once after each successful stream switch.
bool getCameraInfo(sensor_msgs::msg::CameraInfo &camera_info) const
Get the camera information data for the camera.
bool getPoseBodyCamera(Eigen::Quaterniond &q_body_camera, Eigen::Vector3d &t_body_camera) const
Get the pose of the camera with respect to the body frame.
void subscribeImage(const ImageCallback &callback)
Eigen::Vector3d unprojectImagePoint(const cv::Point &image_point) const
Determine the (relative) camera-frame 3D position of a point in an image.
bool getDeltaRotationWorldCamera(Eigen::Quaterniond &q_delta_world_camera, tf2::TimePoint &time_start, tf2::TimePoint &time_end)
Calculates how much the camera has rotated between two points in time based only on gyro data.
std::optional< cv::Size > getLastImageSize() const
Returns the size of the last received image, if available.
static Camera waitForCamera(SDK &sdk, const std::function< bool(const CameraDescriptor &)> &find_callback)
bool switchTo(const std::function< bool(const CameraDescriptor &)> &find_callback)
Switch active UnixFD input to a discovered camera selected by callback.
bool getPoseWorldCamera(Eigen::Quaterniond &q_enu_camera, Eigen::Vector3d &t_enu_camera, const tf2::TimePoint &time=tf2::TimePointZero) const
Get the pose of the camera with respect to the ENU world frame.
Definition camera.hpp:252
Contains image data with a specific encoding.
Definition camera.hpp:222
cv::Mat asOpenCVMat(int plane=-1) const
SDK execution class. All callbacks are called on the same thread.
Definition auterion.hpp:119
Struct describing a camera configuration.
Definition camera.hpp:338
Contains image metadata.
Definition camera.hpp:66
uint16_t frame_id
Definition camera.hpp:211
int step
full row size in bytes
Definition camera.hpp:201
std::optional< uint64_t > monotonic_timestamp_us
hardware timestamp [μs]
Definition camera.hpp:205
Encoding
Image encoding, following FourCC.
Definition camera.hpp:70
@ NV12
YUV 4:2:0, https://fourcc.org/pixel-format/yuv-nv12/.
@ Y800
8 bit monochrome image (GREY), https://fourcc.org/pixel-format/yuv-y800/
@ Y16
16 bit monochrome image, https://fourcc.org/pixel-format/yuv-y16/
@ BA81
8-bit Bayer (https://docs.kernel.org/userspace-api/media/v4l/pixfmt-srggb8.html)
@ UYVY
YUV 4:2:2, https://fourcc.org/pixel-format/yuv-uyvy/.
@ YUY2
YUV 4:2:2 (YUYV), https://fourcc.org/pixel-format/yuv-yuy2/.
@ I420
YUV 4:2:0, https://fourcc.org/pixel-format/yuv-i420/.
@ RGBA
32-bit RGBA used for rendering