CameraDepthAI.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2021, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 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  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 #include <rtabmap/core/util2d.h>
30 #include <rtabmap/utilite/UTimer.h>
34 #include <rtabmap/utilite/UFile.h>
35 
36 
37 namespace rtabmap {
38 
40 {
41 #ifdef RTABMAP_DEPTHAI
42  return true;
43 #else
44  return false;
45 #endif
46 }
47 
49  const std::string & mxidOrName,
50  int imageWidth,
51  float imageRate,
52  const Transform & localTransform) :
53  Camera(imageRate, localTransform)
54 #ifdef RTABMAP_DEPTHAI
55  ,
56  mxidOrName_(mxidOrName),
57  outputMode_(0),
58  confThreshold_(200),
59  lrcThreshold_(5),
60  imageWidth_(imageWidth),
61  extendedDisparity_(false),
62  enableCompanding_(false),
63  subpixelFractionalBits_(3),
64  disparityWidth_(1),
65  medianFilter_(5),
66  useSpecTranslation_(false),
67  alphaScaling_(0.0),
68  imagesRectified_(true),
69  imuPublished_(true),
70  publishInterIMU_(false),
71  dotIntensity_(0.0),
72  floodIntensity_(0.0),
73  detectFeatures_(0),
74  useHarrisDetector_(false),
75  minDistance_(7.0),
76  numTargetFeatures_(320),
77  threshold_(0.01),
78  nms_(true),
79  nmsRadius_(4)
80 #endif
81 {
82 #ifdef RTABMAP_DEPTHAI
83  UASSERT(imageWidth_ == 640 || imageWidth_ == 1280);
84  if(this->getImageRate() <= 0)
85  this->setImageRate(30);
86 #endif
87 }
88 
90 {
91 #ifdef RTABMAP_DEPTHAI
92  if(device_.get())
93  device_->close();
94 #endif
95 }
96 
97 void CameraDepthAI::setOutputMode(int outputMode)
98 {
99 #ifdef RTABMAP_DEPTHAI
100  outputMode_ = outputMode;
101 #else
102  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
103 #endif
104 }
105 
106 void CameraDepthAI::setDepthProfile(int confThreshold, int lrcThreshold)
107 {
108 #ifdef RTABMAP_DEPTHAI
109  confThreshold_ = confThreshold;
110  lrcThreshold_ = lrcThreshold;
111 #else
112  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
113 #endif
114 }
115 
116 void CameraDepthAI::setExtendedDisparity(bool extendedDisparity, bool enableCompanding)
117 {
118 #ifdef RTABMAP_DEPTHAI
119  extendedDisparity_ = extendedDisparity;
120  enableCompanding_ = enableCompanding;
121  if(extendedDisparity_ && enableCompanding_)
122  {
123  UWARN("Extended disparity has been enabled while companding being also enabled, disabling companding...");
124  enableCompanding_ = false;
125  }
126 #else
127  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
128 #endif
129 }
130 
131 void CameraDepthAI::setSubpixelMode(bool enabled, int fractionalBits)
132 {
133 #ifdef RTABMAP_DEPTHAI
134  UASSERT(fractionalBits>=3 && fractionalBits<=5);
135  subpixelFractionalBits_ = enabled?fractionalBits:0;
136 #else
137  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
138 #endif
139 }
140 
141 void CameraDepthAI::setDisparityWidthAndFilter(int disparityWidth, int medianFilter)
142 {
143 #ifdef RTABMAP_DEPTHAI
144  UASSERT(disparityWidth == 64 || disparityWidth == 96);
145  disparityWidth_ = disparityWidth;
146  medianFilter_ = medianFilter;
147  int maxDisp = (extendedDisparity_?2:1) * std::pow(2,subpixelFractionalBits_) * (disparityWidth_-1);
148  if(medianFilter_ && maxDisp > 1024)
149  {
150  UWARN("Maximum disparity value '%d' exceeds the maximum supported '1024' by median filter, disabling median filter...", maxDisp);
151  medianFilter_ = 0;
152  }
153 #else
154  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
155 #endif
156 }
157 
158 void CameraDepthAI::setRectification(bool useSpecTranslation, float alphaScaling, bool enabled)
159 {
160 #ifdef RTABMAP_DEPTHAI
161  useSpecTranslation_ = useSpecTranslation;
162  alphaScaling_ = alphaScaling;
163  imagesRectified_ = enabled;
164 #else
165  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
166 #endif
167 }
168 
169 void CameraDepthAI::setIMU(bool imuPublished, bool publishInterIMU)
170 {
171 #ifdef RTABMAP_DEPTHAI
172  imuPublished_ = imuPublished;
173  publishInterIMU_ = publishInterIMU;
174 #else
175  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
176 #endif
177 }
178 
179 void CameraDepthAI::setIrIntensity(float dotIntensity, float floodIntensity)
180 {
181 #ifdef RTABMAP_DEPTHAI
182  dotIntensity_ = dotIntensity;
183  floodIntensity_ = floodIntensity;
184 #else
185  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
186 #endif
187 }
188 
189 void CameraDepthAI::setDetectFeatures(int detectFeatures, const std::string & blobPath)
190 {
191 #ifdef RTABMAP_DEPTHAI
192  detectFeatures_ = detectFeatures;
193  blobPath_ = blobPath;
194  if(detectFeatures_>=2 && blobPath_.empty())
195  {
196  UWARN("Missing MyriadX blob file, disabling on-device feature detector");
197  detectFeatures_ = 0;
198  }
199  if(detectFeatures_>=2 && this->getImageRate()>15)
200  {
201  UWARN("On-device SuperPoint or HF-Net enabled, image rate is limited to 15 FPS!");
202  this->setImageRate(15);
203  }
204 #else
205  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
206 #endif
207 }
208 
209 void CameraDepthAI::setGFTTDetector(bool useHarrisDetector, float minDistance, int numTargetFeatures)
210 {
211 #ifdef RTABMAP_DEPTHAI
212  useHarrisDetector_ = useHarrisDetector;
213  minDistance_ = minDistance;
214  numTargetFeatures_ = numTargetFeatures;
215 #else
216  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
217 #endif
218 }
219 
220 void CameraDepthAI::setSuperPointDetector(float threshold, bool nms, int nmsRadius)
221 {
222 #ifdef RTABMAP_DEPTHAI
223  threshold_ = threshold;
224  nms_ = nms;
225  nmsRadius_ = nmsRadius;
226 #else
227  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
228 #endif
229 }
230 
231 bool CameraDepthAI::init(const std::string & calibrationFolder, const std::string & cameraName)
232 {
233  UDEBUG("");
234 #ifdef RTABMAP_DEPTHAI
235 
236  std::vector<dai::DeviceInfo> devices = dai::Device::getAllAvailableDevices();
237  if(devices.empty() && mxidOrName_.empty())
238  {
239  UERROR("No DepthAI device found or specified");
240  return false;
241  }
242 
243  accBuffer_.clear();
244  gyroBuffer_.clear();
245 
246  bool deviceFound = false;
247  dai::DeviceInfo deviceToUse(mxidOrName_);
248  if(mxidOrName_.empty())
249  std::tie(deviceFound, deviceToUse) = dai::Device::getFirstAvailableDevice();
250  else if(!deviceToUse.mxid.empty())
251  std::tie(deviceFound, deviceToUse) = dai::Device::getDeviceByMxId(deviceToUse.mxid);
252  else
253  deviceFound = true;
254 
255  if(!deviceFound)
256  {
257  UERROR("Could not find DepthAI device with MXID or IP/USB name \"%s\", found devices:", mxidOrName_.c_str());
258  for(auto& device : devices)
259  UERROR("%s", device.toString().c_str());
260  return false;
261  }
262 
263  device_ = std::make_unique<dai::Device>(deviceToUse);
264  auto deviceName = device_->getDeviceName();
265  auto imuType = device_->getConnectedIMU();
266  UINFO("Device Name: %s, Device Serial: %s", deviceName.c_str(), device_->getMxId().c_str());
267  UINFO("Available Camera Sensors: ");
268  for(auto& sensor : device_->getCameraSensorNames()) {
269  UINFO("Socket: CAM_%c - %s", 'A'+(unsigned char)sensor.first, sensor.second.c_str());
270  }
271  UINFO("IMU Type: %s", imuType.c_str());
272 
273  UINFO("Loading eeprom calibration data");
274  auto calibHandler = device_->readCalibration();
275  auto boardName = calibHandler.getEepromData().boardName;
276 
277  stereoModel_ = StereoCameraModel();
278  targetSize_ = cv::Size(imageWidth_, imageWidth_/640*((outputMode_==2&&boardName!="BC2087")?360:400));
279 
280  if(!calibrationFolder.empty() && !cameraName.empty() && imagesRectified_)
281  {
282  UINFO("Flashing camera...");
283  if(outputMode_ == 2)
284  {
285  stereoModel_.setName(cameraName, "rgb", "depth");
286  }
287  if(stereoModel_.load(calibrationFolder, cameraName, false))
288  {
289  std::vector<std::vector<float> > intrinsicsLeft(3);
290  std::vector<std::vector<float> > intrinsicsRight(3);
291  for(int row = 0; row<3; ++row)
292  {
293  intrinsicsLeft[row].resize(3);
294  intrinsicsRight[row].resize(3);
295  for(int col = 0; col<3; ++col)
296  {
297  intrinsicsLeft[row][col] = stereoModel_.left().K_raw().at<double>(row,col);
298  intrinsicsRight[row][col] = stereoModel_.right().K_raw().at<double>(row,col);
299  }
300  }
301 
302  std::vector<float> distortionsLeft = stereoModel_.left().D_raw();
303  std::vector<float> distortionsRight = stereoModel_.right().D_raw();
304  std::vector<std::vector<float> > rotationMatrix(3);
305  for(int row = 0; row<3; ++row)
306  {
307  rotationMatrix[row].resize(3);
308  for(int col = 0; col<3; ++col)
309  {
310  rotationMatrix[row][col] = stereoModel_.stereoTransform()(row,col);
311  }
312  }
313 
314  std::vector<float> translation(3);
315  translation[0] = stereoModel_.stereoTransform().x()*100.0f;
316  translation[1] = stereoModel_.stereoTransform().y()*100.0f;
317  translation[2] = stereoModel_.stereoTransform().z()*100.0f;
318 
319  if(outputMode_ == 2)
320  {
321  // Only set RGB intrinsics
322  calibHandler.setCameraIntrinsics(dai::CameraBoardSocket::CAM_A, intrinsicsLeft, stereoModel_.left().imageWidth(), stereoModel_.left().imageHeight());
323  calibHandler.setDistortionCoefficients(dai::CameraBoardSocket::CAM_A, distortionsLeft);
324  std::vector<float> specTranslation = calibHandler.getCameraTranslationVector(dai::CameraBoardSocket::CAM_A, dai::CameraBoardSocket::CAM_C, true);
325  calibHandler.setCameraExtrinsics(dai::CameraBoardSocket::CAM_A, dai::CameraBoardSocket::CAM_C, rotationMatrix, translation, specTranslation);
326  }
327  else
328  {
329  calibHandler.setCameraIntrinsics(dai::CameraBoardSocket::CAM_B, intrinsicsLeft, stereoModel_.left().imageWidth(), stereoModel_.left().imageHeight());
330  calibHandler.setDistortionCoefficients(dai::CameraBoardSocket::CAM_B, distortionsLeft);
331  calibHandler.setCameraIntrinsics(dai::CameraBoardSocket::CAM_C, intrinsicsRight, stereoModel_.right().imageWidth(), stereoModel_.right().imageHeight());
332  calibHandler.setDistortionCoefficients(dai::CameraBoardSocket::CAM_C, distortionsRight);
333  std::vector<float> specTranslation = calibHandler.getCameraTranslationVector(dai::CameraBoardSocket::CAM_B, dai::CameraBoardSocket::CAM_C, true);
334  calibHandler.setCameraExtrinsics(dai::CameraBoardSocket::CAM_B, dai::CameraBoardSocket::CAM_C, rotationMatrix, translation, specTranslation);
335  }
336 
337  try {
338  UINFO("Flashing camera with calibration from %s with camera name %s", calibrationFolder.c_str(), cameraName.c_str());
340  {
341  std::cout << "K left: " << stereoModel_.left().K_raw() << std::endl;
342  std::cout << "K right: " << stereoModel_.right().K_raw() << std::endl;
343  std::cout << "D left: " << stereoModel_.left().D_raw() << std::endl;
344  std::cout << "D right: " << stereoModel_.right().D_raw() << std::endl;
345  std::cout << "Extrinsics: " << stereoModel_.stereoTransform() << std::endl;
346  std::cout << "Expected K with rectification_alpha=0: " << stereoModel_.left().K()*(double(targetSize_.width)/double(stereoModel_.left().imageWidth())) << std::endl;
347  }
348  device_->flashCalibration2(calibHandler);
349  }
350  catch(const std::runtime_error & e) {
351  UERROR("Failed flashing calibration: %s", e.what());
352  }
353  }
354  else
355  {
356  UERROR("Failed loading calibration from %s with camera name %s", calibrationFolder.c_str(), cameraName.c_str());
357  }
358 
359  //Reload calibration
360  calibHandler = device_->readCalibration();
361  }
362 
363  auto cameraId = outputMode_==2?dai::CameraBoardSocket::CAM_A:dai::CameraBoardSocket::CAM_B;
364  cv::Mat cameraMatrix, distCoeffs, newCameraMatrix;
365 
366  std::vector<std::vector<float> > matrix = calibHandler.getCameraIntrinsics(cameraId, targetSize_.width, targetSize_.height);
367  cameraMatrix = (cv::Mat_<double>(3,3) <<
368  matrix[0][0], matrix[0][1], matrix[0][2],
369  matrix[1][0], matrix[1][1], matrix[1][2],
370  matrix[2][0], matrix[2][1], matrix[2][2]);
371 
372  std::vector<float> coeffs = calibHandler.getDistortionCoefficients(cameraId);
373  if(calibHandler.getDistortionModel(cameraId) == dai::CameraModel::Perspective)
374  distCoeffs = (cv::Mat_<double>(1,8) << coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5], coeffs[6], coeffs[7]);
375 
376  if(alphaScaling_>-1.0f)
377  newCameraMatrix = cv::getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, targetSize_, alphaScaling_);
378  else
379  newCameraMatrix = cameraMatrix;
380 
381  double fx = newCameraMatrix.at<double>(0, 0);
382  double fy = newCameraMatrix.at<double>(1, 1);
383  double cx = newCameraMatrix.at<double>(0, 2);
384  double cy = newCameraMatrix.at<double>(1, 2);
385  UINFO("fx=%f fy=%f cx=%f cy=%f (target size = %dx%d)", fx, fy, cx, cy, targetSize_.width, targetSize_.height);
386  if(outputMode_ == 2) {
387  stereoModel_ = StereoCameraModel(deviceName, fx, fy, cx, cy, 0, this->getLocalTransform(), targetSize_);
388  }
389  else {
390  double baseline = calibHandler.getBaselineDistance(dai::CameraBoardSocket::CAM_C, dai::CameraBoardSocket::CAM_B, false)/100.0;
391  UINFO("baseline=%f", baseline);
392  stereoModel_ = StereoCameraModel(deviceName, fx, fy, cx, cy, outputMode_==0?baseline:0, this->getLocalTransform()*Transform(-calibHandler.getBaselineDistance(dai::CameraBoardSocket::CAM_A)/100.0, 0, 0), targetSize_);
393  }
394 
395  if(imuPublished_ || imuType.empty())
396  {
397  // Cannot test the following, I get "IMU calibration data is not available on device yet." with my camera
398  // Update: now (as March 6, 2022) it crashes in "dai::CalibrationHandler::getImuToCameraExtrinsics(dai::CameraBoardSocket, bool)"
399  //matrix = calibHandler.getImuToCameraExtrinsics(dai::CameraBoardSocket::CAM_B);
400  //imuLocalTransform_ = Transform(
401  // matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
402  // matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
403  // matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3]);
404  if(deviceName == "OAK-D")
405  {
406  imuLocalTransform_ = Transform(
407  0, -1, 0, 0.0525,
408  1, 0, 0, 0.013662,
409  0, 0, 1, 0);
410  }
411  else if(boardName == "BC2087") // OAK-D LR
412  {
413  imuLocalTransform_ = Transform(
414  1, 0, 0, 0.021425,
415  0, 1, 0, 0.009925,
416  0, 0, 1, 0);
417  }
418  else if(boardName == "DM2080") // OAK-D SR
419  {
420  imuLocalTransform_ = Transform(
421  -1, 0, 0, 0,
422  0, -1, 0, -0.0024,
423  0, 0, 1, 0);
424  }
425  else if(boardName == "DM9098") // OAK-D S2, OAK-D W, OAK-D Pro, OAK-D Pro W
426  {
427  imuLocalTransform_ = Transform(
428  0, 1, 0, 0.037945,
429  1, 0, 0, 0.00079,
430  0, 0, -1, 0);
431  }
432  else if(boardName == "NG2094") // OAK-D Pro W Dev
433  {
434  imuLocalTransform_ = Transform(
435  0, 1, 0, 0.0374,
436  1, 0, 0, 0.00176,
437  0, 0, -1, 0);
438  }
439  else if(boardName == "NG9097") // OAK-D S2 PoE, OAK-D W PoE, OAK-D Pro PoE, OAK-D Pro W PoE
440  {
441  if(imuType == "BMI270")
442  {
443  imuLocalTransform_ = Transform(
444  0, 1, 0, 0.04,
445  1, 0, 0, 0.020265,
446  0, 0, -1, 0);
447  }
448  else // BNO085/086
449  {
450  imuLocalTransform_ = Transform(
451  0, -1, 0, 0.04,
452  -1, 0, 0, 0.020265,
453  0, 0, -1, 0);
454  }
455  }
456  else
457  {
458  UWARN("Unsupported boardName (%s)! Disabling IMU!", boardName.c_str());
459  imuPublished_ = false;
460  }
461  }
462  else
463  {
464  UINFO("IMU disabled");
465  imuPublished_ = false;
466  }
467 
468  dai::Pipeline pipeline;
469 
470  auto sync = pipeline.create<dai::node::Sync>();
471  sync->setSyncThreshold(std::chrono::milliseconds(int(500 / this->getImageRate())));
472 
473  std::shared_ptr<dai::node::Camera> rgbCamera;
474  if(outputMode_ == 2)
475  {
476  rgbCamera = pipeline.create<dai::node::Camera>();
477  rgbCamera->setCamera("color");
478  if(boardName == "BC2087")
479  rgbCamera->setSize(1920, 1200);
480  else if(boardName == "NG2094")
481  rgbCamera->setSize(1280, 720);
482  else
483  rgbCamera->setSize(1920, 1080);
484  rgbCamera->setVideoSize(targetSize_.width, targetSize_.height);
485  rgbCamera->setPreviewSize(targetSize_.width, targetSize_.height);
486  rgbCamera->setFps(this->getImageRate());
487  rgbCamera->setMeshSource(imagesRectified_?dai::CameraProperties::WarpMeshSource::CALIBRATION:dai::CameraProperties::WarpMeshSource::NONE);
488  if(imagesRectified_ && alphaScaling_>-1.0f)
489  rgbCamera->setCalibrationAlpha(alphaScaling_);
490  rgbCamera->properties.ispScale.horizNumerator = rgbCamera->properties.ispScale.vertNumerator = imageWidth_/640;
491  rgbCamera->properties.ispScale.horizDenominator = rgbCamera->properties.ispScale.vertDenominator = boardName=="NG2094"?2:3;
492 
493  auto rgbEncoder = pipeline.create<dai::node::VideoEncoder>();
494  rgbEncoder->setDefaultProfilePreset(this->getImageRate(), dai::VideoEncoderProperties::Profile::MJPEG);
495 
496  rgbCamera->video.link(rgbEncoder->input);
497  rgbEncoder->bitstream.link(sync->inputs["rgb"]);
498  }
499 
500  auto stereoDepth = pipeline.create<dai::node::StereoDepth>();
501  if(outputMode_ == 2)
502  stereoDepth->setDepthAlign(dai::CameraBoardSocket::CAM_A);
503  else
504  stereoDepth->setDepthAlign(dai::StereoDepthProperties::DepthAlign::RECTIFIED_LEFT);
505  if(subpixelFractionalBits_>=3 && subpixelFractionalBits_<=5)
506  {
507  stereoDepth->setSubpixel(true);
508  stereoDepth->setSubpixelFractionalBits(subpixelFractionalBits_);
509  }
510  stereoDepth->setExtendedDisparity(extendedDisparity_);
511  stereoDepth->enableDistortionCorrection(true);
512  stereoDepth->setDisparityToDepthUseSpecTranslation(useSpecTranslation_);
513  stereoDepth->setDepthAlignmentUseSpecTranslation(useSpecTranslation_);
514  if(alphaScaling_ > -1.0f)
515  stereoDepth->setAlphaScaling(alphaScaling_);
516  stereoDepth->initialConfig.setConfidenceThreshold(confThreshold_);
517  stereoDepth->initialConfig.setLeftRightCheck(lrcThreshold_>=0);
518  if(lrcThreshold_>=0)
519  stereoDepth->initialConfig.setLeftRightCheckThreshold(lrcThreshold_);
520  stereoDepth->initialConfig.setMedianFilter(dai::MedianFilter(medianFilter_));
521  auto config = stereoDepth->initialConfig.get();
522  config.censusTransform.kernelSize = dai::StereoDepthConfig::CensusTransform::KernelSize::KERNEL_7x9;
523  config.censusTransform.kernelMask = 0X5092A28C5152428;
524  config.costMatching.disparityWidth = disparityWidth_==64?dai::StereoDepthConfig::CostMatching::DisparityWidth::DISPARITY_64:dai::StereoDepthConfig::CostMatching::DisparityWidth::DISPARITY_96;
525  config.costMatching.enableCompanding = enableCompanding_;
526  config.costMatching.linearEquationParameters.alpha = 2;
527  config.costMatching.linearEquationParameters.beta = 4;
528  config.costAggregation.horizontalPenaltyCostP1 = 100;
529  config.costAggregation.horizontalPenaltyCostP2 = 500;
530  config.costAggregation.verticalPenaltyCostP1 = 100;
531  config.costAggregation.verticalPenaltyCostP2 = 500;
532  config.postProcessing.brightnessFilter.maxBrightness = 255;
533  stereoDepth->initialConfig.set(config);
534 
535  stereoDepth->depth.link(sync->inputs["depth"]);
536 
537  if(outputMode_ < 2)
538  {
539  auto leftEncoder = pipeline.create<dai::node::VideoEncoder>();
540  leftEncoder->setDefaultProfilePreset(this->getImageRate(), dai::VideoEncoderProperties::Profile::MJPEG);
541 
542  if(imagesRectified_)
543  stereoDepth->rectifiedLeft.link(leftEncoder->input);
544  else
545  stereoDepth->syncedLeft.link(leftEncoder->input);
546  leftEncoder->bitstream.link(sync->inputs["left"]);
547  }
548 
549  if(!outputMode_)
550  {
551  auto rightEncoder = pipeline.create<dai::node::VideoEncoder>();
552  rightEncoder->setDefaultProfilePreset(this->getImageRate(), dai::VideoEncoderProperties::Profile::MJPEG);
553 
554  if(imagesRectified_)
555  stereoDepth->rectifiedRight.link(rightEncoder->input);
556  else
557  stereoDepth->syncedRight.link(rightEncoder->input);
558  rightEncoder->bitstream.link(sync->inputs["right"]);
559  }
560 
561  if(boardName == "BC2087")
562  {
563  auto leftCamera = pipeline.create<dai::node::ColorCamera>();
564  leftCamera->setCamera("left");
565  leftCamera->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1200_P);
566  leftCamera->setIspScale(imageWidth_/640, 3);
567  leftCamera->setFps(this->getImageRate());
568 
569  auto rightCamera = pipeline.create<dai::node::ColorCamera>();
570  rightCamera->setCamera("right");
571  rightCamera->setResolution(dai::ColorCameraProperties::SensorResolution::THE_1200_P);
572  rightCamera->setIspScale(imageWidth_/640, 3);
573  rightCamera->setFps(this->getImageRate());
574 
575  leftCamera->isp.link(stereoDepth->left);
576  rightCamera->isp.link(stereoDepth->right);
577  }
578  else
579  {
580  auto leftCamera = pipeline.create<dai::node::MonoCamera>();
581  leftCamera->setCamera("left");
582  leftCamera->setResolution(imageWidth_==640?dai::MonoCameraProperties::SensorResolution::THE_400_P:dai::MonoCameraProperties::SensorResolution::THE_800_P);
583  leftCamera->setFps(this->getImageRate());
584 
585  auto rightCamera = pipeline.create<dai::node::MonoCamera>();
586  rightCamera->setCamera("right");
587  rightCamera->setResolution(imageWidth_==640?dai::MonoCameraProperties::SensorResolution::THE_400_P:dai::MonoCameraProperties::SensorResolution::THE_800_P);
588  rightCamera->setFps(this->getImageRate());
589 
590  leftCamera->out.link(stereoDepth->left);
591  rightCamera->out.link(stereoDepth->right);
592  }
593 
594  if(detectFeatures_ == 1)
595  {
596  auto gfttDetector = pipeline.create<dai::node::FeatureTracker>();
597  gfttDetector->setHardwareResources(2, 2);
598  gfttDetector->initialConfig.setCornerDetector(
599  useHarrisDetector_?dai::FeatureTrackerConfig::CornerDetector::Type::HARRIS:dai::FeatureTrackerConfig::CornerDetector::Type::SHI_THOMASI);
600  gfttDetector->initialConfig.setNumTargetFeatures(numTargetFeatures_);
601  gfttDetector->initialConfig.setMotionEstimator(false);
602  auto cfg = gfttDetector->initialConfig.get();
603  cfg.featureMaintainer.minimumDistanceBetweenFeatures = minDistance_ * minDistance_;
604  gfttDetector->initialConfig.set(cfg);
605 
606  if(outputMode_ == 2)
607  rgbCamera->video.link(gfttDetector->inputImage);
608  else if(imagesRectified_)
609  stereoDepth->rectifiedLeft.link(gfttDetector->inputImage);
610  else
611  stereoDepth->syncedLeft.link(gfttDetector->inputImage);
612  gfttDetector->outputFeatures.link(sync->inputs["feat"]);
613  }
614  else if(detectFeatures_ >= 2)
615  {
616  auto imageManip = pipeline.create<dai::node::ImageManip>();
617  imageManip->setKeepAspectRatio(false);
618  imageManip->setMaxOutputFrameSize(320 * 200);
619  imageManip->initialConfig.setResize(320, 200);
620  imageManip->initialConfig.setFrameType(dai::ImgFrame::Type::GRAY8);
621 
622  auto neuralNetwork = pipeline.create<dai::node::NeuralNetwork>();
623  neuralNetwork->setBlobPath(blobPath_);
624  neuralNetwork->setNumInferenceThreads(2);
625  neuralNetwork->setNumNCEPerInferenceThread(1);
626  neuralNetwork->input.setBlocking(false);
627 
628  if(outputMode_ == 2)
629  rgbCamera->video.link(imageManip->inputImage);
630  else if(imagesRectified_)
631  stereoDepth->rectifiedLeft.link(imageManip->inputImage);
632  else
633  stereoDepth->syncedLeft.link(imageManip->inputImage);
634  imageManip->out.link(neuralNetwork->input);
635  neuralNetwork->out.link(sync->inputs["feat"]);
636  }
637 
638  auto xoutCamera = pipeline.create<dai::node::XLinkOut>();
639  xoutCamera->setStreamName("camera");
640 
641  sync->out.link(xoutCamera->input);
642 
643  if(imuPublished_)
644  {
645  auto imu = pipeline.create<dai::node::IMU>();
646  if(imuType == "BMI270")
647  imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER_RAW, dai::IMUSensor::GYROSCOPE_RAW}, 200);
648  else // BNO085/086
649  imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER, dai::IMUSensor::GYROSCOPE_UNCALIBRATED}, 200);
650  imu->setBatchReportThreshold(boardName=="NG9097"?4:1);
651  imu->setMaxBatchReports(10);
652 
653  auto xoutIMU = pipeline.create<dai::node::XLinkOut>();
654  xoutIMU->setStreamName("imu");
655 
656  imu->out.link(xoutIMU->input);
657  }
658 
659  device_->startPipeline(pipeline);
660  if(!device_->getIrDrivers().empty())
661  {
662  UINFO("Setting IR intensity");
663  device_->setIrLaserDotProjectorIntensity(dotIntensity_);
664  device_->setIrFloodLightIntensity(floodIntensity_);
665  }
666  else if(dotIntensity_ > 0 || floodIntensity_ > 0)
667  {
668  UWARN("No IR drivers were detected! IR intensity cannot be set.");
669  }
670 
671  cameraQueue_ = device_->getOutputQueue("camera", 8, false);
672  if(imuPublished_)
673  {
674  imuLocalTransform_ = this->getLocalTransform() * imuLocalTransform_;
675  UINFO("IMU local transform = %s", imuLocalTransform_.prettyPrint().c_str());
676  device_->getOutputQueue("imu", 50, false)->addCallback([this](const std::shared_ptr<dai::ADatatype> data) {
677  auto imuData = std::dynamic_pointer_cast<dai::IMUData>(data);
678  auto imuPackets = imuData->packets;
679 
680  for(auto& imuPacket : imuPackets)
681  {
682  auto& acceleroValues = imuPacket.acceleroMeter;
683  auto& gyroValues = imuPacket.gyroscope;
684  double accStamp = std::chrono::duration<double>(acceleroValues.getTimestampDevice().time_since_epoch()).count();
685  double gyroStamp = std::chrono::duration<double>(gyroValues.getTimestampDevice().time_since_epoch()).count();
686 
687  if(publishInterIMU_)
688  {
689  IMU imu(cv::Vec3f(gyroValues.x, gyroValues.y, gyroValues.z), cv::Mat::eye(3,3,CV_64FC1),
690  cv::Vec3f(acceleroValues.x, acceleroValues.y, acceleroValues.z), cv::Mat::eye(3,3,CV_64FC1),
691  imuLocalTransform_);
692  UEventsManager::post(new IMUEvent(imu, (accStamp+gyroStamp)/2));
693  }
694  else
695  {
696  UScopeMutex lock(imuMutex_);
697  accBuffer_.emplace_hint(accBuffer_.end(), accStamp, cv::Vec3f(acceleroValues.x, acceleroValues.y, acceleroValues.z));
698  gyroBuffer_.emplace_hint(gyroBuffer_.end(), gyroStamp, cv::Vec3f(gyroValues.x, gyroValues.y, gyroValues.z));
699  }
700  }
701  });
702  }
703 
704  this->setImageRate(0);
705  uSleep(2000); // avoid bad frames on start
706 
707  return true;
708 #else
709  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
710 #endif
711  return false;
712 }
713 
715 {
716 #ifdef RTABMAP_DEPTHAI
717  return outputMode_ == 0?stereoModel_.isValidForProjection():stereoModel_.left().isValidForProjection();
718 #else
719  return false;
720 #endif
721 }
722 
723 std::string CameraDepthAI::getSerial() const
724 {
725 #ifdef RTABMAP_DEPTHAI
726  return device_->getMxId();
727 #endif
728  return "";
729 }
730 
732 {
734 #ifdef RTABMAP_DEPTHAI
735 
736  auto messageGroup = cameraQueue_->get<dai::MessageGroup>();
737  auto rgbOrLeft = messageGroup->get<dai::ImgFrame>(outputMode_==2?"rgb":"left");
738  auto depthOrRight = messageGroup->get<dai::ImgFrame>(outputMode_?"depth":"right");
739 
740  double stamp = std::chrono::duration<double>(depthOrRight->getTimestampDevice(dai::CameraExposureOffset::MIDDLE).time_since_epoch()).count();
741  if(outputMode_)
742  data = SensorData(cv::imdecode(rgbOrLeft->getData(), cv::IMREAD_ANYCOLOR), depthOrRight->getCvFrame(), stereoModel_.left(), this->getNextSeqID(), stamp);
743  else
744  data = SensorData(cv::imdecode(rgbOrLeft->getData(), cv::IMREAD_GRAYSCALE), cv::imdecode(depthOrRight->getData(), cv::IMREAD_GRAYSCALE), stereoModel_, this->getNextSeqID(), stamp);
745 
746  if(imuPublished_ && !publishInterIMU_)
747  {
748  cv::Vec3d acc, gyro;
749  std::map<double, cv::Vec3f>::const_iterator iterA, iterB;
750 
751  imuMutex_.lock();
752  while(accBuffer_.empty() || gyroBuffer_.empty() || accBuffer_.rbegin()->first < stamp || gyroBuffer_.rbegin()->first < stamp)
753  {
754  imuMutex_.unlock();
755  uSleep(1);
756  imuMutex_.lock();
757  }
758 
759  //acc
760  iterB = accBuffer_.lower_bound(stamp);
761  iterA = iterB;
762  if(iterA != accBuffer_.begin())
763  iterA = --iterA;
764  if(iterA == iterB || stamp == iterB->first)
765  {
766  acc = iterB->second;
767  }
768  else if(stamp > iterA->first && stamp < iterB->first)
769  {
770  float t = (stamp-iterA->first) / (iterB->first-iterA->first);
771  acc = iterA->second + t*(iterB->second - iterA->second);
772  }
773  accBuffer_.erase(accBuffer_.begin(), iterB);
774 
775  //gyro
776  iterB = gyroBuffer_.lower_bound(stamp);
777  iterA = iterB;
778  if(iterA != gyroBuffer_.begin())
779  iterA = --iterA;
780  if(iterA == iterB || stamp == iterB->first)
781  {
782  gyro = iterB->second;
783  }
784  else if(stamp > iterA->first && stamp < iterB->first)
785  {
786  float t = (stamp-iterA->first) / (iterB->first-iterA->first);
787  gyro = iterA->second + t*(iterB->second - iterA->second);
788  }
789  gyroBuffer_.erase(gyroBuffer_.begin(), iterB);
790 
791  imuMutex_.unlock();
792  data.setIMU(IMU(gyro, cv::Mat::eye(3, 3, CV_64FC1), acc, cv::Mat::eye(3, 3, CV_64FC1), imuLocalTransform_));
793  }
794 
795  if(detectFeatures_ == 1)
796  {
797  auto features = messageGroup->get<dai::TrackedFeatures>("feat")->trackedFeatures;
798  std::vector<cv::KeyPoint> keypoints;
799  for(auto& feature : features)
800  keypoints.emplace_back(cv::KeyPoint(feature.position.x, feature.position.y, 3));
801  data.setFeatures(keypoints, std::vector<cv::Point3f>(), cv::Mat());
802  }
803  else if(detectFeatures_ >= 2)
804  {
805  auto features = messageGroup->get<dai::NNData>("feat");
806  std::vector<float> scores_dense, local_descriptor_map, global_descriptor;
807  if(detectFeatures_ == 2)
808  {
809  scores_dense = features->getLayerFp16("heatmap");
810  local_descriptor_map = features->getLayerFp16("desc");
811  }
812  else if(detectFeatures_ == 3)
813  {
814  scores_dense = features->getLayerFp16("pred/local_head/detector/Squeeze");
815  local_descriptor_map = features->getLayerFp16("pred/local_head/descriptor/transpose");
816  global_descriptor = features->getLayerFp16("pred/global_head/l2_normalize_1");
817  }
818 
819  cv::Mat scores(200, 320, CV_32FC1, scores_dense.data());
820  cv::resize(scores, scores, targetSize_, 0, 0, cv::INTER_CUBIC);
821  if(nms_)
822  {
823  cv::Mat dilated_scores(targetSize_, CV_32FC1);
824  cv::dilate(scores, dilated_scores, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(nmsRadius_*2+1, nmsRadius_*2+1)));
825  cv::Mat max_mask = scores == dilated_scores;
826  cv::dilate(scores, dilated_scores, cv::Mat());
827  cv::Mat max_mask_r1 = scores == dilated_scores;
828  cv::Mat supp_mask(targetSize_, CV_8UC1);
829  for(size_t i=0; i<2; i++)
830  {
831  cv::dilate(max_mask, supp_mask, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(nmsRadius_*2+1, nmsRadius_*2+1)));
832  cv::Mat supp_scores = scores.clone();
833  supp_scores.setTo(0, supp_mask);
834  cv::dilate(supp_scores, dilated_scores, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(nmsRadius_*2+1, nmsRadius_*2+1)));
835  cv::Mat new_max_mask = cv::Mat::zeros(targetSize_, CV_8UC1);
836  cv::bitwise_not(supp_mask, supp_mask);
837  cv::bitwise_and(supp_scores == dilated_scores, supp_mask, new_max_mask, max_mask_r1);
838  cv::bitwise_or(max_mask, new_max_mask, max_mask);
839  }
840  cv::bitwise_not(max_mask, supp_mask);
841  scores.setTo(0, supp_mask);
842  }
843 
844  std::vector<cv::Point> kpts;
845  cv::findNonZero(scores > threshold_, kpts);
846 
847  if(!kpts.empty()){
848  std::vector<cv::KeyPoint> keypoints;
849  for(auto& kpt : kpts)
850  {
851  float response = scores.at<float>(kpt);
852  keypoints.emplace_back(cv::KeyPoint(kpt, 8, -1, response));
853  }
854 
855  cv::Mat coarse_desc(25, 40, CV_32FC(256), local_descriptor_map.data());
856  if(detectFeatures_ == 2)
857  coarse_desc.forEach<cv::Vec<float, 256>>([&](cv::Vec<float, 256>& descriptor, const int position[]) -> void {
859  });
860  cv::Mat mapX(keypoints.size(), 1, CV_32FC1);
861  cv::Mat mapY(keypoints.size(), 1, CV_32FC1);
862  for(size_t i=0; i<keypoints.size(); ++i)
863  {
864  mapX.at<float>(i) = (keypoints[i].pt.x - (targetSize_.width-1)/2) * 40/targetSize_.width + (40-1)/2;
865  mapY.at<float>(i) = (keypoints[i].pt.y - (targetSize_.height-1)/2) * 25/targetSize_.height + (25-1)/2;
866  }
867  cv::Mat map1, map2, descriptors;
868  cv::convertMaps(mapX, mapY, map1, map2, CV_16SC2);
869  cv::remap(coarse_desc, descriptors, map1, map2, cv::INTER_LINEAR);
870  descriptors.forEach<cv::Vec<float, 256>>([&](cv::Vec<float, 256>& descriptor, const int position[]) -> void {
872  });
873  descriptors = descriptors.reshape(1);
874 
875  data.setFeatures(keypoints, std::vector<cv::Point3f>(), descriptors);
876  }
877 
878  if(detectFeatures_ == 3)
879  data.addGlobalDescriptor(GlobalDescriptor(1, cv::Mat(1, global_descriptor.size(), CV_32FC1, global_descriptor.data()).clone()));
880  }
881 
882 #else
883  UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
884 #endif
885  return data;
886 }
887 
888 } // namespace rtabmap
rtabmap::SensorData
Definition: SensorData.h:51
rtabmap::CameraDepthAI::getSerial
virtual std::string getSerial() const
Definition: CameraDepthAI.cpp:723
UINFO
#define UINFO(...)
rtabmap::Camera::setImageRate
void setImageRate(float imageRate)
Definition: Camera.h:50
rtabmap::StereoCameraModel
Definition: StereoCameraModel.h:35
rtabmap::CameraDepthAI::setDetectFeatures
void setDetectFeatures(int detectFeatures=0, const std::string &blobPath="")
Definition: CameraDepthAI.cpp:189
cy
const double cy
rtabmap_netvlad.descriptor
def descriptor
Definition: rtabmap_netvlad.py:81
fx
const double fx
rtabmap::CameraDepthAI::setExtendedDisparity
void setExtendedDisparity(bool extendedDisparity=false, bool enableCompanding=false)
Definition: CameraDepthAI.cpp:116
this
this
rtabmap::CameraDepthAI::setDepthProfile
void setDepthProfile(int confThreshold=200, int lrcThreshold=5)
Definition: CameraDepthAI.cpp:106
rtabmap::IMUEvent
Definition: IMU.h:85
col
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ColXpr col(Index i) const
threshold_
Index threshold_
rtabmap::CameraDepthAI::isCalibrated
virtual bool isCalibrated() const
Definition: CameraDepthAI.cpp:714
rtabmap::SensorCapture::getLocalTransform
const Transform & getLocalTransform() const
Definition: SensorCapture.h:62
true
#define true
Definition: ConvertUTF.c:57
glm::normalize
GLM_FUNC_DECL genType normalize(genType const &x)
UTimer.h
glm::pow
GLM_FUNC_DECL genType pow(genType const &base, genType const &exponent)
rtabmap::SensorCaptureInfo
Definition: SensorCaptureInfo.h:36
rtabmap::IMU
Definition: IMU.h:19
translation
translation
data
int data[]
UScopeMutex
Definition: UMutex.h:157
rtabmap::CameraDepthAI::available
static bool available()
Definition: CameraDepthAI.cpp:39
UConversion.h
Some conversion functions.
rtabmap_superglue.device
string device
Definition: rtabmap_superglue.py:21
rtabmap::CameraDepthAI::setDisparityWidthAndFilter
void setDisparityWidthAndFilter(int disparityWidth=96, int medianFilter=5)
Definition: CameraDepthAI.cpp:141
rtabmap::CameraDepthAI::setGFTTDetector
void setGFTTDetector(bool useHarrisDetector=false, float minDistance=7.0f, int numTargetFeatures=1000)
Definition: CameraDepthAI.cpp:209
first
constexpr int first(int i)
ULogger::kInfo
@ kInfo
Definition: ULogger.h:252
glm::row
GLM_FUNC_DECL genType::row_type row(genType const &m, length_t const &index)
UASSERT
#define UASSERT(condition)
rtabmap::GlobalDescriptor
Definition: GlobalDescriptor.h:35
rtabmap::Camera
Definition: Camera.h:43
rtabmap::Camera::getImageRate
float getImageRate() const
Definition: Camera.h:49
UWARN
#define UWARN(...)
f
Point2(* f)(const Point3 &, OptionalJacobian< 2, 3 >)
rtabmap::CameraDepthAI::~CameraDepthAI
virtual ~CameraDepthAI()
Definition: CameraDepthAI.cpp:89
ULogger::level
static ULogger::Level level()
Definition: ULogger.h:340
rtabmap::CameraDepthAI::setSubpixelMode
void setSubpixelMode(bool enabled=false, int fractionalBits=3)
Definition: CameraDepthAI.cpp:131
e
Array< double, 1, 3 > e(1./3., 0.5, 2.)
rtabmap::Transform
Definition: Transform.h:41
util2d.h
rtabmap::CameraDepthAI::init
virtual bool init(const std::string &calibrationFolder=".", const std::string &cameraName="")
Definition: CameraDepthAI.cpp:231
rtabmap::CameraDepthAI::setIrIntensity
void setIrIntensity(float dotIntensity=0.0f, float floodIntensity=0.0f)
Definition: CameraDepthAI.cpp:179
rtabmap::Camera::publishInterIMU_
bool publishInterIMU_
Definition: Camera.h:75
rtabmap::CameraDepthAI::setRectification
void setRectification(bool useSpecTranslation=false, float alphaScaling=0.0f, bool enabled=true)
Definition: CameraDepthAI.cpp:158
rtabmap::CameraDepthAI::setSuperPointDetector
void setSuperPointDetector(float threshold=0.01f, bool nms=true, int nmsRadius=4)
Definition: CameraDepthAI.cpp:220
UThread.h
position
Point3 position(const NavState &X, OptionalJacobian< 3, 9 > H)
UDEBUG
#define UDEBUG(...)
UEventsManager::post
static void post(UEvent *event, bool async=true, const UEventsSender *sender=0)
Definition: UEventsManager.cpp:54
uSleep
void uSleep(unsigned int ms)
Definition: Posix/UThreadC.h:23
false
#define false
Definition: ConvertUTF.c:56
rtabmap::CameraDepthAI::CameraDepthAI
CameraDepthAI(const std::string &mxidOrName="", int imageWidth=1280, float imageRate=0.0f, const Transform &localTransform=Transform::getIdentity())
Definition: CameraDepthAI.cpp:48
cx
const double cx
rtabmap::CameraDepthAI::setIMU
void setIMU(bool imuPublished, bool publishInterIMU)
Definition: CameraDepthAI.cpp:169
matrix
Map< const Matrix< T, Dynamic, Dynamic, ColMajor >, 0, OuterStride<> > matrix(const T *data, int rows, int cols, int stride)
t
Point2 t(10, 10)
UFile.h
rtabmap
Definition: CameraARCore.cpp:35
UEventsManager.h
config
config
UERROR
#define UERROR(...)
i
int i
baseline
double baseline
CameraDepthAI.h
fy
const double fy
rtabmap::CameraDepthAI::setOutputMode
void setOutputMode(int outputMode=0)
Definition: CameraDepthAI.cpp:97
rtabmap::CameraDepthAI::captureImage
virtual SensorData captureImage(SensorCaptureInfo *info=0)
Definition: CameraDepthAI.cpp:731


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Apr 28 2025 02:45:51