39 #include <gtsam/geometry/Pose2.h> 40 #include <gtsam/geometry/Pose3.h> 41 #include <gtsam/inference/Key.h> 42 #include <gtsam/inference/Symbol.h> 43 #include <gtsam/slam/PriorFactor.h> 44 #include <gtsam/slam/BetweenFactor.h> 45 #include <gtsam/sam/BearingRangeFactor.h> 46 #include <gtsam/nonlinear/NonlinearFactorGraph.h> 47 #include <gtsam/nonlinear/GaussNewtonOptimizer.h> 48 #include <gtsam/nonlinear/DoglegOptimizer.h> 49 #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h> 50 #include <gtsam/nonlinear/NonlinearOptimizer.h> 51 #include <gtsam/nonlinear/Marginals.h> 52 #include <gtsam/nonlinear/Values.h> 57 #ifdef RTABMAP_VERTIGO 63 #endif // end RTABMAP_GTSAM 84 const std::map<int, Transform> & poses,
85 const std::multimap<int, Link> & edgeConstraints,
86 cv::Mat & outputCovariance,
87 std::list<std::map<int, Transform> > * intermediateGraphes,
91 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
92 std::map<int, Transform> optimizedPoses;
95 #ifndef RTABMAP_VERTIGO 98 UWARN(
"Vertigo robust optimization is not available! Robust optimization is now disabled.");
103 UDEBUG(
"Optimizing graph...");
104 if(edgeConstraints.size()>=1 && poses.size()>=2 &&
iterations() > 0)
106 gtsam::NonlinearFactorGraph graph;
109 bool gpsPriorOnly =
false;
110 bool hasPriorPoses =
false;
113 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
115 if(iter->second.from() == iter->second.to() && iter->second.type() ==
Link::kPosePrior)
117 hasPriorPoses =
true;
118 if ((
isSlam2d() && 1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) < 9999) ||
119 (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) < 9999.0 &&
120 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) < 9999.0 &&
121 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) < 9999.0))
124 gpsPriorOnly =
false;
140 const Transform & initialPose = poses.at(rootId);
141 UDEBUG(
"hasPriorPoses=%s, gpsPriorOnly=%s", hasPriorPoses?
"true":
"false", gpsPriorOnly?
"true":
"false");
144 gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector3(0.01, 0.01, hasPriorPoses?1
e-2:
std::numeric_limits<double>::min()));
145 graph.add(gtsam::PriorFactor<gtsam::Pose2>(rootId, gtsam::Pose2(initialPose.
x(), initialPose.
y(), initialPose.
theta()), priorNoise));
149 gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(
152 (gpsPriorOnly?2:1
e-2), gpsPriorOnly?2:1
e-2, gpsPriorOnly?2:1
e-2
154 graph.add(gtsam::PriorFactor<gtsam::Pose3>(rootId, gtsam::Pose3(initialPose.
toEigen4d()), priorNoise));
158 UDEBUG(
"fill poses to gtsam... rootId=%d (priorsIgnored=%d gpsPriorOnly=%d landmarksIgnored=%d)",
160 gtsam::Values initialEstimate;
161 std::map<int, bool> isLandmarkWithRotation;
162 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
164 UASSERT(!iter->second.isNull());
169 initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
174 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(iter->first);
175 UASSERT_MSG(jter != edgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!", iter->first).c_str());
177 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
179 initialEstimate.insert(iter->first, gtsam::Point2(iter->second.x(), iter->second.y()));
180 isLandmarkWithRotation.insert(std::make_pair(iter->first,
false));
184 initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
185 isLandmarkWithRotation.insert(std::make_pair(iter->first,
true));
194 initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
199 std::multimap<int, Link>::const_iterator jter=edgeConstraints.find(iter->first);
200 UASSERT_MSG(jter != edgeConstraints.end(),
uFormat(
"Not found landmark %d in edges!", iter->first).c_str());
202 if (1 / static_cast<double>(jter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
203 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
204 1 /
static_cast<double>(jter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
206 initialEstimate.insert(iter->first, gtsam::Point3(iter->second.x(), iter->second.y(), iter->second.z()));
207 isLandmarkWithRotation.insert(std::make_pair(iter->first,
false));
211 initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
212 isLandmarkWithRotation.insert(std::make_pair(iter->first,
true));
218 UDEBUG(
"fill edges to gtsam...");
219 int switchCounter = poses.rbegin()->first+1;
220 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
222 int id1 = iter->second.from();
223 int id2 = iter->second.to();
224 UASSERT(!iter->second.transform().isNull());
231 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
233 noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Variances(Vector2(
234 1/iter->second.infMatrix().at<
double>(0,0),
235 1/iter->second.infMatrix().at<
double>(1,1)));
236 graph.add(
GPSPose2XYFactor(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
240 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
243 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
244 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
245 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
246 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
247 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
248 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
249 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
250 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
251 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
254 gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
255 graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
260 if (1 / static_cast<double>(iter->second.infMatrix().at<
double>(3,3)) >= 9999.0 ||
261 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(4,4)) >= 9999.0 ||
262 1 /
static_cast<double>(iter->second.infMatrix().at<
double>(5,5)) >= 9999.0)
264 noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Precisions(Vector3(
265 iter->second.infMatrix().at<
double>(0,0),
266 iter->second.infMatrix().at<
double>(1,1),
267 iter->second.infMatrix().at<
double>(2,2)));
268 graph.add(
GPSPose3XYZFactor(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
272 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
275 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
278 Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
279 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
280 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
281 mgtsam.block(0,3,3,3) = information.block(0,3,3,3);
282 mgtsam.block(3,0,3,3) = information.block(3,0,3,3);
283 gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
285 graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
291 Vector3 r = gtsam::Pose3(iter->second.transform().toEigen4d()).
rotation().xyz();
292 gtsam::Unit3 nG = gtsam::Rot3::RzRyRx(r.x(), r.y(), 0).
rotate(gtsam::Unit3(0,0,-1));
293 gtsam::SharedNoiseModel model = gtsam::noiseModel::Isotropic::Sigmas(gtsam::Vector2(
gravitySigma(), 10));
297 else if(id1<0 || id2 < 0)
302 UASSERT((id1 < 0 && id2 > 0) || (id1 > 0 && id2 < 0));
306 t = iter->second.transform();
310 t = iter->second.transform().
inverse();
315 if(isLandmarkWithRotation.at(id2))
317 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
320 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
321 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
322 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
323 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
324 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
325 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
326 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
327 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
328 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
330 gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
331 graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(t.
x(), t.
y(), t.
theta()), model));
335 Eigen::Matrix<double, 2, 2> information = Eigen::Matrix<double, 2, 2>::Identity();
338 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,2), cv::Range(0,2)).clone();;
339 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
341 gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(information);
343 gtsam::Point2 landmark(t.
x(), t.
y());
345 graph.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(id1, id2, p.bearing(landmark), p.range(landmark), model));
350 if(isLandmarkWithRotation.at(id2))
352 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
355 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
358 Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
359 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
360 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
361 mgtsam.block(0,3,3,3) = information.block(0,3,3,3);
362 mgtsam.block(3,0,3,3) = information.block(3,0,3,3);
363 gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
364 graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(t.
toEigen4d()), model));
368 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
371 cv::Mat linearCov = cv::Mat(iter->second.infMatrix(), cv::Range(0,3), cv::Range(0,3)).clone();;
372 memcpy(information.data(), linearCov.data, linearCov.total()*
sizeof(double));
374 gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(information);
376 gtsam::Point3 landmark(t.
x(), t.
y(), t.
z());
378 graph.add(gtsam::BearingRangeFactor<gtsam::Pose3, gtsam::Point3>(id1, id2, p.bearing(landmark), p.range(landmark), model));
385 #ifdef RTABMAP_VERTIGO 403 gtsam::noiseModel::Diagonal::shared_ptr switchPriorModel = gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector1(1.0));
404 graph.add(gtsam::PriorFactor<vertigo::SwitchVariableLinear> (gtsam::Symbol(
's',switchCounter),
vertigo::SwitchVariableLinear(prior), switchPriorModel));
410 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
413 information(0,0) = iter->second.infMatrix().at<
double>(0,0);
414 information(0,1) = iter->second.infMatrix().at<
double>(0,1);
415 information(0,2) = iter->second.infMatrix().at<
double>(0,5);
416 information(1,0) = iter->second.infMatrix().at<
double>(1,0);
417 information(1,1) = iter->second.infMatrix().at<
double>(1,1);
418 information(1,2) = iter->second.infMatrix().at<
double>(1,5);
419 information(2,0) = iter->second.infMatrix().at<
double>(5,0);
420 information(2,1) = iter->second.infMatrix().at<
double>(5,1);
421 information(2,2) = iter->second.infMatrix().at<
double>(5,5);
423 gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
425 #ifdef RTABMAP_VERTIGO 436 graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
441 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
444 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*
sizeof(double));
447 Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
448 mgtsam.block(0,0,3,3) = information.block(3,3,3,3);
449 mgtsam.block(3,3,3,3) = information.block(0,0,3,3);
450 mgtsam.block(0,3,3,3) = information.block(0,3,3,3);
451 mgtsam.block(3,0,3,3) = information.block(3,0,3,3);
452 gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
454 #ifdef RTABMAP_VERTIGO 465 graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
471 UDEBUG(
"create optimizer");
472 gtsam::NonlinearOptimizer * optimizer;
476 gtsam::DoglegParams parameters;
477 parameters.relativeErrorTol =
epsilon();
479 optimizer =
new gtsam::DoglegOptimizer(graph, initialEstimate, parameters);
483 gtsam::GaussNewtonParams parameters;
484 parameters.relativeErrorTol =
epsilon();
486 optimizer =
new gtsam::GaussNewtonOptimizer(graph, initialEstimate, parameters);
490 gtsam::LevenbergMarquardtParams parameters;
491 parameters.relativeErrorTol =
epsilon();
493 optimizer =
new gtsam::LevenbergMarquardtOptimizer(graph, initialEstimate, parameters);
499 double lastError = optimizer->error();
502 if(intermediateGraphes && i > 0)
505 std::map<int, Transform> tmpPoses;
506 for(gtsam::Values::const_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
508 if(iter->value.dim() > 1)
510 int key = (int)iter->key;
515 gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
516 tmpPoses.insert(std::make_pair(key,
Transform(p.x(), p.y(), p.theta())));
518 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
520 if(isLandmarkWithRotation.at(key))
522 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
523 gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
524 tmpPoses.insert(std::make_pair(key,
Transform(p.x(), p.y(), z,
roll,
pitch, p.theta())));
528 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
529 gtsam::Point2 p = iter->value.cast<gtsam::Point2>();
538 gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
541 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
543 if(isLandmarkWithRotation.at(key))
545 gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
550 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
551 gtsam::Point3 p = iter->value.cast<gtsam::Point3>();
558 intermediateGraphes->push_back(tmpPoses);
562 optimizer->iterate();
565 catch(gtsam::IndeterminantLinearSystemException &
e)
567 UWARN(
"GTSAM exception caught: %s\n Graph has %d edges and %d vertices", e.what(),
568 (int)edgeConstraints.size(),
571 return optimizedPoses;
575 double error = optimizer->error();
576 UDEBUG(
"iteration %d error =%f", i+1, error);
577 double errorDelta = lastError - error;
578 if(i>0 && errorDelta < this->
epsilon())
582 UDEBUG(
"Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->
epsilon());
586 UDEBUG(
"Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->
epsilon());
590 else if(i==0 && error < this->
epsilon())
592 UINFO(
"Stop optimizing, error is already under epsilon (%f < %f)", error, this->
epsilon());
599 *finalError = lastError;
603 *iterationsDone = it;
605 UDEBUG(
"GTSAM optimizing end (%d iterations done, error=%f (initial=%f final=%f), time=%f s)",
606 optimizer->iterations(), optimizer->error(), graph.error(initialEstimate), graph.error(optimizer->values()), timer.
ticks());
609 for(gtsam::Values::const_iterator iter=optimizer->values().begin(); iter!=optimizer->values().end(); ++iter)
611 if(iter->value.dim() > 1)
613 int key = (int)iter->key;
618 gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
619 optimizedPoses.insert(std::make_pair(key,
Transform(p.x(), p.y(), p.theta())));
621 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
623 if(isLandmarkWithRotation.at(key))
625 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
626 gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
627 optimizedPoses.insert(std::make_pair(key,
Transform(p.x(), p.y(), z,
roll,
pitch, p.theta())));
631 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
632 gtsam::Point2 p = iter->value.cast<gtsam::Point2>();
641 gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
644 else if(!
landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
646 if(isLandmarkWithRotation.at(key))
648 gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
653 poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
654 gtsam::Point3 p = iter->value.cast<gtsam::Point3>();
664 UDEBUG(
"Computing marginals...");
666 gtsam::Marginals marginals(graph, optimizer->values());
667 gtsam::Matrix info = marginals.marginalCovariance(poses.rbegin()->first);
668 UDEBUG(
"Computed marginals = %fs (key=%d)", t.
ticks(), poses.rbegin()->first);
669 if(
isSlam2d() && info.cols() == 3 && info.cols() == 3)
671 outputCovariance.at<
double>(0,0) = info(0,0);
672 outputCovariance.at<
double>(0,1) = info(0,1);
673 outputCovariance.at<
double>(0,5) = info(0,2);
674 outputCovariance.at<
double>(1,0) = info(1,0);
675 outputCovariance.at<
double>(1,1) = info(1,1);
676 outputCovariance.at<
double>(1,5) = info(1,2);
677 outputCovariance.at<
double>(5,0) = info(2,0);
678 outputCovariance.at<
double>(5,1) = info(2,1);
679 outputCovariance.at<
double>(5,5) = info(2,2);
681 else if(!
isSlam2d() && info.cols() == 6 && info.cols() == 6)
683 Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
684 mgtsam.block(3,3,3,3) = info.block(0,0,3,3);
685 mgtsam.block(0,0,3,3) = info.block(3,3,3,3);
686 mgtsam.block(0,3,3,3) = info.block(0,3,3,3);
687 mgtsam.block(3,0,3,3) = info.block(3,0,3,3);
688 memcpy(outputCovariance.data, mgtsam.data(), outputCovariance.total()*
sizeof(double));
692 UWARN(
"GTSAM: Could not compute marginal covariance!");
695 catch(gtsam::IndeterminantLinearSystemException &
e)
697 UWARN(
"GTSAM exception caught: %s", e.what());
699 catch(std::exception& e)
701 UWARN(
"GTSAM exception caught: %s", e.what());
706 else if(poses.size() == 1 ||
iterations() <= 0)
708 optimizedPoses = poses;
712 UWARN(
"This method should be called at least with 1 pose!");
714 UDEBUG(
"Optimizing graph...end!");
716 UERROR(
"Not built with GTSAM support!");
718 return optimizedPoses;
GLM_FUNC_DECL T roll(detail::tquat< T, P > const &x)
bool priorsIgnored() const
static bool parse(const ParametersMap ¶meters, const std::string &key, bool &value)
float gravitySigma() const
virtual void parseParameters(const ParametersMap ¶meters)
GLM_FUNC_DECL genType min(genType const &x, genType const &y)
GLM_FUNC_DECL genType e()
std::map< std::string, std::string > ParametersMap
bool isCovarianceIgnored() const
Basic mathematics functions.
Some conversion functions.
GLM_FUNC_DECL T pitch(detail::tquat< T, P > const &x)
#define UASSERT(condition)
Wrappers of STL for convenient functions.
bool landmarksIgnored() const
virtual void parseParameters(const ParametersMap ¶meters)
#define UASSERT_MSG(condition, msg_str)
bool uContains(const std::list< V > &list, const V &value)
GLM_FUNC_DECL detail::tquat< T, P > rotation(detail::tvec3< T, P > const &orig, detail::tvec3< T, P > const &dest)
void setRobust(bool enabled)
ULogger class and convenient macros.
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
virtual std::map< int, Transform > optimize(int rootId, const std::map< int, Transform > &poses, const std::multimap< int, Link > &edgeConstraints, cv::Mat &outputCovariance, std::list< std::map< int, Transform > > *intermediateGraphes=0, double *finalError=0, int *iterationsDone=0)
std::string UTILITE_EXP uFormat(const char *fmt,...)