mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 09:30:25 +08:00
Added stereo multi-camera support (#884)
* Integrated OpenGV * Fixed build without opengv * Cmake: moved OpenGV dependency status under solvers group * Added multi-stereocamera models support * Fixed OpenGV 0 sample error when one of the camera doesn't have features. Fixed g2o BA id offset with multi-camera. * Fixed multicam 3d points generated from stereo correspondences * db: Fixed multi stereo models not loaded correctly * gui: fixed stereo rectification option, RegVis: fixed projection error with old databases (image size not set in calibration) * OdomF2M: Fixed map.at error when bundle adjustment is not used * depthai: added imu firmware update option for convenience * Fixed various refactor errors * Moved "large number stereo correspondences rejected" warning outside computeCorrespondences function for multicam * Added error log if ba correspondences are computed with empty signatures * fixed compilation errors with latest opencv Co-authored-by: mathieu86 <mathieu@robust.ai>
This commit is contained in:
@@ -469,6 +469,13 @@ IF(FastCV_FOUND)
|
||||
)
|
||||
ENDIF(FastCV_FOUND)
|
||||
|
||||
IF(opengv_FOUND)
|
||||
SET(LIBRARIES
|
||||
${LIBRARIES}
|
||||
opengv
|
||||
)
|
||||
ENDIF(opengv_FOUND)
|
||||
|
||||
IF(PDAL_FOUND)
|
||||
SET(INCLUDE_DIRS
|
||||
${INCLUDE_DIRS}
|
||||
|
||||
@@ -287,9 +287,10 @@ void CameraThread::mainLoop()
|
||||
model.setLocalTransform(_extrinsicsOdomToCamera);
|
||||
data.setCameraModel(model);
|
||||
}
|
||||
else
|
||||
else if(!data.stereoCameraModels().empty())
|
||||
{
|
||||
StereoCameraModel model = data.stereoCameraModel();
|
||||
UASSERT(data.stereoCameraModels().size()==1);
|
||||
StereoCameraModel model = data.stereoCameraModels()[0];
|
||||
model.setLocalTransform(_extrinsicsOdomToCamera);
|
||||
data.setStereoCameraModel(model);
|
||||
}
|
||||
@@ -358,7 +359,12 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
}
|
||||
else if(!data.rightRaw().empty())
|
||||
{
|
||||
data.setRGBDImage(data.imageRaw(), cv::Mat(), data.stereoCameraModel().left());
|
||||
std::vector<CameraModel> models;
|
||||
for(size_t i=0; i<data.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
models.push_back(data.stereoCameraModels()[i].left());
|
||||
}
|
||||
data.setRGBDImage(data.imageRaw(), cv::Mat(), models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,91 +441,116 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
{
|
||||
data.setRGBDImage(image, depthOrRight, models);
|
||||
}
|
||||
else
|
||||
|
||||
|
||||
std::vector<StereoCameraModel> stereoModels = data.stereoCameraModels();
|
||||
for(unsigned int i=0; i<stereoModels.size(); ++i)
|
||||
{
|
||||
StereoCameraModel stereoModel = data.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
if(stereoModels[i].isValidForProjection())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
stereoModels[i].scale(1.0/double(_imageDecimation));
|
||||
}
|
||||
data.setStereoImage(image, depthOrRight, stereoModel);
|
||||
}
|
||||
if(!stereoModels.empty())
|
||||
{
|
||||
data.setStereoImage(image, depthOrRight, stereoModels);
|
||||
}
|
||||
}
|
||||
if(info) info->timeImageDecimation = timer.ticks();
|
||||
}
|
||||
if(_mirroring && !data.imageRaw().empty() && data.cameraModels().size() == 1)
|
||||
if(_mirroring && !data.imageRaw().empty() && data.cameraModels().size()>=1)
|
||||
{
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Mat tmpRgb;
|
||||
cv::flip(data.imageRaw(), tmpRgb, 1);
|
||||
if(data.cameraModels().size() == 1)
|
||||
{
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Mat tmpRgb;
|
||||
cv::flip(data.imageRaw(), tmpRgb, 1);
|
||||
|
||||
UASSERT_MSG(data.cameraModels().size() <= 1 && !data.stereoCameraModel().isValidForProjection(), "Only single RGBD cameras are supported for mirroring.");
|
||||
CameraModel tmpModel = data.cameraModels()[0];
|
||||
if(data.cameraModels()[0].cx())
|
||||
{
|
||||
tmpModel = CameraModel(
|
||||
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.cameraModels()[0].Tx(),
|
||||
data.cameraModels()[0].imageSize());
|
||||
CameraModel tmpModel = data.cameraModels()[0];
|
||||
if(data.cameraModels()[0].cx())
|
||||
{
|
||||
tmpModel = CameraModel(
|
||||
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.cameraModels()[0].Tx(),
|
||||
data.cameraModels()[0].imageSize());
|
||||
}
|
||||
cv::Mat tmpDepth = data.depthOrRightRaw();
|
||||
if(!data.depthRaw().empty())
|
||||
{
|
||||
cv::flip(data.depthRaw(), tmpDepth, 1);
|
||||
}
|
||||
data.setRGBDImage(tmpRgb, tmpDepth, tmpModel);
|
||||
if(info) info->timeMirroring = timer.ticks();
|
||||
}
|
||||
cv::Mat tmpDepth = data.depthOrRightRaw();
|
||||
if(!data.depthRaw().empty())
|
||||
else
|
||||
{
|
||||
cv::flip(data.depthRaw(), tmpDepth, 1);
|
||||
UWARN("Mirroring is not implemented for multiple cameras or stereo...");
|
||||
}
|
||||
data.setRGBDImage(tmpRgb, tmpDepth, tmpModel);
|
||||
if(info) info->timeMirroring = timer.ticks();
|
||||
}
|
||||
|
||||
if(_stereoExposureCompensation && !data.imageRaw().empty() && !data.rightRaw().empty())
|
||||
{
|
||||
if(data.stereoCameraModels().size()==1)
|
||||
{
|
||||
#if CV_MAJOR_VERSION < 3
|
||||
UWARN("Stereo exposure compensation not implemented for OpenCV version under 3.");
|
||||
UWARN("Stereo exposure compensation not implemented for OpenCV version under 3.");
|
||||
#else
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Ptr<cv::detail::ExposureCompensator> compensator = cv::detail::ExposureCompensator::createDefault(cv::detail::ExposureCompensator::GAIN);
|
||||
std::vector<cv::Point> topLeftCorners(2, cv::Point(0,0));
|
||||
std::vector<cv::UMat> images;
|
||||
std::vector<cv::UMat> masks(2, cv::UMat(data.imageRaw().size(), CV_8UC1, cv::Scalar(255)));
|
||||
images.push_back(data.imageRaw().getUMat(cv::ACCESS_READ));
|
||||
images.push_back(data.rightRaw().getUMat(cv::ACCESS_READ));
|
||||
compensator->feed(topLeftCorners, images, masks);
|
||||
cv::Mat imgLeft = data.imageRaw().clone();
|
||||
compensator->apply(0, cv::Point(0,0), imgLeft, masks[0]);
|
||||
cv::Mat imgRight = data.rightRaw().clone();
|
||||
compensator->apply(1, cv::Point(0,0), imgRight, masks[1]);
|
||||
data.setStereoImage(imgLeft, imgRight, data.stereoCameraModel());
|
||||
cv::detail::GainCompensator * gainCompensator = (cv::detail::GainCompensator*)compensator.get();
|
||||
UDEBUG("gains = %f %f ", gainCompensator->gains()[0], gainCompensator->gains()[1]);
|
||||
if(info) info->timeStereoExposureCompensation = timer.ticks();
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Ptr<cv::detail::ExposureCompensator> compensator = cv::detail::ExposureCompensator::createDefault(cv::detail::ExposureCompensator::GAIN);
|
||||
std::vector<cv::Point> topLeftCorners(2, cv::Point(0,0));
|
||||
std::vector<cv::UMat> images;
|
||||
std::vector<cv::UMat> masks(2, cv::UMat(data.imageRaw().size(), CV_8UC1, cv::Scalar(255)));
|
||||
images.push_back(data.imageRaw().getUMat(cv::ACCESS_READ));
|
||||
images.push_back(data.rightRaw().getUMat(cv::ACCESS_READ));
|
||||
compensator->feed(topLeftCorners, images, masks);
|
||||
cv::Mat imgLeft = data.imageRaw().clone();
|
||||
compensator->apply(0, cv::Point(0,0), imgLeft, masks[0]);
|
||||
cv::Mat imgRight = data.rightRaw().clone();
|
||||
compensator->apply(1, cv::Point(0,0), imgRight, masks[1]);
|
||||
data.setStereoImage(imgLeft, imgRight, data.stereoCameraModels()[0]);
|
||||
cv::detail::GainCompensator * gainCompensator = (cv::detail::GainCompensator*)compensator.get();
|
||||
UDEBUG("gains = %f %f ", gainCompensator->gains()[0], gainCompensator->gains()[1]);
|
||||
if(info) info->timeStereoExposureCompensation = timer.ticks();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Stereo exposure compensation only is not implemented to multiple stereo cameras...");
|
||||
}
|
||||
}
|
||||
|
||||
if(_stereoToDepth && !data.imageRaw().empty() && data.stereoCameraModel().isValidForProjection() && !data.rightRaw().empty())
|
||||
if(_stereoToDepth && !data.imageRaw().empty() && !data.stereoCameraModels().empty() && data.stereoCameraModels()[0].isValidForProjection() && !data.rightRaw().empty())
|
||||
{
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Mat depth = util2d::depthFromDisparity(
|
||||
_stereoDense->computeDisparity(data.imageRaw(), data.rightRaw()),
|
||||
data.stereoCameraModel().left().fx(),
|
||||
data.stereoCameraModel().baseline());
|
||||
// set Tx for stereo bundle adjustment (when used)
|
||||
CameraModel model = CameraModel(
|
||||
data.stereoCameraModel().left().fx(),
|
||||
data.stereoCameraModel().left().fy(),
|
||||
data.stereoCameraModel().left().cx(),
|
||||
data.stereoCameraModel().left().cy(),
|
||||
data.stereoCameraModel().localTransform(),
|
||||
-data.stereoCameraModel().baseline()*data.stereoCameraModel().left().fx(),
|
||||
data.stereoCameraModel().left().imageSize());
|
||||
data.setRGBDImage(data.imageRaw(), depth, model);
|
||||
if(info) info->timeDisparity = timer.ticks();
|
||||
if(data.stereoCameraModels().size()==1)
|
||||
{
|
||||
UDEBUG("");
|
||||
UTimer timer;
|
||||
cv::Mat depth = util2d::depthFromDisparity(
|
||||
_stereoDense->computeDisparity(data.imageRaw(), data.rightRaw()),
|
||||
data.stereoCameraModels()[0].left().fx(),
|
||||
data.stereoCameraModels()[0].baseline());
|
||||
// set Tx for stereo bundle adjustment (when used)
|
||||
CameraModel model = CameraModel(
|
||||
data.stereoCameraModels()[0].left().fx(),
|
||||
data.stereoCameraModels()[0].left().fy(),
|
||||
data.stereoCameraModels()[0].left().cx(),
|
||||
data.stereoCameraModels()[0].left().cy(),
|
||||
data.stereoCameraModels()[0].localTransform(),
|
||||
-data.stereoCameraModels()[0].baseline()*data.stereoCameraModels()[0].left().fx(),
|
||||
data.stereoCameraModels()[0].left().imageSize());
|
||||
data.setRGBDImage(data.imageRaw(), depth, model);
|
||||
if(info) info->timeDisparity = timer.ticks();
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Stereo to depth is not implemented for multiple stereo cameras...");
|
||||
}
|
||||
}
|
||||
if(_scanFromDepth &&
|
||||
data.cameraModels().size() &&
|
||||
|
||||
@@ -726,7 +726,7 @@ void DBDriver::getNodeData(
|
||||
bool DBDriver::getCalibration(
|
||||
int signatureId,
|
||||
std::vector<CameraModel> & models,
|
||||
StereoCameraModel & stereoModel) const
|
||||
std::vector<StereoCameraModel> & stereoModels) const
|
||||
{
|
||||
UDEBUG("");
|
||||
bool found = false;
|
||||
@@ -735,7 +735,7 @@ bool DBDriver::getCalibration(
|
||||
if(uContains(_trashSignatures, signatureId))
|
||||
{
|
||||
models = _trashSignatures.at(signatureId)->sensorData().cameraModels();
|
||||
stereoModel = _trashSignatures.at(signatureId)->sensorData().stereoCameraModel();
|
||||
stereoModels = _trashSignatures.at(signatureId)->sensorData().stereoCameraModels();
|
||||
found = true;
|
||||
}
|
||||
_trashesMutex.unlock();
|
||||
@@ -743,7 +743,7 @@ bool DBDriver::getCalibration(
|
||||
if(!found)
|
||||
{
|
||||
_dbSafeAccessMutex.lock();
|
||||
found = this->getCalibrationQuery(signatureId, models, stereoModel);
|
||||
found = this->getCalibrationQuery(signatureId, models, stereoModels);
|
||||
_dbSafeAccessMutex.unlock();
|
||||
}
|
||||
return found;
|
||||
|
||||
@@ -1448,7 +1448,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
cv::Mat imageCompressed;
|
||||
cv::Mat depthOrRightCompressed;
|
||||
std::vector<CameraModel> models;
|
||||
StereoCameraModel stereoModel;
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
Transform localTransform = Transform::getIdentity();
|
||||
cv::Mat scanCompressed;
|
||||
cv::Mat userDataCompressed;
|
||||
@@ -1515,8 +1515,16 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
}
|
||||
else if(type == 1) // stereo
|
||||
{
|
||||
int bytesRead = (int)stereoModel.deserialize((unsigned char*)data, dataSize);
|
||||
UASSERT(bytesRead == dataSize);
|
||||
StereoCameraModel model;
|
||||
int bytesReadTotal = 0;
|
||||
unsigned int bytesRead = 0;
|
||||
while(bytesReadTotal < dataSize &&
|
||||
(bytesRead=model.deserialize((const unsigned char *)data+bytesReadTotal, dataSize-bytesReadTotal))!=0)
|
||||
{
|
||||
bytesReadTotal+=bytesRead;
|
||||
stereoModels.push_back(model);
|
||||
}
|
||||
UASSERT(bytesReadTotal == dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1589,14 +1597,14 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back(StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform,
|
||||
cv::Size(dataFloat[5],dataFloat[6]));
|
||||
cv::Size(dataFloat[5],dataFloat[6])));
|
||||
}
|
||||
else if((unsigned int)dataSize == (5+localTransform.size())*sizeof(float))
|
||||
{
|
||||
@@ -1606,13 +1614,13 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back(StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform);
|
||||
localTransform));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1632,7 +1640,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
if(fyOrBaseline < 1.0)
|
||||
{
|
||||
//it is a baseline
|
||||
stereoModel = StereoCameraModel(fx,fx,cx,cy,fyOrBaseline, localTransform);
|
||||
stereoModels.push_back(StereoCameraModel(fx,fx,cx,cy,fyOrBaseline, localTransform));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1818,7 +1826,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
}
|
||||
else
|
||||
{
|
||||
(*iter)->sensorData().setStereoImage(imageCompressed, depthOrRightCompressed, stereoModel);
|
||||
(*iter)->sensorData().setStereoImage(imageCompressed, depthOrRightCompressed, stereoModels);
|
||||
}
|
||||
}
|
||||
if(userData)
|
||||
@@ -1850,7 +1858,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
bool DBDriverSqlite3::getCalibrationQuery(
|
||||
int signatureId,
|
||||
std::vector<CameraModel> & models,
|
||||
StereoCameraModel & stereoModel) const
|
||||
std::vector<StereoCameraModel> & stereoModels) const
|
||||
{
|
||||
bool found = false;
|
||||
if(_ppDb && signatureId)
|
||||
@@ -1936,8 +1944,16 @@ bool DBDriverSqlite3::getCalibrationQuery(
|
||||
}
|
||||
else if(type == 1) // stereo
|
||||
{
|
||||
int bytesRead = (int)stereoModel.deserialize((unsigned char*)data, dataSize);
|
||||
UASSERT(bytesRead == dataSize);
|
||||
StereoCameraModel model;
|
||||
int bytesReadTotal = 0;
|
||||
unsigned int bytesRead = 0;
|
||||
while(bytesReadTotal < dataSize &&
|
||||
(bytesRead=model.deserialize((const unsigned char *)data+bytesReadTotal, dataSize-bytesReadTotal))!=0)
|
||||
{
|
||||
bytesReadTotal+=bytesRead;
|
||||
stereoModels.push_back(model);
|
||||
}
|
||||
UASSERT(bytesReadTotal == dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2010,14 +2026,14 @@ bool DBDriverSqlite3::getCalibrationQuery(
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back(StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform,
|
||||
cv::Size(dataFloat[5],dataFloat[6]));
|
||||
cv::Size(dataFloat[5],dataFloat[6])));
|
||||
}
|
||||
else if((unsigned int)dataSize == (5+localTransform.size())*sizeof(float))
|
||||
{
|
||||
@@ -2027,13 +2043,13 @@ bool DBDriverSqlite3::getCalibrationQuery(
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back((StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform);
|
||||
localTransform)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2054,7 +2070,7 @@ bool DBDriverSqlite3::getCalibrationQuery(
|
||||
if(fyOrBaseline < 1.0)
|
||||
{
|
||||
//it is a baseline
|
||||
stereoModel = StereoCameraModel(fx,fx,cx,cy,fyOrBaseline, localTransform);
|
||||
stereoModels.push_back(StereoCameraModel(fx,fx,cx,cy,fyOrBaseline, localTransform));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3278,7 +3294,7 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
|
||||
int dataSize = 0;
|
||||
Transform localTransform;
|
||||
std::vector<CameraModel> models;
|
||||
StereoCameraModel stereoModel;
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
|
||||
// calibration
|
||||
data = sqlite3_column_blob(ppStmt, index);
|
||||
@@ -3308,8 +3324,16 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
|
||||
}
|
||||
else if(type == 1) // stereo
|
||||
{
|
||||
int bytesRead = (int)stereoModel.deserialize((unsigned char*)data, dataSize);
|
||||
UASSERT(bytesRead == dataSize);
|
||||
StereoCameraModel model;
|
||||
int bytesReadTotal = 0;
|
||||
unsigned int bytesRead = 0;
|
||||
while(bytesReadTotal < dataSize &&
|
||||
(bytesRead=model.deserialize((const unsigned char *)data+bytesReadTotal, dataSize-bytesReadTotal))!=0)
|
||||
{
|
||||
bytesReadTotal+=bytesRead;
|
||||
stereoModels.push_back(model);
|
||||
}
|
||||
UASSERT(bytesReadTotal == dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3383,14 +3407,14 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back(StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform,
|
||||
cv::Size(dataFloat[5], dataFloat[6]));
|
||||
cv::Size(dataFloat[5], dataFloat[6])));
|
||||
}
|
||||
else if((unsigned int)dataSize == (5+localTransform.size())*sizeof(float))
|
||||
{
|
||||
@@ -3400,13 +3424,13 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
|
||||
{
|
||||
localTransform.normalizeRotation();
|
||||
}
|
||||
stereoModel = StereoCameraModel(
|
||||
stereoModels.push_back(StereoCameraModel(
|
||||
dataFloat[0], // fx
|
||||
dataFloat[1], // fy
|
||||
dataFloat[2], // cx
|
||||
dataFloat[3], // cy
|
||||
dataFloat[4], // baseline
|
||||
localTransform);
|
||||
localTransform));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3415,7 +3439,7 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
|
||||
}
|
||||
|
||||
(*iter)->sensorData().setCameraModels(models);
|
||||
(*iter)->sensorData().setStereoCameraModel(stereoModel);
|
||||
(*iter)->sensorData().setStereoCameraModels(stereoModels);
|
||||
}
|
||||
rc = sqlite3_step(ppStmt);
|
||||
}
|
||||
@@ -4382,8 +4406,8 @@ void DBDriverSqlite3::saveQuery(const std::list<Signature *> & signatures)
|
||||
!(*i)->sensorData().depthOrRightCompressed().empty() ||
|
||||
!(*i)->sensorData().laserScanCompressed().isEmpty() ||
|
||||
!(*i)->sensorData().userDataCompressed().empty() ||
|
||||
!(*i)->sensorData().cameraModels().size() ||
|
||||
!(*i)->sensorData().stereoCameraModel().isValidForProjection())
|
||||
!(*i)->sensorData().cameraModels().empty() ||
|
||||
!(*i)->sensorData().stereoCameraModels().empty())
|
||||
{
|
||||
UASSERT((*i)->id() == (*i)->sensorData().id());
|
||||
stepSensorData(ppStmt, (*i)->sensorData());
|
||||
@@ -5691,13 +5715,15 @@ void DBDriverSqlite3::stepDepth(sqlite3_stmt * ppStmt, const SensorData & sensor
|
||||
cy = sensorData.cameraModels()[0].cy();
|
||||
localTransform = sensorData.cameraModels()[0].localTransform();
|
||||
}
|
||||
else if(sensorData.stereoCameraModel().isValidForProjection())
|
||||
else if(sensorData.stereoCameraModels().size())
|
||||
{
|
||||
fx = sensorData.stereoCameraModel().left().fx();
|
||||
fyOrBaseline = sensorData.stereoCameraModel().baseline();
|
||||
cx = sensorData.stereoCameraModel().left().cx();
|
||||
cy = sensorData.stereoCameraModel().left().cy();
|
||||
localTransform = sensorData.stereoCameraModel().left().localTransform();
|
||||
UASSERT_MSG(sensorData.stereoCameraModels().size() == 1,
|
||||
uFormat("Database version %s doesn't support multi-camera!", _version.c_str()).c_str());
|
||||
fx = sensorData.stereoCameraModels()[0].left().fx();
|
||||
fyOrBaseline = sensorData.stereoCameraModels()[0].baseline();
|
||||
cx = sensorData.stereoCameraModels()[0].left().cx();
|
||||
cy = sensorData.stereoCameraModels()[0].left().cy();
|
||||
localTransform = sensorData.stereoCameraModels()[0].left().localTransform();
|
||||
}
|
||||
|
||||
if(uStrNumCmp(_version, "0.7.0") >= 0)
|
||||
@@ -6040,24 +6066,32 @@ void DBDriverSqlite3::stepSensorData(sqlite3_stmt * ppStmt,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sensorData.stereoCameraModel().isValidForProjection())
|
||||
else if(sensorData.stereoCameraModels().size() && sensorData.stereoCameraModels()[0].isValidForProjection())
|
||||
{
|
||||
if(uStrNumCmp(_version, "0.18.0") >= 0)
|
||||
{
|
||||
calibrationData = sensorData.stereoCameraModel().serialize();
|
||||
UASSERT(!calibrationData.empty());
|
||||
for(unsigned int i=0; i<sensorData.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
UASSERT(sensorData.stereoCameraModels()[i].isValidForProjection());
|
||||
std::vector<unsigned char> data = sensorData.stereoCameraModels()[i].serialize();
|
||||
UASSERT(!data.empty());
|
||||
unsigned int oldSize = calibrationData.size();
|
||||
calibrationData.resize(calibrationData.size() + data.size());
|
||||
memcpy(calibrationData.data()+oldSize, data.data(), data.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Transform & localTransform = sensorData.stereoCameraModel().left().localTransform();
|
||||
UASSERT_MSG(sensorData.stereoCameraModels().size()==1, uFormat("Database version (%s) is too old for saving multiple stereo cameras", _version.c_str()).c_str());
|
||||
const Transform & localTransform = sensorData.stereoCameraModels()[0].left().localTransform();
|
||||
calibration.resize(7+localTransform.size());
|
||||
calibration[0] = sensorData.stereoCameraModel().left().fx();
|
||||
calibration[1] = sensorData.stereoCameraModel().left().fy();
|
||||
calibration[2] = sensorData.stereoCameraModel().left().cx();
|
||||
calibration[3] = sensorData.stereoCameraModel().left().cy();
|
||||
calibration[4] = sensorData.stereoCameraModel().baseline();
|
||||
calibration[5] = sensorData.stereoCameraModel().left().imageWidth();
|
||||
calibration[6] = sensorData.stereoCameraModel().left().imageHeight();
|
||||
calibration[0] = sensorData.stereoCameraModels()[0].left().fx();
|
||||
calibration[1] = sensorData.stereoCameraModels()[0].left().fy();
|
||||
calibration[2] = sensorData.stereoCameraModels()[0].left().cx();
|
||||
calibration[3] = sensorData.stereoCameraModels()[0].left().cy();
|
||||
calibration[4] = sensorData.stereoCameraModels()[0].baseline();
|
||||
calibration[5] = sensorData.stereoCameraModels()[0].left().imageWidth();
|
||||
calibration[6] = sensorData.stereoCameraModels()[0].left().imageHeight();
|
||||
memcpy(calibration.data()+7, localTransform.data(), localTransform.size()*sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +186,8 @@ bool DBReader::init(
|
||||
if(_ids.size())
|
||||
{
|
||||
std::vector<CameraModel> models;
|
||||
StereoCameraModel stereoModel;
|
||||
if(_dbDriver->getCalibration(*_ids.begin(), models, stereoModel))
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
if(_dbDriver->getCalibration(*_ids.begin(), models, stereoModels))
|
||||
{
|
||||
if(models.size())
|
||||
{
|
||||
@@ -208,7 +208,7 @@ bool DBReader::init(
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(stereoModel.isValidForProjection())
|
||||
else if(stereoModels.size() && stereoModels.at(0).isValidForProjection())
|
||||
{
|
||||
_calibrated = true;
|
||||
}
|
||||
|
||||
@@ -792,7 +792,9 @@ std::vector<cv::Point3f> Feature2D::generateKeypoints3D(
|
||||
std::vector<cv::Point3f> keypoints3D;
|
||||
if(keypoints.size())
|
||||
{
|
||||
if(!data.rightRaw().empty() && !data.imageRaw().empty() && data.stereoCameraModel().isValidForProjection())
|
||||
if(!data.rightRaw().empty() && !data.imageRaw().empty() &&
|
||||
!data.stereoCameraModels().empty() &&
|
||||
data.stereoCameraModels()[0].isValidForProjection())
|
||||
{
|
||||
//stereo
|
||||
cv::Mat imageMono;
|
||||
@@ -808,22 +810,121 @@ std::vector<cv::Point3f> Feature2D::generateKeypoints3D(
|
||||
|
||||
std::vector<cv::Point2f> leftCorners;
|
||||
cv::KeyPoint::convert(keypoints, leftCorners);
|
||||
std::vector<unsigned char> status;
|
||||
|
||||
std::vector<cv::Point2f> rightCorners;
|
||||
rightCorners = _stereo->computeCorrespondences(
|
||||
imageMono,
|
||||
data.rightRaw(),
|
||||
leftCorners,
|
||||
status);
|
||||
|
||||
keypoints3D = util3d::generateKeypoints3DStereo(
|
||||
leftCorners,
|
||||
rightCorners,
|
||||
data.stereoCameraModel(),
|
||||
status,
|
||||
_minDepth,
|
||||
_maxDepth);
|
||||
if(data.stereoCameraModels().size() == 1)
|
||||
{
|
||||
std::vector<unsigned char> status;
|
||||
rightCorners = _stereo->computeCorrespondences(
|
||||
imageMono,
|
||||
data.rightRaw(),
|
||||
leftCorners,
|
||||
status);
|
||||
|
||||
if(ULogger::level() >= ULogger::kWarning)
|
||||
{
|
||||
int rejected = 0;
|
||||
for(size_t i=0; i<status.size(); ++i)
|
||||
{
|
||||
if(status[i]==0)
|
||||
{
|
||||
++rejected;
|
||||
}
|
||||
}
|
||||
if(rejected > (int)status.size()/2)
|
||||
{
|
||||
UWARN("A large number (%d/%d) of stereo correspondences are rejected! "
|
||||
"Optical flow may have failed because images are not calibrated, "
|
||||
"the background is too far (no disparity between the images), "
|
||||
"maximum disparity may be too small (%f) or that exposure between "
|
||||
"left and right images is too different.",
|
||||
rejected,
|
||||
(int)status.size(),
|
||||
_stereo->maxDisparity());
|
||||
}
|
||||
}
|
||||
|
||||
keypoints3D = util3d::generateKeypoints3DStereo(
|
||||
leftCorners,
|
||||
rightCorners,
|
||||
data.stereoCameraModels()[0],
|
||||
status,
|
||||
_minDepth,
|
||||
_maxDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
int subImageWith = imageMono.cols / data.stereoCameraModels().size();
|
||||
UASSERT(imageMono.cols % subImageWith == 0);
|
||||
std::vector<std::vector<cv::Point2f> > subLeftCorners(data.stereoCameraModels().size());
|
||||
std::vector<std::vector<int> > subIndex(data.stereoCameraModels().size());
|
||||
// Assign keypoints per camera
|
||||
for(size_t i=0; i<leftCorners.size(); ++i)
|
||||
{
|
||||
int cameraIndex = int(leftCorners[i].x / subImageWith);
|
||||
leftCorners[i].x -= cameraIndex*subImageWith;
|
||||
subLeftCorners[cameraIndex].push_back(leftCorners[i]);
|
||||
subIndex[cameraIndex].push_back(i);
|
||||
}
|
||||
|
||||
keypoints3D.resize(keypoints.size());
|
||||
int total = 0;
|
||||
int rejected = 0;
|
||||
for(size_t i=0; i<data.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
if(!subLeftCorners[i].empty())
|
||||
{
|
||||
std::vector<unsigned char> status;
|
||||
rightCorners = _stereo->computeCorrespondences(
|
||||
imageMono.colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
||||
data.rightRaw().colRange(cv::Range(subImageWith*i, subImageWith*(i+1))),
|
||||
subLeftCorners[i],
|
||||
status);
|
||||
|
||||
std::vector<cv::Point3f> subKeypoints3D = util3d::generateKeypoints3DStereo(
|
||||
subLeftCorners[i],
|
||||
rightCorners,
|
||||
data.stereoCameraModels()[i],
|
||||
status,
|
||||
_minDepth,
|
||||
_maxDepth);
|
||||
|
||||
if(ULogger::level() >= ULogger::kWarning)
|
||||
{
|
||||
for(size_t i=0; i<status.size(); ++i)
|
||||
{
|
||||
if(status[i]==0)
|
||||
{
|
||||
++rejected;
|
||||
}
|
||||
}
|
||||
total+=status.size();
|
||||
}
|
||||
|
||||
UASSERT(subIndex[i].size() == subKeypoints3D.size());
|
||||
for(size_t j=0; j<subKeypoints3D.size(); ++j)
|
||||
{
|
||||
keypoints3D[subIndex[i][j]] = subKeypoints3D[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ULogger::level() >= ULogger::kWarning)
|
||||
{
|
||||
if(rejected > total/2)
|
||||
{
|
||||
UWARN("A large number (%d/%d) of stereo correspondences are rejected! "
|
||||
"Optical flow may have failed because images are not calibrated, "
|
||||
"the background is too far (no disparity between the images), "
|
||||
"maximum disparity may be too small (%f) or that exposure between "
|
||||
"left and right images is too different.",
|
||||
rejected,
|
||||
total,
|
||||
_stereo->maxDisparity());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!data.depthRaw().empty() && data.cameraModels().size())
|
||||
{
|
||||
|
||||
@@ -1812,7 +1812,7 @@ void Memory::clear()
|
||||
_linksChanged = false;
|
||||
_gpsOrigin = GPS();
|
||||
_rectCameraModels.clear();
|
||||
_rectStereoCameraModel = StereoCameraModel();
|
||||
_rectStereoCameraModels.clear();
|
||||
_odomMaxInf.clear();
|
||||
_groundTruths.clear();
|
||||
_labels.clear();
|
||||
@@ -2963,7 +2963,7 @@ Transform Memory::computeTransform(
|
||||
}
|
||||
std::map<int, Transform> bundlePoses;
|
||||
std::multimap<int, Link> bundleLinks;
|
||||
std::map<int, CameraModel> bundleModels;
|
||||
std::map<int, std::vector<CameraModel> > bundleModels;
|
||||
std::map<int, std::map<int, FeatureBA> > wordReferences;
|
||||
|
||||
std::multimap<int, Link> links = fromS.getLinks();
|
||||
@@ -2989,28 +2989,32 @@ Transform Memory::computeTransform(
|
||||
}
|
||||
if(s)
|
||||
{
|
||||
CameraModel model;
|
||||
if(s->sensorData().cameraModels().size() == 1 && s->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
std::vector<CameraModel> models;
|
||||
if(s->sensorData().cameraModels().size() >= 1 && s->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = s->sensorData().cameraModels()[0];
|
||||
models = s->sensorData().cameraModels();
|
||||
}
|
||||
else if(s->sensorData().stereoCameraModel().isValidForProjection())
|
||||
else if(s->sensorData().stereoCameraModels().size() >= 1 && s->sensorData().stereoCameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = s->sensorData().stereoCameraModel().left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-s->sensorData().stereoCameraModel().baseline()*model.fx());
|
||||
for(size_t i=0; i<s->sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel model = s->sensorData().stereoCameraModels()[i].left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-s->sensorData().stereoCameraModels()[i].baseline()*model.fx(),
|
||||
model.imageSize());
|
||||
models.push_back(model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("no valid camera model to use local bundle adjustment on loop closure!");
|
||||
}
|
||||
bundleModels.insert(std::make_pair(id, model));
|
||||
Transform invLocalTransform = model.localTransform().inverse();
|
||||
bundleModels.insert(std::make_pair(id, models));
|
||||
UASSERT(iter->second.isValid() || iter->first == fromS.id());
|
||||
|
||||
if(iter->second.transform().isNull())
|
||||
@@ -3030,16 +3034,27 @@ Transform Memory::computeTransform(
|
||||
if(points3DMap.find(jter->first)!=points3DMap.end() &&
|
||||
(id == tmpTo.id() || jter->first > 0)) // Since we added negative words of "from", only accept matches with current frame
|
||||
{
|
||||
cv::KeyPoint kpts = s->getWordsKpts()[jter->second];
|
||||
int cameraIndex = 0;
|
||||
if(models.size()>1)
|
||||
{
|
||||
UASSERT(models[0].imageWidth()>0);
|
||||
float subImageWidth = models[0].imageWidth();
|
||||
cameraIndex = int(kpts.pt.x / subImageWidth);
|
||||
kpts.pt.x = kpts.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
//get depth
|
||||
float d = 0.0f;
|
||||
if( !s->getWords3().empty() &&
|
||||
util3d::isFinite(s->getWords3()[jter->second]))
|
||||
{
|
||||
//move back point in camera frame (to get depth along z)
|
||||
Transform invLocalTransform = models[cameraIndex].localTransform().inverse();
|
||||
d = util3d::transformPoint(s->getWords3()[jter->second], invLocalTransform).z;
|
||||
}
|
||||
wordReferences.insert(std::make_pair(jter->first, std::map<int, FeatureBA>()));
|
||||
wordReferences.at(jter->first).insert(std::make_pair(id, FeatureBA(s->getWordsKpts()[jter->second], d)));
|
||||
wordReferences.at(jter->first).insert(std::make_pair(id, FeatureBA(kpts, d, cv::Mat(), cameraIndex)));
|
||||
++totalWordReferences;
|
||||
}
|
||||
}
|
||||
@@ -4110,19 +4125,19 @@ void Memory::getNodeWordsAndGlobalDescriptors(int nodeId,
|
||||
|
||||
void Memory::getNodeCalibration(int nodeId,
|
||||
std::vector<CameraModel> & models,
|
||||
StereoCameraModel & stereoModel) const
|
||||
std::vector<StereoCameraModel> & stereoModels) const
|
||||
{
|
||||
//UDEBUG("nodeId=%d", nodeId);
|
||||
Signature * s = this->_getSignature(nodeId);
|
||||
if(s)
|
||||
{
|
||||
models = s->sensorData().cameraModels();
|
||||
stereoModel = s->sensorData().stereoCameraModel();
|
||||
stereoModels = s->sensorData().stereoCameraModels();
|
||||
}
|
||||
else if(_dbDriver)
|
||||
{
|
||||
// load from database
|
||||
_dbDriver->getCalibration(nodeId, models, stereoModel);
|
||||
_dbDriver->getCalibration(nodeId, models, stereoModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4445,15 +4460,11 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
CV_16UC1, CV_32FC1, CV_8UC1).c_str());
|
||||
|
||||
if(!data.depthOrRightRaw().empty() &&
|
||||
data.cameraModels().size() == 0 &&
|
||||
!data.stereoCameraModel().isValidForProjection() &&
|
||||
data.cameraModels().empty() &&
|
||||
data.stereoCameraModels().empty() &&
|
||||
!pose.isNull())
|
||||
{
|
||||
UERROR("Camera calibration not valid, calibrate your camera!");
|
||||
if(data.cameraModels().empty())
|
||||
std::cout << data.stereoCameraModel() << std::endl;
|
||||
else
|
||||
std::cout << data.cameraModels()[0] << std::endl;
|
||||
UERROR("No camera calibration found, calibrate your camera!");
|
||||
return 0;
|
||||
}
|
||||
UASSERT(_feature2D != 0);
|
||||
@@ -4504,6 +4515,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
// we assume that once rtabmap is receiving data, the calibration won't change over time
|
||||
if(data.cameraModels().size())
|
||||
{
|
||||
UDEBUG("Monocular rectification");
|
||||
// Note that only RGB image is rectified, the depth image is assumed to be already registered to rectified RGB camera.
|
||||
UASSERT(int((data.imageRaw().cols/data.cameraModels().size())*data.cameraModels().size()) == data.imageRaw().cols);
|
||||
int subImageWidth = data.imageRaw().cols/data.cameraModels().size();
|
||||
@@ -4545,25 +4557,58 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
}
|
||||
data.setRGBDImage(rectifiedImages, data.depthOrRightRaw(), data.cameraModels());
|
||||
}
|
||||
else if(data.stereoCameraModel().isValidForRectification())
|
||||
else if(data.stereoCameraModels().size())
|
||||
{
|
||||
if(!_rectStereoCameraModel.isValidForRectification())
|
||||
UDEBUG("Stereo rectification");
|
||||
UASSERT(int((data.imageRaw().cols/data.stereoCameraModels().size())*data.stereoCameraModels().size()) == data.imageRaw().cols);
|
||||
int subImageWidth = data.imageRaw().cols/data.stereoCameraModels().size();
|
||||
UASSERT(subImageWidth == data.rightRaw().cols/(int)data.stereoCameraModels().size());
|
||||
cv::Mat rectifiedLefts(data.imageRaw().size(), data.imageRaw().type());
|
||||
cv::Mat rectifiedRights(data.rightRaw().size(), data.rightRaw().type());
|
||||
bool initRectMaps = _rectStereoCameraModels.empty();
|
||||
if(initRectMaps)
|
||||
{
|
||||
_rectStereoCameraModel = data.stereoCameraModel();
|
||||
if(!_rectStereoCameraModel.isRectificationMapInitialized())
|
||||
_rectStereoCameraModels.resize(data.stereoCameraModels().size());
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<data.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
if(data.stereoCameraModels()[i].isValidForRectification())
|
||||
{
|
||||
UWARN("Initializing rectification maps (only done for the first image received)...");
|
||||
_rectStereoCameraModel.initRectificationMap();
|
||||
UWARN("Initializing rectification maps (only done for the first image received)...done!");
|
||||
if(initRectMaps)
|
||||
{
|
||||
_rectStereoCameraModels[i] = data.stereoCameraModels()[i];
|
||||
if(!_rectStereoCameraModels[i].isRectificationMapInitialized())
|
||||
{
|
||||
UWARN("Initializing rectification maps (only done for the first image received)...");
|
||||
_rectStereoCameraModels[i].initRectificationMap();
|
||||
UWARN("Initializing rectification maps (only done for the first image received)...done!");
|
||||
}
|
||||
}
|
||||
UASSERT(_rectStereoCameraModels[i].left().imageWidth() == data.stereoCameraModels()[i].left().imageWidth());
|
||||
UASSERT(_rectStereoCameraModels[i].left().imageHeight() == data.stereoCameraModels()[i].left().imageHeight());
|
||||
|
||||
cv::Mat rectifiedLeft = _rectStereoCameraModels[i].left().rectifyImage(cv::Mat(data.imageRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
|
||||
cv::Mat rectifiedRight = _rectStereoCameraModels[i].right().rectifyImage(cv::Mat(data.rightRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, data.rightRaw().rows)));
|
||||
rectifiedLeft.copyTo(cv::Mat(rectifiedLefts, cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
|
||||
rectifiedRight.copyTo(cv::Mat(rectifiedRights, cv::Rect(subImageWidth*i, 0, subImageWidth, data.rightRaw().rows)));
|
||||
imagesRectified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Calibration for camera %d cannot be used to rectify the image. Make sure to do a "
|
||||
"full calibration. If images are already rectified, set %s parameter back to true.",
|
||||
(int)i,
|
||||
Parameters::kRtabmapImagesAlreadyRectified().c_str());
|
||||
std::cout << data.stereoCameraModels()[i] << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
UASSERT(_rectStereoCameraModel.left().imageWidth() == data.stereoCameraModel().left().imageWidth());
|
||||
UASSERT(_rectStereoCameraModel.left().imageHeight() == data.stereoCameraModel().left().imageHeight());
|
||||
|
||||
data.setStereoImage(
|
||||
_rectStereoCameraModel.left().rectifyImage(data.imageRaw()),
|
||||
_rectStereoCameraModel.right().rectifyImage(data.rightRaw()),
|
||||
data.stereoCameraModel());
|
||||
imagesRectified = true;
|
||||
rectifiedLefts,
|
||||
rectifiedRights,
|
||||
data.stereoCameraModels());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4636,17 +4681,18 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
util2d::decimate(decimatedData.depthOrRightRaw(), decimationDepth),
|
||||
cameraModels);
|
||||
}
|
||||
else
|
||||
|
||||
std::vector<StereoCameraModel> stereoCameraModels = decimatedData.stereoCameraModels();
|
||||
for(unsigned int i=0; i<stereoCameraModels.size(); ++i)
|
||||
{
|
||||
stereoCameraModels[i].scale(1.0/double(_imagePreDecimation));
|
||||
}
|
||||
if(!stereoCameraModels.empty())
|
||||
{
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imagePreDecimation));
|
||||
}
|
||||
decimatedData.setStereoImage(
|
||||
util2d::decimate(decimatedData.imageRaw(), _imagePreDecimation),
|
||||
util2d::decimate(decimatedData.depthOrRightRaw(), _imagePreDecimation),
|
||||
stereoModel);
|
||||
stereoCameraModels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4885,7 +4931,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
keypoints3D = data.keypoints3D();
|
||||
}
|
||||
else if((!decimatedData.depthRaw().empty() && decimatedData.cameraModels().size() && decimatedData.cameraModels()[0].isValidForProjection()) ||
|
||||
(!decimatedData.rightRaw().empty() && decimatedData.stereoCameraModel().isValidForProjection()))
|
||||
(!decimatedData.rightRaw().empty() && decimatedData.stereoCameraModels().size() && decimatedData.stereoCameraModels()[0].isValidForProjection()))
|
||||
{
|
||||
keypoints3D = _feature2D->generateKeypoints3D(decimatedData, keypoints);
|
||||
t = timer.ticks();
|
||||
@@ -5090,7 +5136,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
|
||||
if(keypoints3D.empty() &&
|
||||
((!data.depthRaw().empty() && data.cameraModels().size() && data.cameraModels()[0].isValidForProjection()) ||
|
||||
(!data.rightRaw().empty() && data.stereoCameraModel().isValidForProjection())))
|
||||
(!data.rightRaw().empty() && data.stereoCameraModels().size() && data.stereoCameraModels()[0].isValidForProjection())))
|
||||
{
|
||||
keypoints3D = _feature2D->generateKeypoints3D(data, keypoints);
|
||||
}
|
||||
@@ -5280,9 +5326,21 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
markers = _markerDetector->detect(data.imageRaw(), data.cameraModels()[0], data.depthRaw(), _landmarksSize);
|
||||
}
|
||||
}
|
||||
else if(data.stereoCameraModel().isValidForProjection())
|
||||
else if(!data.stereoCameraModels().empty() && data.stereoCameraModels()[0].isValidForProjection())
|
||||
{
|
||||
markers = _markerDetector->detect(data.imageRaw(), data.stereoCameraModel().left(), cv::Mat(), _landmarksSize);
|
||||
if(data.stereoCameraModels().size() > 1)
|
||||
{
|
||||
static bool warned = false;
|
||||
if(!warned)
|
||||
{
|
||||
UWARN("Detecting markers in multi-camera setup is not yet implemented, aborting marker detection. This message is only printed once.");
|
||||
}
|
||||
warned = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
markers = _markerDetector->detect(data.imageRaw(), data.stereoCameraModels()[0].left(), cv::Mat(), _landmarksSize);
|
||||
}
|
||||
}
|
||||
for(std::map<int, MarkerInfo>::iterator iter=markers.begin(); iter!=markers.end(); ++iter)
|
||||
{
|
||||
@@ -5310,7 +5368,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
cv::Mat image = data.imageRaw();
|
||||
cv::Mat depthOrRightImage = data.depthOrRightRaw();
|
||||
std::vector<CameraModel> cameraModels = data.cameraModels();
|
||||
StereoCameraModel stereoCameraModel = data.stereoCameraModel();
|
||||
std::vector<StereoCameraModel> stereoCameraModels = data.stereoCameraModels();
|
||||
|
||||
// apply decimation?
|
||||
if(_imagePostDecimation > 1 && !isIntermediateNode)
|
||||
@@ -5320,7 +5378,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
image = decimatedData.imageRaw();
|
||||
depthOrRightImage = decimatedData.depthOrRightRaw();
|
||||
cameraModels = decimatedData.cameraModels();
|
||||
stereoCameraModel = decimatedData.stereoCameraModel();
|
||||
stereoCameraModels = decimatedData.stereoCameraModels();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5348,9 +5406,9 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
{
|
||||
cameraModels[i] = cameraModels[i].scaled(1.0/double(_imagePostDecimation));
|
||||
}
|
||||
if(stereoCameraModel.isValidForProjection())
|
||||
for(unsigned int i=0; i<stereoCameraModels.size(); ++i)
|
||||
{
|
||||
stereoCameraModel.scale(1.0/double(_imagePostDecimation));
|
||||
stereoCameraModels[i].scale(1.0/double(_imagePostDecimation));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5598,7 +5656,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
"",
|
||||
pose,
|
||||
data.groundTruth(),
|
||||
stereoCameraModel.isValidForProjection()?
|
||||
!stereoCameraModels.empty()?
|
||||
SensorData(
|
||||
laserScan.angleIncrement() == 0.0f?
|
||||
LaserScan(compressedScan,
|
||||
@@ -5616,7 +5674,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
laserScan.localTransform()),
|
||||
compressedImage,
|
||||
compressedDepth,
|
||||
stereoCameraModel,
|
||||
stereoCameraModels,
|
||||
id,
|
||||
0,
|
||||
compressedUserData):
|
||||
@@ -5682,7 +5740,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
"",
|
||||
pose,
|
||||
data.groundTruth(),
|
||||
stereoCameraModel.isValidForProjection()?
|
||||
!stereoCameraModels.empty()?
|
||||
SensorData(
|
||||
laserScan.angleIncrement() == 0.0f?
|
||||
LaserScan(compressedScan,
|
||||
@@ -5700,7 +5758,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
laserScan.localTransform()),
|
||||
cv::Mat(),
|
||||
cv::Mat(),
|
||||
stereoCameraModel,
|
||||
stereoCameraModels,
|
||||
id,
|
||||
0,
|
||||
compressedUserData):
|
||||
@@ -5738,7 +5796,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
}
|
||||
else
|
||||
{
|
||||
s->sensorData().setStereoImage(image, depthOrRightImage, stereoCameraModel, false);
|
||||
s->sensorData().setStereoImage(image, depthOrRightImage, stereoCameraModels, false);
|
||||
}
|
||||
s->sensorData().setLaserScan(laserScan, false);
|
||||
s->sensorData().setUserData(data.userDataRaw(), false);
|
||||
|
||||
@@ -430,8 +430,25 @@ void OccupancyGrid::createLocalMap(
|
||||
}
|
||||
else
|
||||
{
|
||||
const Transform & t = node.sensorData().stereoCameraModel().localTransform();
|
||||
viewPoint = cv::Point3f(t.x(), t.y(), t.z());
|
||||
// average of all local transforms
|
||||
float sum = 0;
|
||||
for(unsigned int i=0; i<node.sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
const Transform & t = node.sensorData().stereoCameraModels()[i].localTransform();
|
||||
if(!t.isNull())
|
||||
{
|
||||
viewPoint.x += t.x();
|
||||
viewPoint.y += t.y();
|
||||
viewPoint.z += t.z();
|
||||
sum += 1.0f;
|
||||
}
|
||||
}
|
||||
if(sum > 0.0f)
|
||||
{
|
||||
viewPoint.x /= sum;
|
||||
viewPoint.y /= sum;
|
||||
viewPoint.z /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat scanGroundCells;
|
||||
|
||||
@@ -328,35 +328,73 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
|
||||
|
||||
if(!_imagesAlreadyRectified && !this->canProcessRawImages() && !data.imageRaw().empty())
|
||||
{
|
||||
if(data.stereoCameraModel().isValidForRectification())
|
||||
if(!data.stereoCameraModels().empty())
|
||||
{
|
||||
if(!stereoModel_.isRectificationMapInitialized() ||
|
||||
stereoModel_.left().imageSize() != data.stereoCameraModel().left().imageSize())
|
||||
bool valid = true;
|
||||
if(data.stereoCameraModels().size() != stereoModels_.size())
|
||||
{
|
||||
stereoModel_ = data.stereoCameraModel();
|
||||
stereoModel_.initRectificationMap();
|
||||
if(stereoModel_.isRectificationMapInitialized())
|
||||
stereoModels_.clear();
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i=0; i<data.stereoCameraModels().size() && valid; ++i)
|
||||
{
|
||||
valid = stereoModels_[i].isRectificationMapInitialized() &&
|
||||
stereoModels_[i].left().imageSize() == data.stereoCameraModels()[i].left().imageSize();
|
||||
}
|
||||
}
|
||||
|
||||
if(!valid)
|
||||
{
|
||||
stereoModels_ = data.stereoCameraModels();
|
||||
valid = true;
|
||||
for(size_t i=0; i<stereoModels_.size() && valid; ++i)
|
||||
{
|
||||
stereoModels_[i].initRectificationMap();
|
||||
valid = stereoModels_[i].isRectificationMapInitialized();
|
||||
}
|
||||
if(valid)
|
||||
{
|
||||
UWARN("%s parameter is set to false but the selected odometry approach cannot "
|
||||
"process raw images. We will rectify them for convenience.",
|
||||
"process raw stereo images. We will rectify them for convenience.",
|
||||
Parameters::kRtabmapImagesAlreadyRectified().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Odometry approach chosen cannot process raw images (not rectified images) "
|
||||
UERROR("Odometry approach chosen cannot process raw stereo images (not rectified images) "
|
||||
"and we cannot rectify them as the rectification map failed to initialize (valid calibration?). "
|
||||
"Make sure images are rectified and set %s parameter back to true, or make sure "
|
||||
"calibration is valid for rectification.",
|
||||
"Make sure images are rectified and set %s parameter back to true, or "
|
||||
"make sure calibration is valid for rectification",
|
||||
Parameters::kRtabmapImagesAlreadyRectified().c_str());
|
||||
stereoModels_.clear();
|
||||
}
|
||||
}
|
||||
if(stereoModel_.isRectificationMapInitialized())
|
||||
if(valid)
|
||||
{
|
||||
data.setStereoImage(
|
||||
stereoModel_.left().rectifyImage(data.imageRaw()),
|
||||
stereoModel_.right().rectifyImage(data.rightRaw()),
|
||||
stereoModel_,
|
||||
false);
|
||||
if(stereoModels_.size()==1)
|
||||
{
|
||||
data.setStereoImage(
|
||||
stereoModels_[0].left().rectifyImage(data.imageRaw()),
|
||||
stereoModels_[0].right().rectifyImage(data.rightRaw()),
|
||||
stereoModels_,
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(int((data.imageRaw().cols/data.stereoCameraModels().size())*data.stereoCameraModels().size()) == data.imageRaw().cols);
|
||||
int subImageWidth = data.imageRaw().cols/data.stereoCameraModels().size();
|
||||
cv::Mat rectifiedLeftImages = data.imageRaw().clone();
|
||||
cv::Mat rectifiedRightImages = data.imageRaw().clone();
|
||||
for(size_t i=0; i<stereoModels_.size() && valid; ++i)
|
||||
{
|
||||
cv::Mat rectifiedLeft = stereoModels_[i].left().rectifyImage(cv::Mat(data.imageRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
|
||||
cv::Mat rectifiedRight = stereoModels_[i].right().rectifyImage(cv::Mat(data.rightRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, data.rightRaw().rows)));
|
||||
rectifiedLeft.copyTo(cv::Mat(rectifiedLeftImages, cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
|
||||
rectifiedRight.copyTo(cv::Mat(rectifiedRightImages, cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
|
||||
}
|
||||
data.setStereoImage(rectifiedLeftImages, rectifiedRightImages, stereoModels_, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!data.cameraModels().empty())
|
||||
@@ -599,12 +637,15 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
|
||||
}
|
||||
else
|
||||
{
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
std::vector<StereoCameraModel> stereoModels = decimatedData.stereoCameraModels();
|
||||
for(unsigned int i=0; i<stereoModels.size(); ++i)
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
stereoModels[i].scale(1.0/double(_imageDecimation));
|
||||
}
|
||||
if(!stereoModels.empty())
|
||||
{
|
||||
decimatedData.setStereoImage(rgbLeft, depthRight, stereoModels);
|
||||
}
|
||||
decimatedData.setStereoImage(rgbLeft, depthRight, stereoModel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ void OdometryThread::addData(const SensorData & data)
|
||||
{
|
||||
if(dynamic_cast<OdometryMono*>(_odometry) == 0)
|
||||
{
|
||||
if((data.imageRaw().empty() || data.depthOrRightRaw().empty() || (data.cameraModels().size()==0 && !data.stereoCameraModel().isValidForProjection())) &&
|
||||
if((data.imageRaw().empty() || data.depthOrRightRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty())) &&
|
||||
data.laserScanRaw().empty())
|
||||
{
|
||||
ULOGGER_ERROR("Missing some information (images/scans empty or missing calibration)!?");
|
||||
@@ -144,7 +144,7 @@ void OdometryThread::addData(const SensorData & data)
|
||||
else
|
||||
{
|
||||
// Mono can accept RGB only
|
||||
if(data.imageRaw().empty() || (data.cameraModels().size()==0 && !data.stereoCameraModel().isValidForProjection()))
|
||||
if(data.imageRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty()))
|
||||
{
|
||||
ULOGGER_ERROR("Missing some information (image empty or missing calibration)!?");
|
||||
return;
|
||||
|
||||
@@ -438,7 +438,7 @@ std::map<int, Transform> Optimizer::optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, CameraModel> & models,
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
std::set<int> * outliers)
|
||||
@@ -457,37 +457,35 @@ std::map<int, Transform> Optimizer::optimizeBA(
|
||||
bool rematchFeatures)
|
||||
{
|
||||
UDEBUG("");
|
||||
std::map<int, CameraModel> models;
|
||||
std::map<int, std::vector<CameraModel> > multiModels;
|
||||
std::map<int, Transform> poses;
|
||||
for(std::map<int, Transform>::const_iterator iter=posesIn.lower_bound(1); iter!=posesIn.end(); ++iter)
|
||||
{
|
||||
// Get camera model
|
||||
CameraModel model;
|
||||
std::vector<CameraModel> models;
|
||||
if(uContains(signatures, iter->first))
|
||||
{
|
||||
if(signatures.at(iter->first).sensorData().cameraModels().size() == 1 && signatures.at(iter->first).sensorData().cameraModels().at(0).isValidForProjection())
|
||||
const SensorData & s = signatures.at(iter->first).sensorData();
|
||||
if(s.cameraModels().size() >= 1 && s.cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = signatures.at(iter->first).sensorData().cameraModels()[0];
|
||||
models = s.cameraModels();
|
||||
}
|
||||
else if(signatures.at(iter->first).sensorData().stereoCameraModel().isValidForProjection())
|
||||
else if(!s.stereoCameraModels().empty() && s.stereoCameraModels()[0].isValidForProjection())
|
||||
{
|
||||
model = signatures.at(iter->first).sensorData().stereoCameraModel().left();
|
||||
for(size_t i=0; i<s.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel model = s.stereoCameraModels()[i].left();
|
||||
|
||||
// Set Tx = -baseline*fx for stereo BA
|
||||
model = CameraModel(
|
||||
model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-signatures.at(iter->first).sensorData().stereoCameraModel().baseline()*model.fx());
|
||||
}
|
||||
else if(signatures.at(iter->first).sensorData().cameraModels().size() > 1)
|
||||
{
|
||||
UERROR("Multi-cameras (%d) is not supported (id=%d).",
|
||||
signatures.at(iter->first).sensorData().cameraModels().size(),
|
||||
iter->first);
|
||||
return std::map<int, Transform>();
|
||||
// Set Tx = -baseline*fx for stereo BA
|
||||
models.push_back(CameraModel(
|
||||
model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-s.stereoCameraModels()[i].baseline()*model.fx(),
|
||||
model.imageSize()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -501,16 +499,14 @@ std::map<int, Transform> Optimizer::optimizeBA(
|
||||
return std::map<int, Transform>();
|
||||
}
|
||||
|
||||
UASSERT(model.isValidForProjection());
|
||||
|
||||
models.insert(std::make_pair(iter->first, model));
|
||||
multiModels.insert(std::make_pair(iter->first, models));
|
||||
poses.insert(*iter);
|
||||
}
|
||||
|
||||
// compute correspondences
|
||||
this->computeBACorrespondences(poses, links, signatures, points3DMap, wordReferences, rematchFeatures);
|
||||
|
||||
return optimizeBA(rootId, poses, links, models, points3DMap, wordReferences);
|
||||
return optimizeBA(rootId, poses, links, multiModels, points3DMap, wordReferences);
|
||||
}
|
||||
|
||||
std::map<int, Transform> Optimizer::optimizeBA(
|
||||
@@ -537,9 +533,11 @@ Transform Optimizer::optimizeBA(
|
||||
poses.insert(std::make_pair(link.to(), link.transform()));
|
||||
std::multimap<int, Link> links;
|
||||
links.insert(std::make_pair(link.from(), link));
|
||||
std::map<int, CameraModel> models;
|
||||
models.insert(std::make_pair(link.from(), model));
|
||||
models.insert(std::make_pair(link.to(), model));
|
||||
std::map<int, std::vector<CameraModel> > models;
|
||||
std::vector<CameraModel> tmp;
|
||||
tmp.push_back(model);
|
||||
models.insert(std::make_pair(link.from(), tmp));
|
||||
models.insert(std::make_pair(link.to(), tmp));
|
||||
poses = optimizeBA(link.from(), poses, links, models, points3DMap, wordReferences, outliers);
|
||||
if(poses.size() == 2)
|
||||
{
|
||||
@@ -567,7 +565,7 @@ void Optimizer::computeBACorrespondences(
|
||||
std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
bool rematchFeatures)
|
||||
{
|
||||
UDEBUG("");
|
||||
UDEBUG("rematchFeatures=%d", rematchFeatures?1:0);
|
||||
int wordCount = 0;
|
||||
int edgeWithWordsAdded = 0;
|
||||
std::map<int, std::map<cv::KeyPoint, int, KeyPointCompare> > frameToWordMap; // <FrameId, <Keypoint, wordId> >
|
||||
@@ -587,6 +585,14 @@ void Optimizer::computeBACorrespondences(
|
||||
if(sFrom.getWeight() >= 0) // ignore intermediate links
|
||||
{
|
||||
Signature sTo = signatures.at(link.to());
|
||||
|
||||
if((sFrom.sensorData().cameraModels().empty() && sFrom.sensorData().stereoCameraModels().empty()) ||
|
||||
(sTo.sensorData().cameraModels().empty() && sTo.sensorData().stereoCameraModels().empty()))
|
||||
{
|
||||
UERROR("No camera models found");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(sTo.getWeight() < 0)
|
||||
{
|
||||
for(std::multimap<int, Link>::const_iterator jter=links.find(sTo.id());
|
||||
@@ -675,8 +681,7 @@ void Optimizer::computeBACorrespondences(
|
||||
wordId = ++wordCount;
|
||||
wordReferences.insert(std::make_pair(wordId, std::map<int, FeatureBA>()));
|
||||
|
||||
p = util3d::transformPoint(p, pose);
|
||||
points3DMap.insert(std::make_pair(wordId, p));
|
||||
points3DMap.insert(std::make_pair(wordId, util3d::transformPoint(p, pose)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -692,7 +697,27 @@ void Optimizer::computeBACorrespondences(
|
||||
UASSERT(indexFrom < sFrom.getWordsDescriptors().rows);
|
||||
descriptorFrom = sFrom.getWordsDescriptors().row(indexFrom);
|
||||
}
|
||||
wordReferences.at(wordId).insert(std::make_pair(sFrom.id(), FeatureBA(ptFrom, p.x, descriptorFrom)));
|
||||
int cameraIndex = 0;
|
||||
if(sFrom.sensorData().cameraModels().size()>1 || sFrom.sensorData().stereoCameraModels().size()>1)
|
||||
{
|
||||
float subImageWidth = sFrom.sensorData().cameraModels().size()>1?sFrom.sensorData().cameraModels()[0].imageWidth():sFrom.sensorData().stereoCameraModels()[0].left().imageWidth();
|
||||
cameraIndex = int(ptFrom.pt.x / subImageWidth);
|
||||
ptFrom.pt.x = ptFrom.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
float depth = 0.0f;
|
||||
if(!sFrom.sensorData().cameraModels().empty())
|
||||
{
|
||||
depth = util3d::transformPoint(p, sFrom.sensorData().cameraModels()[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(!sFrom.sensorData().stereoCameraModels().empty());
|
||||
depth = util3d::transformPoint(p, sFrom.sensorData().stereoCameraModels()[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
|
||||
|
||||
wordReferences.at(wordId).insert(std::make_pair(sFrom.id(), FeatureBA(ptFrom, depth, descriptorFrom, cameraIndex)));
|
||||
frameToWordMap.insert(std::make_pair(sFrom.id(), std::map<cv::KeyPoint, int, KeyPointCompare>()));
|
||||
frameToWordMap.at(sFrom.id()).insert(std::make_pair(ptFrom, wordId));
|
||||
}
|
||||
@@ -705,17 +730,32 @@ void Optimizer::computeBACorrespondences(
|
||||
UASSERT(indexTo < sTo.getWordsDescriptors().rows);
|
||||
descriptorTo = sTo.getWordsDescriptors().row(indexTo);
|
||||
}
|
||||
|
||||
int cameraIndex = 0;
|
||||
if(sTo.sensorData().cameraModels().size()>1 || sTo.sensorData().stereoCameraModels().size()>1)
|
||||
{
|
||||
float subImageWidth = sTo.sensorData().cameraModels().size()>1?sTo.sensorData().cameraModels()[0].imageWidth():sTo.sensorData().stereoCameraModels()[0].left().imageWidth();
|
||||
cameraIndex = int(ptTo.pt.x / subImageWidth);
|
||||
ptTo.pt.x = ptTo.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
float depth = 0.0f;
|
||||
if(!sTo.getWords3().empty())
|
||||
{
|
||||
UASSERT(indexTo < (int)sTo.getWords3().size());
|
||||
const cv::Point3f & pt = sTo.getWords3()[indexTo];
|
||||
if( pt.x > 0)
|
||||
if(!sTo.sensorData().cameraModels().empty())
|
||||
{
|
||||
depth = pt.x;
|
||||
depth = util3d::transformPoint(pt, sTo.sensorData().cameraModels()[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(!sTo.sensorData().stereoCameraModels().empty());
|
||||
depth = util3d::transformPoint(pt, sTo.sensorData().stereoCameraModels()[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
}
|
||||
wordReferences.at(wordId).insert(std::make_pair(sTo.id(), FeatureBA(ptTo, depth, descriptorTo)));
|
||||
|
||||
wordReferences.at(wordId).insert(std::make_pair(sTo.id(), FeatureBA(ptTo, depth, descriptorTo, cameraIndex)));
|
||||
frameToWordMap.insert(std::make_pair(sTo.id(), std::map<cv::KeyPoint, int, KeyPointCompare>()));
|
||||
frameToWordMap.at(sTo.id()).insert(std::make_pair(ptTo, wordId));
|
||||
}
|
||||
@@ -732,6 +772,14 @@ void Optimizer::computeBACorrespondences(
|
||||
}
|
||||
}
|
||||
UDEBUG("Added %d words (edges with words=%d/%d)", wordCount, edgeWithWordsAdded, links.size());
|
||||
if(links.empty())
|
||||
{
|
||||
UERROR("No links found for BA?!");
|
||||
}
|
||||
else if(wordCount == 0)
|
||||
{
|
||||
UERROR("No words added for BA?!");
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -661,6 +661,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With OpenGV:";
|
||||
#ifdef RTABMAP_OPENGV
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With Madgwick:";
|
||||
#ifdef RTABMAP_MADGWICK
|
||||
|
||||
@@ -310,7 +310,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
fromSignature.sensorData().imageRaw().cols,
|
||||
fromSignature.sensorData().imageRaw().rows,
|
||||
(int)fromSignature.sensorData().cameraModels().size(),
|
||||
fromSignature.sensorData().stereoCameraModel().isValidForProjection()?1:0);
|
||||
(int)fromSignature.sensorData().stereoCameraModels().size());
|
||||
|
||||
UDEBUG("Input(%d): to=%d words, %d 3D words, %d words descriptors, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
|
||||
toSignature.id(),
|
||||
@@ -323,7 +323,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
toSignature.sensorData().imageRaw().cols,
|
||||
toSignature.sensorData().imageRaw().rows,
|
||||
(int)toSignature.sensorData().cameraModels().size(),
|
||||
toSignature.sensorData().stereoCameraModel().isValidForProjection()?1:0);
|
||||
(int)toSignature.sensorData().stereoCameraModels().size());
|
||||
|
||||
std::string msg;
|
||||
info.projectedIDs.clear();
|
||||
@@ -487,17 +487,24 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
bool guessSet = !guess.isIdentity() && !guess.isNull();
|
||||
if(guessSet)
|
||||
{
|
||||
Transform localTransform = fromSignature.sensorData().cameraModels().size()?fromSignature.sensorData().cameraModels()[0].localTransform():fromSignature.sensorData().stereoCameraModel().left().localTransform();
|
||||
Transform guessCameraRef = (guess * localTransform).inverse();
|
||||
cv::Mat R = (cv::Mat_<double>(3,3) <<
|
||||
(double)guessCameraRef.r11(), (double)guessCameraRef.r12(), (double)guessCameraRef.r13(),
|
||||
(double)guessCameraRef.r21(), (double)guessCameraRef.r22(), (double)guessCameraRef.r23(),
|
||||
(double)guessCameraRef.r31(), (double)guessCameraRef.r32(), (double)guessCameraRef.r33());
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Rodrigues(R, rvec);
|
||||
cv::Mat tvec = (cv::Mat_<double>(1,3) << (double)guessCameraRef.x(), (double)guessCameraRef.y(), (double)guessCameraRef.z());
|
||||
cv::Mat K = fromSignature.sensorData().cameraModels().size()?fromSignature.sensorData().cameraModels()[0].K():fromSignature.sensorData().stereoCameraModel().left().K();
|
||||
cv::projectPoints(kptsFrom3D, rvec, tvec, K, cv::Mat(), cornersTo);
|
||||
if(fromSignature.sensorData().cameraModels().size() == 1 || fromSignature.sensorData().cameraModels().size() == 1)
|
||||
{
|
||||
Transform localTransform = fromSignature.sensorData().cameraModels().size()?fromSignature.sensorData().cameraModels()[0].localTransform():fromSignature.sensorData().stereoCameraModels()[0].left().localTransform();
|
||||
Transform guessCameraRef = (guess * localTransform).inverse();
|
||||
cv::Mat R = (cv::Mat_<double>(3,3) <<
|
||||
(double)guessCameraRef.r11(), (double)guessCameraRef.r12(), (double)guessCameraRef.r13(),
|
||||
(double)guessCameraRef.r21(), (double)guessCameraRef.r22(), (double)guessCameraRef.r23(),
|
||||
(double)guessCameraRef.r31(), (double)guessCameraRef.r32(), (double)guessCameraRef.r33());
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Rodrigues(R, rvec);
|
||||
cv::Mat tvec = (cv::Mat_<double>(1,3) << (double)guessCameraRef.x(), (double)guessCameraRef.y(), (double)guessCameraRef.z());
|
||||
cv::Mat K = fromSignature.sensorData().cameraModels().size()?fromSignature.sensorData().cameraModels()[0].K():fromSignature.sensorData().stereoCameraModels()[0].left().K();
|
||||
cv::projectPoints(kptsFrom3D, rvec, tvec, K, cv::Mat(), cornersTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Optical flow guess with multi-cameras is not implemented, guess ignored...");
|
||||
}
|
||||
}
|
||||
|
||||
// Find features in the new left image
|
||||
@@ -735,7 +742,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
|
||||
if(!kptsFrom3D.empty() &&
|
||||
(_detectorFrom->getMinDepth() > 0.0f || _detectorFrom->getMaxDepth() > 0.0f) &&
|
||||
(!fromSignature.sensorData().cameraModels().empty() || fromSignature.sensorData().stereoCameraModel().isValidForProjection())) // Ignore local map from OdometryF2M
|
||||
(!fromSignature.sensorData().cameraModels().empty() || !fromSignature.sensorData().stereoCameraModels().empty())) // Ignore local map from OdometryF2M
|
||||
{
|
||||
_detectorFrom->filterKeypointsByDepth(kptsFrom, descriptorsFrom, kptsFrom3D, _detectorFrom->getMinDepth(), _detectorFrom->getMaxDepth());
|
||||
}
|
||||
@@ -770,7 +777,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
|
||||
if(kptsTo3D.size() &&
|
||||
(_detectorTo->getMinDepth() > 0.0f || _detectorTo->getMaxDepth() > 0.0f) &&
|
||||
(!toSignature.sensorData().cameraModels().empty() || toSignature.sensorData().stereoCameraModel().isValidForProjection())) // Ignore local map from OdometryF2M
|
||||
(!toSignature.sensorData().cameraModels().empty() || !toSignature.sensorData().stereoCameraModels().empty())) // Ignore local map from OdometryF2M
|
||||
{
|
||||
_detectorTo->filterKeypointsByDepth(kptsTo, descriptorsTo, kptsTo3D, _detectorTo->getMinDepth(), _detectorTo->getMaxDepth());
|
||||
}
|
||||
@@ -787,15 +794,37 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
// We have all data we need here, so match!
|
||||
if(descriptorsFrom.rows > 0 && descriptorsTo.rows > 0)
|
||||
{
|
||||
cv::Size imageSize = imageTo.size();
|
||||
bool isCalibrated = false; // multiple cameras not supported.
|
||||
if(imageSize.height == 0 || imageSize.width == 0)
|
||||
std::vector<CameraModel> models;
|
||||
if(!toSignature.sensorData().stereoCameraModels().empty())
|
||||
{
|
||||
imageSize = toSignature.sensorData().cameraModels().size() == 1?toSignature.sensorData().cameraModels()[0].imageSize():toSignature.sensorData().stereoCameraModel().left().imageSize();
|
||||
for(size_t i=0; i<toSignature.sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
models.push_back(toSignature.sensorData().stereoCameraModels()[i].left());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
models = toSignature.sensorData().cameraModels();
|
||||
}
|
||||
|
||||
isCalibrated = imageSize.height != 0 && imageSize.width != 0 &&
|
||||
(toSignature.sensorData().cameraModels().size()==1?toSignature.sensorData().cameraModels()[0].isValidForProjection():toSignature.sensorData().stereoCameraModel().isValidForProjection());
|
||||
bool isCalibrated = !models.empty();
|
||||
for(size_t i=0; i<models.size() && isCalibrated; ++i)
|
||||
{
|
||||
isCalibrated = models[i].isValidForProjection();
|
||||
|
||||
// For old database formats
|
||||
if(isCalibrated && (models[i].imageWidth()==0 || models[i].imageHeight()==0))
|
||||
{
|
||||
if(!toSignature.sensorData().imageRaw().empty())
|
||||
{
|
||||
models[i].setImageSize(cv::Size(toSignature.sensorData().imageRaw().cols/models.size(), toSignature.sensorData().imageRaw().rows));
|
||||
}
|
||||
else
|
||||
{
|
||||
isCalibrated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If guess is set, limit the search of matches using optical flow window size
|
||||
bool guessSet = !guess.isIdentity() && !guess.isNull();
|
||||
@@ -803,52 +832,62 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
isCalibrated && // needed for projection
|
||||
_estimationType != 2) // To make sure we match all features for 2D->2D
|
||||
{
|
||||
// Use guess to project 3D "from" keypoints into "to" image
|
||||
UDEBUG("");
|
||||
UASSERT((int)kptsTo.size() == descriptorsTo.rows);
|
||||
UASSERT((int)kptsFrom3D.size() == descriptorsFrom.rows);
|
||||
|
||||
// Use guess to project 3D "from" keypoints into "to" image
|
||||
if(toSignature.sensorData().cameraModels().size() > 1)
|
||||
std::vector<cv::Point2f> cornersProjected;
|
||||
std::vector<int> projectedIndexToDescIndex;
|
||||
float subImageWidth = models[0].imageWidth();
|
||||
std::set<int> added;
|
||||
int duplicates=0;
|
||||
for(size_t m=0; m<models.size(); ++m)
|
||||
{
|
||||
UFATAL("Guess reprojection feature matching is not supported for multiple cameras.");
|
||||
}
|
||||
Transform guessCameraRef = (guess * models[m].localTransform()).inverse();
|
||||
cv::Mat R = (cv::Mat_<double>(3,3) <<
|
||||
(double)guessCameraRef.r11(), (double)guessCameraRef.r12(), (double)guessCameraRef.r13(),
|
||||
(double)guessCameraRef.r21(), (double)guessCameraRef.r22(), (double)guessCameraRef.r23(),
|
||||
(double)guessCameraRef.r31(), (double)guessCameraRef.r32(), (double)guessCameraRef.r33());
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Rodrigues(R, rvec);
|
||||
cv::Mat tvec = (cv::Mat_<double>(1,3) << (double)guessCameraRef.x(), (double)guessCameraRef.y(), (double)guessCameraRef.z());
|
||||
cv::Mat K = models[m].K();
|
||||
std::vector<cv::Point2f> projected;
|
||||
cv::projectPoints(kptsFrom3D, rvec, tvec, K, cv::Mat(), projected);
|
||||
UDEBUG("Projected points=%d", (int)projected.size());
|
||||
|
||||
Transform localTransform = toSignature.sensorData().cameraModels().size()?toSignature.sensorData().cameraModels()[0].localTransform():toSignature.sensorData().stereoCameraModel().left().localTransform();
|
||||
Transform guessCameraRef = (guess * localTransform).inverse();
|
||||
cv::Mat R = (cv::Mat_<double>(3,3) <<
|
||||
(double)guessCameraRef.r11(), (double)guessCameraRef.r12(), (double)guessCameraRef.r13(),
|
||||
(double)guessCameraRef.r21(), (double)guessCameraRef.r22(), (double)guessCameraRef.r23(),
|
||||
(double)guessCameraRef.r31(), (double)guessCameraRef.r32(), (double)guessCameraRef.r33());
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Rodrigues(R, rvec);
|
||||
cv::Mat tvec = (cv::Mat_<double>(1,3) << (double)guessCameraRef.x(), (double)guessCameraRef.y(), (double)guessCameraRef.z());
|
||||
cv::Mat K = toSignature.sensorData().cameraModels().size()?toSignature.sensorData().cameraModels()[0].K():toSignature.sensorData().stereoCameraModel().left().K();
|
||||
std::vector<cv::Point2f> projected;
|
||||
cv::projectPoints(kptsFrom3D, rvec, tvec, K, cv::Mat(), projected);
|
||||
UDEBUG("Projected points=%d", (int)projected.size());
|
||||
//remove projected points outside of the image
|
||||
UASSERT((int)projected.size() == descriptorsFrom.rows);
|
||||
std::vector<cv::Point2f> cornersProjected(projected.size());
|
||||
std::vector<int> projectedIndexToDescIndex(projected.size());
|
||||
int oi=0;
|
||||
for(unsigned int i=0; i<projected.size(); ++i)
|
||||
{
|
||||
if(uIsInBounds(projected[i].x, 0.0f, float(imageSize.width-1)) &&
|
||||
uIsInBounds(projected[i].y, 0.0f, float(imageSize.height-1)) &&
|
||||
util3d::transformPoint(kptsFrom3D[i], guessCameraRef).z > 0.0)
|
||||
//remove projected points outside of the image
|
||||
UASSERT((int)projected.size() == descriptorsFrom.rows);
|
||||
int cornersInFrame = 0;
|
||||
for(unsigned int i=0; i<projected.size(); ++i)
|
||||
{
|
||||
projectedIndexToDescIndex[oi] = i;
|
||||
cornersProjected[oi++] = projected[i];
|
||||
if(uIsInBounds(projected[i].x, 0.0f, float(models[m].imageWidth()-1)) &&
|
||||
uIsInBounds(projected[i].y, 0.0f, float(models[m].imageHeight()-1)) &&
|
||||
util3d::transformPoint(kptsFrom3D[i], guessCameraRef).z > 0.0)
|
||||
{
|
||||
if(added.find(i) != added.end())
|
||||
{
|
||||
++duplicates;
|
||||
continue;
|
||||
}
|
||||
|
||||
projectedIndexToDescIndex.push_back(i);
|
||||
projected[i].x += subImageWidth*float(m); // Convert in multicam stitched image
|
||||
cornersProjected.push_back(projected[i]);
|
||||
++cornersInFrame;
|
||||
added.insert(i);
|
||||
}
|
||||
}
|
||||
UDEBUG("corners in frame=%d (camera index=%ld)", cornersInFrame, m);
|
||||
}
|
||||
projectedIndexToDescIndex.resize(oi);
|
||||
cornersProjected.resize(oi);
|
||||
UDEBUG("corners in frame=%d", (int)cornersProjected.size());
|
||||
|
||||
// For each projected feature guess of "from" in "to", find its matching feature in
|
||||
// the radius around the projected guess.
|
||||
// TODO: do cross-check?
|
||||
UDEBUG("guessMatchToProjection=%d, cornersProjected=%d", _guessMatchToProjection?1:0, (int)cornersProjected.size());
|
||||
UDEBUG("guessMatchToProjection=%d, cornersProjected=%d orignalWordsFromIds=%d (added=%ld, duplicates=%d)",
|
||||
_guessMatchToProjection?1:0, (int)cornersProjected.size(), (int)orignalWordsFromIds.size(),
|
||||
added.size(), duplicates);
|
||||
if(cornersProjected.size())
|
||||
{
|
||||
if(_guessMatchToProjection)
|
||||
@@ -1147,19 +1186,9 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
{
|
||||
if(guessSet && _guessWinSize > 0 && kptsFrom3D.size() && !isCalibrated)
|
||||
{
|
||||
if(fromSignature.sensorData().cameraModels().size() > 1 || toSignature.sensorData().cameraModels().size() > 1)
|
||||
{
|
||||
UWARN("Finding correspondences with the guess cannot "
|
||||
"be done with multiple cameras, global matching is "
|
||||
"done instead. Please set \"%s\" to 0 to avoid this warning.",
|
||||
Parameters::kVisCorGuessWinSize().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Calibration not found! Finding correspondences "
|
||||
"with the guess cannot be done, global matching is "
|
||||
"done instead.");
|
||||
}
|
||||
UWARN("Calibration not found! Finding correspondences "
|
||||
"with the guess cannot be done, global matching is "
|
||||
"done instead.");
|
||||
}
|
||||
|
||||
UDEBUG("");
|
||||
@@ -1194,16 +1223,16 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
descriptorsTo.type() == CV_32F &&
|
||||
descriptorsFrom.type() == CV_32F &&
|
||||
descriptorsFrom.rows == (int)kptsFrom.size() &&
|
||||
imageSize.width > 0 && imageSize.height > 0)
|
||||
models.size() == 1)
|
||||
{
|
||||
UDEBUG("Python matching");
|
||||
matches = _pyMatcher->match(descriptorsTo, descriptorsFrom, kptsTo, kptsFrom, imageSize);
|
||||
matches = _pyMatcher->match(descriptorsTo, descriptorsFrom, kptsTo, kptsFrom, models[0].imageSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_nnType == 6 && _pyMatcher)
|
||||
{
|
||||
UDEBUG("Invalid inputs for Python matching (desc type=%d, only float descriptors supported), doing bruteforce matching instead.", descriptorsFrom.type());
|
||||
UDEBUG("Invalid inputs for Python matching (desc type=%d, only float descriptors supported, multicam not supported), doing bruteforce matching instead.", descriptorsFrom.type());
|
||||
}
|
||||
#else
|
||||
{
|
||||
@@ -1215,11 +1244,11 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
if(_nnType == 7)
|
||||
{
|
||||
imageSizeFrom = imageFrom.size();
|
||||
if(imageSizeFrom.height == 0 || imageSizeFrom.width == 0)
|
||||
if((imageSizeFrom.height == 0 || imageSizeFrom.width == 0) && (fromSignature.sensorData().cameraModels().size() || fromSignature.sensorData().stereoCameraModels().size()))
|
||||
{
|
||||
imageSizeFrom = fromSignature.sensorData().cameraModels().size() == 1?fromSignature.sensorData().cameraModels()[0].imageSize():fromSignature.sensorData().stereoCameraModel().left().imageSize();
|
||||
imageSizeFrom = fromSignature.sensorData().cameraModels().size() == 1?fromSignature.sensorData().cameraModels()[0].imageSize():fromSignature.sensorData().stereoCameraModels()[0].left().imageSize();
|
||||
}
|
||||
if(imageSize.height > 0 && imageSize.width > 0 &&
|
||||
if(!models.empty() && models[0].imageSize().height > 0 && models[0].imageSize().width > 0 &&
|
||||
imageSizeFrom.height > 0 && imageSizeFrom.width > 0)
|
||||
{
|
||||
doCrossCheck = false;
|
||||
@@ -1239,8 +1268,9 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
#if defined(HAVE_OPENCV_XFEATURES2D) && (CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION==3 && CV_MINOR_VERSION >=4 && CV_SUBMINOR_VERSION >= 1))
|
||||
if(!doCrossCheck)
|
||||
{
|
||||
UASSERT(!models.empty());
|
||||
std::vector<cv::DMatch> matchesGMS;
|
||||
cv::xfeatures2d::matchGMS(imageSize, imageSizeFrom, kptsTo, kptsFrom, matches, matchesGMS, _gmsWithRotation, _gmsWithScale, _gmsThresholdFactor);
|
||||
cv::xfeatures2d::matchGMS(models[0].imageSize(), imageSizeFrom, kptsTo, kptsFrom, matches, matchesGMS, _gmsWithRotation, _gmsWithScale, _gmsThresholdFactor);
|
||||
matches = matchesGMS;
|
||||
}
|
||||
#endif
|
||||
@@ -1385,7 +1415,8 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
if(_estimationType == 2) // Epipolar Geometry
|
||||
{
|
||||
UDEBUG("");
|
||||
if(!signatureB->sensorData().stereoCameraModel().isValidForProjection() &&
|
||||
if((signatureB->sensorData().stereoCameraModels().size() != 1 ||
|
||||
!signatureB->sensorData().stereoCameraModels()[0].isValidForProjection()) &&
|
||||
(signatureB->sensorData().cameraModels().size() != 1 ||
|
||||
!signatureB->sensorData().cameraModels()[0].isValidForProjection()))
|
||||
{
|
||||
@@ -1394,8 +1425,8 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
else if((int)signatureA->getWords().size() >= _minInliers &&
|
||||
(int)signatureB->getWords().size() >= _minInliers)
|
||||
{
|
||||
UASSERT(signatureA->sensorData().stereoCameraModel().isValidForProjection() || (signatureA->sensorData().cameraModels().size() == 1 && signatureA->sensorData().cameraModels()[0].isValidForProjection()));
|
||||
const CameraModel & cameraModel = signatureA->sensorData().stereoCameraModel().isValidForProjection()?signatureA->sensorData().stereoCameraModel().left():signatureA->sensorData().cameraModels()[0];
|
||||
UASSERT((signatureA->sensorData().stereoCameraModels().size() == 1 && signatureA->sensorData().stereoCameraModels()[0].isValidForProjection()) || (signatureA->sensorData().cameraModels().size() == 1 && signatureA->sensorData().cameraModels()[0].isValidForProjection()));
|
||||
const CameraModel & cameraModel = signatureA->sensorData().stereoCameraModels().size()?signatureA->sensorData().stereoCameraModels()[0].left():signatureA->sensorData().cameraModels()[0];
|
||||
|
||||
// we only need the camera transform, send guess words3 for scale estimation
|
||||
Transform cameraTransform;
|
||||
@@ -1479,16 +1510,22 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
else if(_estimationType == 1) // PnP
|
||||
{
|
||||
UDEBUG("");
|
||||
if(!signatureB->sensorData().stereoCameraModel().isValidForProjection() &&
|
||||
(signatureB->sensorData().cameraModels().size() != 1 ||
|
||||
!signatureB->sensorData().cameraModels()[0].isValidForProjection()))
|
||||
if((signatureB->sensorData().stereoCameraModels().empty() || !signatureB->sensorData().stereoCameraModels()[0].isValidForProjection()) &&
|
||||
(signatureB->sensorData().cameraModels().empty() || !signatureB->sensorData().cameraModels()[0].isValidForProjection()))
|
||||
{
|
||||
UERROR("Calibrated camera required (multi-cameras not supported). Id=%d Models=%d StereoModel=%d weight=%d",
|
||||
UERROR("Calibrated camera required. Id=%d Models=%d StereoModels=%d weight=%d",
|
||||
signatureB->id(),
|
||||
(int)signatureB->sensorData().cameraModels().size(),
|
||||
signatureB->sensorData().stereoCameraModel().isValidForProjection()?1:0,
|
||||
signatureB->sensorData().stereoCameraModels().size(),
|
||||
signatureB->getWeight());
|
||||
}
|
||||
#ifndef RTABMAP_OPENGV
|
||||
else if(signatureB->sensorData().cameraModels().size() > 1)
|
||||
{
|
||||
UERROR("Multi-camera 2D-3D PnP registration is only available if rtabmap is built "
|
||||
"with OpenGV dependency. Use 3D-3D registration approach instead for multi-camera.");
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
UDEBUG("words from3D=%d to2D=%d", (int)signatureA->getWords3().size(), (int)signatureB->getWords().size());
|
||||
@@ -1496,9 +1533,6 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
if((int)signatureA->getWords3().size() >= _minInliers &&
|
||||
(int)signatureB->getWords().size() >= _minInliers)
|
||||
{
|
||||
UASSERT(signatureB->sensorData().stereoCameraModel().isValidForProjection() || (signatureB->sensorData().cameraModels().size() == 1 && signatureB->sensorData().cameraModels()[0].isValidForProjection()));
|
||||
const CameraModel & cameraModel = signatureB->sensorData().stereoCameraModel().isValidForProjection()?signatureB->sensorData().stereoCameraModel().left():signatureB->sensorData().cameraModels()[0];
|
||||
|
||||
std::vector<int> inliersV;
|
||||
std::vector<int> matchesV;
|
||||
std::map<int, int> uniqueWordsA = uMultimapToMapUnique(signatureA->getWords());
|
||||
@@ -1518,22 +1552,63 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
words3B.insert(std::make_pair(iter->first, signatureB->getWords3()[iter->second]));
|
||||
}
|
||||
}
|
||||
transforms[dir] = util3d::estimateMotion3DTo2D(
|
||||
words3A,
|
||||
wordsB,
|
||||
cameraModel,
|
||||
_minInliers,
|
||||
_iterations,
|
||||
_PnPReprojError,
|
||||
_PnPFlags,
|
||||
_PnPRefineIterations,
|
||||
dir==0?(!guess.isNull()?guess:Transform::getIdentity()):!transforms[0].isNull()?transforms[0].inverse():(!guess.isNull()?guess.inverse():Transform::getIdentity()),
|
||||
words3B,
|
||||
&covariances[dir],
|
||||
&matchesV,
|
||||
&inliersV);
|
||||
inliers[dir] = inliersV;
|
||||
matches[dir] = matchesV;
|
||||
|
||||
std::vector<CameraModel> models;
|
||||
if(signatureB->sensorData().stereoCameraModels().size())
|
||||
{
|
||||
for(size_t i=0; i<signatureB->sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
models.push_back(signatureB->sensorData().stereoCameraModels()[i].left());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
models = signatureB->sensorData().cameraModels();
|
||||
}
|
||||
|
||||
if(models.size()>1)
|
||||
{
|
||||
// Multi-Camera
|
||||
UASSERT(models[0].isValidForProjection());
|
||||
|
||||
transforms[dir] = util3d::estimateMotion3DTo2D(
|
||||
words3A,
|
||||
wordsB,
|
||||
models,
|
||||
_minInliers,
|
||||
_iterations,
|
||||
_PnPReprojError,
|
||||
_PnPFlags,
|
||||
_PnPRefineIterations,
|
||||
dir==0?(!guess.isNull()?guess:Transform::getIdentity()):!transforms[0].isNull()?transforms[0].inverse():(!guess.isNull()?guess.inverse():Transform::getIdentity()),
|
||||
words3B,
|
||||
&covariances[dir],
|
||||
&matchesV,
|
||||
&inliersV);
|
||||
inliers[dir] = inliersV;
|
||||
matches[dir] = matchesV;
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(models.size() == 1 && models[0].isValidForProjection());
|
||||
|
||||
transforms[dir] = util3d::estimateMotion3DTo2D(
|
||||
words3A,
|
||||
wordsB,
|
||||
models[0],
|
||||
_minInliers,
|
||||
_iterations,
|
||||
_PnPReprojError,
|
||||
_PnPFlags,
|
||||
_PnPRefineIterations,
|
||||
dir==0?(!guess.isNull()?guess:Transform::getIdentity()):!transforms[0].isNull()?transforms[0].inverse():(!guess.isNull()?guess.inverse():Transform::getIdentity()),
|
||||
words3B,
|
||||
&covariances[dir],
|
||||
&matchesV,
|
||||
&inliersV);
|
||||
inliers[dir] = inliersV;
|
||||
matches[dir] = matchesV;
|
||||
}
|
||||
UDEBUG("inliers: %d/%d", (int)inliersV.size(), (int)matchesV.size());
|
||||
if(transforms[dir].isNull())
|
||||
{
|
||||
@@ -1652,8 +1727,8 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
allInliers.size() &&
|
||||
fromSignature.getWords3().size() &&
|
||||
toSignature.getWords().size() &&
|
||||
fromSignature.sensorData().cameraModels().size() <= 1 &&
|
||||
toSignature.sensorData().cameraModels().size() <= 1)
|
||||
(fromSignature.sensorData().stereoCameraModels().size() >= 1 || fromSignature.sensorData().cameraModels().size() >= 1) &&
|
||||
(toSignature.sensorData().stereoCameraModels().size() >= 1 || toSignature.sensorData().cameraModels().size() >= 1))
|
||||
{
|
||||
UDEBUG("Refine with bundle adjustment");
|
||||
Optimizer * sba = Optimizer::create(_bundleAdjustment==3?Optimizer::kTypeCeres:_bundleAdjustment==2?Optimizer::kTypeCVSBA:Optimizer::kTypeG2O, _bundleParameters);
|
||||
@@ -1693,60 +1768,61 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
|
||||
std::map<int, Transform> optimizedPoses;
|
||||
|
||||
UASSERT(toSignature.sensorData().stereoCameraModel().isValidForProjection() ||
|
||||
(toSignature.sensorData().cameraModels().size() == 1 && toSignature.sensorData().cameraModels()[0].isValidForProjection()));
|
||||
UASSERT((toSignature.sensorData().stereoCameraModels().size() >= 1 && toSignature.sensorData().stereoCameraModels()[0].isValidForProjection()) ||
|
||||
(toSignature.sensorData().cameraModels().size() >= 1 && toSignature.sensorData().cameraModels()[0].isValidForProjection()));
|
||||
|
||||
std::map<int, CameraModel> models;
|
||||
std::map<int, std::vector<CameraModel> > models;
|
||||
|
||||
Transform invLocalTransformFrom;
|
||||
CameraModel cameraModelFrom;
|
||||
if(fromSignature.sensorData().stereoCameraModel().isValidForProjection())
|
||||
std::vector<CameraModel> cameraModelsFrom;
|
||||
if(fromSignature.sensorData().stereoCameraModels().size())
|
||||
{
|
||||
cameraModelFrom = fromSignature.sensorData().stereoCameraModel().left();
|
||||
// Set Tx=-baseline*fx for Stereo BA
|
||||
cameraModelFrom = CameraModel(cameraModelFrom.fx(),
|
||||
cameraModelFrom.fy(),
|
||||
cameraModelFrom.cx(),
|
||||
cameraModelFrom.cy(),
|
||||
cameraModelFrom.localTransform(),
|
||||
-fromSignature.sensorData().stereoCameraModel().baseline()*cameraModelFrom.fy());
|
||||
invLocalTransformFrom = toSignature.sensorData().stereoCameraModel().localTransform().inverse();
|
||||
for(size_t i=0; i<fromSignature.sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel cameraModel = fromSignature.sensorData().stereoCameraModels()[i].left();
|
||||
// Set Tx=-baseline*fx for Stereo BA
|
||||
cameraModel = CameraModel(cameraModel.fx(),
|
||||
cameraModel.fy(),
|
||||
cameraModel.cx(),
|
||||
cameraModel.cy(),
|
||||
cameraModel.localTransform(),
|
||||
-fromSignature.sensorData().stereoCameraModels()[0].baseline()*cameraModel.fx(),
|
||||
cameraModel.imageSize());
|
||||
cameraModelsFrom.push_back(cameraModel);
|
||||
}
|
||||
}
|
||||
else if(fromSignature.sensorData().cameraModels().size() == 1)
|
||||
else
|
||||
{
|
||||
cameraModelFrom = fromSignature.sensorData().cameraModels()[0];
|
||||
invLocalTransformFrom = toSignature.sensorData().cameraModels()[0].localTransform().inverse();
|
||||
cameraModelsFrom = fromSignature.sensorData().cameraModels();
|
||||
}
|
||||
|
||||
Transform invLocalTransformTo = Transform::getIdentity();
|
||||
CameraModel cameraModelTo;
|
||||
if(toSignature.sensorData().stereoCameraModel().isValidForProjection())
|
||||
std::vector<CameraModel> cameraModelsTo;
|
||||
if(toSignature.sensorData().stereoCameraModels().size())
|
||||
{
|
||||
cameraModelTo = toSignature.sensorData().stereoCameraModel().left();
|
||||
// Set Tx=-baseline*fx for Stereo BA
|
||||
cameraModelTo = CameraModel(cameraModelTo.fx(),
|
||||
cameraModelTo.fy(),
|
||||
cameraModelTo.cx(),
|
||||
cameraModelTo.cy(),
|
||||
cameraModelTo.localTransform(),
|
||||
-toSignature.sensorData().stereoCameraModel().baseline()*cameraModelTo.fy());
|
||||
invLocalTransformTo = toSignature.sensorData().stereoCameraModel().localTransform().inverse();
|
||||
for(size_t i=0; i<toSignature.sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel cameraModel = toSignature.sensorData().stereoCameraModels()[i].left();
|
||||
// Set Tx=-baseline*fx for Stereo BA
|
||||
cameraModel = CameraModel(cameraModel.fx(),
|
||||
cameraModel.fy(),
|
||||
cameraModel.cx(),
|
||||
cameraModel.cy(),
|
||||
cameraModel.localTransform(),
|
||||
-toSignature.sensorData().stereoCameraModels()[0].baseline()*cameraModel.fx(),
|
||||
cameraModel.imageSize());
|
||||
cameraModelsTo.push_back(cameraModel);
|
||||
}
|
||||
}
|
||||
else if(toSignature.sensorData().cameraModels().size() == 1)
|
||||
else
|
||||
{
|
||||
cameraModelTo = toSignature.sensorData().cameraModels()[0];
|
||||
invLocalTransformTo = toSignature.sensorData().cameraModels()[0].localTransform().inverse();
|
||||
}
|
||||
if(invLocalTransformFrom.isNull())
|
||||
{
|
||||
invLocalTransformFrom = invLocalTransformTo;
|
||||
cameraModelsTo = toSignature.sensorData().cameraModels();
|
||||
}
|
||||
|
||||
models.insert(std::make_pair(1, cameraModelFrom.isValidForProjection()?cameraModelFrom:cameraModelTo));
|
||||
models.insert(std::make_pair(2, cameraModelTo));
|
||||
models.insert(std::make_pair(1, cameraModelsFrom));
|
||||
models.insert(std::make_pair(2, cameraModelsTo));
|
||||
|
||||
std::map<int, std::map<int, FeatureBA> > wordReferences;
|
||||
std::set<int> sbaOutliers;
|
||||
UDEBUG("");
|
||||
for(unsigned int i=0; i<allInliers.size(); ++i)
|
||||
{
|
||||
int wordId = allInliers[i];
|
||||
@@ -1762,22 +1838,50 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
points3DMap.insert(std::make_pair(wordId, pt3D));
|
||||
|
||||
std::map<int, FeatureBA> ptMap;
|
||||
if(!fromSignature.getWordsKpts().empty() && cameraModelFrom.isValidForProjection())
|
||||
if(!fromSignature.getWordsKpts().empty())
|
||||
{
|
||||
float depthFrom = util3d::transformPoint(pt3D, invLocalTransformFrom).z;
|
||||
const cv::KeyPoint & kpt = fromSignature.getWordsKpts()[indexFrom];
|
||||
ptMap.insert(std::make_pair(1,FeatureBA(kpt, depthFrom)));
|
||||
cv::KeyPoint kpt = fromSignature.getWordsKpts()[indexFrom];
|
||||
|
||||
int cameraIndex = 0;
|
||||
const std::vector<CameraModel> & cam = models.at(1);
|
||||
if(cam.size()>1)
|
||||
{
|
||||
UASSERT(cam[0].imageWidth()>0);
|
||||
float subImageWidth = cam[0].imageWidth();
|
||||
cameraIndex = int(kpt.pt.x / subImageWidth);
|
||||
kpt.pt.x = kpt.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
UASSERT(cam[cameraIndex].isValidForProjection());
|
||||
|
||||
float depthFrom = util3d::transformPoint(pt3D, cam[cameraIndex].localTransform().inverse()).z;
|
||||
ptMap.insert(std::make_pair(1,FeatureBA(kpt, depthFrom, cv::Mat(), cameraIndex)));
|
||||
}
|
||||
if(!toSignature.getWordsKpts().empty() && cameraModelTo.isValidForProjection())
|
||||
|
||||
if(!toSignature.getWordsKpts().empty())
|
||||
{
|
||||
int indexTo = toSignature.getWords().find(wordId)->second;
|
||||
cv::KeyPoint kpt = toSignature.getWordsKpts()[indexTo];
|
||||
|
||||
int cameraIndex = 0;
|
||||
const std::vector<CameraModel> & cam = models.at(2);
|
||||
if(cam.size()>1)
|
||||
{
|
||||
UASSERT(cam[0].imageWidth()>0);
|
||||
float subImageWidth = cam[0].imageWidth();
|
||||
cameraIndex = int(kpt.pt.x / subImageWidth);
|
||||
kpt.pt.x = kpt.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
UASSERT(cam[cameraIndex].isValidForProjection());
|
||||
|
||||
float depthTo = 0.0f;
|
||||
if(!toSignature.getWords3().empty())
|
||||
{
|
||||
depthTo = util3d::transformPoint(toSignature.getWords3()[indexTo], invLocalTransformTo).z;
|
||||
depthTo = util3d::transformPoint(toSignature.getWords3()[indexTo], cam[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
const cv::KeyPoint & kpt = toSignature.getWordsKpts()[indexTo];
|
||||
ptMap.insert(std::make_pair(2,FeatureBA(kpt, depthTo)));
|
||||
|
||||
ptMap.insert(std::make_pair(2,FeatureBA(kpt, depthTo, cv::Mat(), cameraIndex)));
|
||||
}
|
||||
|
||||
wordReferences.insert(std::make_pair(wordId, ptMap));
|
||||
@@ -1876,10 +1980,10 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
float cx=0, cy=0, w=0, h=0;
|
||||
if(_minInliersDistributionThr > 0)
|
||||
{
|
||||
if(toSignature.sensorData().stereoCameraModel().isValidForProjection() ||
|
||||
if((toSignature.sensorData().stereoCameraModels().size() == 1 && toSignature.sensorData().stereoCameraModels()[0].isValidForProjection()) ||
|
||||
(toSignature.sensorData().cameraModels().size() == 1 && toSignature.sensorData().cameraModels()[0].isValidForReprojection()))
|
||||
{
|
||||
const CameraModel & cameraModel = toSignature.sensorData().stereoCameraModel().isValidForProjection()?toSignature.sensorData().stereoCameraModel().left():toSignature.sensorData().cameraModels()[0];
|
||||
const CameraModel & cameraModel = toSignature.sensorData().stereoCameraModels().size()?toSignature.sensorData().stereoCameraModels()[0].left():toSignature.sensorData().cameraModels()[0];
|
||||
cx = cameraModel.cx();
|
||||
cy = cameraModel.cy();
|
||||
w = cameraModel.imageWidth();
|
||||
@@ -1894,7 +1998,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
UERROR("Invalid calibration image size (%dx%d), cannot compute inliers distribution! (see %s=%f)", w, h, Parameters::kVisMinInliersDistribution().c_str(), _minInliersDistributionThr);
|
||||
}
|
||||
}
|
||||
else if(toSignature.sensorData().cameraModels().size() > 1)
|
||||
else if(toSignature.sensorData().cameraModels().size() > 1 || toSignature.sensorData().stereoCameraModels().size() > 1)
|
||||
{
|
||||
UERROR("Multi-camera not supported when computing inliers distribution! (see %s=%f)", Parameters::kVisMinInliersDistribution().c_str(), _minInliersDistributionThr);
|
||||
}
|
||||
|
||||
@@ -5007,10 +5007,10 @@ Signature Rtabmap::getSignatureCopy(int id, bool images, bool scan, bool userDat
|
||||
if(!images && withWords)
|
||||
{
|
||||
std::vector<CameraModel> models;
|
||||
StereoCameraModel stereoModel;
|
||||
_memory->getNodeCalibration(id, models, stereoModel);
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
_memory->getNodeCalibration(id, models, stereoModels);
|
||||
data.setCameraModels(models);
|
||||
data.setStereoCameraModel(stereoModel);
|
||||
data.setStereoCameraModels(stereoModels);
|
||||
}
|
||||
|
||||
s=Signature(id,
|
||||
|
||||
@@ -175,6 +175,40 @@ SensorData::SensorData(
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Multi-Stereo constructor
|
||||
SensorData::SensorData(
|
||||
const cv::Mat & left,
|
||||
const cv::Mat & right,
|
||||
const std::vector<StereoCameraModel> & cameraModels,
|
||||
int id,
|
||||
double stamp,
|
||||
const cv::Mat & userData):
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
setStereoImage(left, right, cameraModels);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Multi-Stereo constructor + 2d laser scan
|
||||
SensorData::SensorData(
|
||||
const LaserScan & laserScan,
|
||||
const cv::Mat & left,
|
||||
const cv::Mat & right,
|
||||
const std::vector<StereoCameraModel> & cameraModels,
|
||||
int id,
|
||||
double stamp,
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
setStereoImage(left, right, cameraModels);
|
||||
setLaserScan(laserScan);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
SensorData::SensorData(
|
||||
const IMU & imu,
|
||||
int id,
|
||||
@@ -206,16 +240,16 @@ void SensorData::setRGBDImage(
|
||||
const std::vector<CameraModel> & models,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
if(!clearPreviousData && _stereoCameraModel.isValidForProjection())
|
||||
if(!clearPreviousData && !_stereoCameraModels.empty())
|
||||
{
|
||||
UERROR("Sensor data has previously stereo images "
|
||||
"but clearPreviousData parameter is false. We "
|
||||
"will still clear previous data to avoid incompatibilities "
|
||||
"between raw and compressed data!");
|
||||
}
|
||||
bool clearData = clearPreviousData || _stereoCameraModel.isValidForProjection();
|
||||
bool clearData = clearPreviousData || !_stereoCameraModels.empty();
|
||||
|
||||
_stereoCameraModel = StereoCameraModel();
|
||||
_stereoCameraModels.clear();
|
||||
_cameraModels = models;
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
@@ -272,6 +306,16 @@ void SensorData::setStereoImage(
|
||||
const cv::Mat & right,
|
||||
const StereoCameraModel & stereoCameraModel,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
std::vector<StereoCameraModel> models;
|
||||
models.push_back(stereoCameraModel);
|
||||
setStereoImage(left, right, models, clearPreviousData);
|
||||
}
|
||||
void SensorData::setStereoImage(
|
||||
const cv::Mat & left,
|
||||
const cv::Mat & right,
|
||||
const std::vector<StereoCameraModel> & stereoCameraModels,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
if(!clearPreviousData && !_cameraModels.empty())
|
||||
{
|
||||
@@ -283,7 +327,7 @@ void SensorData::setStereoImage(
|
||||
bool clearData = clearPreviousData || !_cameraModels.empty();
|
||||
|
||||
_cameraModels.clear();
|
||||
_stereoCameraModel = stereoCameraModel;
|
||||
_stereoCameraModels = stereoCameraModels;
|
||||
|
||||
if(left.rows == 1)
|
||||
{
|
||||
@@ -842,15 +886,24 @@ bool SensorData::isPointVisibleFromCameras(const cv::Point3f & pt) const
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(_stereoCameraModel.isValidForProjection())
|
||||
else if(_stereoCameraModels.size() >= 1)
|
||||
{
|
||||
cv::Point3f ptInCameraFrame = util3d::transformPoint(pt, _stereoCameraModel.localTransform().inverse());
|
||||
if(ptInCameraFrame.z > 0.0f)
|
||||
for(unsigned int i=0; i<_stereoCameraModels.size(); ++i)
|
||||
{
|
||||
int u, v;
|
||||
_stereoCameraModel.left().reproject(ptInCameraFrame.x, ptInCameraFrame.y, ptInCameraFrame.z, u, v);
|
||||
return uIsInBounds(u, 0, _stereoCameraModel.left().imageWidth()) &&
|
||||
uIsInBounds(v, 0, _stereoCameraModel.left().imageHeight());
|
||||
if(_stereoCameraModels[i].isValidForProjection() && !_stereoCameraModels[i].localTransform().isNull())
|
||||
{
|
||||
cv::Point3f ptInCameraFrame = util3d::transformPoint(pt, _stereoCameraModels[i].localTransform().inverse());
|
||||
if(ptInCameraFrame.z > 0.0f)
|
||||
{
|
||||
int u, v;
|
||||
_stereoCameraModels[i].left().reproject(ptInCameraFrame.x, ptInCameraFrame.y, ptInCameraFrame.z, u, v);
|
||||
if(uIsInBounds(u, 0, _stereoCameraModels[i].left().imageWidth()) &&
|
||||
uIsInBounds(v, 0, _stereoCameraModels[i].left().imageHeight()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -148,18 +148,6 @@ std::vector<cv::Point2f> StereoOpticalFlow::computeCorrespondences(
|
||||
}
|
||||
UDEBUG("total=%d countFlowRejected=%d countDisparityRejected=%d", (int)status.size(), countFlowRejected, countDisparityRejected);
|
||||
|
||||
if(countFlowRejected + countDisparityRejected > (int)status.size()/2)
|
||||
{
|
||||
UWARN("A large number (%d/%d) of stereo correspondences are rejected! "
|
||||
"Optical flow may have failed because images are not calibrated, "
|
||||
"the background is too far (no disparity between the images), "
|
||||
"maximum disparity may be too small (%f) or that exposure between "
|
||||
"left and right images is too different.",
|
||||
countFlowRejected+countDisparityRejected,
|
||||
(int)status.size(),
|
||||
this->maxDisparity());
|
||||
}
|
||||
|
||||
return rightCorners;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,6 +442,19 @@ Transform Transform::fromEigen3d(const Eigen::Isometry3d & matrix)
|
||||
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
|
||||
}
|
||||
|
||||
Transform Transform::fromEigen3f(const Eigen::Matrix<float, 3, 4> & matrix)
|
||||
{
|
||||
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
|
||||
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
|
||||
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
|
||||
}
|
||||
Transform Transform::fromEigen3d(const Eigen::Matrix<double, 3, 4> & matrix)
|
||||
{
|
||||
return Transform(matrix(0,0), matrix(0,1), matrix(0,2), matrix(0,3),
|
||||
matrix(1,0), matrix(1,1), matrix(1,2), matrix(1,3),
|
||||
matrix(2,0), matrix(2,1), matrix(2,2), matrix(2,3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format (3 values): x y z
|
||||
* Format (6 values): x y z roll pitch yaw
|
||||
|
||||
@@ -55,7 +55,8 @@ CameraDepthAI::CameraDepthAI(
|
||||
deviceSerial_(deviceSerial),
|
||||
outputDepth_(false),
|
||||
depthConfidence_(200),
|
||||
resolution_(resolution)
|
||||
resolution_(resolution),
|
||||
imuFirmwareUpdate_(false)
|
||||
#endif
|
||||
{
|
||||
#ifdef RTABMAP_DEPTHAI
|
||||
@@ -86,6 +87,15 @@ void CameraDepthAI::setOutputDepth(bool enabled, int confidence)
|
||||
#endif
|
||||
}
|
||||
|
||||
void CameraDepthAI::setIMUFirmwareUpdate(bool enabled)
|
||||
{
|
||||
#ifdef RTABMAP_DEPTHAI
|
||||
imuFirmwareUpdate_ = enabled;
|
||||
#else
|
||||
UERROR("CameraDepthAI: RTAB-Map is not built with depthai-core support!");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CameraDepthAI::init(const std::string & calibrationFolder, const std::string & cameraName)
|
||||
{
|
||||
UDEBUG("");
|
||||
@@ -195,6 +205,8 @@ bool CameraDepthAI::init(const std::string & calibrationFolder, const std::strin
|
||||
// Link plugins IMU -> XLINK
|
||||
imu->out.link(xoutIMU->input);
|
||||
|
||||
imu->enableFirmwareUpdate(imuFirmwareUpdate_);
|
||||
|
||||
device_.reset(new dai::Device(p, deviceToUse));
|
||||
|
||||
UINFO("Loading eeprom calibration data");
|
||||
|
||||
@@ -73,9 +73,10 @@ Transform OdometryF2F::computeTransform(
|
||||
{
|
||||
UTimer timer;
|
||||
Transform output;
|
||||
if(!data.rightRaw().empty() && !data.stereoCameraModel().isValidForProjection())
|
||||
if(!data.rightRaw().empty() &&
|
||||
(data.stereoCameraModels().size() != 1 || !data.stereoCameraModels()[0].isValidForProjection()))
|
||||
{
|
||||
UERROR("Calibrated stereo camera required");
|
||||
UERROR("Calibrated stereo camera required (multi-cameras not supported)");
|
||||
return output;
|
||||
}
|
||||
if(!data.depthRaw().empty() &&
|
||||
|
||||
@@ -225,12 +225,6 @@ Transform OdometryF2M::computeTransform(
|
||||
lastFrame_ = new Signature(data);
|
||||
data.setId(id);
|
||||
|
||||
if(bundleAdjustment_ > 0 &&
|
||||
data.cameraModels().size() > 1)
|
||||
{
|
||||
UERROR("Odometry bundle adjustment doesn't work with multi-cameras. It is disabled.");
|
||||
bundleAdjustment_ = 0;
|
||||
}
|
||||
bool addKeyFrame = false;
|
||||
int totalBundleWordReferencesUsed = 0;
|
||||
int totalBundleOutliers = 0;
|
||||
@@ -252,7 +246,7 @@ Transform OdometryF2M::computeTransform(
|
||||
std::map<int, cv::Point3f> points3DMap;
|
||||
std::map<int, Transform> bundlePoses;
|
||||
std::multimap<int, Link> bundleLinks;
|
||||
std::map<int, CameraModel> bundleModels;
|
||||
std::map<int, std::vector<CameraModel> > bundleModels;
|
||||
|
||||
for(int guessIteration=0;
|
||||
guessIteration<(!guess.isNull()&®Pipeline_->isImageRequired()?2:1) && transform.isNull();
|
||||
@@ -315,7 +309,7 @@ Transform OdometryF2M::computeTransform(
|
||||
// local bundle adjustment
|
||||
if(bundleAdjustment_>0 && sba_ &&
|
||||
regPipeline_->isImageRequired() &&
|
||||
lastFrame_->sensorData().cameraModels().size() <= 1 && // multi-cameras not supported
|
||||
(!lastFrame_->sensorData().stereoCameraModels().empty() || !lastFrame_->sensorData().cameraModels().empty()) &&
|
||||
regInfo.inliersIDs.size())
|
||||
{
|
||||
UDEBUG("Local Bundle Adjustment");
|
||||
@@ -326,7 +320,12 @@ Transform OdometryF2M::computeTransform(
|
||||
map_->getWords().begin()->first != tmpMap.getWords().begin()->first ||
|
||||
map_->getWords().rbegin()->first != tmpMap.getWords().rbegin()->first)
|
||||
{
|
||||
UERROR("Bundle Adjustment cannot be used with a registration approach recomputing features from the \"from\" signature (e.g., Optical Flow).");
|
||||
UERROR("Bundle Adjustment cannot be used with a registration approach recomputing "
|
||||
"features from the \"from\" signature (e.g., Optical Flow) that would change "
|
||||
"their ids (size=old=%ld new=%ld first/last: old=%d->%d new=%d->%d).",
|
||||
map_->getWords().size(), tmpMap.getWords().size(),
|
||||
map_->getWords().begin()->first, map_->getWords().rbegin()->first,
|
||||
tmpMap.getWords().begin()->first, tmpMap.getWords().rbegin()->first);
|
||||
bundleAdjustment_ = 0;
|
||||
}
|
||||
else
|
||||
@@ -350,28 +349,34 @@ Transform OdometryF2M::computeTransform(
|
||||
bundleLinks.insert(std::make_pair(lastFrame_->id(), Link(lastFrame_->id(), lastFrame_->id(), Link::kGravity, imuT)));
|
||||
}
|
||||
|
||||
CameraModel model;
|
||||
if(lastFrame_->sensorData().cameraModels().size() == 1 && lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
std::vector<CameraModel> models;
|
||||
if(!lastFrame_->sensorData().cameraModels().empty() &&
|
||||
lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = lastFrame_->sensorData().cameraModels()[0];
|
||||
models = lastFrame_->sensorData().cameraModels();
|
||||
}
|
||||
else if(lastFrame_->sensorData().stereoCameraModel().isValidForProjection())
|
||||
else if(!lastFrame_->sensorData().stereoCameraModels().empty() &&
|
||||
lastFrame_->sensorData().stereoCameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = lastFrame_->sensorData().stereoCameraModel().left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-lastFrame_->sensorData().stereoCameraModel().baseline()*model.fx());
|
||||
for(size_t i=0; i<lastFrame_->sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel model = lastFrame_->sensorData().stereoCameraModels()[i].left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-lastFrame_->sensorData().stereoCameraModels()[i].baseline()*model.fx(),
|
||||
model.imageSize());
|
||||
models.push_back(model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("no valid camera model to do odometry bundle adjustment!");
|
||||
}
|
||||
bundleModels.insert(std::make_pair(lastFrame_->id(), model));
|
||||
Transform invLocalTransform = model.localTransform().inverse();
|
||||
bundleModels.insert(std::make_pair(lastFrame_->id(), models));
|
||||
|
||||
UDEBUG("Fill matches (%d)", (int)regInfo.inliersIDs.size());
|
||||
std::map<int, std::map<int, FeatureBA> > wordReferences;
|
||||
@@ -416,15 +421,27 @@ Transform OdometryF2M::computeTransform(
|
||||
if(iter2D!=lastFrame_->getWords().end())
|
||||
{
|
||||
UASSERT(!lastFrame_->getWordsKpts().empty());
|
||||
cv::KeyPoint kpt = lastFrame_->getWordsKpts()[iter2D->second];
|
||||
|
||||
int cameraIndex = 0;
|
||||
const std::vector<CameraModel> & cam = bundleModels.at(lastFrame_->id());
|
||||
if(cam.size()>1)
|
||||
{
|
||||
UASSERT(cam[0].imageWidth()>0);
|
||||
float subImageWidth = cam[0].imageWidth();
|
||||
cameraIndex = int(kpt.pt.x / subImageWidth);
|
||||
kpt.pt.x = kpt.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
//get depth
|
||||
float d = 0.0f;
|
||||
if( !lastFrame_->getWords3().empty() &&
|
||||
util3d::isFinite(lastFrame_->getWords3()[iter2D->second]))
|
||||
{
|
||||
//move back point in camera frame (to get depth along z)
|
||||
d = util3d::transformPoint(lastFrame_->getWords3()[iter2D->second], invLocalTransform).z;
|
||||
d = util3d::transformPoint(lastFrame_->getWords3()[iter2D->second], cam[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
references.insert(std::make_pair(lastFrame_->id(), FeatureBA(lastFrame_->getWordsKpts()[iter2D->second], d)));
|
||||
references.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, d, cv::Mat(), cameraIndex)));
|
||||
}
|
||||
wordReferences.insert(std::make_pair(wordId, references));
|
||||
|
||||
@@ -626,26 +643,10 @@ Transform OdometryF2M::computeTransform(
|
||||
}
|
||||
|
||||
// sort by feature response
|
||||
std::multimap<float, std::pair<int, std::pair<cv::KeyPoint, std::pair<cv::Point3f, cv::Mat> > > > newIds;
|
||||
std::multimap<float, std::pair<int, std::pair<cv::KeyPoint, std::pair<cv::Point3f, std::pair<cv::Mat, int> > > > > newIds;
|
||||
UASSERT(lastFrame_->getWords3().size() == lastFrame_->getWords().size());
|
||||
UDEBUG("new frame words3=%d", (int)lastFrame_->getWords3().size());
|
||||
std::set<int> seenStatusUpdated;
|
||||
Transform invLocalTransform;
|
||||
if(bundleAdjustment_>0)
|
||||
{
|
||||
if(lastFrame_->sensorData().cameraModels().size() == 1 && lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
invLocalTransform = lastFrame_->sensorData().cameraModels()[0].localTransform().inverse();
|
||||
}
|
||||
else if(lastFrame_->sensorData().stereoCameraModel().isValidForProjection())
|
||||
{
|
||||
invLocalTransform = lastFrame_->sensorData().stereoCameraModel().left().localTransform().inverse();
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("no valid camera model!");
|
||||
}
|
||||
}
|
||||
|
||||
// add points without depth only if the local map has reached its maximum size
|
||||
bool addPointsWithoutDepth = false;
|
||||
@@ -673,7 +674,18 @@ Transform OdometryF2M::computeTransform(
|
||||
for(std::multimap<int, int>::const_iterator iter = lastFrame_->getWords().begin(); iter!=lastFrame_->getWords().end(); ++iter)
|
||||
{
|
||||
const cv::Point3f & pt = lastFrame_->getWords3()[iter->second];
|
||||
const cv::KeyPoint & kpt = lastFrame_->getWordsKpts()[iter->second];
|
||||
cv::KeyPoint kpt = lastFrame_->getWordsKpts()[iter->second];
|
||||
|
||||
int cameraIndex = 0;
|
||||
const std::vector<CameraModel> & cam = bundleModels.at(lastFrame_->id());
|
||||
if(cam.size()>1)
|
||||
{
|
||||
UASSERT(cam[0].imageWidth()>0);
|
||||
float subImageWidth = cam[0].imageWidth();
|
||||
cameraIndex = int(kpt.pt.x / subImageWidth);
|
||||
kpt.pt.x = kpt.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
if(mapWords.find(iter->first) == mapWords.end()) // Point not in map
|
||||
{
|
||||
if(util3d::isFinite(pt) || addPointsWithoutDepth)
|
||||
@@ -682,7 +694,8 @@ Transform OdometryF2M::computeTransform(
|
||||
std::make_pair(kpt.response>0?1.0f/kpt.response:0.0f,
|
||||
std::make_pair(iter->first,
|
||||
std::make_pair(kpt,
|
||||
std::make_pair(pt, lastFrame_->getWordsDescriptors().row(iter->second))))));
|
||||
std::make_pair(pt,
|
||||
std::make_pair(lastFrame_->getWordsDescriptors().row(iter->second), cameraIndex))))));
|
||||
}
|
||||
}
|
||||
else if(bundleAdjustment_>0)
|
||||
@@ -702,17 +715,17 @@ Transform OdometryF2M::computeTransform(
|
||||
float depth = 0.0f;
|
||||
if(util3d::isFinite(pt))
|
||||
{
|
||||
depth = util3d::transformPoint(pt, invLocalTransform).z;
|
||||
depth = util3d::transformPoint(pt, cam[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
if(bundleWordReferences_.find(iter->first) == bundleWordReferences_.end())
|
||||
{
|
||||
std::map<int, FeatureBA> framePt;
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, depth)));
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, depth, cv::Mat(), cameraIndex)));
|
||||
bundleWordReferences_.insert(std::make_pair(iter->first, framePt));
|
||||
}
|
||||
else
|
||||
{
|
||||
bundleWordReferences_.find(iter->first)->second.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, depth)));
|
||||
bundleWordReferences_.find(iter->first)->second.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, depth, cv::Mat(), cameraIndex)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -721,7 +734,9 @@ Transform OdometryF2M::computeTransform(
|
||||
|
||||
int lastFrameOldestNewId = lastFrameOldestNewId_;
|
||||
lastFrameOldestNewId_ = lastFrame_->getWords().size()?lastFrame_->getWords().rbegin()->first:0;
|
||||
for(std::multimap<float, std::pair<int, std::pair<cv::KeyPoint, std::pair<cv::Point3f, cv::Mat> > > >::reverse_iterator iter=newIds.rbegin();
|
||||
const std::vector<CameraModel> * cam = bundleModels.find(lastFrame_->id()) != bundleModels.end()?&bundleModels.at(lastFrame_->id()):0;
|
||||
UASSERT(bundleAdjustment_ == 0 || cam);
|
||||
for(std::multimap<float, std::pair<int, std::pair<cv::KeyPoint, std::pair<cv::Point3f, std::pair<cv::Mat, int> > > > >::reverse_iterator iter=newIds.rbegin();
|
||||
iter!=newIds.rend();
|
||||
++iter)
|
||||
{
|
||||
@@ -736,19 +751,20 @@ Transform OdometryF2M::computeTransform(
|
||||
|
||||
//move back point in camera frame (to get depth along z)
|
||||
float depth = 0.0f;
|
||||
int cameraIndex = iter->second.second.second.second.second;
|
||||
if(util3d::isFinite(iter->second.second.second.first))
|
||||
{
|
||||
depth = util3d::transformPoint(iter->second.second.second.first, invLocalTransform).z;
|
||||
depth = util3d::transformPoint(iter->second.second.second.first, (*cam)[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
if(bundleWordReferences_.find(iter->second.first) == bundleWordReferences_.end())
|
||||
{
|
||||
std::map<int, FeatureBA> framePt;
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(iter->second.second.first, depth)));
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(iter->second.second.first, depth, cv::Mat(), cameraIndex)));
|
||||
bundleWordReferences_.insert(std::make_pair(iter->second.first, framePt));
|
||||
}
|
||||
else
|
||||
{
|
||||
bundleWordReferences_.find(iter->second.first)->second.insert(std::make_pair(lastFrame_->id(), FeatureBA(iter->second.second.first, depth)));
|
||||
bundleWordReferences_.find(iter->second.first)->second.insert(std::make_pair(lastFrame_->id(), FeatureBA(iter->second.second.first, depth, cv::Mat(), cameraIndex)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -774,9 +790,16 @@ Transform OdometryF2M::computeTransform(
|
||||
{
|
||||
model = lastFrame_->sensorData().cameraModels()[0];
|
||||
}
|
||||
else
|
||||
else if(lastFrame_->sensorData().stereoCameraModels().size() > 1)
|
||||
{
|
||||
model = lastFrame_->sensorData().stereoCameraModel().left();
|
||||
subImageWidth = lastFrame_->sensorData().imageRaw().cols/lastFrame_->sensorData().stereoCameraModels().size();
|
||||
int cameraIndex = int(x / subImageWidth);
|
||||
model = lastFrame_->sensorData().stereoCameraModels()[cameraIndex].left();
|
||||
x = x-subImageWidth*cameraIndex;
|
||||
}
|
||||
else if(lastFrame_->sensorData().stereoCameraModels().size() == 1)
|
||||
{
|
||||
model = lastFrame_->sensorData().stereoCameraModels()[0].left();
|
||||
}
|
||||
|
||||
Eigen::Vector3f ray = util3d::projectDepthTo3DRay(
|
||||
@@ -791,7 +814,7 @@ Transform OdometryF2M::computeTransform(
|
||||
pt = util3d::transformPoint(cv::Point3f(ray[0]*scaleInf, ray[1]*scaleInf, ray[2]*scaleInf), model.localTransform()); // in base_link frame
|
||||
}
|
||||
mapPoints.push_back(util3d::transformPoint(pt, newFramePose));
|
||||
mapDescriptors.push_back(iter->second.second.second.second);
|
||||
mapDescriptors.push_back(iter->second.second.second.second.first);
|
||||
if(lastFrameOldestNewId_ > iter->second.first)
|
||||
{
|
||||
lastFrameOldestNewId_ = iter->second.first;
|
||||
@@ -799,6 +822,7 @@ Transform OdometryF2M::computeTransform(
|
||||
++added;
|
||||
}
|
||||
}
|
||||
UDEBUG("");
|
||||
|
||||
// remove words in map if max size is reached
|
||||
if((int)mapWords.size() > maximumMapSize_)
|
||||
@@ -1207,18 +1231,32 @@ Transform OdometryF2M::computeTransform(
|
||||
|
||||
if(bundleAdjustment_>0)
|
||||
{
|
||||
Transform invLocalTransform;
|
||||
if(lastFrame_->sensorData().cameraModels().size() == 1 && lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
std::vector<CameraModel> models;
|
||||
if(!lastFrame_->sensorData().cameraModels().empty() &&
|
||||
lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
invLocalTransform = lastFrame_->sensorData().cameraModels()[0].localTransform().inverse();
|
||||
models = lastFrame_->sensorData().cameraModels();
|
||||
}
|
||||
else if(lastFrame_->sensorData().stereoCameraModel().isValidForProjection())
|
||||
else if(!lastFrame_->sensorData().stereoCameraModels().empty() &&
|
||||
lastFrame_->sensorData().stereoCameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
invLocalTransform = lastFrame_->sensorData().stereoCameraModel().left().localTransform().inverse();
|
||||
for(size_t i=0; i<lastFrame_->sensorData().stereoCameraModels().size(); ++i)
|
||||
{
|
||||
CameraModel model = lastFrame_->sensorData().stereoCameraModels()[i].left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-lastFrame_->sensorData().stereoCameraModels()[i].baseline()*model.fx(),
|
||||
model.imageSize());
|
||||
models.push_back(model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("no valid camera model!");
|
||||
UFATAL("invalid camera model!");
|
||||
}
|
||||
|
||||
// update bundleWordReferences_: used for bundle adjustment
|
||||
@@ -1231,6 +1269,17 @@ Transform OdometryF2M::computeTransform(
|
||||
UASSERT(bundleWordReferences_.find(iter->first) == bundleWordReferences_.end());
|
||||
std::map<int, FeatureBA> framePt;
|
||||
|
||||
cv::KeyPoint kpt = wordsKpts[iter->second];
|
||||
|
||||
int cameraIndex = 0;
|
||||
if(models.size()>1)
|
||||
{
|
||||
UASSERT(models[0].imageWidth()>0);
|
||||
float subImageWidth = models[0].imageWidth();
|
||||
cameraIndex = int(kpt.pt.x / subImageWidth);
|
||||
kpt.pt.x = kpt.pt.x - (subImageWidth*float(cameraIndex));
|
||||
}
|
||||
|
||||
//get depth
|
||||
float d = 0.0f;
|
||||
if(lastFrame_->getWords().count(iter->first) == 1 &&
|
||||
@@ -1238,39 +1287,18 @@ Transform OdometryF2M::computeTransform(
|
||||
util3d::isFinite(lastFrame_->getWords3()[lastFrame_->getWords().find(iter->first)->second]))
|
||||
{
|
||||
//move back point in camera frame (to get depth along z)
|
||||
d = util3d::transformPoint(lastFrame_->getWords3()[lastFrame_->getWords().find(iter->first)->second], invLocalTransform).z;
|
||||
d = util3d::transformPoint(lastFrame_->getWords3()[lastFrame_->getWords().find(iter->first)->second], models[cameraIndex].localTransform().inverse()).z;
|
||||
}
|
||||
|
||||
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(wordsKpts[iter->second], d)));
|
||||
framePt.insert(std::make_pair(lastFrame_->id(), FeatureBA(kpt, d, cv::Mat(), cameraIndex)));
|
||||
bundleWordReferences_.insert(std::make_pair(iter->first, framePt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bundlePoseReferences_.insert(std::make_pair(lastFrame_->id(), (int)bundleWordReferences_.size()));
|
||||
|
||||
CameraModel model;
|
||||
if(lastFrame_->sensorData().cameraModels().size() == 1 && lastFrame_->sensorData().cameraModels().at(0).isValidForProjection())
|
||||
{
|
||||
model = lastFrame_->sensorData().cameraModels()[0];
|
||||
}
|
||||
else if(lastFrame_->sensorData().stereoCameraModel().isValidForProjection())
|
||||
{
|
||||
model = lastFrame_->sensorData().stereoCameraModel().left();
|
||||
// Set Tx for stereo BA
|
||||
model = CameraModel(model.fx(),
|
||||
model.fy(),
|
||||
model.cx(),
|
||||
model.cy(),
|
||||
model.localTransform(),
|
||||
-lastFrame_->sensorData().stereoCameraModel().baseline()*model.fx());
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("invalid camera model!");
|
||||
}
|
||||
bundleModels_.insert(std::make_pair(lastFrame_->id(), model));
|
||||
bundleModels_.insert(std::make_pair(lastFrame_->id(), models));
|
||||
bundlePoses_.insert(std::make_pair(lastFrame_->id(), newFramePose));
|
||||
|
||||
if(!imuT.isNull())
|
||||
|
||||
@@ -170,7 +170,8 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
return output;
|
||||
}
|
||||
|
||||
if(!(((data.cameraModels().size() == 1 && data.cameraModels()[0].isValidForProjection()) || data.stereoCameraModel().isValidForProjection())))
|
||||
if(!((data.cameraModels().size() == 1 && data.cameraModels()[0].isValidForProjection()) ||
|
||||
(data.stereoCameraModels().size() == 1 && data.stereoCameraModels()[0].isValidForProjection())))
|
||||
{
|
||||
UERROR("Odometry cannot be done without calibration or on multi-camera!");
|
||||
return output;
|
||||
@@ -178,21 +179,24 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
|
||||
|
||||
CameraModel cameraModel;
|
||||
if(data.stereoCameraModel().isValidForProjection())
|
||||
if(data.stereoCameraModels().size())
|
||||
{
|
||||
cameraModel = data.stereoCameraModel().left();
|
||||
cameraModel = data.stereoCameraModels()[0].left();
|
||||
// Set Tx for stereo BA
|
||||
cameraModel = CameraModel(cameraModel.fx(),
|
||||
cameraModel.fy(),
|
||||
cameraModel.cx(),
|
||||
cameraModel.cy(),
|
||||
cameraModel.localTransform(),
|
||||
-data.stereoCameraModel().baseline()*cameraModel.fx());
|
||||
-data.stereoCameraModels()[0].baseline()*cameraModel.fx(),
|
||||
cameraModel.imageSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
cameraModel = data.cameraModels()[0];
|
||||
}
|
||||
std::vector<CameraModel> newModel;
|
||||
newModel.push_back(cameraModel);
|
||||
|
||||
UTimer timer;
|
||||
|
||||
@@ -205,9 +209,9 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
{
|
||||
cv::Mat newFrame;
|
||||
cv::cvtColor(data.imageRaw(), newFrame, cv::COLOR_BGR2GRAY);
|
||||
if(data.stereoCameraModel().isValidForProjection())
|
||||
if(!data.stereoCameraModels().empty())
|
||||
{
|
||||
data.setStereoImage(newFrame, data.rightRaw(), data.stereoCameraModel());
|
||||
data.setStereoImage(newFrame, data.rightRaw(), data.stereoCameraModels());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -436,9 +440,9 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
{
|
||||
UWARN("Bundle adjustment: fill arguments");
|
||||
std::multimap<int, Link> links = keyFrameLinks_;
|
||||
std::map<int, CameraModel> models = keyFrameModels_;
|
||||
std::map<int, std::vector<CameraModel> > models = keyFrameModels_;
|
||||
links.insert(std::make_pair(keyFramePoses_.rbegin()->first, newLink));
|
||||
models.insert(std::make_pair(newS->id(), cameraModel));
|
||||
models.insert(std::make_pair(newS->id(), newModel));
|
||||
std::map<int, std::map<int, FeatureBA> > wordReferences;
|
||||
|
||||
for(std::set<int>::iterator iter = memory_->getStMem().begin(); iter!=memory_->getStMem().end(); ++iter)
|
||||
@@ -596,7 +600,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
}
|
||||
keyFramePoses_ = poses;
|
||||
keyFrameLinks_.insert(std::make_pair(newLink.from(), newLink));
|
||||
keyFrameModels_.insert(std::make_pair(newS->id(), cameraModel));
|
||||
keyFrameModels_.insert(std::make_pair(newS->id(), newModel));
|
||||
|
||||
// keep only the two last signatures
|
||||
while(localHistoryMaxSize_ && (int)localMap_.size() > localHistoryMaxSize_ && memory_->getStMem().size()>2)
|
||||
@@ -798,7 +802,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
keyFrameWords3D_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), refWords3));
|
||||
}
|
||||
keyFramePoses_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), this->getPose()));
|
||||
keyFrameModels_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), cameraModel));
|
||||
keyFrameModels_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), newModel));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -137,9 +137,6 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
|
||||
CV_Assert(ipoints.depth() == CV_32F || ipoints.depth() == CV_64F);
|
||||
CV_Assert((ipoints.rows == 1 && ipoints.channels() == 2) || ipoints.cols*ipoints.channels() == 2);
|
||||
|
||||
_rvec.create(3, 1, CV_64FC1);
|
||||
_tvec.create(3, 1, CV_64FC1);
|
||||
|
||||
Mat rvec = useExtrinsicGuess ? _rvec.getMat() : Mat(3, 1, CV_64FC1);
|
||||
Mat tvec = useExtrinsicGuess ? _tvec.getMat() : Mat(3, 1, CV_64FC1);
|
||||
Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();
|
||||
@@ -181,7 +178,7 @@ bool solvePnPRansac(InputArray _opoints, InputArray _ipoints,
|
||||
opoints_inliers.resize(npoints1);
|
||||
ipoints_inliers.resize(npoints1);
|
||||
result = solvePnP(opoints_inliers, ipoints_inliers, cameraMatrix,
|
||||
distCoeffs, rvec, tvec, false, flags == CV_P3P ? CV_EPNP : flags) ? 1 : -1;
|
||||
distCoeffs, rvec, tvec, useExtrinsicGuess, flags == CV_P3P ? CV_EPNP : flags) ? 1 : -1;
|
||||
}
|
||||
|
||||
if( result <= 0 || _local_model.rows <= 0)
|
||||
|
||||
@@ -57,7 +57,7 @@ std::map<int, Transform> OptimizerCVSBA::optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & posesIn,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, CameraModel> & models,
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint/Disparity>)
|
||||
std::set<int> * outliers)
|
||||
|
||||
@@ -110,6 +110,8 @@ enum {
|
||||
PARAM_OFFSET=0,
|
||||
};
|
||||
|
||||
#define MULTICAM_OFFSET 10 // 10 means max 10 cameras per pose
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
bool OptimizerG2O::available()
|
||||
@@ -1395,7 +1397,7 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, CameraModel> & models,
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
std::set<int> * outliers)
|
||||
@@ -1488,50 +1490,55 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
if(iter->first > 0)
|
||||
{
|
||||
// Get camera model
|
||||
std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
|
||||
UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
|
||||
std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(iter->first);
|
||||
UASSERT(iterModel != models.end() && !iterModel->second.empty());
|
||||
for(size_t i=0; i<iterModel->second.size(); ++i)
|
||||
{
|
||||
UASSERT(iterModel->second[i].isValidForProjection());
|
||||
|
||||
Transform camPose = iter->second * iterModel->second.localTransform();
|
||||
Transform camPose = iter->second * iterModel->second[i].localTransform();
|
||||
|
||||
// Add node's pose
|
||||
UASSERT(!camPose.isNull());
|
||||
// Add node's pose
|
||||
UASSERT(!camPose.isNull());
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::VertexSE3Expmap * vCam = new g2o::VertexSE3Expmap();
|
||||
g2o::VertexSE3Expmap * vCam = new g2o::VertexSE3Expmap();
|
||||
#else
|
||||
g2o::VertexCam * vCam = new g2o::VertexCam();
|
||||
g2o::VertexCam * vCam = new g2o::VertexCam();
|
||||
#endif
|
||||
|
||||
Eigen::Affine3d a = camPose.toEigen3d();
|
||||
Eigen::Affine3d a = camPose.toEigen3d();
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
a = a.inverse();
|
||||
vCam->setEstimate(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
a = a.inverse();
|
||||
vCam->setEstimate(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
#else
|
||||
g2o::SBACam cam(Eigen::Quaterniond(a.linear()), a.translation());
|
||||
cam.setKcam(
|
||||
iterModel->second.fx(),
|
||||
iterModel->second.fy(),
|
||||
iterModel->second.cx(),
|
||||
iterModel->second.cy(),
|
||||
iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_); // baseline in meters
|
||||
vCam->setEstimate(cam);
|
||||
g2o::SBACam cam(Eigen::Quaterniond(a.linear()), a.translation());
|
||||
cam.setKcam(
|
||||
iterModel->second[i].fx(),
|
||||
iterModel->second[i].fy(),
|
||||
iterModel->second[i].cx(),
|
||||
iterModel->second[i].cy(),
|
||||
iterModel->second[i].Tx()<0.0?-iterModel->second[i].Tx()/iterModel->second[i].fx():baseline_); // baseline in meters
|
||||
vCam->setEstimate(cam);
|
||||
#endif
|
||||
vCam->setId(iter->first);
|
||||
vCam->setId(iter->first*MULTICAM_OFFSET + i);
|
||||
|
||||
// negative root means that all other poses should be fixed instead of the root
|
||||
vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
|
||||
// negative root means that all other poses should be fixed instead of the root
|
||||
vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
|
||||
|
||||
/*UDEBUG("cam %d (fixed=%d) fx=%f fy=%f cx=%f cy=%f Tx=%f baseline=%f t=%s",
|
||||
iter->first,
|
||||
vCam->fixed()?1:0,
|
||||
iterModel->second.fx(),
|
||||
iterModel->second.fy(),
|
||||
iterModel->second.cx(),
|
||||
iterModel->second.cy(),
|
||||
iterModel->second.Tx(),
|
||||
iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_,
|
||||
camPose.prettyPrint().c_str());*/
|
||||
/*UDEBUG("camPose %d (camid=%d) (fixed=%d) fx=%f fy=%f cx=%f cy=%f Tx=%f baseline=%f t=%s",
|
||||
iter->first,
|
||||
vCam->id(),
|
||||
vCam->fixed()?1:0,
|
||||
iterModel->second[i].fx(),
|
||||
iterModel->second[i].fy(),
|
||||
iterModel->second[i].cx(),
|
||||
iterModel->second[i].cy(),
|
||||
iterModel->second[i].Tx(),
|
||||
iterModel->second[i].Tx()<0.0?-iterModel->second[i].Tx()/iterModel->second[i].fx():baseline_,
|
||||
camPose.prettyPrint().c_str());*/
|
||||
|
||||
UASSERT_MSG(optimizer.addVertex(vCam), uFormat("cannot insert vertex %d!?", iter->first).c_str());
|
||||
UASSERT_MSG(optimizer.addVertex(vCam), uFormat("cannot insert cam vertex %d (pose=%d)!?", vCam->id(), iter->first).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1563,11 +1570,12 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
|
||||
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1*MULTICAM_OFFSET);
|
||||
EdgeSBACamGravity* priorEdge(new EdgeSBACamGravity());
|
||||
std::map<int, CameraModel>::const_iterator iterModel = models.find(iter->first);
|
||||
UASSERT(iterModel != models.end() && !iterModel->second.localTransform().isNull());
|
||||
priorEdge->setCameraInvLocalTransform(iterModel->second.localTransform().inverse().toEigen3d().linear());
|
||||
std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(iter->first);
|
||||
// Gravity constraint added only to first camera of a pose
|
||||
UASSERT(iterModel != models.end() && !iterModel->second.empty() && !iterModel->second[0].localTransform().isNull());
|
||||
priorEdge->setCameraInvLocalTransform(iterModel->second[0].localTransform().inverse().toEigen3d().linear());
|
||||
priorEdge->setMeasurement(m);
|
||||
priorEdge->setInformation(information);
|
||||
priorEdge->vertices()[0] = v1;
|
||||
@@ -1592,15 +1600,17 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
}
|
||||
|
||||
// between cameras, not base_link
|
||||
Transform camLink = models.at(id1).localTransform().inverse()*iter->second.transform()*models.at(id2).localTransform();
|
||||
//UDEBUG("added edge %d->%d (in cam frame=%s)",
|
||||
// id1,
|
||||
// id2,
|
||||
// camLink.prettyPrint().c_str());
|
||||
Transform camLink = models.at(id1)[0].localTransform().inverse()*iter->second.transform()*models.at(id2)[0].localTransform();
|
||||
/*UDEBUG("added edge %d->%d (camIDs %d->%d) (in cam frame=%s)",
|
||||
id1,
|
||||
id2,
|
||||
id1*MULTICAM_OFFSET,
|
||||
id2*MULTICAM_OFFSET,
|
||||
camLink.prettyPrint().c_str());*/
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
EdgeSE3Expmap * e = new EdgeSE3Expmap();
|
||||
g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1);
|
||||
g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2);
|
||||
g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id1*MULTICAM_OFFSET);
|
||||
g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id2*MULTICAM_OFFSET);
|
||||
|
||||
Transform camPose1 = Transform::fromEigen3d(v1->estimate()).inverse();
|
||||
Transform camPose2Inv = Transform::fromEigen3d(v2->estimate());
|
||||
@@ -1608,8 +1618,8 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
|
||||
#else
|
||||
g2o::EdgeSBACam * e = new g2o::EdgeSBACam();
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
|
||||
g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id2);
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1*MULTICAM_OFFSET);
|
||||
g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id2*MULTICAM_OFFSET);
|
||||
#endif
|
||||
UASSERT(v1 != 0);
|
||||
UASSERT(v2 != 0);
|
||||
@@ -1629,8 +1639,60 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
}
|
||||
}
|
||||
|
||||
UDEBUG("fill hard edges between camera 0 and other cameras (multicam)...");
|
||||
for(std::map<int, std::vector<CameraModel> >::const_iterator iter=models.begin(); iter!=models.end(); ++iter)
|
||||
{
|
||||
int id = iter->first;
|
||||
if(uContains(poses, id))
|
||||
{
|
||||
for(size_t i=1; i<iter->second.size(); ++i)
|
||||
{
|
||||
// add edge
|
||||
// Set large information matrix to keep these links fixed
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity()*9999999;
|
||||
|
||||
// between cameras, not base_link
|
||||
Transform camLink = iter->second[0].localTransform().inverse()*iter->second[i].localTransform();
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
EdgeSE3Expmap * e = new EdgeSE3Expmap();
|
||||
g2o::VertexSE3Expmap* v1 = (g2o::VertexSE3Expmap*)optimizer.vertex(id*MULTICAM_OFFSET);
|
||||
g2o::VertexSE3Expmap* v2 = (g2o::VertexSE3Expmap*)optimizer.vertex(id*MULTICAM_OFFSET+i);
|
||||
|
||||
Transform camPose1 = Transform::fromEigen3d(v1->estimate()).inverse();
|
||||
Transform camPose2Inv = Transform::fromEigen3d(v2->estimate());
|
||||
|
||||
camLink = camPose1 * camPose1 * camLink * camPose2Inv * camPose2Inv;
|
||||
#else
|
||||
g2o::EdgeSBACam * e = new g2o::EdgeSBACam();
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id*MULTICAM_OFFSET);
|
||||
g2o::VertexCam* v2 = (g2o::VertexCam*)optimizer.vertex(id*MULTICAM_OFFSET+i);
|
||||
#endif
|
||||
/*UDEBUG("added edge between subcam 0->%d id:%d->%d (in cam frame=%s)",
|
||||
i,
|
||||
v1->id(),
|
||||
v2->id(),
|
||||
camLink.prettyPrint().c_str());*/
|
||||
|
||||
UASSERT(v1 != 0);
|
||||
UASSERT(v2 != 0);
|
||||
e->setVertex(0, v1);
|
||||
e->setVertex(1, v2);
|
||||
Eigen::Affine3d a = camLink.toEigen3d();
|
||||
e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
e->setInformation(information);
|
||||
|
||||
if (!optimizer.addEdge(e))
|
||||
{
|
||||
delete e;
|
||||
UERROR("Map: Failed adding constraint between %d and %d, skipping", v1->id(), v2->id());
|
||||
return optimizedPoses;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UDEBUG("fill 3D points to g2o...");
|
||||
const int stepVertexId = poses.rbegin()->first+1;
|
||||
const int stepVertexId = poses.rbegin()->first*MULTICAM_OFFSET+MULTICAM_OFFSET;
|
||||
int negVertexOffset = stepVertexId;
|
||||
if(wordReferences.size() && wordReferences.rbegin()->first>0)
|
||||
{
|
||||
@@ -1669,22 +1731,24 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
// set observations
|
||||
for(std::map<int, FeatureBA>::const_iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter)
|
||||
{
|
||||
int camId = jter->first;
|
||||
if(poses.find(camId) != poses.end() && optimizer.vertex(camId) != 0)
|
||||
int poseId = jter->first;
|
||||
int camIndex = jter->second.cameraIndex;
|
||||
int camId = poseId*MULTICAM_OFFSET+camIndex;
|
||||
if(poses.find(poseId) != poses.end() && optimizer.vertex(camId) != 0)
|
||||
{
|
||||
const FeatureBA & pt = jter->second;
|
||||
double depth = pt.depth;
|
||||
|
||||
//UDEBUG("Added observation pt=%d to cam=%d (%d,%d) depth=%f", vpt3d->id()-stepVertexId, camId, (int)pt.kpt.pt.x, (int)pt.kpt.pt.y, depth);
|
||||
//UDEBUG("Added observation pt=%d to cam=%d (%d,%d) depth=%f (camIndex=%d)", vpt3d->id()-stepVertexId, camId, (int)pt.kpt.pt.x, (int)pt.kpt.pt.y, depth, camIndex);
|
||||
|
||||
g2o::OptimizableGraph::Edge * e;
|
||||
double baseline = 0.0;
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::VertexSE3Expmap* vcam = dynamic_cast<g2o::VertexSE3Expmap*>(optimizer.vertex(camId));
|
||||
std::map<int, CameraModel>::const_iterator iterModel = models.find(camId);
|
||||
std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(poseId);
|
||||
|
||||
UASSERT(iterModel != models.end() && iterModel->second.isValidForProjection());
|
||||
baseline = iterModel->second.Tx()<0.0?-iterModel->second.Tx()/iterModel->second.fx():baseline_;
|
||||
UASSERT(iterModel != models.end() && camIndex<iterModel->second.size() && iterModel->second[camIndex].isValidForProjection());
|
||||
baseline = iterModel->second[camIndex].Tx()<0.0?-iterModel->second[camIndex].Tx()/iterModel->second[camIndex].fx():baseline_;
|
||||
#else
|
||||
g2o::VertexCam* vcam = dynamic_cast<g2o::VertexCam*>(optimizer.vertex(camId));
|
||||
baseline = vcam->estimate().baseline;
|
||||
@@ -1695,15 +1759,15 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
// stereo edge
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::EdgeStereoSE3ProjectXYZ* es = new g2o::EdgeStereoSE3ProjectXYZ();
|
||||
float disparity = baseline * iterModel->second.fx() / depth;
|
||||
float disparity = baseline * iterModel->second[camIndex].fx() / depth;
|
||||
Eigen::Vector3d obs( pt.kpt.pt.x, pt.kpt.pt.y, pt.kpt.pt.x-disparity);
|
||||
es->setMeasurement(obs);
|
||||
//variance *= log(exp(1)+disparity);
|
||||
es->setInformation(Eigen::Matrix3d::Identity() / variance);
|
||||
es->fx = iterModel->second.fx();
|
||||
es->fy = iterModel->second.fy();
|
||||
es->cx = iterModel->second.cx();
|
||||
es->cy = iterModel->second.cy();
|
||||
es->fx = iterModel->second[camIndex].fx();
|
||||
es->fy = iterModel->second[camIndex].fy();
|
||||
es->cx = iterModel->second[camIndex].cx();
|
||||
es->cy = iterModel->second[camIndex].cy();
|
||||
es->bf = baseline*es->fx;
|
||||
e = es;
|
||||
#else
|
||||
@@ -1731,10 +1795,10 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
Eigen::Vector2d obs( pt.kpt.pt.x, pt.kpt.pt.y);
|
||||
em->setMeasurement(obs);
|
||||
em->setInformation(Eigen::Matrix2d::Identity() / variance);
|
||||
em->fx = iterModel->second.fx();
|
||||
em->fy = iterModel->second.fy();
|
||||
em->cx = iterModel->second.cx();
|
||||
em->cy = iterModel->second.cy();
|
||||
em->fx = iterModel->second[camIndex].fx();
|
||||
em->fy = iterModel->second[camIndex].fy();
|
||||
em->cx = iterModel->second[camIndex].cx();
|
||||
em->cy = iterModel->second[camIndex].cy();
|
||||
e = em;
|
||||
|
||||
#else
|
||||
@@ -1809,13 +1873,13 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
{
|
||||
d = ((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[0]-((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->measurement()[2];
|
||||
}
|
||||
UDEBUG("Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
|
||||
//UDEBUG("Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeStereoSE3ProjectXYZ*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
|
||||
#else
|
||||
if(dynamic_cast<g2o::EdgeProjectP2SC*>(*iter) != 0)
|
||||
{
|
||||
d = ((g2o::EdgeProjectP2SC*)(*iter))->measurement()[0]-((g2o::EdgeProjectP2SC*)(*iter))->measurement()[2];
|
||||
}
|
||||
UDEBUG("Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeProjectP2SC*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
|
||||
//UDEBUG("Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeProjectP2SC*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
|
||||
#endif
|
||||
|
||||
cv::Point3f pt3d;
|
||||
@@ -1858,10 +1922,11 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
{
|
||||
if(iter->first > 0)
|
||||
{
|
||||
int camId = iter->first*MULTICAM_OFFSET;
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
const g2o::VertexSE3Expmap* v = (const g2o::VertexSE3Expmap*)optimizer.vertex(iter->first);
|
||||
const g2o::VertexSE3Expmap* v = (const g2o::VertexSE3Expmap*)optimizer.vertex(camId);
|
||||
#else
|
||||
const g2o::VertexCam* v = (const g2o::VertexCam*)optimizer.vertex(iter->first);
|
||||
const g2o::VertexCam* v = (const g2o::VertexCam*)optimizer.vertex(camId);
|
||||
#endif
|
||||
if(v)
|
||||
{
|
||||
@@ -1872,7 +1937,7 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
#endif
|
||||
|
||||
// remove model local transform
|
||||
t *= models.at(iter->first).localTransform().inverse();
|
||||
t *= models.at(iter->first)[0].localTransform().inverse();
|
||||
|
||||
//UDEBUG("%d from=%s to=%s", iter->first, iter->second.prettyPrint().c_str(), t.prettyPrint().c_str());
|
||||
if(t.isNull())
|
||||
@@ -1896,7 +1961,7 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Vertex (pose) %d not found!?", iter->first);
|
||||
UERROR("Vertex (pose) %d (cam=%d) not found!?", iter->first, camId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -953,18 +953,8 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP cloudFromSensorData(
|
||||
UERROR("Camera model %d is invalid", i);
|
||||
}
|
||||
}
|
||||
|
||||
if(cloud->is_dense && validIndices)
|
||||
{
|
||||
//generate indices for all points (they are all valid)
|
||||
validIndices->resize(cloud->size());
|
||||
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||
{
|
||||
validIndices->at(i) = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!sensorData.imageRaw().empty() && !sensorData.rightRaw().empty() && sensorData.stereoCameraModel().isValidForProjection())
|
||||
else if(!sensorData.imageRaw().empty() && !sensorData.rightRaw().empty() && !sensorData.stereoCameraModels().empty())
|
||||
{
|
||||
//stereo
|
||||
UASSERT(sensorData.rightRaw().type() == CV_8UC1);
|
||||
@@ -979,51 +969,85 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP cloudFromSensorData(
|
||||
leftMono = sensorData.imageRaw();
|
||||
}
|
||||
|
||||
cv::Mat right(sensorData.rightRaw());
|
||||
StereoCameraModel model = sensorData.stereoCameraModel();
|
||||
if( roiRatios.size() == 4 &&
|
||||
((roiRatios[0] > 0.0f && roiRatios[0] <= 1.0f) ||
|
||||
(roiRatios[1] > 0.0f && roiRatios[1] <= 1.0f) ||
|
||||
(roiRatios[2] > 0.0f && roiRatios[2] <= 1.0f) ||
|
||||
(roiRatios[3] > 0.0f && roiRatios[3] <= 1.0f)))
|
||||
UASSERT(int((sensorData.imageRaw().cols/sensorData.stereoCameraModels().size())*sensorData.stereoCameraModels().size()) == sensorData.imageRaw().cols);
|
||||
UASSERT(int((sensorData.rightRaw().cols/sensorData.stereoCameraModels().size())*sensorData.stereoCameraModels().size()) == sensorData.rightRaw().cols);
|
||||
int subImageWidth = sensorData.rightRaw().cols/sensorData.stereoCameraModels().size();
|
||||
for(unsigned int i=0; i<sensorData.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
cv::Rect roi = util2d::computeRoi(leftMono, roiRatios);
|
||||
if( roi.width%decimation==0 &&
|
||||
roi.height%decimation==0)
|
||||
if(sensorData.stereoCameraModels()[i].isValidForProjection())
|
||||
{
|
||||
leftMono = cv::Mat(leftMono, roi);
|
||||
right = cv::Mat(right, roi);
|
||||
model.roi(roi);
|
||||
cv::Mat left(leftMono, cv::Rect(subImageWidth*i, 0, subImageWidth, leftMono.rows));
|
||||
cv::Mat right(sensorData.rightRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, sensorData.rightRaw().rows));
|
||||
StereoCameraModel model = sensorData.stereoCameraModels()[i];
|
||||
if( roiRatios.size() == 4 &&
|
||||
((roiRatios[0] > 0.0f && roiRatios[0] <= 1.0f) ||
|
||||
(roiRatios[1] > 0.0f && roiRatios[1] <= 1.0f) ||
|
||||
(roiRatios[2] > 0.0f && roiRatios[2] <= 1.0f) ||
|
||||
(roiRatios[3] > 0.0f && roiRatios[3] <= 1.0f)))
|
||||
{
|
||||
cv::Rect roi = util2d::computeRoi(left, roiRatios);
|
||||
if( roi.width%decimation==0 &&
|
||||
roi.height%decimation==0)
|
||||
{
|
||||
left = cv::Mat(left, roi);
|
||||
right = cv::Mat(right, roi);
|
||||
model.roi(roi);
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (left=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios[0],
|
||||
roiRatios[1],
|
||||
roiRatios[2],
|
||||
roiRatios[3],
|
||||
roi.width,
|
||||
roi.height,
|
||||
decimation);
|
||||
}
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr tmp = cloudFromDisparity(
|
||||
util2d::disparityFromStereoImages(left, right, stereoParameters),
|
||||
model,
|
||||
decimation,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
validIndices);
|
||||
|
||||
if(tmp->size())
|
||||
{
|
||||
if(!model.localTransform().isNull() && !model.localTransform().isIdentity())
|
||||
{
|
||||
tmp = util3d::transformPointCloud(tmp, model.localTransform());
|
||||
}
|
||||
|
||||
if(sensorData.stereoCameraModels().size() > 1)
|
||||
{
|
||||
tmp = util3d::removeNaNFromPointCloud(tmp);
|
||||
*cloud += *tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
cloud = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (left=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios[0],
|
||||
roiRatios[1],
|
||||
roiRatios[2],
|
||||
roiRatios[3],
|
||||
roi.width,
|
||||
roi.height,
|
||||
decimation);
|
||||
UERROR("Stereo camera model %d is invalid", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloud = cloudFromDisparity(
|
||||
util2d::disparityFromStereoImages(leftMono, right, stereoParameters),
|
||||
model,
|
||||
decimation,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
validIndices);
|
||||
|
||||
if(cloud->size())
|
||||
if(!cloud->empty() && cloud->is_dense && validIndices)
|
||||
{
|
||||
//generate indices for all points (they are all valid)
|
||||
validIndices->resize(cloud->size());
|
||||
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||
{
|
||||
if(cloud->size() && !sensorData.stereoCameraModel().left().localTransform().isNull() && !sensorData.stereoCameraModel().left().localTransform().isIdentity())
|
||||
{
|
||||
cloud = util3d::transformPointCloud(cloud, sensorData.stereoCameraModel().left().localTransform());
|
||||
}
|
||||
validIndices->at(i) = i;
|
||||
}
|
||||
}
|
||||
return cloud;
|
||||
@@ -1129,69 +1153,96 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP cloudRGBFromSensorData(
|
||||
UERROR("Camera model %d is invalid", i);
|
||||
}
|
||||
}
|
||||
|
||||
if(cloud->is_dense && validIndices)
|
||||
{
|
||||
//generate indices for all points (they are all valid)
|
||||
validIndices->resize(cloud->size());
|
||||
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||
{
|
||||
validIndices->at(i) = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!sensorData.imageRaw().empty() && !sensorData.rightRaw().empty() && sensorData.stereoCameraModel().isValidForProjection())
|
||||
else if(!sensorData.imageRaw().empty() && !sensorData.rightRaw().empty() && !sensorData.stereoCameraModels().empty())
|
||||
{
|
||||
//stereo
|
||||
UDEBUG("");
|
||||
|
||||
cv::Mat left(sensorData.imageRaw());
|
||||
cv::Mat right(sensorData.rightRaw());
|
||||
StereoCameraModel model = sensorData.stereoCameraModel();
|
||||
if( roiRatios.size() == 4 &&
|
||||
((roiRatios[0] > 0.0f && roiRatios[0] <= 1.0f) ||
|
||||
(roiRatios[1] > 0.0f && roiRatios[1] <= 1.0f) ||
|
||||
(roiRatios[2] > 0.0f && roiRatios[2] <= 1.0f) ||
|
||||
(roiRatios[3] > 0.0f && roiRatios[3] <= 1.0f)))
|
||||
UASSERT(int((sensorData.imageRaw().cols/sensorData.stereoCameraModels().size())*sensorData.stereoCameraModels().size()) == sensorData.imageRaw().cols);
|
||||
UASSERT(int((sensorData.rightRaw().cols/sensorData.stereoCameraModels().size())*sensorData.stereoCameraModels().size()) == sensorData.rightRaw().cols);
|
||||
int subImageWidth = sensorData.rightRaw().cols/sensorData.stereoCameraModels().size();
|
||||
for(unsigned int i=0; i<sensorData.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
cv::Rect roi = util2d::computeRoi(left, roiRatios);
|
||||
if( roi.width%decimation==0 &&
|
||||
roi.height%decimation==0)
|
||||
if(sensorData.stereoCameraModels()[i].isValidForProjection())
|
||||
{
|
||||
left = cv::Mat(left, roi);
|
||||
right = cv::Mat(right, roi);
|
||||
model.roi(roi);
|
||||
cv::Mat left(sensorData.imageRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, sensorData.imageRaw().rows));
|
||||
cv::Mat right(sensorData.rightRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, sensorData.rightRaw().rows));
|
||||
StereoCameraModel model = sensorData.stereoCameraModels()[i];
|
||||
if( roiRatios.size() == 4 &&
|
||||
((roiRatios[0] > 0.0f && roiRatios[0] <= 1.0f) ||
|
||||
(roiRatios[1] > 0.0f && roiRatios[1] <= 1.0f) ||
|
||||
(roiRatios[2] > 0.0f && roiRatios[2] <= 1.0f) ||
|
||||
(roiRatios[3] > 0.0f && roiRatios[3] <= 1.0f)))
|
||||
{
|
||||
cv::Rect roi = util2d::computeRoi(left, roiRatios);
|
||||
if( roi.width%decimation==0 &&
|
||||
roi.height%decimation==0)
|
||||
{
|
||||
left = cv::Mat(left, roi);
|
||||
right = cv::Mat(right, roi);
|
||||
model.roi(roi);
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (left=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios[0],
|
||||
roiRatios[1],
|
||||
roiRatios[2],
|
||||
roiRatios[3],
|
||||
roi.width,
|
||||
roi.height,
|
||||
decimation);
|
||||
}
|
||||
}
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp = cloudFromStereoImages(
|
||||
left,
|
||||
right,
|
||||
model,
|
||||
decimation,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
validIndices,
|
||||
stereoParameters);
|
||||
|
||||
if(tmp->size())
|
||||
{
|
||||
if(!model.localTransform().isNull() && !model.localTransform().isIdentity())
|
||||
{
|
||||
tmp = util3d::transformPointCloud(tmp, model.localTransform());
|
||||
}
|
||||
|
||||
if(sensorData.stereoCameraModels().size() > 1)
|
||||
{
|
||||
tmp = util3d::removeNaNFromPointCloud(tmp);
|
||||
*cloud += *tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
cloud = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Cannot apply ROI ratios [%f,%f,%f,%f] because resulting "
|
||||
"dimension (left=%dx%d) cannot be divided exactly "
|
||||
"by decimation parameter (%d). Ignoring ROI ratios...",
|
||||
roiRatios[0],
|
||||
roiRatios[1],
|
||||
roiRatios[2],
|
||||
roiRatios[3],
|
||||
roi.width,
|
||||
roi.height,
|
||||
decimation);
|
||||
UERROR("Stereo camera model %d is invalid", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloud = cloudFromStereoImages(
|
||||
left,
|
||||
right,
|
||||
model,
|
||||
decimation,
|
||||
maxDepth,
|
||||
minDepth,
|
||||
validIndices,
|
||||
stereoParameters);
|
||||
|
||||
if(cloud->size() && !sensorData.stereoCameraModel().left().localTransform().isNull() && !sensorData.stereoCameraModel().left().localTransform().isIdentity())
|
||||
if(cloud->is_dense && validIndices)
|
||||
{
|
||||
//generate indices for all points (they are all valid)
|
||||
validIndices->resize(cloud->size());
|
||||
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||
{
|
||||
cloud = util3d::transformPointCloud(cloud, sensorData.stereoCameraModel().left().localTransform());
|
||||
validIndices->at(i) = i;
|
||||
}
|
||||
}
|
||||
|
||||
return cloud;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "opencv/solvepnp.h"
|
||||
|
||||
#ifdef RTABMAP_OPENGV
|
||||
#include <opengv/absolute_pose/methods.hpp>
|
||||
#include <opengv/absolute_pose/NoncentralAbsoluteAdapter.hpp>
|
||||
#include <opengv/sac/Ransac.hpp>
|
||||
#include <opengv/sac_problems/absolute_pose/AbsolutePoseSacProblem.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
@@ -109,9 +116,9 @@ Transform estimateMotion3DTo2D(
|
||||
(double)guessCameraFrame.r21(), (double)guessCameraFrame.r22(), (double)guessCameraFrame.r23(),
|
||||
(double)guessCameraFrame.r31(), (double)guessCameraFrame.r32(), (double)guessCameraFrame.r33());
|
||||
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Mat rvec(3,1, CV_64FC1);
|
||||
cv::Rodrigues(R, rvec);
|
||||
cv::Mat tvec = (cv::Mat_<double>(1,3) <<
|
||||
cv::Mat tvec = (cv::Mat_<double>(3,1) <<
|
||||
(double)guessCameraFrame.x(), (double)guessCameraFrame.y(), (double)guessCameraFrame.z());
|
||||
|
||||
util3d::solvePnPRansac(
|
||||
@@ -240,6 +247,235 @@ Transform estimateMotion3DTo2D(
|
||||
return transform;
|
||||
}
|
||||
|
||||
Transform estimateMotion3DTo2D(
|
||||
const std::map<int, cv::Point3f> & words3A,
|
||||
const std::map<int, cv::KeyPoint> & words2B,
|
||||
const std::vector<CameraModel> & cameraModels,
|
||||
int minInliers,
|
||||
int iterations,
|
||||
double reprojError,
|
||||
int flagsPnP,
|
||||
int refineIterations,
|
||||
const Transform & guess,
|
||||
const std::map<int, cv::Point3f> & words3B,
|
||||
cv::Mat * covariance,
|
||||
std::vector<int> * matchesOut,
|
||||
std::vector<int> * inliersOut)
|
||||
{
|
||||
Transform transform;
|
||||
#ifndef RTABMAP_OPENGV
|
||||
UERROR("This function is only available if rtabmap is built with OpenGV dependency.");
|
||||
#else
|
||||
UASSERT(!cameraModels.empty() && cameraModels[0].imageWidth() > 0);
|
||||
int subImageWidth = cameraModels[0].imageWidth();
|
||||
for(size_t i=0; i<cameraModels.size(); ++i)
|
||||
{
|
||||
UASSERT(cameraModels[i].isValidForProjection());
|
||||
UASSERT(subImageWidth == cameraModels[i].imageWidth());
|
||||
}
|
||||
|
||||
UASSERT(!guess.isNull());
|
||||
|
||||
std::vector<int> matches, inliers;
|
||||
|
||||
if(covariance)
|
||||
{
|
||||
*covariance = cv::Mat::eye(6,6,CV_64FC1);
|
||||
}
|
||||
|
||||
// find correspondences
|
||||
std::vector<int> ids = uKeys(words2B);
|
||||
std::vector<cv::Point3f> objectPoints(ids.size());
|
||||
std::vector<cv::Point2f> imagePoints(ids.size());
|
||||
int oi=0;
|
||||
matches.resize(ids.size());
|
||||
std::vector<int> cameraIndexes(ids.size());
|
||||
for(unsigned int i=0; i<ids.size(); ++i)
|
||||
{
|
||||
std::map<int, cv::Point3f>::const_iterator iter=words3A.find(ids[i]);
|
||||
if(iter != words3A.end() && util3d::isFinite(iter->second))
|
||||
{
|
||||
const cv::Point2f & kpt = words2B.find(ids[i])->second.pt;
|
||||
int cameraIndex = int(kpt.x / subImageWidth);
|
||||
UASSERT_MSG(cameraIndex >= 0 && cameraIndex < (int)cameraModels.size(),
|
||||
uFormat("cameraIndex=%d, models=%d, kpt.x=%f, subImageWidth=%f (Camera model image width=%d)",
|
||||
cameraIndex, (int)cameraModels.size(), kpt.x, subImageWidth, cameraModels[cameraIndex].imageWidth()).c_str());
|
||||
|
||||
const cv::Point3f & pt = iter->second;
|
||||
objectPoints[oi] = pt;
|
||||
imagePoints[oi] = kpt;
|
||||
// convert in image space
|
||||
imagePoints[oi].x = imagePoints[oi].x - (cameraIndex*subImageWidth);
|
||||
cameraIndexes[oi] = cameraIndex;
|
||||
matches[oi++] = ids[i];
|
||||
}
|
||||
}
|
||||
|
||||
objectPoints.resize(oi);
|
||||
imagePoints.resize(oi);
|
||||
cameraIndexes.resize(oi);
|
||||
matches.resize(oi);
|
||||
|
||||
UDEBUG("words3A=%d words2B=%d matches=%d words3B=%d guess=%s reprojError=%f iterations=%d",
|
||||
(int)words3A.size(), (int)words2B.size(), (int)matches.size(), (int)words3B.size(),
|
||||
guess.prettyPrint().c_str(), reprojError, iterations);
|
||||
|
||||
if((int)matches.size() >= minInliers)
|
||||
{
|
||||
// convert cameras
|
||||
opengv::translations_t camOffsets;
|
||||
opengv::rotations_t camRotations;
|
||||
for(size_t i=0; i<cameraModels.size(); ++i)
|
||||
{
|
||||
camOffsets.push_back(opengv::translation_t(
|
||||
cameraModels[i].localTransform().x(),
|
||||
cameraModels[i].localTransform().y(),
|
||||
cameraModels[i].localTransform().z()));
|
||||
camRotations.push_back(cameraModels[i].localTransform().toEigen4d().block<3,3>(0, 0));
|
||||
}
|
||||
|
||||
// convert 3d points
|
||||
opengv::points_t points;
|
||||
// convert 2d-3d correspondences into bearing vectors
|
||||
opengv::bearingVectors_t bearingVectors;
|
||||
opengv::absolute_pose::NoncentralAbsoluteAdapter::camCorrespondences_t camCorrespondences;
|
||||
for(size_t i=0; i<objectPoints.size(); ++i)
|
||||
{
|
||||
int cameraIndex = cameraIndexes[i];
|
||||
points.push_back(opengv::point_t(objectPoints[i].x,objectPoints[i].y,objectPoints[i].z));
|
||||
cv::Vec3f pt;
|
||||
cameraModels[cameraIndex].project(imagePoints[i].x, imagePoints[i].y, 1, pt[0], pt[1], pt[2]);
|
||||
pt = cv::normalize(pt);
|
||||
bearingVectors.push_back(opengv::bearingVector_t(pt[0], pt[1], pt[2]));
|
||||
camCorrespondences.push_back(cameraIndex);
|
||||
}
|
||||
|
||||
//create a non-central absolute adapter
|
||||
opengv::absolute_pose::NoncentralAbsoluteAdapter adapter(
|
||||
bearingVectors,
|
||||
camCorrespondences,
|
||||
points,
|
||||
camOffsets,
|
||||
camRotations );
|
||||
|
||||
adapter.setR(guess.toEigen4d().block<3,3>(0, 0));
|
||||
adapter.sett(opengv::translation_t(guess.x(), guess.y(), guess.z()));
|
||||
|
||||
//Create a AbsolutePoseSacProblem and Ransac
|
||||
//The method is set to GP3P
|
||||
opengv::sac::Ransac<opengv::sac_problems::absolute_pose::AbsolutePoseSacProblem> ransac;
|
||||
std::shared_ptr<opengv::sac_problems::absolute_pose::AbsolutePoseSacProblem> absposeproblem_ptr(
|
||||
new opengv::sac_problems::absolute_pose::AbsolutePoseSacProblem(adapter, opengv::sac_problems::absolute_pose::AbsolutePoseSacProblem::GP3P));
|
||||
|
||||
ransac.sac_model_ = absposeproblem_ptr;
|
||||
ransac.threshold_ = 1.0 - cos(atan(reprojError/cameraModels[0].fx()));
|
||||
ransac.max_iterations_ = iterations;
|
||||
UDEBUG("Ransac params: threshold = %f (reprojError=%f fx=%f), max iterations=%d", ransac.threshold_, reprojError, cameraModels[0].fx(), ransac.max_iterations_);
|
||||
|
||||
//Run the experiment
|
||||
ransac.computeModel();
|
||||
|
||||
Transform pnp = Transform::fromEigen3d(ransac.model_coefficients_);
|
||||
|
||||
UDEBUG("Ransac result: %s", pnp.prettyPrint().c_str());
|
||||
UDEBUG("Ransac iterations done: %d", ransac.iterations_);
|
||||
inliers = ransac.inliers_;
|
||||
UDEBUG("Ransac inliers: %ld", inliers.size());
|
||||
|
||||
if((int)inliers.size() >= minInliers)
|
||||
{
|
||||
transform = pnp;
|
||||
|
||||
// compute variance (like in PCL computeVariance() method of sac_model.h)
|
||||
if(covariance)
|
||||
{
|
||||
std::vector<float> errorSqrdDists(inliers.size());
|
||||
std::vector<float> errorSqrdAngles(inliers.size());
|
||||
oi = 0;
|
||||
for(unsigned int i=0; i<inliers.size(); ++i)
|
||||
{
|
||||
std::map<int, cv::Point3f>::const_iterator iter = words3B.find(matches[inliers[i]]);
|
||||
if(words3B.empty() || (iter != words3B.end() && util3d::isFinite(iter->second)))
|
||||
{
|
||||
const cv::Point3f & objPt = objectPoints[inliers[i]];
|
||||
|
||||
cv::Point3f newPt;
|
||||
if(iter!=words3B.end())
|
||||
{
|
||||
newPt = util3d::transformPoint(iter->second, transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
//compute from projection
|
||||
int cameraIndex = cameraIndexes[inliers[i]];
|
||||
Transform transformCameraFrame = transform * cameraModels[cameraIndex].localTransform();
|
||||
Transform transformCameraFrameInv = transformCameraFrame.inverse();
|
||||
Eigen::Vector3f ray = projectDepthTo3DRay(
|
||||
cameraModels[cameraIndex].imageSize(),
|
||||
imagePoints.at(inliers[i]).x,
|
||||
imagePoints.at(inliers[i]).y,
|
||||
cameraModels[cameraIndex].cx(),
|
||||
cameraModels[cameraIndex].cy(),
|
||||
cameraModels[cameraIndex].fx(),
|
||||
cameraModels[cameraIndex].fy());
|
||||
// transform in camera B frame
|
||||
newPt = util3d::transformPoint(objPt, transformCameraFrameInv);
|
||||
newPt = cv::Point3f(ray.x(), ray.y(), ray.z()) * newPt.z*1.1; // Add 10 % error
|
||||
// put back in frame of camera A
|
||||
newPt = util3d::transformPoint(newPt, transformCameraFrame);
|
||||
}
|
||||
|
||||
errorSqrdDists[oi] = uNormSquared(objPt.x-newPt.x, objPt.y-newPt.y, objPt.z-newPt.z);
|
||||
|
||||
Eigen::Vector4f v1(objPt.x - transform.x(), objPt.y - transform.y(), objPt.z - transform.z(), 0);
|
||||
Eigen::Vector4f v2(newPt.x - transform.x(), newPt.y - transform.y(), newPt.z - transform.z(), 0);
|
||||
errorSqrdAngles[oi++] = pcl::getAngle3D(v1, v2);
|
||||
}
|
||||
}
|
||||
|
||||
errorSqrdDists.resize(oi);
|
||||
errorSqrdAngles.resize(oi);
|
||||
if(errorSqrdDists.size())
|
||||
{
|
||||
std::sort(errorSqrdDists.begin(), errorSqrdDists.end());
|
||||
//divide by 4 instead of 2 to ignore very very far features (stereo)
|
||||
double median_error_sqr = 2.1981 * (double)errorSqrdDists[errorSqrdDists.size () >> 2];
|
||||
UASSERT(uIsFinite(median_error_sqr));
|
||||
(*covariance)(cv::Range(0,3), cv::Range(0,3)) *= median_error_sqr;
|
||||
std::sort(errorSqrdAngles.begin(), errorSqrdAngles.end());
|
||||
median_error_sqr = 2.1981 * (double)errorSqrdAngles[errorSqrdAngles.size () >> 2];
|
||||
UASSERT(uIsFinite(median_error_sqr));
|
||||
(*covariance)(cv::Range(3,6), cv::Range(3,6)) *= median_error_sqr;
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Not enough close points to compute covariance!");
|
||||
}
|
||||
|
||||
if(float(oi) / float(inliers.size()) < 0.2f)
|
||||
{
|
||||
UWARN("A very low number of inliers have valid depth (%d/%d), the transform returned may be wrong!", oi, (int)inliers.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(matchesOut)
|
||||
{
|
||||
*matchesOut = matches;
|
||||
}
|
||||
if(inliersOut)
|
||||
{
|
||||
inliersOut->resize(inliers.size());
|
||||
for(unsigned int i=0; i<inliers.size(); ++i)
|
||||
{
|
||||
inliersOut->at(i) = matches[inliers[i]];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return transform;
|
||||
}
|
||||
|
||||
Transform estimateMotion3DTo3D(
|
||||
const std::map<int, cv::Point3f> & words3A,
|
||||
const std::map<int, cv::Point3f> & words3B,
|
||||
|
||||
@@ -1568,18 +1568,19 @@ cv::Mat mergeTextures(
|
||||
else if(memory)
|
||||
{
|
||||
SensorData data = memory->getNodeData(textureId, true, false, false, false);
|
||||
std::vector<CameraModel> models = data.cameraModels();
|
||||
StereoCameraModel stereoModel = data.stereoCameraModel();
|
||||
const std::vector<CameraModel> & models = data.cameraModels();
|
||||
const std::vector<StereoCameraModel> & stereoModels = data.stereoCameraModels();
|
||||
if(models.size()>=1 &&
|
||||
models[0].imageHeight()>0 &&
|
||||
models[0].imageWidth()>0)
|
||||
{
|
||||
imageSize = models[0].imageSize();
|
||||
}
|
||||
else if(stereoModel.left().imageHeight() > 0 &&
|
||||
stereoModel.left().imageWidth() > 0)
|
||||
else if(stereoModels.size()>=1 &&
|
||||
stereoModels[0].left().imageHeight() > 0 &&
|
||||
stereoModels[0].left().imageWidth() > 0)
|
||||
{
|
||||
imageSize = stereoModel.left().imageSize();
|
||||
imageSize = stereoModels[0].left().imageSize();
|
||||
}
|
||||
else // backward compatibility for image size not set in CameraModel
|
||||
{
|
||||
@@ -1596,18 +1597,19 @@ cv::Mat mergeTextures(
|
||||
else if(dbDriver)
|
||||
{
|
||||
std::vector<CameraModel> models;
|
||||
StereoCameraModel stereoModel;
|
||||
dbDriver->getCalibration(textureId, models, stereoModel);
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
dbDriver->getCalibration(textureId, models, stereoModels);
|
||||
if(models.size()>=1 &&
|
||||
models[0].imageHeight()>0 &&
|
||||
models[0].imageWidth()>0)
|
||||
{
|
||||
imageSize = models[0].imageSize();
|
||||
}
|
||||
else if(stereoModel.left().imageHeight() > 0 &&
|
||||
stereoModel.left().imageWidth() > 0)
|
||||
else if(stereoModels.size()>=1 &&
|
||||
stereoModels[0].left().imageHeight() > 0 &&
|
||||
stereoModels[0].left().imageWidth() > 0)
|
||||
{
|
||||
imageSize = stereoModel.left().imageSize();
|
||||
imageSize = stereoModels[0].left().imageSize();
|
||||
}
|
||||
else // backward compatibility for image size not set in CameraModel
|
||||
{
|
||||
@@ -1698,6 +1700,13 @@ cv::Mat mergeTextures(
|
||||
{
|
||||
SensorData data = memory->getNodeData(textures[t].first, true, false, false, false);
|
||||
models = data.cameraModels();
|
||||
if(models.empty() && !data.stereoCameraModels().empty())
|
||||
{
|
||||
for(size_t i=0; i<data.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
models.push_back(data.stereoCameraModels()[i].left());
|
||||
}
|
||||
}
|
||||
data.uncompressDataConst(&image, 0);
|
||||
}
|
||||
else if(dbDriver)
|
||||
@@ -1705,8 +1714,15 @@ cv::Mat mergeTextures(
|
||||
SensorData data;
|
||||
dbDriver->getNodeData(textures[t].first, data, true, false, false, false);
|
||||
data.uncompressDataConst(&image, 0);
|
||||
StereoCameraModel stereoModel;
|
||||
dbDriver->getCalibration(textures[t].first, models, stereoModel);
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
dbDriver->getCalibration(textures[t].first, models, stereoModels);
|
||||
if(models.empty() && !stereoModels.empty())
|
||||
{
|
||||
for(size_t i=0; i<stereoModels.size(); ++i)
|
||||
{
|
||||
models.push_back(stereoModels[i].left());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousImage = image;
|
||||
@@ -2401,9 +2417,12 @@ bool multiBandTexturing(
|
||||
{
|
||||
SensorData data = memory->getNodeData(camId, true, false, false, false);
|
||||
models = data.cameraModels();
|
||||
if(models.empty() && data.stereoCameraModel().isValidForProjection())
|
||||
if(models.empty() && data.stereoCameraModels().size())
|
||||
{
|
||||
models.push_back(data.stereoCameraModel().left());
|
||||
for(size_t i=0; i<data.stereoCameraModels().size(); ++i)
|
||||
{
|
||||
models.push_back(data.stereoCameraModels()[i].left());
|
||||
}
|
||||
}
|
||||
if(data.imageRaw().empty())
|
||||
{
|
||||
@@ -2432,11 +2451,14 @@ bool multiBandTexturing(
|
||||
}
|
||||
else if(dbDriver)
|
||||
{
|
||||
StereoCameraModel stereoModel;
|
||||
dbDriver->getCalibration(camId, models, stereoModel);
|
||||
if(models.empty() && stereoModel.isValidForProjection())
|
||||
std::vector<StereoCameraModel> stereoModels;
|
||||
dbDriver->getCalibration(camId, models, stereoModels);
|
||||
if(models.empty() && stereoModels.size())
|
||||
{
|
||||
models.push_back(stereoModel.left());
|
||||
for(size_t i=0; i<stereoModels.size(); ++i)
|
||||
{
|
||||
models.push_back(stereoModels[i].left());
|
||||
}
|
||||
}
|
||||
|
||||
SensorData data;
|
||||
|
||||
@@ -218,6 +218,16 @@ cv::Point3f transformPoint(
|
||||
ret.z = transform (2, 0) * point.x + transform (2, 1) * point.y + transform (2, 2) * point.z + transform (2, 3);
|
||||
return ret;
|
||||
}
|
||||
cv::Point3d transformPoint(
|
||||
const cv::Point3d & point,
|
||||
const Transform & transform)
|
||||
{
|
||||
cv::Point3d ret = point;
|
||||
ret.x = transform (0, 0) * point.x + transform (0, 1) * point.y + transform (0, 2) * point.z + transform (0, 3);
|
||||
ret.y = transform (1, 0) * point.x + transform (1, 1) * point.y + transform (1, 2) * point.z + transform (1, 3);
|
||||
ret.z = transform (2, 0) * point.x + transform (2, 1) * point.y + transform (2, 2) * point.z + transform (2, 3);
|
||||
return ret;
|
||||
}
|
||||
pcl::PointXYZ transformPoint(
|
||||
const pcl::PointXYZ & pt,
|
||||
const Transform & transform)
|
||||
|
||||
Reference in New Issue
Block a user