Auterion App SDK
Auterion SDK is a library that can be used by AuterionOS apps to communicate with the system.
camera.hpp
1 /****************************************************************************
2  *
3  * Copyright 2023 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/image1m.hpp>
44 #include <auterion_core_msgs/msg/image8m.hpp>
45 #include <chrono>
46 #include <functional>
47 #include <opencv2/core/mat.hpp>
48 #include <rclcpp/rclcpp.hpp>
49 #include <sensor_msgs/msg/camera_info.hpp>
50 #include <string>
51 #include <utility>
52 
53 namespace auterion {
57 using namespace std::chrono_literals;
58 
59 class SDK;
60 class CameraMonitorImpl;
61 class CameraImpl;
62 
67 struct ImageHeader {
68  ImageHeader() = default;
69 
71  enum class Encoding {
72  Unknown,
73  Y800,
74  Y16,
75  UYVY,
76  YUY2,
77  I420,
78  NV12,
79  RGB8,
80  BGR8,
82  BA81,
83  GBRG,
84  GRBG,
85  RGGB,
86  RGBA
87  };
88 
89  ImageHeader(int width_, int height_, Encoding encoding_, int step_ = 0);
90 
91  template <typename ImageT = auterion_core_msgs::msg::Image8m>
92  explicit ImageHeader(const ImageT& image)
93  : width(image.width), height(image.height), step(image.step), timestamp(image.stamp) {
94  const int encoding_len =
95  strnlen(reinterpret_cast<const char*>(image.encoding.data()), image.encoding.size());
96  const std::string encoding_str(reinterpret_cast<const char*>(image.encoding.data()),
97  encoding_len);
98  if (encoding_str == "mono8") {
99  encoding = Encoding::Y800;
100  } else if (encoding_str == "mono16") {
101  encoding = Encoding::Y16;
102  } else if (encoding_str == "uyvy") {
103  encoding = Encoding::UYVY;
104  } else if (encoding_str == "yuyv") {
105  encoding = Encoding::YUY2;
106  } else if (encoding_str == "i420") {
107  // (This does not exist in ROS:
108  // https://github.com/ros2/common_interfaces/blob/master/sensor_msgs/include/sensor_msgs/image_encodings.hpp)
109  encoding = Encoding::I420;
110  } else if (encoding_str == "nv12") {
111  // Does not exist in ROS 2
112  encoding = Encoding::NV12;
113  } else if (encoding_str == "rgb8") {
114  encoding = Encoding::RGB8;
115  } else if (encoding_str == "rgba8") {
116  encoding = Encoding::RGBA;
117  } else if (encoding_str == "bgr8") {
118  encoding = Encoding::BGR8;
119  } else if (encoding_str == "bayer_bggr8") {
120  encoding = Encoding::BA81;
121  } else if (encoding_str == "bayer_gbrg8") {
122  encoding = Encoding::GBRG;
123  } else if (encoding_str == "bayer_grbg8") {
124  encoding = Encoding::GRBG;
125  } else if (encoding_str == "bayer_rggb8") {
126  encoding = Encoding::RGGB;
127  }
128  }
129 
130  template <typename ImageT = auterion_core_msgs::msg::Image8m>
131  void toImage(ImageT& image) {
132  image.width = width;
133  image.height = height;
134  image.step = step;
135  image.stamp = timestamp;
136  switch (encoding) {
137  case Encoding::Unknown:
138  strncpy(reinterpret_cast<char*>(image.encoding.data()), "unknown",
139  image.encoding.size());
140  break;
141  case Encoding::Y800:
142  strncpy(reinterpret_cast<char*>(image.encoding.data()), "mono8",
143  image.encoding.size());
144  break;
145  case Encoding::Y16:
146  strncpy(reinterpret_cast<char*>(image.encoding.data()), "mono16",
147  image.encoding.size());
148  break;
149  case Encoding::UYVY:
150  strncpy(reinterpret_cast<char*>(image.encoding.data()), "uyvy",
151  image.encoding.size());
152  break;
153  case Encoding::YUY2:
154  strncpy(reinterpret_cast<char*>(image.encoding.data()), "yuyv",
155  image.encoding.size());
156  break;
157  case Encoding::I420:
158  strncpy(reinterpret_cast<char*>(image.encoding.data()), "i420",
159  image.encoding.size());
160  break;
161  case Encoding::NV12:
162  strncpy(reinterpret_cast<char*>(image.encoding.data()), "nv12",
163  image.encoding.size());
164  break;
165  case Encoding::RGB8:
166  strncpy(reinterpret_cast<char*>(image.encoding.data()), "rgb8",
167  image.encoding.size());
168  break;
169  case Encoding::BGR8:
170  strncpy(reinterpret_cast<char*>(image.encoding.data()), "bgr8",
171  image.encoding.size());
172  break;
173  case Encoding::RGBA:
174  strncpy(reinterpret_cast<char*>(image.encoding.data()), "rgba8",
175  image.encoding.size());
176  break;
177  case Encoding::BA81:
178  strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_bggr8",
179  image.encoding.size());
180  break;
181  case Encoding::GBRG:
182  strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_gbrg8",
183  image.encoding.size());
184  break;
185  case Encoding::GRBG:
186  strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_grbg8",
187  image.encoding.size());
188  break;
189  case Encoding::RGGB:
190  strncpy(reinterpret_cast<char*>(image.encoding.data()), "bayer_rggb8",
191  image.encoding.size());
192  break;
193  }
194  }
195 
196  size_t planeOffset(int plane) const;
197  size_t planeSizeBytes(int plane) const;
198  size_t imageSizeBytes() const;
199 
200  int width{};
201  int height{};
202  int step{};
203  Encoding encoding{Encoding::Unknown};
204  rclcpp::Time timestamp{};
205 };
206 
207 std::ostream& operator<<(std::ostream& os, const ImageHeader::Encoding& encoding);
208 
213 class Image {
214  public:
215  Image(ImageHeader header, uint8_t* data) : _header(std::move(header)), _data(data) {}
216 
217  const ImageHeader& header() const { return _header; }
218 
219  uint8_t* data() { return _data; }
220  const uint8_t* data() const { return _data; }
221 
222  int width() const { return _header.width; }
223  int height() const { return _header.height; }
224 
229  cv::Mat asOpenCVMat(int plane = -1) const;
230 
231  private:
232  ImageHeader _header{};
233  uint8_t* _data{nullptr};
234 };
235 
241  enum class SensorType {
242  Unknown = 0,
243  Monochrome = 1,
244  RGB = 2,
245  Depth = 3,
246  RGBD = 4,
247  IR = 5,
248  };
249  enum class MountOrientation {
250  Unknown = 0,
251  Dynamic = 1,
252  Forward = 2,
253  Backward = 3,
254  Upward = 4,
255  Downward = 5,
256  Leftward = 6,
257  Rightward = 7,
258  };
259  enum class MountOrientationDetail {
260  Unknown = 0,
261  Left = 1,
262  Right = 2,
263  Upper = 3,
264  Lower = 4,
265  };
266  enum class PrimaryPurpose {
267  Unknown = 0,
268  Generic = 1,
269  OpticalFlow = 2,
270  VIO = 3,
271  FPV = 4,
272  Survey = 5,
273  Tracking = 6,
274  };
276  Eigen::Quaterniond rotation{-0.5, 0.5, -0.5, 0.5}; // {q, x, y, z} default front facing
277  Eigen::Vector3d translation{0.0, 0.0, 0.0};
278  };
279 
280  CameraDescriptor() = default;
281  explicit CameraDescriptor(const auterion_core_msgs::msg::CameraInfo& camera_info);
282 
283  operator auterion_core_msgs::msg::CameraInfo() const;
284 
285  std::string unique_name;
286 
287  std::string camera_model;
288 
289  SensorType sensor_type{SensorType::Unknown};
290  MountOrientation mount_orientation{MountOrientation::Unknown};
291  MountOrientationDetail mount_orientation_detail{MountOrientationDetail::Unknown};
292  PrimaryPurpose primary_purpose{PrimaryPurpose::Unknown};
293  ExtrinsicCalibration extrinsic_calibration;
294 };
295 
302  public:
303  using NotificationCallback = std::function<void(const CameraDescriptor&)>;
304 
305  CameraMonitor(SDK& sdk, const NotificationCallback& on_camera_added,
306  const NotificationCallback& on_camera_removed = nullptr);
307 
308  ~CameraMonitor() = default;
309 
310  private:
311  std::shared_ptr<CameraMonitorImpl> _impl;
312 };
313 
318 class Camera {
319  public:
320  Camera(SDK& sdk, const CameraDescriptor& camera_descriptor);
321  ~Camera() = default;
322 
326  static Camera waitForCamera(SDK& sdk,
327  const std::function<bool(const CameraDescriptor&)>& find_callback);
328 
329  [[deprecated(
330  "waitForCamera with 'camera_descriptor' not used anymore. This will be removed in some "
331  "later release.")]] static Camera
332  waitForCamera(SDK& sdk,
333  std::optional<auterion::CameraDescriptor> camera_descriptor = std::nullopt);
334 
335  static std::optional<Camera> open(SDK& sdk, const std::string& unique_name,
336  const std::chrono::seconds& timeout = 30s);
337  static std::optional<Camera> open(
338  SDK& sdk, const std::function<bool(const CameraDescriptor&)>& find_callback,
339  const std::chrono::seconds& timeout = 30s);
340  static std::optional<Camera> openFirst(SDK& sdk, const std::chrono::seconds& timeout = 30s);
341 
342  using ImageCallback = std::function<void(const Image& image)>;
343 
349  void subscribeImage(const ImageCallback& callback);
350 
351  void subscribeCameraInfo(
352  std::function<void(const sensor_msgs::msg::CameraInfo::SharedPtr)> callback);
353 
359  bool cameraInfoValid() const;
360 
365  std::optional<cv::Size> getLastImageSize() const;
366 
373  Eigen::Vector3d unprojectImagePoint(const cv::Point& image_point) const;
374 
389  bool getPoseBodyCamera(Eigen::Quaterniond& q_body_camera, Eigen::Vector3d& t_body_camera) const;
390 
409  bool getPoseWorldCamera(Eigen::Quaterniond& q_enu_camera, Eigen::Vector3d& t_enu_camera,
410  const tf2::TimePoint& time = tf2::TimePointZero) const;
411 
441  bool getDeltaRotationWorldCamera(Eigen::Quaterniond& q_delta_world_camera,
442  tf2::TimePoint& time_start, tf2::TimePoint& time_end);
443 
450  bool getCameraInfo(sensor_msgs::msg::CameraInfo& camera_info) const;
451 
452  const CameraDescriptor& descriptor() const;
453 
454  private:
455  std::shared_ptr<CameraImpl> _impl;
456 };
457 
459 } // namespace auterion
Monitor for new or removed cameras. This will also list all existing cameras upon creating the object...
Definition: camera.hpp:301
Camera client to subscribe to a camera image stream.
Definition: camera.hpp:318
bool cameraInfoValid() const
Determine whether a camera info message contains valid data.
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)
std::optional< cv::Size > getLastImageSize() const
Returns the last image size based on the camera image message.
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.
static Camera waitForCamera(SDK &sdk, const std::function< bool(const CameraDescriptor &)> &find_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.
Contains image data with a specific encoding.
Definition: camera.hpp:213
cv::Mat asOpenCVMat(int plane=-1) const
SDK execution class. All callbacks are called on the same thread.
Definition: auterion.hpp:97
std::ostream & operator<<(std::ostream &os, const TrackingState &state)
Overloaded << operator to stringify the TrackingState enum.
Struct describing a camera configuration.
Definition: camera.hpp:240
Contains image metadata.
Definition: camera.hpp:67
Encoding
Image encoding, following FourCC.
Definition: camera.hpp:71