2013-02-04 16:38:00 +00:00
|
|
|
/*
|
2016-07-17 21:57:10 -04:00
|
|
|
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
2014-08-11 17:00:55 +00:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
|
|
|
names of its contributors may be used to endorse or promote products
|
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*/
|
2013-02-04 16:38:00 +00:00
|
|
|
|
|
|
|
|
#include "rtabmap/core/CameraThread.h"
|
|
|
|
|
#include "rtabmap/core/Camera.h"
|
|
|
|
|
#include "rtabmap/core/CameraEvent.h"
|
2015-07-09 10:55:54 -04:00
|
|
|
#include "rtabmap/core/CameraRGBD.h"
|
2015-08-27 17:16:12 -04:00
|
|
|
#include "rtabmap/core/util2d.h"
|
|
|
|
|
#include "rtabmap/core/util3d.h"
|
2016-01-08 19:15:49 -05:00
|
|
|
#include "rtabmap/core/util3d_surface.h"
|
2016-03-06 15:11:09 -05:00
|
|
|
#include "rtabmap/core/util3d_filtering.h"
|
2015-12-11 17:44:15 -05:00
|
|
|
#include "rtabmap/core/StereoDense.h"
|
2016-09-19 14:01:06 -04:00
|
|
|
#include "rtabmap/core/clams/discrete_depth_distortion_model.h"
|
2013-02-04 16:38:00 +00:00
|
|
|
|
2013-04-30 20:16:01 +00:00
|
|
|
#include <rtabmap/utilite/UTimer.h>
|
2014-06-15 03:31:35 +00:00
|
|
|
#include <rtabmap/utilite/ULogger.h>
|
2013-02-04 16:38:00 +00:00
|
|
|
|
2016-03-06 15:11:09 -05:00
|
|
|
#include <pcl/io/io.h>
|
|
|
|
|
|
2013-02-04 16:38:00 +00:00
|
|
|
namespace rtabmap
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// ownership transferred
|
2015-12-11 17:44:15 -05:00
|
|
|
CameraThread::CameraThread(Camera * camera, const ParametersMap & parameters) :
|
2013-02-04 16:38:00 +00:00
|
|
|
_camera(camera),
|
2015-06-26 18:21:32 -04:00
|
|
|
_mirroring(false),
|
2015-08-27 17:16:12 -04:00
|
|
|
_colorOnly(false),
|
2016-01-08 19:15:49 -05:00
|
|
|
_imageDecimation(1),
|
2015-12-11 17:44:15 -05:00
|
|
|
_stereoToDepth(false),
|
2016-01-06 17:26:55 -05:00
|
|
|
_scanFromDepth(false),
|
|
|
|
|
_scanDecimation(4),
|
|
|
|
|
_scanMaxDepth(4.0f),
|
2016-04-12 15:14:04 -04:00
|
|
|
_scanMinDepth(0.0f),
|
2016-01-08 19:15:49 -05:00
|
|
|
_scanVoxelSize(0.0f),
|
|
|
|
|
_scanNormalsK(0),
|
2016-09-19 14:01:06 -04:00
|
|
|
_stereoDense(new StereoBM(parameters)),
|
2016-11-03 17:04:31 -04:00
|
|
|
_distortionModel(0),
|
|
|
|
|
_bilateralFiltering(false),
|
|
|
|
|
_bilateralSigmaS(10),
|
|
|
|
|
_bilateralSigmaR(0.1)
|
2013-02-04 16:38:00 +00:00
|
|
|
{
|
|
|
|
|
UASSERT(_camera != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CameraThread::~CameraThread()
|
|
|
|
|
{
|
2015-12-17 14:13:19 -05:00
|
|
|
UDEBUG("");
|
2013-02-04 16:38:00 +00:00
|
|
|
join(true);
|
2014-06-15 03:31:35 +00:00
|
|
|
if(_camera)
|
|
|
|
|
{
|
|
|
|
|
delete _camera;
|
|
|
|
|
}
|
2016-09-19 14:01:06 -04:00
|
|
|
if(_distortionModel)
|
|
|
|
|
{
|
|
|
|
|
delete _distortionModel;
|
|
|
|
|
}
|
2015-12-11 17:44:15 -05:00
|
|
|
delete _stereoDense;
|
2014-06-15 03:31:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CameraThread::setImageRate(float imageRate)
|
|
|
|
|
{
|
|
|
|
|
if(_camera)
|
|
|
|
|
{
|
|
|
|
|
_camera->setImageRate(imageRate);
|
|
|
|
|
}
|
2013-12-11 00:12:44 +00:00
|
|
|
}
|
|
|
|
|
|
2016-09-19 14:01:06 -04:00
|
|
|
void CameraThread::setDistortionModel(const std::string & path)
|
|
|
|
|
{
|
|
|
|
|
if(_distortionModel)
|
|
|
|
|
{
|
|
|
|
|
delete _distortionModel;
|
|
|
|
|
_distortionModel = 0;
|
|
|
|
|
}
|
|
|
|
|
if(!path.empty())
|
|
|
|
|
{
|
|
|
|
|
_distortionModel = new clams::DiscreteDepthDistortionModel();
|
|
|
|
|
_distortionModel->load(path);
|
|
|
|
|
if(!_distortionModel->isValid())
|
|
|
|
|
{
|
|
|
|
|
UERROR("Loaded distortion model \"%s\" is not valid!", path.c_str());
|
|
|
|
|
delete _distortionModel;
|
|
|
|
|
_distortionModel = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 17:04:31 -04:00
|
|
|
void CameraThread::enableBilateralFiltering(float sigmaS, float sigmaR)
|
|
|
|
|
{
|
|
|
|
|
UASSERT(sigmaS > 0.0f && sigmaR > 0.0f);
|
|
|
|
|
_bilateralFiltering = true;
|
|
|
|
|
_bilateralSigmaS = sigmaS;
|
|
|
|
|
_bilateralSigmaR = sigmaR;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 19:54:31 -05:00
|
|
|
void CameraThread::mainLoopBegin()
|
|
|
|
|
{
|
|
|
|
|
ULogger::registerCurrentThread("Camera");
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-04 16:38:00 +00:00
|
|
|
void CameraThread::mainLoop()
|
|
|
|
|
{
|
2016-09-19 14:01:06 -04:00
|
|
|
UTimer totalTime;
|
2014-06-15 03:31:35 +00:00
|
|
|
UDEBUG("");
|
2015-11-30 18:49:28 -05:00
|
|
|
CameraInfo info;
|
|
|
|
|
SensorData data = _camera->takeImage(&info);
|
2014-06-15 03:31:35 +00:00
|
|
|
|
2015-06-26 18:21:32 -04:00
|
|
|
if(!data.imageRaw().empty())
|
2013-02-04 16:38:00 +00:00
|
|
|
{
|
2015-06-26 18:21:32 -04:00
|
|
|
if(_colorOnly && !data.depthRaw().empty())
|
2013-02-04 16:38:00 +00:00
|
|
|
{
|
2015-06-26 18:21:32 -04:00
|
|
|
data.setDepthOrRightRaw(cv::Mat());
|
2013-02-04 16:38:00 +00:00
|
|
|
}
|
2016-09-19 14:01:06 -04:00
|
|
|
|
|
|
|
|
if(_distortionModel && !data.depthRaw().empty())
|
|
|
|
|
{
|
|
|
|
|
UTimer timer;
|
|
|
|
|
if(_distortionModel->getWidth() == data.depthRaw().cols &&
|
|
|
|
|
_distortionModel->getHeight() == data.depthRaw().rows )
|
|
|
|
|
{
|
|
|
|
|
cv::Mat depth = data.depthRaw().clone();// make sure we are not modifying data in cached signatures.
|
|
|
|
|
_distortionModel->undistort(depth);
|
|
|
|
|
data.setDepthOrRightRaw(depth);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UERROR("Distortion model size is %dx%d but dpeth image is %dx%d!",
|
|
|
|
|
_distortionModel->getWidth(), _distortionModel->getHeight(),
|
|
|
|
|
data.depthRaw().cols, data.depthRaw().rows);
|
|
|
|
|
}
|
|
|
|
|
info.timeUndistortDepth = timer.ticks();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 17:04:31 -04:00
|
|
|
if(_bilateralFiltering && !data.depthRaw().empty())
|
|
|
|
|
{
|
|
|
|
|
UTimer timer;
|
|
|
|
|
data.setDepthOrRightRaw(util2d::fastBilateralFiltering(data.depthRaw(), _bilateralSigmaS, _bilateralSigmaR));
|
|
|
|
|
info.timeBilateralFiltering = timer.ticks();
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 19:15:49 -05:00
|
|
|
if(_imageDecimation>1 && !data.imageRaw().empty())
|
|
|
|
|
{
|
|
|
|
|
UDEBUG("");
|
|
|
|
|
UTimer timer;
|
|
|
|
|
if(!data.depthRaw().empty() &&
|
|
|
|
|
!(data.depthRaw().rows % _imageDecimation == 0 && data.depthRaw().cols % _imageDecimation == 0))
|
|
|
|
|
{
|
|
|
|
|
UERROR("Decimation of depth images should be exact (decimation=%d, size=(%d,%d))! "
|
|
|
|
|
"Images won't be resized.", _imageDecimation, data.depthRaw().cols, data.depthRaw().rows);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
data.setImageRaw(util2d::decimate(data.imageRaw(), _imageDecimation));
|
|
|
|
|
data.setDepthOrRightRaw(util2d::decimate(data.depthOrRightRaw(), _imageDecimation));
|
|
|
|
|
std::vector<CameraModel> models = data.cameraModels();
|
|
|
|
|
for(unsigned int i=0; i<models.size(); ++i)
|
|
|
|
|
{
|
2016-01-19 21:01:56 -05:00
|
|
|
if(models[i].isValidForProjection())
|
2016-01-08 19:15:49 -05:00
|
|
|
{
|
|
|
|
|
models[i] = models[i].scaled(1.0/double(_imageDecimation));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
data.setCameraModels(models);
|
|
|
|
|
StereoCameraModel stereoModel = data.stereoCameraModel();
|
2016-01-19 21:01:56 -05:00
|
|
|
if(stereoModel.isValidForProjection())
|
2016-01-08 19:15:49 -05:00
|
|
|
{
|
|
|
|
|
stereoModel.scale(1.0/double(_imageDecimation));
|
|
|
|
|
data.setStereoCameraModel(stereoModel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
info.timeImageDecimation = timer.ticks();
|
|
|
|
|
}
|
2015-06-26 18:21:32 -04:00
|
|
|
if(_mirroring && data.cameraModels().size() == 1)
|
|
|
|
|
{
|
2016-01-08 19:15:49 -05:00
|
|
|
UDEBUG("");
|
2015-11-30 18:49:28 -05:00
|
|
|
UTimer timer;
|
2015-06-26 18:21:32 -04:00
|
|
|
cv::Mat tmpRgb;
|
|
|
|
|
cv::flip(data.imageRaw(), tmpRgb, 1);
|
|
|
|
|
data.setImageRaw(tmpRgb);
|
2016-01-19 21:01:56 -05:00
|
|
|
UASSERT_MSG(data.cameraModels().size() <= 1 && !data.stereoCameraModel().isValidForProjection(), "Only single RGBD cameras are supported for mirroring.");
|
2016-01-08 19:15:49 -05:00
|
|
|
if(data.cameraModels().size() && data.cameraModels()[0].cx())
|
2015-06-26 18:21:32 -04:00
|
|
|
{
|
|
|
|
|
CameraModel tmpModel(
|
|
|
|
|
data.cameraModels()[0].fx(),
|
|
|
|
|
data.cameraModels()[0].fy(),
|
|
|
|
|
float(data.imageRaw().cols) - data.cameraModels()[0].cx(),
|
|
|
|
|
data.cameraModels()[0].cy(),
|
|
|
|
|
data.cameraModels()[0].localTransform());
|
|
|
|
|
data.setCameraModel(tmpModel);
|
|
|
|
|
}
|
|
|
|
|
if(!data.depthRaw().empty())
|
|
|
|
|
{
|
|
|
|
|
cv::Mat tmpDepth;
|
|
|
|
|
cv::flip(data.depthRaw(), tmpDepth, 1);
|
|
|
|
|
data.setDepthOrRightRaw(tmpDepth);
|
|
|
|
|
}
|
2016-01-06 17:26:55 -05:00
|
|
|
info.timeMirroring = timer.ticks();
|
2015-06-26 18:21:32 -04:00
|
|
|
}
|
2016-01-19 21:01:56 -05:00
|
|
|
if(_stereoToDepth && data.stereoCameraModel().isValidForProjection() && !data.rightRaw().empty())
|
2015-08-27 17:16:12 -04:00
|
|
|
{
|
2016-01-08 19:15:49 -05:00
|
|
|
UDEBUG("");
|
2015-11-30 18:49:28 -05:00
|
|
|
UTimer timer;
|
2015-08-27 17:16:12 -04:00
|
|
|
cv::Mat depth = util2d::depthFromDisparity(
|
2015-12-11 17:44:15 -05:00
|
|
|
_stereoDense->computeDisparity(data.imageRaw(), data.rightRaw()),
|
2015-08-27 17:16:12 -04:00
|
|
|
data.stereoCameraModel().left().fx(),
|
|
|
|
|
data.stereoCameraModel().baseline());
|
|
|
|
|
data.setCameraModel(data.stereoCameraModel().left());
|
|
|
|
|
data.setDepthOrRightRaw(depth);
|
|
|
|
|
data.setStereoCameraModel(StereoCameraModel());
|
2016-01-06 17:26:55 -05:00
|
|
|
info.timeDisparity = timer.ticks();
|
2016-03-11 16:54:54 -05:00
|
|
|
UDEBUG("Computing disparity = %f s", info.timeDisparity);
|
2015-08-27 17:16:12 -04:00
|
|
|
}
|
2016-01-06 17:26:55 -05:00
|
|
|
if(_scanFromDepth &&
|
|
|
|
|
data.cameraModels().size() &&
|
2016-01-19 21:01:56 -05:00
|
|
|
data.cameraModels().at(0).isValidForProjection() &&
|
2016-01-06 17:26:55 -05:00
|
|
|
!data.depthRaw().empty())
|
|
|
|
|
{
|
2016-01-08 19:15:49 -05:00
|
|
|
UDEBUG("");
|
2016-01-06 17:26:55 -05:00
|
|
|
if(data.laserScanRaw().empty())
|
|
|
|
|
{
|
|
|
|
|
UASSERT(_scanDecimation >= 1);
|
|
|
|
|
UTimer timer;
|
2016-03-06 15:11:09 -05:00
|
|
|
pcl::IndicesPtr validIndices(new std::vector<int>);
|
2016-08-21 19:33:01 -04:00
|
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = util3d::cloudFromSensorData(
|
|
|
|
|
data,
|
|
|
|
|
_scanDecimation,
|
|
|
|
|
_scanMaxDepth,
|
|
|
|
|
_scanMinDepth,
|
|
|
|
|
validIndices.get());
|
2016-03-06 15:11:09 -05:00
|
|
|
float maxPoints = (data.depthRaw().rows/_scanDecimation)*(data.depthRaw().cols/_scanDecimation);
|
2016-01-08 19:15:49 -05:00
|
|
|
cv::Mat scan;
|
2016-08-21 19:33:01 -04:00
|
|
|
const Transform & baseToScan = data.cameraModels()[0].localTransform();
|
2016-06-21 11:36:50 -04:00
|
|
|
if(validIndices->size())
|
2016-01-08 19:15:49 -05:00
|
|
|
{
|
2016-06-21 11:36:50 -04:00
|
|
|
if(_scanVoxelSize>0.0f)
|
2016-06-12 17:53:35 -04:00
|
|
|
{
|
2016-06-21 11:36:50 -04:00
|
|
|
cloud = util3d::voxelize(cloud, validIndices, _scanVoxelSize);
|
|
|
|
|
float ratio = float(cloud->size()) / float(validIndices->size());
|
|
|
|
|
maxPoints = ratio * maxPoints;
|
2016-06-12 17:53:35 -04:00
|
|
|
}
|
2016-06-21 11:36:50 -04:00
|
|
|
else if(!cloud->is_dense)
|
2016-06-12 17:53:35 -04:00
|
|
|
{
|
2016-06-21 11:36:50 -04:00
|
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr denseCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
|
|
|
|
pcl::copyPointCloud(*cloud, *validIndices, *denseCloud);
|
|
|
|
|
cloud = denseCloud;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(cloud->size())
|
|
|
|
|
{
|
|
|
|
|
if(_scanNormalsK>0)
|
|
|
|
|
{
|
2016-08-21 19:33:01 -04:00
|
|
|
Eigen::Vector3f viewPoint(baseToScan.x(), baseToScan.y(), baseToScan.z());
|
2016-06-21 11:36:50 -04:00
|
|
|
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloud, _scanNormalsK, viewPoint);
|
|
|
|
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloudNormals(new pcl::PointCloud<pcl::PointNormal>);
|
|
|
|
|
pcl::concatenateFields(*cloud, *normals, *cloudNormals);
|
2016-08-21 19:33:01 -04:00
|
|
|
scan = util3d::laserScanFromPointCloud(*cloudNormals, baseToScan.inverse());
|
2016-06-21 11:36:50 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-08-21 19:33:01 -04:00
|
|
|
scan = util3d::laserScanFromPointCloud(*cloud, baseToScan.inverse());
|
2016-06-21 11:36:50 -04:00
|
|
|
}
|
2016-06-12 17:53:35 -04:00
|
|
|
}
|
2016-01-08 19:15:49 -05:00
|
|
|
}
|
2016-08-21 19:33:01 -04:00
|
|
|
data.setLaserScanRaw(scan, LaserScanInfo((int)maxPoints, _scanMaxDepth, baseToScan));
|
2016-01-06 17:26:55 -05:00
|
|
|
info.timeScanFromDepth = timer.ticks();
|
2016-03-11 16:54:54 -05:00
|
|
|
UDEBUG("Computing scan from depth = %f s", info.timeScanFromDepth);
|
2016-01-06 17:26:55 -05:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UWARN("Option to create laser scan from depth image is enabled, but "
|
|
|
|
|
"there is already a laser scan in the captured sensor data. Scan from "
|
|
|
|
|
"depth will not be created.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-08 19:15:49 -05:00
|
|
|
|
2016-01-06 17:26:55 -05:00
|
|
|
info.cameraName = _camera->getSerial();
|
2016-09-19 14:01:06 -04:00
|
|
|
info.timeTotal = totalTime.ticks();
|
2015-11-30 18:49:28 -05:00
|
|
|
this->post(new CameraEvent(data, info));
|
2013-02-04 16:38:00 +00:00
|
|
|
}
|
2014-06-15 03:31:35 +00:00
|
|
|
else if(!this->isKilled())
|
|
|
|
|
{
|
2015-06-26 18:21:32 -04:00
|
|
|
UWARN("no more images...");
|
2014-06-15 03:31:35 +00:00
|
|
|
this->kill();
|
|
|
|
|
this->post(new CameraEvent());
|
|
|
|
|
}
|
2013-02-04 16:38:00 +00:00
|
|
|
}
|
|
|
|
|
|
2015-07-09 10:55:54 -04:00
|
|
|
void CameraThread::mainLoopKill()
|
|
|
|
|
{
|
2015-12-17 14:13:19 -05:00
|
|
|
UDEBUG("");
|
2015-07-09 10:55:54 -04:00
|
|
|
if(dynamic_cast<CameraFreenect2*>(_camera) != 0)
|
|
|
|
|
{
|
|
|
|
|
int i=20;
|
|
|
|
|
while(i-->0)
|
|
|
|
|
{
|
|
|
|
|
uSleep(100);
|
|
|
|
|
if(!this->isKilled())
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(this->isKilled())
|
|
|
|
|
{
|
|
|
|
|
//still in killed state, maybe a deadlock
|
|
|
|
|
UERROR("CameraFreenect2: Failed to kill normally the Freenect2 driver! The thread is locked "
|
|
|
|
|
"on waitForNewFrame() method of libfreenect2. This maybe caused by not linking on the right libusb. "
|
|
|
|
|
"Note that rtabmap should link on libusb of libfreenect2. "
|
|
|
|
|
"Tip before starting rtabmap: \"$ export LD_LIBRARY_PATH=~/libfreenect2/depends/libusb/lib:$LD_LIBRARY_PATH\"");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-04 16:38:00 +00:00
|
|
|
} // namespace rtabmap
|