mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-01 17:10:26 +08:00
GUI: Added "Edit->Post processing..." action to detect more loop closures and/or refine all links in the graph with ICP 3D.
git-svn-id: http://rtabmap.googlecode.com/svn/trunk/rtabmap@1940 f169173b-cf89-36c8-b27e-44dbe73f0c83
This commit is contained in:
@@ -245,6 +245,8 @@ private:
|
|||||||
float _icpMaxCorrespondenceDistance;
|
float _icpMaxCorrespondenceDistance;
|
||||||
int _icpMaxIterations;
|
int _icpMaxIterations;
|
||||||
float _icpMaxFitness;
|
float _icpMaxFitness;
|
||||||
|
bool _icpPointToPlane;
|
||||||
|
int _icpPointToPlaneNormalNeighbors;
|
||||||
float _icp2MaxCorrespondenceDistance;
|
float _icp2MaxCorrespondenceDistance;
|
||||||
int _icp2MaxIterations;
|
int _icp2MaxIterations;
|
||||||
float _icp2MaxFitness;
|
float _icp2MaxFitness;
|
||||||
|
|||||||
@@ -302,11 +302,13 @@ class RTABMAP_EXP Parameters
|
|||||||
|
|
||||||
RTABMAP_PARAM(LccIcp3, Decimation, int, 8, "Depth image decimation.");
|
RTABMAP_PARAM(LccIcp3, Decimation, int, 8, "Depth image decimation.");
|
||||||
RTABMAP_PARAM(LccIcp3, MaxDepth, float, 4.0, "Max cloud depth.");
|
RTABMAP_PARAM(LccIcp3, MaxDepth, float, 4.0, "Max cloud depth.");
|
||||||
RTABMAP_PARAM(LccIcp3, VoxelSize, float, 0.005, "Voxel size to be used for ICP computation.");
|
RTABMAP_PARAM(LccIcp3, VoxelSize, float, 0.01, "Voxel size to be used for ICP computation.");
|
||||||
RTABMAP_PARAM(LccIcp3, Samples, int, 0, "Random samples to be used for ICP computation. Not used if voxelSize is set.");
|
RTABMAP_PARAM(LccIcp3, Samples, int, 0, "Random samples to be used for ICP computation. Not used if voxelSize is set.");
|
||||||
RTABMAP_PARAM(LccIcp3, MaxCorrespondenceDistance, float, 0.05, "ICP 3D: Max distance for point correspondences.");
|
RTABMAP_PARAM(LccIcp3, MaxCorrespondenceDistance, float, 0.05, "ICP 3D: Max distance for point correspondences.");
|
||||||
RTABMAP_PARAM(LccIcp3, Iterations, int, 30, "ICP 3D: Max iterations.");
|
RTABMAP_PARAM(LccIcp3, Iterations, int, 30, "ICP 3D: Max iterations.");
|
||||||
RTABMAP_PARAM(LccIcp3, MaxFitness, float, 1.0, "ICP 3D: Maximum fitness to accept the computed transform.");
|
RTABMAP_PARAM(LccIcp3, MaxFitness, float, 1.0, "ICP 3D: Maximum fitness to accept the computed transform.");
|
||||||
|
RTABMAP_PARAM(LccIcp3, PointToPlane, bool, false, "ICP 3D: Use point to plane ICP.");
|
||||||
|
RTABMAP_PARAM(LccIcp3, PointToPlaneNormalNeighbors, int, 20, "ICP 3D: Number of neighbors to compute normals for point to plane.");
|
||||||
|
|
||||||
RTABMAP_PARAM(LccIcp2, MaxCorrespondenceDistance, float, 0.1, "ICP 2D: Max distance for point correspondences.");
|
RTABMAP_PARAM(LccIcp2, MaxCorrespondenceDistance, float, 0.1, "ICP 2D: Max distance for point correspondences.");
|
||||||
RTABMAP_PARAM(LccIcp2, Iterations, int, 30, "ICP 2D: Max iterations.");
|
RTABMAP_PARAM(LccIcp2, Iterations, int, 30, "ICP 2D: Max iterations.");
|
||||||
|
|||||||
@@ -443,11 +443,30 @@ pcl::PolygonMesh::Ptr RTABMAP_EXP createMesh(
|
|||||||
float gp3MaximumAngle = 2*M_PI/3,
|
float gp3MaximumAngle = 2*M_PI/3,
|
||||||
bool gp3NormalConsistency = false);
|
bool gp3NormalConsistency = false);
|
||||||
|
|
||||||
|
std::multimap<int, Link>::iterator RTABMAP_EXP findLink(
|
||||||
|
std::multimap<int, Link> & links,
|
||||||
|
int from,
|
||||||
|
int to);
|
||||||
|
|
||||||
|
// <int, depth> depth=0 means infinite depth
|
||||||
|
std::map<int, int> RTABMAP_EXP generateDepthGraph(
|
||||||
|
const std::multimap<int, Link> & links,
|
||||||
|
int fromId,
|
||||||
|
int depth = 0);
|
||||||
|
|
||||||
|
void RTABMAP_EXP optimizeTOROGraph(
|
||||||
|
const std::map<int, int> & depthGraph,
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
const std::multimap<int, Link> & links,
|
||||||
|
std::map<int, Transform> & optimizedPoses,
|
||||||
|
int toroIterations = 100,
|
||||||
|
bool toroInitialGuess = true,
|
||||||
|
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
|
||||||
|
|
||||||
void RTABMAP_EXP optimizeTOROGraph(
|
void RTABMAP_EXP optimizeTOROGraph(
|
||||||
const std::map<int, Transform> & poses,
|
const std::map<int, Transform> & poses,
|
||||||
const std::multimap<int, Link> & edgeConstraints,
|
const std::multimap<int, Link> & edgeConstraints,
|
||||||
std::map<int, Transform> & optimizedPoses,
|
std::map<int, Transform> & optimizedPoses,
|
||||||
Transform & mapCorrection,
|
|
||||||
int toroIterations = 100,
|
int toroIterations = 100,
|
||||||
bool toroInitialGuess = true,
|
bool toroInitialGuess = true,
|
||||||
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
|
std::list<std::map<int, Transform> > * intermediateGraphes = 0);
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ Memory::Memory(const ParametersMap & parameters) :
|
|||||||
_icpMaxCorrespondenceDistance(Parameters::defaultLccIcp3MaxCorrespondenceDistance()),
|
_icpMaxCorrespondenceDistance(Parameters::defaultLccIcp3MaxCorrespondenceDistance()),
|
||||||
_icpMaxIterations(Parameters::defaultLccIcp3Iterations()),
|
_icpMaxIterations(Parameters::defaultLccIcp3Iterations()),
|
||||||
_icpMaxFitness(Parameters::defaultLccIcp3MaxFitness()),
|
_icpMaxFitness(Parameters::defaultLccIcp3MaxFitness()),
|
||||||
|
_icpPointToPlane(Parameters::defaultLccIcp3PointToPlane()),
|
||||||
|
_icpPointToPlaneNormalNeighbors(Parameters::defaultLccIcp3PointToPlaneNormalNeighbors()),
|
||||||
|
|
||||||
_icp2MaxCorrespondenceDistance(Parameters::defaultLccIcp2MaxCorrespondenceDistance()),
|
_icp2MaxCorrespondenceDistance(Parameters::defaultLccIcp2MaxCorrespondenceDistance()),
|
||||||
_icp2MaxIterations(Parameters::defaultLccIcp2Iterations()),
|
_icp2MaxIterations(Parameters::defaultLccIcp2Iterations()),
|
||||||
@@ -403,6 +405,8 @@ void Memory::parseParameters(const ParametersMap & parameters)
|
|||||||
Parameters::parse(parameters, Parameters::kLccIcp3MaxCorrespondenceDistance(), _icpMaxCorrespondenceDistance);
|
Parameters::parse(parameters, Parameters::kLccIcp3MaxCorrespondenceDistance(), _icpMaxCorrespondenceDistance);
|
||||||
Parameters::parse(parameters, Parameters::kLccIcp3Iterations(), _icpMaxIterations);
|
Parameters::parse(parameters, Parameters::kLccIcp3Iterations(), _icpMaxIterations);
|
||||||
Parameters::parse(parameters, Parameters::kLccIcp3MaxFitness(), _icpMaxFitness);
|
Parameters::parse(parameters, Parameters::kLccIcp3MaxFitness(), _icpMaxFitness);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3PointToPlane(), _icpPointToPlane);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3PointToPlaneNormalNeighbors(), _icpPointToPlaneNormalNeighbors);
|
||||||
Parameters::parse(parameters, Parameters::kLccIcp2MaxCorrespondenceDistance(), _icp2MaxCorrespondenceDistance);
|
Parameters::parse(parameters, Parameters::kLccIcp2MaxCorrespondenceDistance(), _icp2MaxCorrespondenceDistance);
|
||||||
Parameters::parse(parameters, Parameters::kLccIcp2Iterations(), _icp2MaxIterations);
|
Parameters::parse(parameters, Parameters::kLccIcp2Iterations(), _icp2MaxIterations);
|
||||||
Parameters::parse(parameters, Parameters::kLccIcp2MaxFitness(), _icp2MaxFitness);
|
Parameters::parse(parameters, Parameters::kLccIcp2MaxFitness(), _icp2MaxFitness);
|
||||||
@@ -429,6 +433,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
|
|||||||
UASSERT_MSG(_icpMaxCorrespondenceDistance > 0.0f, uFormat("value=%f", _icpMaxCorrespondenceDistance).c_str());
|
UASSERT_MSG(_icpMaxCorrespondenceDistance > 0.0f, uFormat("value=%f", _icpMaxCorrespondenceDistance).c_str());
|
||||||
UASSERT_MSG(_icpMaxIterations > 0, uFormat("value=%d", _icpMaxIterations).c_str());
|
UASSERT_MSG(_icpMaxIterations > 0, uFormat("value=%d", _icpMaxIterations).c_str());
|
||||||
UASSERT_MSG(_icpMaxFitness > 0.0f, uFormat("value=%f", _icpMaxFitness).c_str());
|
UASSERT_MSG(_icpMaxFitness > 0.0f, uFormat("value=%f", _icpMaxFitness).c_str());
|
||||||
|
UASSERT_MSG(_icpPointToPlaneNormalNeighbors > 0, uFormat("value=%d", _icpPointToPlaneNormalNeighbors).c_str());
|
||||||
UASSERT_MSG(_icp2MaxCorrespondenceDistance > 0.0f, uFormat("value=%f", _icp2MaxCorrespondenceDistance).c_str());
|
UASSERT_MSG(_icp2MaxCorrespondenceDistance > 0.0f, uFormat("value=%f", _icp2MaxCorrespondenceDistance).c_str());
|
||||||
UASSERT_MSG(_icp2MaxIterations > 0, uFormat("value=%d", _icp2MaxIterations).c_str());
|
UASSERT_MSG(_icp2MaxIterations > 0, uFormat("value=%d", _icp2MaxIterations).c_str());
|
||||||
UASSERT_MSG(_icp2MaxFitness > 0.0f, uFormat("value=%f", _icp2MaxFitness).c_str());
|
UASSERT_MSG(_icp2MaxFitness > 0.0f, uFormat("value=%f", _icp2MaxFitness).c_str());
|
||||||
@@ -1844,28 +1849,17 @@ Transform Memory::computeIcpTransform(int oldId, int newId, Transform guess, boo
|
|||||||
//make sure data are uncompressed
|
//make sure data are uncompressed
|
||||||
if(icp3D)
|
if(icp3D)
|
||||||
{
|
{
|
||||||
if(oldS->getDepthRaw().empty())
|
cv::Mat tmp1, tmp2;
|
||||||
{
|
oldS->uncompressData(0, &tmp1, 0);
|
||||||
oldS->setDepthRaw(util3d::uncompressImage(oldS->getDepthCompressed()));
|
newS->uncompressData(0, &tmp2, 0);
|
||||||
}
|
|
||||||
if(newS->getDepthRaw().empty())
|
|
||||||
{
|
|
||||||
newS->setDepthRaw(util3d::uncompressImage(newS->getDepthCompressed()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(oldS->getDepth2DRaw().empty())
|
cv::Mat tmp1, tmp2;
|
||||||
{
|
oldS->uncompressData(0, 0, &tmp1);
|
||||||
oldS->setDepth2DRaw(util3d::uncompressData(oldS->getDepth2DCompressed()));
|
newS->uncompressData(0, 0, &tmp2);
|
||||||
}
|
|
||||||
if(newS->getDepth2DRaw().empty())
|
|
||||||
{
|
|
||||||
newS->setDepth2DRaw(util3d::uncompressData(newS->getDepth2DCompressed()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
t = computeIcpTransform(*oldS, *newS, guess, icp3D, rejectedMsg);
|
t = computeIcpTransform(*oldS, *newS, guess, icp3D, rejectedMsg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1933,25 +1927,40 @@ Transform Memory::computeIcpTransform(const Signature & oldS, const Signature &
|
|||||||
_icpSamples,
|
_icpSamples,
|
||||||
guess * newS.getLocalTransform());
|
guess * newS.getLocalTransform());
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr oldCloud = util3d::computeNormals(oldCloudXYZ);
|
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr newCloud = util3d::computeNormals(newCloudXYZ);
|
|
||||||
|
|
||||||
std::vector<int> indices;
|
|
||||||
newCloud = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(newCloud);
|
|
||||||
oldCloud = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(oldCloud);
|
|
||||||
|
|
||||||
// 3D
|
// 3D
|
||||||
double fitness = 0;
|
double fitness = 0;
|
||||||
bool hasConverged = false;
|
bool hasConverged = false;
|
||||||
Transform icpT;
|
Transform icpT;
|
||||||
if(newCloud->size() && oldCloud->size())
|
if(newCloudXYZ->size() && oldCloudXYZ->size())
|
||||||
{
|
{
|
||||||
icpT = util3d::icpPointToPlane(newCloud,
|
if(_icpPointToPlane)
|
||||||
oldCloud,
|
{
|
||||||
_icpMaxCorrespondenceDistance,
|
pcl::PointCloud<pcl::PointNormal>::Ptr oldCloud = util3d::computeNormals(oldCloudXYZ, _icpPointToPlaneNormalNeighbors);
|
||||||
_icpMaxIterations,
|
pcl::PointCloud<pcl::PointNormal>::Ptr newCloud = util3d::computeNormals(newCloudXYZ, _icpPointToPlaneNormalNeighbors);
|
||||||
hasConverged,
|
|
||||||
fitness);
|
std::vector<int> indices;
|
||||||
|
newCloud = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(newCloud);
|
||||||
|
oldCloud = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(oldCloud);
|
||||||
|
|
||||||
|
if(newCloud->size() && oldCloud->size())
|
||||||
|
{
|
||||||
|
icpT = util3d::icpPointToPlane(newCloud,
|
||||||
|
oldCloud,
|
||||||
|
_icpMaxCorrespondenceDistance,
|
||||||
|
_icpMaxIterations,
|
||||||
|
hasConverged,
|
||||||
|
fitness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform = util3d::icp(newCloudXYZ,
|
||||||
|
oldCloudXYZ,
|
||||||
|
_icpMaxCorrespondenceDistance,
|
||||||
|
_icpMaxIterations,
|
||||||
|
hasConverged,
|
||||||
|
fitness);
|
||||||
|
}
|
||||||
|
|
||||||
//pcl::io::savePCDFile("old.pcd", *oldCloudXYZ);
|
//pcl::io::savePCDFile("old.pcd", *oldCloudXYZ);
|
||||||
//pcl::io::savePCDFile("newguess.pcd", *newCloudXYZ);
|
//pcl::io::savePCDFile("newguess.pcd", *newCloudXYZ);
|
||||||
|
|||||||
@@ -2002,78 +2002,7 @@ void Rtabmap::optimizeCurrentMap(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
util3d::optimizeTOROGraph(ids, poses, edgeConstraints, optimizedPoses, _toroIterations, true);
|
||||||
// Modify IDs using the margin from the current signature (TORO root will be the last signature)
|
|
||||||
int m = 0;
|
|
||||||
int toroId = 1;
|
|
||||||
std::map<int, int> rtabmapToToro; // <RTAB-Map ID, TORO ID>
|
|
||||||
std::map<int, int> toroToRtabmap; // <TORO ID, RTAB-Map ID>
|
|
||||||
while(ids.size())
|
|
||||||
{
|
|
||||||
for(std::map<int, int>::iterator iter = ids.begin(); iter!=ids.end();)
|
|
||||||
{
|
|
||||||
if(m == iter->second)
|
|
||||||
{
|
|
||||||
rtabmapToToro.insert(std::make_pair(iter->first, toroId));
|
|
||||||
toroToRtabmap.insert(std::make_pair(toroId, iter->first));
|
|
||||||
++toroId;
|
|
||||||
ids.erase(iter++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++m;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
std::map<int, Transform> posesToro;
|
|
||||||
std::multimap<int, Link> edgeConstraintsToro;
|
|
||||||
for(std::map<int, Transform>::iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
|
||||||
{
|
|
||||||
posesToro.insert(std::make_pair(rtabmapToToro.at(iter->first), iter->second));
|
|
||||||
}
|
|
||||||
for(std::multimap<int, Link>::iterator iter = edgeConstraints.begin();
|
|
||||||
iter!=edgeConstraints.end();
|
|
||||||
++iter)
|
|
||||||
{
|
|
||||||
edgeConstraintsToro.insert(std::make_pair(rtabmapToToro.at(iter->first), Link(rtabmapToToro.at(iter->first), rtabmapToToro.at(iter->second.to()), iter->second.transform(), iter->second.type())));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(posesToro.size() > 1 && edgeConstraintsToro.size() > 0)
|
|
||||||
{
|
|
||||||
///UDEBUG("TORO optimize begin");
|
|
||||||
//util3d::saveTOROGraph("toroIdRtabmap.graph", poses, edgeConstraints);
|
|
||||||
//util3d::saveTOROGraph("toroIdToro.graph", posesToro, edgeConstraintsToro);
|
|
||||||
//UDEBUG("graph saved");
|
|
||||||
|
|
||||||
std::map<int, Transform> optimizedPosesToro;
|
|
||||||
Transform mapCorrectionToro;
|
|
||||||
util3d::optimizeTOROGraph(posesToro, edgeConstraintsToro, optimizedPosesToro, mapCorrectionToro, _toroIterations, true);
|
|
||||||
|
|
||||||
//UDEBUG("saving optimized graph...");
|
|
||||||
//util3d::saveTOROGraph("toroOptimized.graph", optimizedPosesToro, edgeConstraintsToro);
|
|
||||||
//UDEBUG("TORO optimize end");
|
|
||||||
|
|
||||||
for(std::map<int, Transform>::iterator iter=optimizedPosesToro.begin(); iter!=optimizedPosesToro.end(); ++iter)
|
|
||||||
{
|
|
||||||
optimizedPoses.insert(std::make_pair(toroToRtabmap.at(iter->first), iter->second));
|
|
||||||
}
|
|
||||||
|
|
||||||
//util3d::saveTOROGraph("toro.graph", poses, edgeConstraints);
|
|
||||||
//util3d::saveTOROGraph("toroOptimized.graph", _optimizedPoses, edgeConstraints);
|
|
||||||
}
|
|
||||||
else if(poses.size() == 1 && edgeConstraints.size() == 0)
|
|
||||||
{
|
|
||||||
optimizedPoses = poses;
|
|
||||||
}
|
|
||||||
else if(poses.size() || edgeConstraints.size())
|
|
||||||
{
|
|
||||||
UFATAL("Poses=%d and edges=%d (poses must "
|
|
||||||
"not be null if there are edges, and edges must be null if poses <= 1)",
|
|
||||||
poses.size(), edgeConstraints.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2049,16 +2049,201 @@ pcl::PolygonMesh::Ptr createMesh(
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::multimap<int, Link>::iterator findLink(
|
||||||
|
std::multimap<int, Link> & links,
|
||||||
|
int from,
|
||||||
|
int to)
|
||||||
|
{
|
||||||
|
std::multimap<int, Link>::iterator iter = links.find(from);
|
||||||
|
while(iter != links.end() && iter->first == from)
|
||||||
|
{
|
||||||
|
if(iter->second.to() == to)
|
||||||
|
{
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's try to -> from
|
||||||
|
iter = links.find(to);
|
||||||
|
while(iter != links.end() && iter->first == to)
|
||||||
|
{
|
||||||
|
if(iter->second.to() == from)
|
||||||
|
{
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
return links.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// <int, depth> margin=0 means infinite margin
|
||||||
|
std::map<int, int> generateDepthGraph(
|
||||||
|
const std::multimap<int, Link> & links,
|
||||||
|
int fromId,
|
||||||
|
int depth)
|
||||||
|
{
|
||||||
|
UASSERT(depth >= 0);
|
||||||
|
//UDEBUG("signatureId=%d, neighborsMargin=%d", signatureId, margin);
|
||||||
|
std::map<int, int> ids;
|
||||||
|
if(fromId<=0)
|
||||||
|
{
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<int> curentDepthList;
|
||||||
|
std::set<int> nextDepth;
|
||||||
|
nextDepth.insert(fromId);
|
||||||
|
int d = 0;
|
||||||
|
while((depth == 0 || d < depth) && nextDepth.size())
|
||||||
|
{
|
||||||
|
curentDepthList = std::list<int>(nextDepth.begin(), nextDepth.end());
|
||||||
|
nextDepth.clear();
|
||||||
|
|
||||||
|
for(std::list<int>::iterator jter = curentDepthList.begin(); jter!=curentDepthList.end(); ++jter)
|
||||||
|
{
|
||||||
|
if(ids.find(*jter) == ids.end())
|
||||||
|
{
|
||||||
|
std::set<int> marginIds;
|
||||||
|
|
||||||
|
ids.insert(std::pair<int, int>(*jter, d));
|
||||||
|
|
||||||
|
for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(iter->second.from() == *jter)
|
||||||
|
{
|
||||||
|
marginIds.insert(iter->second.to());
|
||||||
|
}
|
||||||
|
else if(iter->second.to() == *jter)
|
||||||
|
{
|
||||||
|
marginIds.insert(iter->second.from());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Margin links
|
||||||
|
for(std::set<int>::const_iterator iter=marginIds.begin(); iter!=marginIds.end(); ++iter)
|
||||||
|
{
|
||||||
|
if( !uContains(ids, *iter) && nextDepth.find(*iter) == nextDepth.end())
|
||||||
|
{
|
||||||
|
nextDepth.insert(*iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++d;
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
void optimizeTOROGraph(
|
||||||
|
const std::map<int, int> & depthGraph,
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
const std::multimap<int, Link> & links,
|
||||||
|
std::map<int, Transform> & optimizedPoses,
|
||||||
|
int toroIterations,
|
||||||
|
bool toroInitialGuess,
|
||||||
|
std::list<std::map<int, Transform> > * intermediateGraphes)
|
||||||
|
{
|
||||||
|
optimizedPoses.clear();
|
||||||
|
if(depthGraph.size() && poses.size()>=2 && links.size()>=1)
|
||||||
|
{
|
||||||
|
// Modify IDs using the margin from the current signature (TORO root will be the last signature)
|
||||||
|
int m = 0;
|
||||||
|
int toroId = 1;
|
||||||
|
std::map<int, int> rtabmapToToro; // <RTAB-Map ID, TORO ID>
|
||||||
|
std::map<int, int> toroToRtabmap; // <TORO ID, RTAB-Map ID>
|
||||||
|
std::map<int, int> idsTmp = depthGraph;
|
||||||
|
while(idsTmp.size())
|
||||||
|
{
|
||||||
|
for(std::map<int, int>::iterator iter = idsTmp.begin(); iter!=idsTmp.end();)
|
||||||
|
{
|
||||||
|
if(m == iter->second)
|
||||||
|
{
|
||||||
|
rtabmapToToro.insert(std::make_pair(iter->first, toroId));
|
||||||
|
toroToRtabmap.insert(std::make_pair(toroId, iter->first));
|
||||||
|
++toroId;
|
||||||
|
idsTmp.erase(iter++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++m;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, rtabmap::Transform> posesToro;
|
||||||
|
std::multimap<int, rtabmap::Link> edgeConstraintsToro;
|
||||||
|
for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(uContains(depthGraph, iter->first))
|
||||||
|
{
|
||||||
|
posesToro.insert(std::make_pair(rtabmapToToro.at(iter->first), iter->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(std::multimap<int, rtabmap::Link>::const_iterator iter = links.begin();
|
||||||
|
iter!=links.end();
|
||||||
|
++iter)
|
||||||
|
{
|
||||||
|
if(uContains(depthGraph, iter->second.from()) && uContains(depthGraph, iter->second.to()))
|
||||||
|
{
|
||||||
|
edgeConstraintsToro.insert(std::make_pair(rtabmapToToro.at(iter->first), rtabmap::Link(rtabmapToToro.at(iter->first), rtabmapToToro.at(iter->second.to()), iter->second.transform(), iter->second.type())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, rtabmap::Transform> optimizedPosesToro;
|
||||||
|
|
||||||
|
// Optimize!
|
||||||
|
if(posesToro.size() && edgeConstraintsToro.size())
|
||||||
|
{
|
||||||
|
std::list<std::map<int, rtabmap::Transform> > graphesToro;
|
||||||
|
rtabmap::util3d::optimizeTOROGraph(posesToro, edgeConstraintsToro, optimizedPosesToro, toroIterations, toroInitialGuess, &graphesToro);
|
||||||
|
for(std::map<int, rtabmap::Transform>::iterator iter=optimizedPosesToro.begin(); iter!=optimizedPosesToro.end(); ++iter)
|
||||||
|
{
|
||||||
|
optimizedPoses.insert(std::make_pair(toroToRtabmap.at(iter->first), iter->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(intermediateGraphes)
|
||||||
|
{
|
||||||
|
for(std::list<std::map<int, rtabmap::Transform> >::iterator iter = graphesToro.begin(); iter!=graphesToro.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::map<int, rtabmap::Transform> tmp;
|
||||||
|
for(std::map<int, rtabmap::Transform>::iterator jter=iter->begin(); jter!=iter->end(); ++jter)
|
||||||
|
{
|
||||||
|
tmp.insert(std::make_pair(toroToRtabmap.at(jter->first), jter->second));
|
||||||
|
}
|
||||||
|
intermediateGraphes->push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UERROR("No TORO poses and constraints!?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(links.size() == 0 && poses.size() == 1)
|
||||||
|
{
|
||||||
|
optimizedPoses = poses;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UERROR("Wrong inputs! depthGraph=%d poses=%d links=%d",
|
||||||
|
(int)depthGraph.size(), (int)poses.size(), (int)links.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//On success, optimizedPoses is cleared and new poses are inserted in
|
//On success, optimizedPoses is cleared and new poses are inserted in
|
||||||
void optimizeTOROGraph(
|
void optimizeTOROGraph(
|
||||||
const std::map<int, Transform> & poses,
|
const std::map<int, Transform> & poses,
|
||||||
const std::multimap<int, Link> & edgeConstraints,
|
const std::multimap<int, Link> & edgeConstraints,
|
||||||
std::map<int, Transform> & optimizedPoses,
|
std::map<int, Transform> & optimizedPoses,
|
||||||
Transform & mapCorrection,
|
|
||||||
int toroIterations,
|
int toroIterations,
|
||||||
bool toroInitialGuess,
|
bool toroInitialGuess,
|
||||||
std::list<std::map<int, Transform> > * intermediateGraphes) // contains poses after tree init to last one before the end
|
std::list<std::map<int, Transform> > * intermediateGraphes) // contains poses after tree init to last one before the end
|
||||||
{
|
{
|
||||||
|
UASSERT(toroIterations>0);
|
||||||
|
optimizedPoses.clear();
|
||||||
if(edgeConstraints.size()>=1 && poses.size()>=2)
|
if(edgeConstraints.size()>=1 && poses.size()>=2)
|
||||||
{
|
{
|
||||||
// Apply TORO optimization
|
// Apply TORO optimization
|
||||||
@@ -2133,7 +2318,6 @@ void optimizeTOROGraph(
|
|||||||
}
|
}
|
||||||
UDEBUG("TORO iterate end");
|
UDEBUG("TORO iterate end");
|
||||||
|
|
||||||
optimizedPoses.clear();
|
|
||||||
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
||||||
{
|
{
|
||||||
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
|
AISNavigation::TreePoseGraph<AISNavigation::Operations3D<double> >::Vertex* v=pg.vertex(iter->first);
|
||||||
@@ -2143,15 +2327,19 @@ void optimizeTOROGraph(
|
|||||||
optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
|
optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Matrix4f newPose = transformToEigen4f(optimizedPoses.at(poses.rbegin()->first));
|
//Eigen::Matrix4f newPose = transformToEigen4f(optimizedPoses.at(poses.rbegin()->first));
|
||||||
Eigen::Matrix4f oldPose = transformToEigen4f(poses.rbegin()->second);
|
//Eigen::Matrix4f oldPose = transformToEigen4f(poses.rbegin()->second);
|
||||||
Eigen::Matrix4f poseCorrection = oldPose.inverse() * newPose; // transform from odom to correct odom
|
//Eigen::Matrix4f poseCorrection = oldPose.inverse() * newPose; // transform from odom to correct odom
|
||||||
Eigen::Matrix4f result = oldPose*poseCorrection*oldPose.inverse();
|
//Eigen::Matrix4f result = oldPose*poseCorrection*oldPose.inverse();
|
||||||
mapCorrection = transformFromEigen4f(result);
|
//mapCorrection = transformFromEigen4f(result);
|
||||||
|
}
|
||||||
|
else if(edgeConstraints.size() == 0 && poses.size() == 1)
|
||||||
|
{
|
||||||
|
optimizedPoses = poses;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UWARN("This method should be called at least with 2 poses and one link!");
|
UWARN("This method should be called at least with 1 pose!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2707,7 +2895,7 @@ cv::Mat create2DMap(const std::map<int, Transform> & poses,
|
|||||||
pcl::PointCloud<pcl::PointXYZ> minMax;
|
pcl::PointCloud<pcl::PointXYZ> minMax;
|
||||||
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(uContains(scans, iter->first))
|
if(uContains(scans, iter->first) && scans.size())
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = transformPointCloud<pcl::PointXYZ>(scans.at(iter->first), iter->second);
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = transformPointCloud<pcl::PointXYZ>(scans.at(iter->first), iter->second);
|
||||||
pcl::PointXYZ min, max;
|
pcl::PointXYZ min, max;
|
||||||
|
|||||||
@@ -106,10 +106,6 @@ private:
|
|||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudFrom = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>),
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudFrom = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>),
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>));
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>));
|
||||||
void updateConstraintButtons();
|
void updateConstraintButtons();
|
||||||
std::multimap<int, Link>::iterator findLink(
|
|
||||||
std::multimap<int, Link> & links,
|
|
||||||
int from,
|
|
||||||
int to);
|
|
||||||
Link findActiveLink(int from, int to);
|
Link findActiveLink(int from, int to);
|
||||||
bool containsLink(
|
bool containsLink(
|
||||||
std::multimap<int, Link> & links,
|
std::multimap<int, Link> & links,
|
||||||
@@ -120,12 +116,6 @@ private:
|
|||||||
void updateLoopClosuresSlider(int from = 0, int to = 0);
|
void updateLoopClosuresSlider(int from = 0, int to = 0);
|
||||||
void refineConstraint(int from, int to);
|
void refineConstraint(int from, int to);
|
||||||
bool addConstraint(int from, int to, bool silent);
|
bool addConstraint(int from, int to, bool silent);
|
||||||
std::map<int, int> generateGraph(int fromNode, int margin);
|
|
||||||
std::map<int, Transform> optimizeGraph(
|
|
||||||
const std::map<int, int> & ids,
|
|
||||||
const std::map<int, Transform> & poses,
|
|
||||||
const std::multimap<int, Link> & links,
|
|
||||||
std::list<std::map<int, rtabmap::Transform> > * graphes = 0);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui_DatabaseViewer * ui_;
|
Ui_DatabaseViewer * ui_;
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class StatsToolBox;
|
|||||||
class DetailedProgressDialog;
|
class DetailedProgressDialog;
|
||||||
class TwistGridWidget;
|
class TwistGridWidget;
|
||||||
class ExportCloudsDialog;
|
class ExportCloudsDialog;
|
||||||
|
class PostProcessingDialog;
|
||||||
|
|
||||||
class RTABMAPGUI_EXP MainWindow : public QMainWindow, public UEventsHandler
|
class RTABMAPGUI_EXP MainWindow : public QMainWindow, public UEventsHandler
|
||||||
{
|
{
|
||||||
@@ -123,6 +124,7 @@ private slots:
|
|||||||
void generateMap();
|
void generateMap();
|
||||||
void generateLocalMap();
|
void generateLocalMap();
|
||||||
void generateTOROMap();
|
void generateTOROMap();
|
||||||
|
void postProcessing();
|
||||||
void deleteMemory();
|
void deleteMemory();
|
||||||
void openWorkingDirectory();
|
void openWorkingDirectory();
|
||||||
void updateEditMenu();
|
void updateEditMenu();
|
||||||
@@ -249,6 +251,7 @@ private:
|
|||||||
PreferencesDialog * _preferencesDialog;
|
PreferencesDialog * _preferencesDialog;
|
||||||
AboutDialog * _aboutDialog;
|
AboutDialog * _aboutDialog;
|
||||||
ExportCloudsDialog * _exportDialog;
|
ExportCloudsDialog * _exportDialog;
|
||||||
|
PostProcessingDialog * _postProcessingDialog;
|
||||||
|
|
||||||
QSet<int> _lastIds;
|
QSet<int> _lastIds;
|
||||||
int _lastId;
|
int _lastId;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ SET(headers_ui
|
|||||||
../include/${PROJECT_PREFIX}/gui/DataRecorder.h
|
../include/${PROJECT_PREFIX}/gui/DataRecorder.h
|
||||||
../include/${PROJECT_PREFIX}/gui/CalibrationDialog.h
|
../include/${PROJECT_PREFIX}/gui/CalibrationDialog.h
|
||||||
./ExportDialog.h
|
./ExportDialog.h
|
||||||
|
./PostProcessingDialog.h
|
||||||
./ExportCloudsDialog.h
|
./ExportCloudsDialog.h
|
||||||
./MapVisibilityWidget.h
|
./MapVisibilityWidget.h
|
||||||
)
|
)
|
||||||
@@ -31,6 +32,7 @@ SET(uis
|
|||||||
./ui/DatabaseViewer.ui
|
./ui/DatabaseViewer.ui
|
||||||
./ui/loopClosureViewer.ui
|
./ui/loopClosureViewer.ui
|
||||||
./ui/exportDialog.ui
|
./ui/exportDialog.ui
|
||||||
|
./ui/postProcessingDialog.ui
|
||||||
./ui/exportCloudsDialog.ui
|
./ui/exportCloudsDialog.ui
|
||||||
./ui/calibrationDialog.ui
|
./ui/calibrationDialog.ui
|
||||||
)
|
)
|
||||||
@@ -70,6 +72,7 @@ SET(SRC_FILES
|
|||||||
./DataRecorder.cpp
|
./DataRecorder.cpp
|
||||||
./CalibrationDialog.cpp
|
./CalibrationDialog.cpp
|
||||||
./ExportDialog.cpp
|
./ExportDialog.cpp
|
||||||
|
./PostProcessingDialog.cpp
|
||||||
./ExportCloudsDialog.cpp
|
./ExportCloudsDialog.cpp
|
||||||
./MapVisibilityWidget.cpp
|
./MapVisibilityWidget.cpp
|
||||||
./GraphViewer.cpp
|
./GraphViewer.cpp
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ void DatabaseViewer::closeEvent(QCloseEvent* event)
|
|||||||
// Added links
|
// Added links
|
||||||
for(std::multimap<int, rtabmap::Link>::iterator iter=linksAdded_.begin(); iter!=linksAdded_.end(); ++iter)
|
for(std::multimap<int, rtabmap::Link>::iterator iter=linksAdded_.begin(); iter!=linksAdded_.end(); ++iter)
|
||||||
{
|
{
|
||||||
std::multimap<int, rtabmap::Link>::iterator refinedIter = this->findLink(linksRefined_, iter->second.from(), iter->second.to());
|
std::multimap<int, rtabmap::Link>::iterator refinedIter = util3d::findLink(linksRefined_, iter->second.from(), iter->second.to());
|
||||||
if(refinedIter != linksRefined_.end())
|
if(refinedIter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
memory_->addLoopClosureLink(refinedIter->second.to(), refinedIter->second.from(), refinedIter->second.transform(), true);
|
memory_->addLoopClosureLink(refinedIter->second.to(), refinedIter->second.from(), refinedIter->second.transform(), true);
|
||||||
@@ -541,156 +541,6 @@ void DatabaseViewer::generateTOROGraph()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// margin=0 means infinite margin
|
|
||||||
std::map<int, int> DatabaseViewer::generateGraph(int fromNode, int margin)
|
|
||||||
{
|
|
||||||
UASSERT(margin >= 0);
|
|
||||||
//UDEBUG("signatureId=%d, neighborsMargin=%d", signatureId, margin);
|
|
||||||
std::map<int, int> ids;
|
|
||||||
if(fromNode<=0)
|
|
||||||
{
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<int> curentMarginList;
|
|
||||||
std::set<int> currentMargin;
|
|
||||||
std::set<int> nextMargin;
|
|
||||||
nextMargin.insert(fromNode);
|
|
||||||
int m = 0;
|
|
||||||
while((margin == 0 || m < margin) && nextMargin.size())
|
|
||||||
{
|
|
||||||
curentMarginList = std::list<int>(nextMargin.begin(), nextMargin.end());
|
|
||||||
nextMargin.clear();
|
|
||||||
|
|
||||||
for(std::list<int>::iterator jter = curentMarginList.begin(); jter!=curentMarginList.end(); ++jter)
|
|
||||||
{
|
|
||||||
if(ids.find(*jter) == ids.end())
|
|
||||||
{
|
|
||||||
std::set<int> marginIds;
|
|
||||||
|
|
||||||
ids.insert(std::pair<int, int>(*jter, m));
|
|
||||||
|
|
||||||
for(int i=0; i<neighborLinks_.size(); ++i)
|
|
||||||
{
|
|
||||||
if(neighborLinks_[i].from() == *jter)
|
|
||||||
{
|
|
||||||
marginIds.insert(neighborLinks_[i].to());
|
|
||||||
}
|
|
||||||
else if(neighborLinks_[i].to() == *jter)
|
|
||||||
{
|
|
||||||
marginIds.insert(neighborLinks_[i].from());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int i=0; i<loopLinks_.size(); ++i)
|
|
||||||
{
|
|
||||||
if(loopLinks_[i].from() == *jter)
|
|
||||||
{
|
|
||||||
marginIds.insert(loopLinks_[i].to());
|
|
||||||
}
|
|
||||||
else if(loopLinks_[i].to() == *jter)
|
|
||||||
{
|
|
||||||
marginIds.insert(loopLinks_[i].from());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Margin links
|
|
||||||
for(std::set<int>::const_iterator iter=marginIds.begin(); iter!=marginIds.end(); ++iter)
|
|
||||||
{
|
|
||||||
if( !uContains(ids, *iter) && nextMargin.find(*iter) == nextMargin.end())
|
|
||||||
{
|
|
||||||
nextMargin.insert(*iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++m;
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, Transform> DatabaseViewer::optimizeGraph(
|
|
||||||
const std::map<int, int> & ids,
|
|
||||||
const std::map<int, Transform> & poses,
|
|
||||||
const std::multimap<int, Link> & links,
|
|
||||||
std::list<std::map<int, rtabmap::Transform> > * graphes)
|
|
||||||
{
|
|
||||||
std::map<int, rtabmap::Transform> optimizedPoses;
|
|
||||||
if(ids.size() && poses.size())
|
|
||||||
{
|
|
||||||
// Modify IDs using the margin from the current signature (TORO root will be the last signature)
|
|
||||||
int m = 0;
|
|
||||||
int toroId = 1;
|
|
||||||
std::map<int, int> rtabmapToToro; // <RTAB-Map ID, TORO ID>
|
|
||||||
std::map<int, int> toroToRtabmap; // <TORO ID, RTAB-Map ID>
|
|
||||||
std::map<int, int> idsTmp = ids;
|
|
||||||
while(idsTmp.size())
|
|
||||||
{
|
|
||||||
for(std::map<int, int>::iterator iter = idsTmp.begin(); iter!=idsTmp.end();)
|
|
||||||
{
|
|
||||||
if(m == iter->second)
|
|
||||||
{
|
|
||||||
rtabmapToToro.insert(std::make_pair(iter->first, toroId));
|
|
||||||
toroToRtabmap.insert(std::make_pair(toroId, iter->first));
|
|
||||||
++toroId;
|
|
||||||
idsTmp.erase(iter++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++m;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, rtabmap::Transform> posesToro;
|
|
||||||
std::multimap<int, rtabmap::Link> edgeConstraintsToro;
|
|
||||||
for(std::map<int, rtabmap::Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
|
||||||
{
|
|
||||||
if(uContains(ids, iter->first))
|
|
||||||
{
|
|
||||||
posesToro.insert(std::make_pair(rtabmapToToro.at(iter->first), iter->second));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(std::multimap<int, rtabmap::Link>::const_iterator iter = links.begin();
|
|
||||||
iter!=links.end();
|
|
||||||
++iter)
|
|
||||||
{
|
|
||||||
if(uContains(ids, iter->second.from()) && uContains(ids, iter->second.to()))
|
|
||||||
{
|
|
||||||
edgeConstraintsToro.insert(std::make_pair(rtabmapToToro.at(iter->first), rtabmap::Link(rtabmapToToro.at(iter->first), rtabmapToToro.at(iter->second.to()), iter->second.transform(), iter->second.type())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, rtabmap::Transform> optimizedPosesToro;
|
|
||||||
rtabmap::Transform mapCorrectionToro;
|
|
||||||
|
|
||||||
// Optimize!
|
|
||||||
if(posesToro.size() && edgeConstraintsToro.size())
|
|
||||||
{
|
|
||||||
std::list<std::map<int, rtabmap::Transform> > graphesToro;
|
|
||||||
rtabmap::util3d::optimizeTOROGraph(posesToro, edgeConstraintsToro, optimizedPosesToro, mapCorrectionToro, ui_->spinBox_iterations->value(), ui_->checkBox_initGuess->isChecked(), &graphesToro);
|
|
||||||
for(std::map<int, rtabmap::Transform>::iterator iter=optimizedPosesToro.begin(); iter!=optimizedPosesToro.end(); ++iter)
|
|
||||||
{
|
|
||||||
optimizedPoses.insert(std::make_pair(toroToRtabmap.at(iter->first), iter->second));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(graphes)
|
|
||||||
{
|
|
||||||
for(std::list<std::map<int, rtabmap::Transform> >::iterator iter = graphesToro.begin(); iter!=graphesToro.end(); ++iter)
|
|
||||||
{
|
|
||||||
std::map<int, rtabmap::Transform> tmp;
|
|
||||||
for(std::map<int, rtabmap::Transform>::iterator jter=iter->begin(); jter!=iter->end(); ++jter)
|
|
||||||
{
|
|
||||||
tmp.insert(std::make_pair(toroToRtabmap.at(jter->first), jter->second));
|
|
||||||
}
|
|
||||||
graphes->push_back(tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return optimizedPoses;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatabaseViewer::view3DMap()
|
void DatabaseViewer::view3DMap()
|
||||||
{
|
{
|
||||||
if(!ids_.size() || !memory_)
|
if(!ids_.size() || !memory_)
|
||||||
@@ -715,17 +565,19 @@ void DatabaseViewer::view3DMap()
|
|||||||
double maxDepth = QInputDialog::getDouble(this, tr("Camera depth?"), tr("Maximum depth (m, 0=no max):"), 4.0, 0, 10, 2, &ok);
|
double maxDepth = QInputDialog::getDouble(this, tr("Camera depth?"), tr("Maximum depth (m, 0=no max):"), 4.0, 0, 10, 2, &ok);
|
||||||
if(ok)
|
if(ok)
|
||||||
{
|
{
|
||||||
|
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
||||||
// <id, depth>
|
// <id, depth>
|
||||||
std::map<int, int> ids = generateGraph(ui_->spinBox_optimizationsFrom->value(), margin);
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(links, ui_->spinBox_optimizationsFrom->value(), margin);
|
||||||
if(ids.size() > 0)
|
if(depthGraph.size() > 0)
|
||||||
{
|
{
|
||||||
rtabmap::DetailedProgressDialog progressDialog(this);
|
rtabmap::DetailedProgressDialog progressDialog(this);
|
||||||
progressDialog.setMaximumSteps(ids.size()+2);
|
progressDialog.setMaximumSteps(depthGraph.size()+2);
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
|
|
||||||
progressDialog.appendText("Graph optimization...");
|
progressDialog.appendText("Graph optimization...");
|
||||||
std::multimap<int, Link> links = updateLinksWithModifications(links_);
|
std::multimap<int, Link> links = updateLinksWithModifications(links_);
|
||||||
std::map<int, Transform> optimizedPoses = optimizeGraph(ids, poses_, links);
|
std::map<int, Transform> optimizedPoses;
|
||||||
|
util3d::optimizeTOROGraph(depthGraph, poses_, links, optimizedPoses, ui_->spinBox_iterations->value(), ui_->checkBox_initGuess->isChecked());
|
||||||
progressDialog.appendText("Graph optimization... done!");
|
progressDialog.appendText("Graph optimization... done!");
|
||||||
progressDialog.incrementStep();
|
progressDialog.incrementStep();
|
||||||
|
|
||||||
@@ -846,28 +698,29 @@ void DatabaseViewer::generate3DMap()
|
|||||||
QString path = QFileDialog::getExistingDirectory(this, tr("Save directory"), pathDatabase_);
|
QString path = QFileDialog::getExistingDirectory(this, tr("Save directory"), pathDatabase_);
|
||||||
if(!path.isEmpty())
|
if(!path.isEmpty())
|
||||||
{
|
{
|
||||||
std::map<int, int> ids = this->generateGraph(id, margin);
|
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
||||||
if(ids.size() > 0)
|
// <id, depth>
|
||||||
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(links, id, margin);
|
||||||
|
if(depthGraph.size() > 0)
|
||||||
{
|
{
|
||||||
rtabmap::DetailedProgressDialog progressDialog;
|
rtabmap::DetailedProgressDialog progressDialog;
|
||||||
progressDialog.setMaximumSteps((int)ids.size()+2);
|
progressDialog.setMaximumSteps((int)depthGraph.size()+2);
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
|
|
||||||
progressDialog.appendText("Graph generation...");
|
progressDialog.appendText("Graph generation...");
|
||||||
std::map<int, rtabmap::Transform> poses, optimizedPoses;
|
std::map<int, rtabmap::Transform> poses, optimizedPoses;
|
||||||
std::multimap<int, rtabmap::Link> edgeConstraints;
|
std::multimap<int, rtabmap::Link> edgeConstraints;
|
||||||
memory_->getMetricConstraints(uKeys(ids), poses, edgeConstraints, true);
|
memory_->getMetricConstraints(uKeys(depthGraph), poses, edgeConstraints, true);
|
||||||
edgeConstraints = updateLinksWithModifications(edgeConstraints);
|
edgeConstraints = updateLinksWithModifications(edgeConstraints);
|
||||||
progressDialog.appendText("Graph generation... done!");
|
progressDialog.appendText("Graph generation... done!");
|
||||||
progressDialog.incrementStep();
|
progressDialog.incrementStep();
|
||||||
|
|
||||||
progressDialog.appendText("Graph optimization...");
|
progressDialog.appendText("Graph optimization...");
|
||||||
rtabmap::Transform mapCorrection;
|
rtabmap::util3d::optimizeTOROGraph(poses, edgeConstraints, optimizedPoses, ui_->spinBox_iterations->value(), ui_->checkBox_initGuess->isChecked());
|
||||||
rtabmap::util3d::optimizeTOROGraph(poses, edgeConstraints, optimizedPoses, mapCorrection, 100, true);
|
|
||||||
progressDialog.appendText("Graph optimization... done!");
|
progressDialog.appendText("Graph optimization... done!");
|
||||||
progressDialog.incrementStep();
|
progressDialog.incrementStep();
|
||||||
|
|
||||||
for(std::map<int, int>::iterator iter = ids.begin(); iter!=ids.end(); ++iter)
|
for(std::map<int, int>::iterator iter = depthGraph.begin(); iter!=depthGraph.end(); ++iter)
|
||||||
{
|
{
|
||||||
rtabmap::Transform pose = uValue(optimizedPoses, iter->first, rtabmap::Transform());
|
rtabmap::Transform pose = uValue(optimizedPoses, iter->first, rtabmap::Transform());
|
||||||
if(!pose.isNull())
|
if(!pose.isNull())
|
||||||
@@ -920,7 +773,7 @@ void DatabaseViewer::generate3DMap()
|
|||||||
}
|
}
|
||||||
progressDialog.setValue(progressDialog.maximumSteps());
|
progressDialog.setValue(progressDialog.maximumSteps());
|
||||||
|
|
||||||
QMessageBox::information(this, tr("Finished"), tr("%1 clouds generated to %2.").arg(ids.size()).arg(path));
|
QMessageBox::information(this, tr("Finished"), tr("%1 clouds generated to %2.").arg(depthGraph.size()).arg(path));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -936,29 +789,50 @@ void DatabaseViewer::generate3DMap()
|
|||||||
void DatabaseViewer::detectMoreLoopClosures()
|
void DatabaseViewer::detectMoreLoopClosures()
|
||||||
{
|
{
|
||||||
std::map<int, rtabmap::Transform> optimizedPoses;
|
std::map<int, rtabmap::Transform> optimizedPoses;
|
||||||
std::map<int, int> ids = this->generateGraph(ui_->spinBox_optimizationsFrom->value(), 0);
|
|
||||||
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
|
||||||
optimizedPoses = optimizeGraph(ids, poses_, links);
|
|
||||||
|
|
||||||
std::multimap<int, int> clusters = util3d::radiusPosesClustering(
|
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
||||||
optimizedPoses,
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(links, ui_->spinBox_optimizationsFrom->value());
|
||||||
ui_->doubleSpinBox_detectMore_radius->value(),
|
util3d::optimizeTOROGraph(depthGraph, poses_, links, optimizedPoses, ui_->spinBox_iterations->value(), ui_->checkBox_initGuess->isChecked());
|
||||||
ui_->doubleSpinBox_detectMore_angle->value());
|
|
||||||
|
int iterations = ui_->doubleSpinBox_detectMore_iterations->value();
|
||||||
|
UASSERT(iterations > 0);
|
||||||
int added = 0;
|
int added = 0;
|
||||||
for(std::multimap<int, int>::iterator iter=clusters.begin(); iter!= clusters.end(); ++iter)
|
for(int n=0; n<iterations; ++n)
|
||||||
{
|
{
|
||||||
int from = iter->first;
|
UINFO("iteration %d/%d", n+1, iterations);
|
||||||
int to = iter->second;
|
std::multimap<int, int> clusters = util3d::radiusPosesClustering(
|
||||||
if(!findActiveLink(from, to).isValid() && !containsLink(linksRemoved_, from, to))
|
optimizedPoses,
|
||||||
|
ui_->doubleSpinBox_detectMore_radius->value(),
|
||||||
|
ui_->doubleSpinBox_detectMore_angle->value()*CV_PI/180.0);
|
||||||
|
std::set<int> addedLinks;
|
||||||
|
for(std::multimap<int, int>::iterator iter=clusters.begin(); iter!= clusters.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(addConstraint(from, to, true))
|
int from = iter->first;
|
||||||
|
int to = iter->second;
|
||||||
|
if(from < to)
|
||||||
{
|
{
|
||||||
UINFO("Added new loop closure between %d and %d.", from, to);
|
from = iter->second;
|
||||||
++added;
|
to = iter->first;
|
||||||
|
}
|
||||||
|
if(!findActiveLink(from, to).isValid() && !containsLink(linksRemoved_, from, to) &&
|
||||||
|
addedLinks.find(from) == addedLinks.end() && addedLinks.find(to) == addedLinks.end())
|
||||||
|
{
|
||||||
|
if(addConstraint(from, to, true))
|
||||||
|
{
|
||||||
|
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||||
|
++added;
|
||||||
|
addedLinks.insert(from);
|
||||||
|
addedLinks.insert(to);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UINFO("Iteration %d/%d: added %d loop closures.", n+1, iterations, (int)addedLinks.size()/2);
|
||||||
|
if(addedLinks.size() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UINFO("Added %d loop closures.", added);
|
UINFO("Total added %d loop closures.", added);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseViewer::refineAllNeighborLinks()
|
void DatabaseViewer::refineAllNeighborLinks()
|
||||||
@@ -1292,7 +1166,7 @@ void DatabaseViewer::updateConstraintView(const rtabmap::Link & link,
|
|||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudFrom,
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudFrom,
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo)
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloudTo)
|
||||||
{
|
{
|
||||||
std::multimap<int, Link>::iterator iter = findLink(linksRefined_, link.from(), link.to());
|
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, link.from(), link.to());
|
||||||
rtabmap::Transform t = link.transform();
|
rtabmap::Transform t = link.transform();
|
||||||
if(iter != linksRefined_.end())
|
if(iter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
@@ -1342,101 +1216,104 @@ void DatabaseViewer::updateConstraintView(const rtabmap::Link & link,
|
|||||||
false); // don't update constraints view!
|
false); // don't update constraints view!
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cloudFrom->size() == 0 && cloudTo->size() == 0)
|
if(ui_->constraintsViewer->isVisible())
|
||||||
{
|
{
|
||||||
Signature dataFrom, dataTo;
|
if(cloudFrom->size() == 0 && cloudTo->size() == 0)
|
||||||
|
|
||||||
dataFrom = memory_->getSignatureData(link.from(), true);
|
|
||||||
UASSERT(dataFrom.getImageRaw().empty() || dataFrom.getImageRaw().type()==CV_8UC3 || dataFrom.getImageRaw().type() == CV_8UC1);
|
|
||||||
UASSERT(dataFrom.getDepthRaw().empty() || dataFrom.getDepthRaw().type()==CV_8UC1 || dataFrom.getDepthRaw().type() == CV_16UC1 || dataFrom.getDepthRaw().type() == CV_32FC1);
|
|
||||||
|
|
||||||
dataTo = memory_->getSignatureData(link.to(), true);
|
|
||||||
UASSERT(dataTo.getImageRaw().empty() || dataTo.getImageRaw().type()==CV_8UC3 || dataTo.getImageRaw().type() == CV_8UC1);
|
|
||||||
UASSERT(dataTo.getDepthRaw().empty() || dataTo.getDepthRaw().type()==CV_8UC1 || dataTo.getDepthRaw().type() == CV_16UC1 || dataTo.getDepthRaw().type() == CV_32FC1);
|
|
||||||
|
|
||||||
|
|
||||||
//cloud 3d
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom;
|
|
||||||
if(dataFrom.getDepthRaw().type() == CV_8UC1)
|
|
||||||
{
|
{
|
||||||
cloudFrom = rtabmap::util3d::cloudFromStereoImages(
|
Signature dataFrom, dataTo;
|
||||||
dataFrom.getImageRaw(),
|
|
||||||
dataFrom.getDepthRaw(),
|
dataFrom = memory_->getSignatureData(link.from(), true);
|
||||||
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
|
UASSERT(dataFrom.getImageRaw().empty() || dataFrom.getImageRaw().type()==CV_8UC3 || dataFrom.getImageRaw().type() == CV_8UC1);
|
||||||
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
|
UASSERT(dataFrom.getDepthRaw().empty() || dataFrom.getDepthRaw().type()==CV_8UC1 || dataFrom.getDepthRaw().type() == CV_16UC1 || dataFrom.getDepthRaw().type() == CV_32FC1);
|
||||||
1);
|
|
||||||
|
dataTo = memory_->getSignatureData(link.to(), true);
|
||||||
|
UASSERT(dataTo.getImageRaw().empty() || dataTo.getImageRaw().type()==CV_8UC3 || dataTo.getImageRaw().type() == CV_8UC1);
|
||||||
|
UASSERT(dataTo.getDepthRaw().empty() || dataTo.getDepthRaw().type()==CV_8UC1 || dataTo.getDepthRaw().type() == CV_16UC1 || dataTo.getDepthRaw().type() == CV_32FC1);
|
||||||
|
|
||||||
|
|
||||||
|
//cloud 3d
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudFrom;
|
||||||
|
if(dataFrom.getDepthRaw().type() == CV_8UC1)
|
||||||
|
{
|
||||||
|
cloudFrom = rtabmap::util3d::cloudFromStereoImages(
|
||||||
|
dataFrom.getImageRaw(),
|
||||||
|
dataFrom.getDepthRaw(),
|
||||||
|
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
|
||||||
|
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cloudFrom = rtabmap::util3d::cloudFromDepthRGB(
|
||||||
|
dataFrom.getImageRaw(),
|
||||||
|
dataFrom.getDepthRaw(),
|
||||||
|
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
|
||||||
|
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudFrom = rtabmap::util3d::removeNaNFromPointCloud<pcl::PointXYZRGB>(cloudFrom);
|
||||||
|
cloudFrom = rtabmap::util3d::transformPointCloud<pcl::PointXYZRGB>(cloudFrom, dataFrom.getLocalTransform());
|
||||||
|
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudTo;
|
||||||
|
if(dataTo.getDepthRaw().type() == CV_8UC1)
|
||||||
|
{
|
||||||
|
cloudTo = rtabmap::util3d::cloudFromStereoImages(
|
||||||
|
dataTo.getImageRaw(),
|
||||||
|
dataTo.getDepthRaw(),
|
||||||
|
dataTo.getDepthCx(), dataTo.getDepthCy(),
|
||||||
|
dataTo.getDepthFx(), dataTo.getDepthFy(),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cloudTo = rtabmap::util3d::cloudFromDepthRGB(
|
||||||
|
dataTo.getImageRaw(),
|
||||||
|
dataTo.getDepthRaw(),
|
||||||
|
dataTo.getDepthCx(), dataTo.getDepthCy(),
|
||||||
|
dataTo.getDepthFx(), dataTo.getDepthFy(),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudTo = rtabmap::util3d::removeNaNFromPointCloud<pcl::PointXYZRGB>(cloudTo);
|
||||||
|
cloudTo = rtabmap::util3d::transformPointCloud<pcl::PointXYZRGB>(cloudTo, t*dataTo.getLocalTransform());
|
||||||
|
|
||||||
|
//cloud 2d
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr scanA, scanB;
|
||||||
|
scanA = rtabmap::util3d::depth2DToPointCloud(dataFrom.getDepth2DRaw());
|
||||||
|
scanB = rtabmap::util3d::depth2DToPointCloud(dataTo.getDepth2DRaw());
|
||||||
|
scanB = rtabmap::util3d::transformPointCloud<pcl::PointXYZ>(scanB, t);
|
||||||
|
|
||||||
|
if(cloudFrom->size())
|
||||||
|
{
|
||||||
|
ui_->constraintsViewer->addOrUpdateCloud("cloud0", cloudFrom);
|
||||||
|
}
|
||||||
|
if(cloudTo->size())
|
||||||
|
{
|
||||||
|
ui_->constraintsViewer->addOrUpdateCloud("cloud1", cloudTo);
|
||||||
|
}
|
||||||
|
if(scanA->size())
|
||||||
|
{
|
||||||
|
ui_->constraintsViewer->addOrUpdateCloud("scan0", scanA);
|
||||||
|
}
|
||||||
|
if(scanB->size())
|
||||||
|
{
|
||||||
|
ui_->constraintsViewer->addOrUpdateCloud("scan1", scanB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cloudFrom = rtabmap::util3d::cloudFromDepthRGB(
|
if(cloudFrom->size())
|
||||||
dataFrom.getImageRaw(),
|
{
|
||||||
dataFrom.getDepthRaw(),
|
ui_->constraintsViewer->addOrUpdateCloud("cloud0", cloudFrom);
|
||||||
dataFrom.getDepthCx(), dataFrom.getDepthCy(),
|
}
|
||||||
dataFrom.getDepthFx(), dataFrom.getDepthFy(),
|
if(cloudTo->size())
|
||||||
1);
|
{
|
||||||
}
|
ui_->constraintsViewer->addOrUpdateCloud("cloud1", cloudTo);
|
||||||
|
}
|
||||||
cloudFrom = rtabmap::util3d::removeNaNFromPointCloud<pcl::PointXYZRGB>(cloudFrom);
|
|
||||||
cloudFrom = rtabmap::util3d::transformPointCloud<pcl::PointXYZRGB>(cloudFrom, dataFrom.getLocalTransform());
|
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudTo;
|
|
||||||
if(dataTo.getDepthRaw().type() == CV_8UC1)
|
|
||||||
{
|
|
||||||
cloudTo = rtabmap::util3d::cloudFromStereoImages(
|
|
||||||
dataTo.getImageRaw(),
|
|
||||||
dataTo.getDepthRaw(),
|
|
||||||
dataTo.getDepthCx(), dataTo.getDepthCy(),
|
|
||||||
dataTo.getDepthFx(), dataTo.getDepthFy(),
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cloudTo = rtabmap::util3d::cloudFromDepthRGB(
|
|
||||||
dataTo.getImageRaw(),
|
|
||||||
dataTo.getDepthRaw(),
|
|
||||||
dataTo.getDepthCx(), dataTo.getDepthCy(),
|
|
||||||
dataTo.getDepthFx(), dataTo.getDepthFy(),
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
|
|
||||||
cloudTo = rtabmap::util3d::removeNaNFromPointCloud<pcl::PointXYZRGB>(cloudTo);
|
|
||||||
cloudTo = rtabmap::util3d::transformPointCloud<pcl::PointXYZRGB>(cloudTo, t*dataTo.getLocalTransform());
|
|
||||||
|
|
||||||
//cloud 2d
|
|
||||||
pcl::PointCloud<pcl::PointXYZ>::Ptr scanA, scanB;
|
|
||||||
scanA = rtabmap::util3d::depth2DToPointCloud(dataFrom.getDepth2DRaw());
|
|
||||||
scanB = rtabmap::util3d::depth2DToPointCloud(dataTo.getDepth2DRaw());
|
|
||||||
scanB = rtabmap::util3d::transformPointCloud<pcl::PointXYZ>(scanB, t);
|
|
||||||
|
|
||||||
if(cloudFrom->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("cloud0", cloudFrom);
|
|
||||||
}
|
|
||||||
if(cloudTo->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("cloud1", cloudTo);
|
|
||||||
}
|
|
||||||
if(scanA->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("scan0", scanA);
|
|
||||||
}
|
|
||||||
if(scanB->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("scan1", scanB);
|
|
||||||
}
|
}
|
||||||
|
ui_->constraintsViewer->render();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if(cloudFrom->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("cloud0", cloudFrom);
|
|
||||||
}
|
|
||||||
if(cloudTo->size())
|
|
||||||
{
|
|
||||||
ui_->constraintsViewer->addOrUpdateCloud("cloud1", cloudTo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui_->constraintsViewer->render();
|
|
||||||
|
|
||||||
// update buttons
|
// update buttons
|
||||||
updateConstraintButtons();
|
updateConstraintButtons();
|
||||||
@@ -1472,7 +1349,7 @@ void DatabaseViewer::updateConstraintButtons()
|
|||||||
|
|
||||||
//check for modified link
|
//check for modified link
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
std::multimap<int, Link>::iterator iter = findLink(linksRefined_, currentLink.from(), currentLink.to());
|
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, currentLink.from(), currentLink.to());
|
||||||
if(iter != linksRefined_.end())
|
if(iter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
currentLink = iter->second;
|
currentLink = iter->second;
|
||||||
@@ -1560,9 +1437,9 @@ void DatabaseViewer::updateGraphView()
|
|||||||
std::map<int, rtabmap::Transform> finalPoses;
|
std::map<int, rtabmap::Transform> finalPoses;
|
||||||
graphes_.push_back(poses_);
|
graphes_.push_back(poses_);
|
||||||
ui_->actionGenerate_TORO_graph_graph->setEnabled(true);
|
ui_->actionGenerate_TORO_graph_graph->setEnabled(true);
|
||||||
std::map<int, int> ids = this->generateGraph(ui_->spinBox_optimizationsFrom->value(), 0);
|
|
||||||
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
std::multimap<int, rtabmap::Link> links = updateLinksWithModifications(links_);
|
||||||
finalPoses = optimizeGraph(ids, poses_, links, &graphes_);
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(links, ui_->spinBox_optimizationsFrom->value(), 0);
|
||||||
|
util3d::optimizeTOROGraph(depthGraph, poses_, links, finalPoses, ui_->spinBox_iterations->value(), ui_->checkBox_initGuess->isChecked(), &graphes_);
|
||||||
graphes_.push_back(finalPoses);
|
graphes_.push_back(finalPoses);
|
||||||
}
|
}
|
||||||
if(graphes_.size())
|
if(graphes_.size())
|
||||||
@@ -1578,52 +1455,24 @@ void DatabaseViewer::updateGraphView()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::multimap<int, Link>::iterator DatabaseViewer::findLink(
|
|
||||||
std::multimap<int, Link> & links,
|
|
||||||
int from,
|
|
||||||
int to)
|
|
||||||
{
|
|
||||||
std::multimap<int, Link>::iterator iter = links.find(from);
|
|
||||||
while(iter != links.end() && iter->first == from)
|
|
||||||
{
|
|
||||||
if(iter->second.to() == to)
|
|
||||||
{
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's try to -> from
|
|
||||||
iter = links.find(to);
|
|
||||||
while(iter != links.end() && iter->first == to)
|
|
||||||
{
|
|
||||||
if(iter->second.to() == from)
|
|
||||||
{
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
return links.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
Link DatabaseViewer::findActiveLink(int from, int to)
|
Link DatabaseViewer::findActiveLink(int from, int to)
|
||||||
{
|
{
|
||||||
Link link;
|
Link link;
|
||||||
std::multimap<int, Link>::iterator findIter = findLink(linksRefined_, from ,to);
|
std::multimap<int, Link>::iterator findIter = util3d::findLink(linksRefined_, from ,to);
|
||||||
if(findIter != linksRefined_.end())
|
if(findIter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
link = findIter->second;
|
link = findIter->second;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
findIter = findLink(linksAdded_, from ,to);
|
findIter = util3d::findLink(linksAdded_, from ,to);
|
||||||
if(findIter != linksAdded_.end())
|
if(findIter != linksAdded_.end())
|
||||||
{
|
{
|
||||||
link = findIter->second;
|
link = findIter->second;
|
||||||
}
|
}
|
||||||
else if(!containsLink(linksRemoved_, from ,to))
|
else if(!containsLink(linksRemoved_, from ,to))
|
||||||
{
|
{
|
||||||
findIter = findLink(links_, from ,to);
|
findIter = util3d::findLink(links_, from ,to);
|
||||||
if(findIter != links_.end())
|
if(findIter != links_.end())
|
||||||
{
|
{
|
||||||
link = findIter->second;
|
link = findIter->second;
|
||||||
@@ -1635,7 +1484,7 @@ Link DatabaseViewer::findActiveLink(int from, int to)
|
|||||||
|
|
||||||
bool DatabaseViewer::containsLink(std::multimap<int, Link> & links, int from, int to)
|
bool DatabaseViewer::containsLink(std::multimap<int, Link> & links, int from, int to)
|
||||||
{
|
{
|
||||||
return findLink(links, from, to) != links.end();
|
return util3d::findLink(links, from, to) != links.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseViewer::refineConstraint()
|
void DatabaseViewer::refineConstraint()
|
||||||
@@ -1900,7 +1749,7 @@ bool DatabaseViewer::addConstraint(int from, int to, bool silent)
|
|||||||
else if(containsLink(linksRemoved_, from, to))
|
else if(containsLink(linksRemoved_, from, to))
|
||||||
{
|
{
|
||||||
//simply remove from linksRemoved
|
//simply remove from linksRemoved
|
||||||
linksRemoved_.erase(findLink(linksRemoved_, from, to));
|
linksRemoved_.erase(util3d::findLink(linksRemoved_, from, to));
|
||||||
updateSlider = true;
|
updateSlider = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1929,18 +1778,18 @@ void DatabaseViewer::resetConstraint()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::multimap<int, Link>::iterator iter = findLink(linksRefined_, from, to);
|
std::multimap<int, Link>::iterator iter = util3d::findLink(linksRefined_, from, to);
|
||||||
if(iter != linksRefined_.end())
|
if(iter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
linksRefined_.erase(iter);
|
linksRefined_.erase(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = findLink(links_, from, to);
|
iter = util3d::findLink(links_, from, to);
|
||||||
if(iter != links_.end())
|
if(iter != links_.end())
|
||||||
{
|
{
|
||||||
this->updateConstraintView(iter->second);
|
this->updateConstraintView(iter->second);
|
||||||
}
|
}
|
||||||
iter = findLink(linksAdded_, from, to);
|
iter = util3d::findLink(linksAdded_, from, to);
|
||||||
if(iter != linksAdded_.end())
|
if(iter != linksAdded_.end())
|
||||||
{
|
{
|
||||||
this->updateConstraintView(iter->second);
|
this->updateConstraintView(iter->second);
|
||||||
@@ -1966,7 +1815,7 @@ void DatabaseViewer::rejectConstraint()
|
|||||||
|
|
||||||
// find the original one
|
// find the original one
|
||||||
std::multimap<int, Link>::iterator iter;
|
std::multimap<int, Link>::iterator iter;
|
||||||
iter = findLink(links_, from, to);
|
iter = util3d::findLink(links_, from, to);
|
||||||
if(iter != links_.end())
|
if(iter != links_.end())
|
||||||
{
|
{
|
||||||
if(iter->second.type() == Link::kNeighbor)
|
if(iter->second.type() == Link::kNeighbor)
|
||||||
@@ -1978,12 +1827,12 @@ void DatabaseViewer::rejectConstraint()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove from refined and added
|
// remove from refined and added
|
||||||
iter = findLink(linksRefined_, from, to);
|
iter = util3d::findLink(linksRefined_, from, to);
|
||||||
if(iter != linksRefined_.end())
|
if(iter != linksRefined_.end())
|
||||||
{
|
{
|
||||||
linksRefined_.erase(iter);
|
linksRefined_.erase(iter);
|
||||||
}
|
}
|
||||||
iter = findLink(linksAdded_, from, to);
|
iter = util3d::findLink(linksAdded_, from, to);
|
||||||
if(iter != linksAdded_.end())
|
if(iter != linksAdded_.end())
|
||||||
{
|
{
|
||||||
linksAdded_.erase(iter);
|
linksAdded_.erase(iter);
|
||||||
@@ -2001,7 +1850,7 @@ std::multimap<int, rtabmap::Link> DatabaseViewer::updateLinksWithModifications(
|
|||||||
{
|
{
|
||||||
std::multimap<int, rtabmap::Link>::iterator findIter;
|
std::multimap<int, rtabmap::Link>::iterator findIter;
|
||||||
|
|
||||||
findIter = findLink(linksRemoved_, iter->second.from(), iter->second.to());
|
findIter = util3d::findLink(linksRemoved_, iter->second.from(), iter->second.to());
|
||||||
if(findIter != linksRemoved_.end())
|
if(findIter != linksRemoved_.end())
|
||||||
{
|
{
|
||||||
if(!(iter->second.from() == findIter->second.from() &&
|
if(!(iter->second.from() == findIter->second.from() &&
|
||||||
@@ -2019,7 +1868,7 @@ std::multimap<int, rtabmap::Link> DatabaseViewer::updateLinksWithModifications(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findIter = findLink(linksRefined_, iter->second.from(), iter->second.to());
|
findIter = util3d::findLink(linksRefined_, iter->second.from(), iter->second.to());
|
||||||
if(findIter!=linksRefined_.end())
|
if(findIter!=linksRefined_.end())
|
||||||
{
|
{
|
||||||
if(iter->second.from() == findIter->second.from() &&
|
if(iter->second.from() == findIter->second.from() &&
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include "rtabmap/core/Parameters.h"
|
#include "rtabmap/core/Parameters.h"
|
||||||
#include "rtabmap/core/ParamEvent.h"
|
#include "rtabmap/core/ParamEvent.h"
|
||||||
#include "rtabmap/core/Signature.h"
|
#include "rtabmap/core/Signature.h"
|
||||||
|
#include "rtabmap/core/Memory.h"
|
||||||
|
|
||||||
#include "rtabmap/gui/ImageView.h"
|
#include "rtabmap/gui/ImageView.h"
|
||||||
#include "rtabmap/gui/KeypointItem.h"
|
#include "rtabmap/gui/KeypointItem.h"
|
||||||
@@ -55,6 +56,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include "PdfPlot.h"
|
#include "PdfPlot.h"
|
||||||
#include "StatsToolBox.h"
|
#include "StatsToolBox.h"
|
||||||
#include "DetailedProgressDialog.h"
|
#include "DetailedProgressDialog.h"
|
||||||
|
#include "PostProcessingDialog.h"
|
||||||
|
|
||||||
#include <QtGui/QCloseEvent>
|
#include <QtGui/QCloseEvent>
|
||||||
#include <QtGui/QPixmap>
|
#include <QtGui/QPixmap>
|
||||||
@@ -139,6 +141,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
|
|||||||
// Create dialogs
|
// Create dialogs
|
||||||
_aboutDialog = new AboutDialog(this);
|
_aboutDialog = new AboutDialog(this);
|
||||||
_exportDialog = new ExportCloudsDialog(this);
|
_exportDialog = new ExportCloudsDialog(this);
|
||||||
|
_postProcessingDialog = new PostProcessingDialog(this);
|
||||||
|
|
||||||
_ui = new Ui_mainWindow();
|
_ui = new Ui_mainWindow();
|
||||||
_ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
@@ -281,6 +284,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
|
|||||||
connect(_ui->actionReset_Odometry, SIGNAL(triggered()), this, SLOT(resetOdometry()));
|
connect(_ui->actionReset_Odometry, SIGNAL(triggered()), this, SLOT(resetOdometry()));
|
||||||
connect(_ui->actionTrigger_a_new_map, SIGNAL(triggered()), this, SLOT(triggerNewMap()));
|
connect(_ui->actionTrigger_a_new_map, SIGNAL(triggered()), this, SLOT(triggerNewMap()));
|
||||||
connect(_ui->actionData_recorder, SIGNAL(triggered()), this, SLOT(dataRecorder()));
|
connect(_ui->actionData_recorder, SIGNAL(triggered()), this, SLOT(dataRecorder()));
|
||||||
|
connect(_ui->actionPost_processing, SIGNAL(triggered()), this, SLOT(postProcessing()));
|
||||||
|
|
||||||
_ui->actionPause->setShortcut(Qt::Key_Space);
|
_ui->actionPause->setShortcut(Qt::Key_Space);
|
||||||
_ui->actionSave_point_cloud->setEnabled(false);
|
_ui->actionSave_point_cloud->setEnabled(false);
|
||||||
@@ -289,6 +293,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
|
|||||||
_ui->actionView_scans->setEnabled(false);
|
_ui->actionView_scans->setEnabled(false);
|
||||||
_ui->actionView_high_res_point_cloud->setEnabled(false);
|
_ui->actionView_high_res_point_cloud->setEnabled(false);
|
||||||
_ui->actionReset_Odometry->setEnabled(false);
|
_ui->actionReset_Odometry->setEnabled(false);
|
||||||
|
_ui->actionPost_processing->setEnabled(false);
|
||||||
|
|
||||||
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
|
#if defined(Q_WS_MAC) || defined(Q_WS_WIN)
|
||||||
connect(_ui->actionOpen_working_directory, SIGNAL(triggered()), SLOT(openWorkingDirectory()));
|
connect(_ui->actionOpen_working_directory, SIGNAL(triggered()), SLOT(openWorkingDirectory()));
|
||||||
@@ -1011,6 +1016,8 @@ void MainWindow::updateMapCloud(
|
|||||||
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(true);
|
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ui->actionPost_processing->setEnabled(_currentPosesMap.size() >= 2 && _currentLinksMap.size() >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter duplicated poses
|
// filter duplicated poses
|
||||||
@@ -1166,7 +1173,7 @@ void MainWindow::updateMapCloud(
|
|||||||
int fillEmptyRadius = _preferencesDialog->getGridMapFillEmptyRadius();
|
int fillEmptyRadius = _preferencesDialog->getGridMapFillEmptyRadius();
|
||||||
map8S = util3d::create2DMapFromOccupancyLocalMaps(poses, _occupancyLocalMaps, resolution, xMin, yMin, fillEmptyRadius);
|
map8S = util3d::create2DMapFromOccupancyLocalMaps(poses, _occupancyLocalMaps, resolution, xMin, yMin, fillEmptyRadius);
|
||||||
}
|
}
|
||||||
else
|
else if(_createdScans.size())
|
||||||
{
|
{
|
||||||
bool fillEmptySpace = _preferencesDialog->getGridMapFillEmptySpace();
|
bool fillEmptySpace = _preferencesDialog->getGridMapFillEmptySpace();
|
||||||
map8S = util3d::create2DMap(poses, _createdScans, resolution, fillEmptySpace, xMin, yMin);
|
map8S = util3d::create2DMap(poses, _createdScans, resolution, fillEmptySpace, xMin, yMin);
|
||||||
@@ -1602,7 +1609,7 @@ void MainWindow::applyPrefSettings(PreferencesDialog::PANEL_FLAGS flags)
|
|||||||
UDEBUG("Cloud rendering settings changed...");
|
UDEBUG("Cloud rendering settings changed...");
|
||||||
if(_currentPosesMap.size())
|
if(_currentPosesMap.size())
|
||||||
{
|
{
|
||||||
this->updateMapCloud(std::map<int, Transform>(_currentPosesMap), Transform(), std::multimap<int, Link>());
|
this->updateMapCloud(std::map<int, Transform>(_currentPosesMap), Transform(), std::multimap<int, Link>(_currentLinksMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2542,6 +2549,400 @@ void MainWindow::generateTOROMap()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::postProcessing()
|
||||||
|
{
|
||||||
|
if(_postProcessingDialog->exec() != QDialog::Accepted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool detectMoreLoopClosures = _postProcessingDialog->isDetectMoreLoopClosures();
|
||||||
|
bool reextractFeatures = _postProcessingDialog->isReextractFeatures();
|
||||||
|
bool refineNeighborLinks = _postProcessingDialog->isRefineNeighborLinks();
|
||||||
|
bool refineLoopClosureLinks = _postProcessingDialog->isRefineLoopClosureLinks();
|
||||||
|
double clusterRadius = _postProcessingDialog->clusterRadius();
|
||||||
|
double clusterAngle = _postProcessingDialog->clusterAngle();
|
||||||
|
int detectLoopClosureIterations = _postProcessingDialog->iterations();
|
||||||
|
|
||||||
|
if(!detectMoreLoopClosures && !refineNeighborLinks && !refineLoopClosureLinks)
|
||||||
|
{
|
||||||
|
UWARN("No post-processing selection...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, verify that we have all data required in the GUI
|
||||||
|
bool allDataAvailable = true;
|
||||||
|
std::map<int, Transform> odomPoses;
|
||||||
|
for(std::map<int, Transform>::iterator iter = _currentPosesMap.begin();
|
||||||
|
iter!=_currentPosesMap.end() && allDataAvailable;
|
||||||
|
++iter)
|
||||||
|
{
|
||||||
|
QMap<int, Signature>::iterator jter = _cachedSignatures.find(iter->first);
|
||||||
|
if(jter != _cachedSignatures.end())
|
||||||
|
{
|
||||||
|
if(jter->getPose().isNull())
|
||||||
|
{
|
||||||
|
UWARN("Odometry pose of %d is null.", iter->first);
|
||||||
|
allDataAvailable = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
odomPoses.insert(*iter); // fill raw poses
|
||||||
|
}
|
||||||
|
if(jter->getLocalTransform().isNull())
|
||||||
|
{
|
||||||
|
UWARN("Local transform of %d is null.", iter->first);
|
||||||
|
allDataAvailable = false;
|
||||||
|
}
|
||||||
|
if(refineNeighborLinks || refineLoopClosureLinks || reextractFeatures)
|
||||||
|
{
|
||||||
|
// depth data required
|
||||||
|
if(jter->getDepthCompressed().empty() || jter->getDepthFx() <= 0.0f || jter->getDepthFy() <= 0.0f)
|
||||||
|
{
|
||||||
|
UWARN("Depth data of %d missing.", iter->first);
|
||||||
|
allDataAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reextractFeatures)
|
||||||
|
{
|
||||||
|
// rgb required
|
||||||
|
if(jter->getImageCompressed().empty())
|
||||||
|
{
|
||||||
|
UWARN("Rgb of %d missing.", iter->first);
|
||||||
|
allDataAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWARN("Node %d missing.", iter->first);
|
||||||
|
allDataAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!allDataAvailable)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Not all data available in the GUI..."),
|
||||||
|
tr("Some data missing in the cache to respect the constraints chosen. "
|
||||||
|
"Try \"Edit->Download all clouds\" to update the cache and try again."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initProgressDialog->setAutoClose(false, 1);
|
||||||
|
_initProgressDialog->resetProgress();
|
||||||
|
_initProgressDialog->clear();
|
||||||
|
_initProgressDialog->show();
|
||||||
|
_initProgressDialog->appendText("Post-processing beginning!");
|
||||||
|
|
||||||
|
int totalSteps = 0;
|
||||||
|
if(refineNeighborLinks)
|
||||||
|
{
|
||||||
|
totalSteps+=odomPoses.size();
|
||||||
|
}
|
||||||
|
if(refineLoopClosureLinks)
|
||||||
|
{
|
||||||
|
totalSteps+=_currentLinksMap.size() - odomPoses.size();
|
||||||
|
}
|
||||||
|
_initProgressDialog->setMaximumSteps(totalSteps);
|
||||||
|
_initProgressDialog->show();
|
||||||
|
|
||||||
|
ParametersMap parameters = _preferencesDialog->getAllParameters();
|
||||||
|
int toroIterations = 100;
|
||||||
|
bool toroOptimizeFromGraphEnd = false;
|
||||||
|
Parameters::parse(parameters, Parameters::kRGBDToroIterations(), toroIterations);
|
||||||
|
Parameters::parse(parameters, Parameters::kRGBDOptimizeFromGraphEnd(), toroOptimizeFromGraphEnd);
|
||||||
|
|
||||||
|
int loopClosuresAdded = 0;
|
||||||
|
if(detectMoreLoopClosures)
|
||||||
|
{
|
||||||
|
Memory memory(parameters);
|
||||||
|
if(reextractFeatures)
|
||||||
|
{
|
||||||
|
ParametersMap customParameters;
|
||||||
|
// override some parameters
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kMemRehearsalSimilarity(), "1.0")); // desactivate rehearsal
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kMemImageKept(), "false"));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kMemSTMSize(), "0"));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kKpNewWordsComparedTogether(), "false"));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kKpNNStrategy(), parameters.at(Parameters::kLccReextractNNType())));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kKpNndrRatio(), parameters.at(Parameters::kLccReextractNNDR())));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kKpDetectorStrategy(), parameters.at(Parameters::kLccReextractFeatureType())));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kKpWordsPerImage(), parameters.at(Parameters::kLccReextractMaxWords())));
|
||||||
|
customParameters.insert(ParametersPair(Parameters::kMemGenerateIds(), "false"));
|
||||||
|
memory.parseParameters(customParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
UASSERT(detectLoopClosureIterations>0);
|
||||||
|
for(int n=0; n<detectLoopClosureIterations; ++n)
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Looking for more loop closures, clustering poses... (iteration=%1/%2, radius=%3 m angle=%4 degrees)")
|
||||||
|
.arg(n+1).arg(detectLoopClosureIterations).arg(clusterRadius).arg(clusterAngle));
|
||||||
|
|
||||||
|
std::multimap<int, int> clusters = util3d::radiusPosesClustering(
|
||||||
|
_currentPosesMap,
|
||||||
|
clusterRadius,
|
||||||
|
clusterAngle*CV_PI/180.0);
|
||||||
|
|
||||||
|
_initProgressDialog->setMaximumSteps(_initProgressDialog->maximumSteps()+clusters.size());
|
||||||
|
_initProgressDialog->appendText(tr("Looking for more loop closures, clustering poses... found %1 clusters.").arg(clusters.size()));
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
std::set<int> addedLinks;
|
||||||
|
for(std::multimap<int, int>::iterator iter=clusters.begin(); iter!= clusters.end(); ++iter, ++i)
|
||||||
|
{
|
||||||
|
int from = iter->first;
|
||||||
|
int to = iter->second;
|
||||||
|
if(iter->first < iter->second)
|
||||||
|
{
|
||||||
|
from = iter->second;
|
||||||
|
to = iter->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only add new links and one per cluster per iteration
|
||||||
|
if(addedLinks.find(from) == addedLinks.end() && addedLinks.find(to) == addedLinks.end() &&
|
||||||
|
util3d::findLink(_currentLinksMap, from, to) == _currentLinksMap.end())
|
||||||
|
{
|
||||||
|
if(!_cachedSignatures.contains(from))
|
||||||
|
{
|
||||||
|
UERROR("Didn't find signature %d", from);
|
||||||
|
}
|
||||||
|
else if(!_cachedSignatures.contains(to))
|
||||||
|
{
|
||||||
|
UERROR("Didn't find signature %d", to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
Signature & signatureFrom = _cachedSignatures[from];
|
||||||
|
Signature & signatureTo = _cachedSignatures[to];
|
||||||
|
|
||||||
|
Transform transform;
|
||||||
|
std::string rejectedMsg;
|
||||||
|
int inliers;
|
||||||
|
if(reextractFeatures)
|
||||||
|
{
|
||||||
|
memory.init("", true); // clear previously added signatures
|
||||||
|
|
||||||
|
// Add signatures
|
||||||
|
SensorData dataFrom = signatureFrom.toSensorData();
|
||||||
|
SensorData dataTo = signatureTo.toSensorData();
|
||||||
|
|
||||||
|
if(dataFrom.isValid() &&
|
||||||
|
dataFrom.isMetric() &&
|
||||||
|
dataTo.isValid() &&
|
||||||
|
dataTo.isMetric() &&
|
||||||
|
dataFrom.id() != Memory::kIdInvalid &&
|
||||||
|
signatureFrom.id() != Memory::kIdInvalid)
|
||||||
|
{
|
||||||
|
if(from > to)
|
||||||
|
{
|
||||||
|
memory.update(dataTo);
|
||||||
|
memory.update(dataFrom);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memory.update(dataFrom);
|
||||||
|
memory.update(dataTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = memory.computeVisualTransform(dataTo.id(), dataFrom.id(), &rejectedMsg, &inliers);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UERROR("not supposed to be here!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform = memory.computeVisualTransform(signatureTo, signatureFrom, &rejectedMsg, &inliers);
|
||||||
|
}
|
||||||
|
if(!transform.isNull())
|
||||||
|
{
|
||||||
|
UINFO("Added new loop closure between %d and %d.", from, to);
|
||||||
|
addedLinks.insert(from);
|
||||||
|
addedLinks.insert(to);
|
||||||
|
_currentLinksMap.insert(std::make_pair(from, Link(from, to, transform, Link::kUserClosure)));
|
||||||
|
++loopClosuresAdded;
|
||||||
|
_initProgressDialog->appendText(tr("Detected loop closure %1->%2! (%3/%4)").arg(from).arg(to).arg(i+1).arg(clusters.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_initProgressDialog->appendText(tr("Iteration %1/%2: Detected %3 loop closures!")
|
||||||
|
.arg(n+1).arg(detectLoopClosureIterations).arg(addedLinks.size()/2));
|
||||||
|
if(addedLinks.size() == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n+1 < detectLoopClosureIterations)
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Optimizing graph with new links (%1 nodes, %2 constraints)...")
|
||||||
|
.arg(odomPoses.size()).arg(_currentLinksMap.size()));
|
||||||
|
std::map<int, rtabmap::Transform> optimizedPoses;
|
||||||
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first);
|
||||||
|
util3d::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations);
|
||||||
|
_currentPosesMap = optimizedPoses;
|
||||||
|
_initProgressDialog->appendText(tr("Optimizing graph with new links... done!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UINFO("Added %d loop closures.", loopClosuresAdded);
|
||||||
|
_initProgressDialog->appendText(tr("Total new loop closures detected=%1").arg(loopClosuresAdded));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(refineNeighborLinks || refineLoopClosureLinks)
|
||||||
|
{
|
||||||
|
if(refineLoopClosureLinks)
|
||||||
|
{
|
||||||
|
_initProgressDialog->setMaximumSteps(_initProgressDialog->maximumSteps()+loopClosuresAdded);
|
||||||
|
}
|
||||||
|
_initProgressDialog->appendText(tr("Refining links..."));
|
||||||
|
|
||||||
|
int decimation=8;
|
||||||
|
float maxDepth=2.0f;
|
||||||
|
float voxelSize=0.01f;
|
||||||
|
int samples = 0;
|
||||||
|
float minFitness = 1.0f;
|
||||||
|
float maxCorrespondences = 0.05f;
|
||||||
|
float icpIterations = 30;
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3Decimation(), decimation);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3MaxDepth(), maxDepth);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3VoxelSize(), voxelSize);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3Samples(), samples);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3MaxFitness(), minFitness);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3MaxCorrespondenceDistance(), maxCorrespondences);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3Iterations(), icpIterations);
|
||||||
|
bool pointToPlane = false;
|
||||||
|
int pointToPlaneNormalNeighbors = 20;
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3PointToPlane(), pointToPlane);
|
||||||
|
Parameters::parse(parameters, Parameters::kLccIcp3PointToPlaneNormalNeighbors(), pointToPlaneNormalNeighbors);
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
for(std::multimap<int, Link>::iterator iter = _currentLinksMap.begin(); iter!=_currentLinksMap.end(); ++iter, ++i)
|
||||||
|
{
|
||||||
|
int type = iter->second.type();
|
||||||
|
|
||||||
|
if((refineNeighborLinks && type==Link::kNeighbor) ||
|
||||||
|
(refineLoopClosureLinks && type!=Link::kNeighbor))
|
||||||
|
{
|
||||||
|
int from = iter->second.from();
|
||||||
|
int to = iter->second.to();
|
||||||
|
|
||||||
|
_initProgressDialog->appendText(tr("Refining link %1->%2 (%3/%4)").arg(from).arg(to).arg(i+1).arg(_currentLinksMap.size()));
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
if(!_cachedSignatures.contains(from))
|
||||||
|
{
|
||||||
|
UERROR("Didn't find signature %d",from);
|
||||||
|
}
|
||||||
|
else if(!_cachedSignatures.contains(to))
|
||||||
|
{
|
||||||
|
UERROR("Didn't find signature %d", to);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Signature & signatureFrom = _cachedSignatures[from];
|
||||||
|
Signature & signatureTo = _cachedSignatures[to];
|
||||||
|
|
||||||
|
//3D
|
||||||
|
cv::Mat depthA, depthB;
|
||||||
|
signatureFrom.uncompressData(0, &depthA, 0);
|
||||||
|
signatureTo.uncompressData(0, &depthB, 0);
|
||||||
|
|
||||||
|
if(depthA.type() == CV_8UC1 || depthB.type() == CV_8UC1)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("ICP failed"), tr("ICP cannot be done on stereo images!"));
|
||||||
|
UERROR("ICP 3D cannot be done on stereo images! Aborting refining links with ICP...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA = util3d::getICPReadyCloud(depthA,
|
||||||
|
signatureFrom.getDepthFx(), signatureFrom.getDepthFy(), signatureFrom.getDepthCx(), signatureFrom.getDepthCy(),
|
||||||
|
decimation,
|
||||||
|
maxDepth,
|
||||||
|
voxelSize,
|
||||||
|
samples,
|
||||||
|
signatureFrom.getLocalTransform());
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudB = util3d::getICPReadyCloud(depthB,
|
||||||
|
signatureTo.getDepthFx(), signatureTo.getDepthFy(), signatureTo.getDepthCx(), signatureTo.getDepthCy(),
|
||||||
|
decimation,
|
||||||
|
maxDepth,
|
||||||
|
voxelSize,
|
||||||
|
samples,
|
||||||
|
iter->second.transform() * signatureTo.getLocalTransform());
|
||||||
|
|
||||||
|
bool hasConverged = false;
|
||||||
|
double fitness = -1;
|
||||||
|
Transform transform;
|
||||||
|
if(pointToPlane)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloudANormals = util3d::computeNormals(cloudA, pointToPlaneNormalNeighbors);
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloudBNormals = util3d::computeNormals(cloudB, pointToPlaneNormalNeighbors);
|
||||||
|
|
||||||
|
cloudANormals = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(cloudANormals);
|
||||||
|
if(cloudA->size() != cloudANormals->size())
|
||||||
|
{
|
||||||
|
UWARN("removed nan normals...");
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudBNormals = util3d::removeNaNNormalsFromPointCloud<pcl::PointNormal>(cloudBNormals);
|
||||||
|
if(cloudB->size() != cloudBNormals->size())
|
||||||
|
{
|
||||||
|
UWARN("removed nan normals...");
|
||||||
|
}
|
||||||
|
|
||||||
|
transform = util3d::icpPointToPlane(cloudBNormals,
|
||||||
|
cloudANormals,
|
||||||
|
maxCorrespondences,
|
||||||
|
icpIterations,
|
||||||
|
hasConverged,
|
||||||
|
fitness);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform = util3d::icp(cloudB,
|
||||||
|
cloudA,
|
||||||
|
maxCorrespondences,
|
||||||
|
icpIterations,
|
||||||
|
hasConverged,
|
||||||
|
fitness);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasConverged && !transform.isNull() && fitness>=0.0f && fitness <= minFitness)
|
||||||
|
{
|
||||||
|
Link newLink(from, to, transform*iter->second.transform(), iter->second.type());
|
||||||
|
iter->second = newLink;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UWARN("Cannot refine link %d->%d (converged=%s fitness=%f)", from, to, hasConverged?"true":"false", fitness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_initProgressDialog->appendText(tr("Refining links...done!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_initProgressDialog->appendText(tr("Optimizing graph with updated links (%1 nodes, %2 constraints)...")
|
||||||
|
.arg(odomPoses.size()).arg(_currentLinksMap.size()));
|
||||||
|
std::map<int, rtabmap::Transform> optimizedPoses;
|
||||||
|
std::map<int, int> depthGraph = util3d::generateDepthGraph(_currentLinksMap, toroOptimizeFromGraphEnd?odomPoses.rbegin()->first:odomPoses.begin()->first);
|
||||||
|
util3d::optimizeTOROGraph(depthGraph, odomPoses, _currentLinksMap, optimizedPoses, toroIterations);
|
||||||
|
_initProgressDialog->appendText(tr("Optimizing graph with updated links... done!"));
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
|
||||||
|
_initProgressDialog->appendText(tr("Updating map..."));
|
||||||
|
this->updateMapCloud(optimizedPoses, Transform(), _currentLinksMap, false);
|
||||||
|
_initProgressDialog->appendText(tr("Updating map... done!"));
|
||||||
|
|
||||||
|
_initProgressDialog->setValue(_initProgressDialog->maximumSteps());
|
||||||
|
_initProgressDialog->appendText("Post-processing finished!");
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::deleteMemory()
|
void MainWindow::deleteMemory()
|
||||||
{
|
{
|
||||||
QMessageBox::StandardButton button;
|
QMessageBox::StandardButton button;
|
||||||
@@ -2778,12 +3179,14 @@ void MainWindow::clearTheCache()
|
|||||||
_ui->widget_cloudViewer->removeAllClouds();
|
_ui->widget_cloudViewer->removeAllClouds();
|
||||||
_ui->widget_cloudViewer->setBackgroundColor(Qt::black);
|
_ui->widget_cloudViewer->setBackgroundColor(Qt::black);
|
||||||
_ui->widget_cloudViewer->clearTrajectory();
|
_ui->widget_cloudViewer->clearTrajectory();
|
||||||
|
_ui->widget_mapVisibility->clear();
|
||||||
_currentPosesMap.clear();
|
_currentPosesMap.clear();
|
||||||
_odometryCorrection = Transform::getIdentity();
|
_odometryCorrection = Transform::getIdentity();
|
||||||
_lastOdomPose.setNull();
|
_lastOdomPose.setNull();
|
||||||
//disable save cloud action
|
//disable save cloud action
|
||||||
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
|
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
|
||||||
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
|
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
|
||||||
|
_ui->actionPost_processing->setEnabled(false);
|
||||||
_ui->actionSave_point_cloud->setEnabled(false);
|
_ui->actionSave_point_cloud->setEnabled(false);
|
||||||
_ui->actionView_scans->setEnabled(false);
|
_ui->actionView_scans->setEnabled(false);
|
||||||
_ui->actionView_high_res_point_cloud->setEnabled(false);
|
_ui->actionView_high_res_point_cloud->setEnabled(false);
|
||||||
|
|||||||
@@ -57,6 +57,13 @@ void MapVisibilityWidget::showEvent(QShowEvent * event)
|
|||||||
updateCheckBoxes();
|
updateCheckBoxes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MapVisibilityWidget::clear()
|
||||||
|
{
|
||||||
|
_poses.clear();
|
||||||
|
_mask.clear();
|
||||||
|
updateCheckBoxes();
|
||||||
|
}
|
||||||
|
|
||||||
void MapVisibilityWidget::updateCheckBoxes()
|
void MapVisibilityWidget::updateCheckBoxes()
|
||||||
{
|
{
|
||||||
QWidget * area = this->findChild<QWidget*>("area");
|
QWidget * area = this->findChild<QWidget*>("area");
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public:
|
|||||||
void setMap(const std::map<int, Transform> & poses, const std::map<int, bool> & mask);
|
void setMap(const std::map<int, Transform> & poses, const std::map<int, bool> & mask);
|
||||||
std::map<int, Transform> getVisiblePoses() const;
|
std::map<int, Transform> getVisiblePoses() const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent * event);
|
virtual void showEvent(QShowEvent * event);
|
||||||
|
|
||||||
|
|||||||
92
guilib/src/PostProcessingDialog.cpp
Normal file
92
guilib/src/PostProcessingDialog.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Universite de Sherbrooke nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PostProcessingDialog.h"
|
||||||
|
#include "ui_postProcessingDialog.h"
|
||||||
|
|
||||||
|
#include <QtGui/QPushButton>
|
||||||
|
|
||||||
|
namespace rtabmap {
|
||||||
|
|
||||||
|
PostProcessingDialog::PostProcessingDialog(QWidget * parent) :
|
||||||
|
QDialog(parent)
|
||||||
|
{
|
||||||
|
_ui = new Ui_PostProcessingDialog();
|
||||||
|
_ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(_ui->detectMoreLoopClosures, SIGNAL(clicked(bool)), this, SLOT(updateButtonBox()));
|
||||||
|
connect(_ui->refineNeighborLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
|
||||||
|
connect(_ui->refineLoopClosureLinks, SIGNAL(stateChanged(int)), this, SLOT(updateButtonBox()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessingDialog::~PostProcessingDialog()
|
||||||
|
{
|
||||||
|
delete _ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessingDialog::updateButtonBox()
|
||||||
|
{
|
||||||
|
_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
|
||||||
|
isDetectMoreLoopClosures() || isRefineNeighborLinks() || isRefineLoopClosureLinks());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessingDialog::isDetectMoreLoopClosures() const
|
||||||
|
{
|
||||||
|
return _ui->detectMoreLoopClosures->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
double PostProcessingDialog::clusterRadius() const
|
||||||
|
{
|
||||||
|
return _ui->clusterRadius->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
double PostProcessingDialog::clusterAngle() const
|
||||||
|
{
|
||||||
|
return _ui->clusterAngle->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PostProcessingDialog::iterations() const
|
||||||
|
{
|
||||||
|
return _ui->iterations->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessingDialog::isReextractFeatures() const
|
||||||
|
{
|
||||||
|
return _ui->reextractFeatures->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessingDialog::isRefineNeighborLinks() const
|
||||||
|
{
|
||||||
|
return _ui->refineNeighborLinks->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PostProcessingDialog::isRefineLoopClosureLinks() const
|
||||||
|
{
|
||||||
|
return _ui->refineLoopClosureLinks->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
guilib/src/PostProcessingDialog.h
Normal file
63
guilib/src/PostProcessingDialog.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Universite de Sherbrooke nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef POSTPROCESSINGDIALOG_H_
|
||||||
|
#define POSTPROCESSINGDIALOG_H_
|
||||||
|
|
||||||
|
#include <QtGui/QDialog>
|
||||||
|
|
||||||
|
class Ui_PostProcessingDialog;
|
||||||
|
|
||||||
|
namespace rtabmap {
|
||||||
|
|
||||||
|
class PostProcessingDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PostProcessingDialog(QWidget * parent = 0);
|
||||||
|
|
||||||
|
virtual ~PostProcessingDialog();
|
||||||
|
|
||||||
|
bool isDetectMoreLoopClosures() const;
|
||||||
|
double clusterRadius() const;
|
||||||
|
double clusterAngle() const;
|
||||||
|
int iterations() const;
|
||||||
|
bool isReextractFeatures() const;
|
||||||
|
bool isRefineNeighborLinks() const;
|
||||||
|
bool isRefineLoopClosureLinks() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateButtonBox();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui_PostProcessingDialog * _ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* POSTPROCESSINGDIALOG_H_ */
|
||||||
@@ -441,6 +441,8 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
|
|||||||
_ui->loopClosure_icpMaxCorrespondenceDistance->setObjectName(Parameters::kLccIcp3MaxCorrespondenceDistance().c_str());
|
_ui->loopClosure_icpMaxCorrespondenceDistance->setObjectName(Parameters::kLccIcp3MaxCorrespondenceDistance().c_str());
|
||||||
_ui->loopClosure_icpIterations->setObjectName(Parameters::kLccIcp3Iterations().c_str());
|
_ui->loopClosure_icpIterations->setObjectName(Parameters::kLccIcp3Iterations().c_str());
|
||||||
_ui->loopClosure_icpMaxFitness->setObjectName(Parameters::kLccIcp3MaxFitness().c_str());
|
_ui->loopClosure_icpMaxFitness->setObjectName(Parameters::kLccIcp3MaxFitness().c_str());
|
||||||
|
_ui->loopClosure_icpPointToPlane->setObjectName(Parameters::kLccIcp3PointToPlane().c_str());
|
||||||
|
_ui->loopClosure_icpPointToPlaneNormals->setObjectName(Parameters::kLccIcp3PointToPlaneNormalNeighbors().c_str());
|
||||||
|
|
||||||
_ui->loopClosure_icp2MaxCorrespondenceDistance->setObjectName(Parameters::kLccIcp2MaxCorrespondenceDistance().c_str());
|
_ui->loopClosure_icp2MaxCorrespondenceDistance->setObjectName(Parameters::kLccIcp2MaxCorrespondenceDistance().c_str());
|
||||||
_ui->loopClosure_icp2Iterations->setObjectName(Parameters::kLccIcp2Iterations().c_str());
|
_ui->loopClosure_icp2Iterations->setObjectName(Parameters::kLccIcp2Iterations().c_str());
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1076</width>
|
<width>1076</width>
|
||||||
<height>675</height>
|
<height>711</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -825,7 +825,7 @@
|
|||||||
<number>1000</number>
|
<number>1000</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>3</number>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -851,7 +851,7 @@
|
|||||||
<double>1.000000000000000</double>
|
<double>1.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<double>0.010000000000000</double>
|
<double>0.020000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -948,19 +948,39 @@
|
|||||||
<item row="9" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_detectMore_angle">
|
<widget class="QDoubleSpinBox" name="doubleSpinBox_detectMore_angle">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> rad</string>
|
<string> degrees</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="decimals">
|
<property name="decimals">
|
||||||
<number>2</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<double>3.140000000000000</double>
|
<double>180.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<double>0.100000000000000</double>
|
<double>1.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<double>0.300000000000000</double>
|
<double>30.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLabel" name="label_31">
|
||||||
|
<property name="text">
|
||||||
|
<string>Detect more LCs: iterations</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QSpinBox" name="doubleSpinBox_detectMore_iterations">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -68,6 +68,8 @@
|
|||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionData_recorder"/>
|
<addaction name="actionData_recorder"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionPost_processing"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionView_high_res_point_cloud"/>
|
<addaction name="actionView_high_res_point_cloud"/>
|
||||||
<addaction name="actionView_scans"/>
|
<addaction name="actionView_scans"/>
|
||||||
</widget>
|
</widget>
|
||||||
@@ -1088,6 +1090,11 @@
|
|||||||
<string>Edit database...</string>
|
<string>Edit database...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionPost_processing">
|
||||||
|
<property name="text">
|
||||||
|
<string>Post-processing...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|||||||
252
guilib/src/ui/postProcessingDialog.ui
Normal file
252
guilib/src/ui/postProcessingDialog.ui
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PostProcessingDialog</class>
|
||||||
|
<widget class="QDialog" name="PostProcessingDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>486</width>
|
||||||
|
<height>369</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Post-Processing</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="../GuiLib.qrc">
|
||||||
|
<normaloff>:/images/RTAB-Map.ico</normaloff>:/images/RTAB-Map.ico</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="detectMoreLoopClosures">
|
||||||
|
<property name="title">
|
||||||
|
<string>Detect more loop closures</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QDoubleSpinBox" name="clusterAngle">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> degrees</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>180.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>30.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QDoubleSpinBox" name="clusterRadius">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> m</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>99.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.300000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cluster radius</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="clusterAngle_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cluster angle</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Re-extract features</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="reextractFeatures">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Iterations</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QSpinBox" name="iterations">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>See "RTAB-Map settings/RGB-D SLAM/Loop closure constraint" to tune the parameters used.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Refine links with ICP 3D</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>See "RTAB-Map settings/RGB-D SLAM/ICP" to tune the parameters used.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="refineNeighborLinks">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refine neighbor links</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="refineLoopClosureLinks">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refine loop closure links</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../GuiLib.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>PostProcessingDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>PostProcessingDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>5</number>
|
<number>21</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="page_22">
|
<widget class="QWidget" name="page_22">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_29">
|
<layout class="QVBoxLayout" name="verticalLayout_29">
|
||||||
@@ -5642,6 +5642,46 @@ Lower the ratio -> higher the precision. 0 means disabled, matching the neare
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLabel" name="label_144">
|
||||||
|
<property name="text">
|
||||||
|
<string>Point to plane ICP.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QCheckBox" name="loopClosure_icpPointToPlane">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QSpinBox" name="loopClosure_icpPointToPlaneNormals">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLabel" name="label_212">
|
||||||
|
<property name="text">
|
||||||
|
<string>Number of neighbors to compute normals for point to plane.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
Reference in New Issue
Block a user