Tango: Added "Adjust colors" post-processing option. Updated ICP parameters. Added "Mem/LaserScanNormalK" parameter for convenience.

This commit is contained in:
matlabbe
2016-09-07 17:19:21 -04:00
parent ce1acd9d44
commit 4a072b3dfc
18 changed files with 501 additions and 351 deletions

View File

@@ -2,7 +2,7 @@
<!-- BEGIN_INCLUDE(manifest) --> <!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.introlab.rtabmap" package="com.introlab.rtabmap"
android:versionCode="10" android:versionCode="11"
android:versionName="@RTABMAP_VERSION@"> android:versionName="@RTABMAP_VERSION@">
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />

View File

@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/Optimizer.h> #include <rtabmap/core/Optimizer.h>
#include <rtabmap/core/VWDictionary.h> #include <rtabmap/core/VWDictionary.h>
#include <rtabmap/core/Memory.h> #include <rtabmap/core/Memory.h>
#include <rtabmap/core/GainCompensator.h>
#include <pcl/filters/extract_indices.h> #include <pcl/filters/extract_indices.h>
#include <pcl/io/ply_io.h> #include <pcl/io/ply_io.h>
#include <pcl/io/obj_io.h> #include <pcl/io/obj_io.h>
@@ -90,10 +91,13 @@ rtabmap::ParametersMap RTABMapApp::getRtabmapParameters()
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDNeighborLinkRefining(), uBool2Str(driftCorrection_))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRGBDNeighborLinkRefining(), uBool2Str(driftCorrection_)));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRegStrategy(), std::string(driftCorrection_?"1":"0"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kRegStrategy(), std::string(driftCorrection_?"1":"0")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlane(), std::string("false"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlane(), std::string("true")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpPointToPlaneNormalNeighbors(), std::string("6"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kMemLaserScanNormalK(), std::string("6")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpIterations(), std::string("10"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpIterations(), std::string("10")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpEpsilon(), std::string("0.001"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpEpsilon(), std::string("0.001")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxRotation(), std::string("0.17"))); // 10 degrees
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxTranslation(), std::string("0.05")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpCorrespondenceRatio(), std::string("0.3")));
parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxCorrespondenceDistance(), std::string("0.05"))); parameters.insert(rtabmap::ParametersPair(rtabmap::Parameters::kIcpMaxCorrespondenceDistance(), std::string("0.05")));
return parameters; return parameters;
@@ -117,6 +121,7 @@ RTABMapApp::RTABMapApp() :
meshAngleToleranceDeg_(15.0), meshAngleToleranceDeg_(15.0),
clearSceneOnNextRender_(false), clearSceneOnNextRender_(false),
filterPolygonsOnNextRender_(false), filterPolygonsOnNextRender_(false),
gainCompensationOnNextRender_(false),
totalPoints_(0), totalPoints_(0),
totalPolygons_(0), totalPolygons_(0),
lastDrawnCloudsCount_(0), lastDrawnCloudsCount_(0),
@@ -171,8 +176,6 @@ void RTABMapApp::onCreate(JNIEnv* env, jobject caller_activity)
{ {
logHandler_ = new LogHandler(); logHandler_ = new LogHandler();
} }
ULogger::setEventLevel(ULogger::kInfo);
ULogger::setPrintThreadId(true);
this->registerToEventsManager(); this->registerToEventsManager();
@@ -300,6 +303,8 @@ private:
// OpenGL thread // OpenGL thread
int RTABMapApp::Render() int RTABMapApp::Render()
{ {
boost::mutex::scoped_lock lock(renderingMutex_);
// should be before clearSceneOnNextRender_ in case openDatabase is called // should be before clearSceneOnNextRender_ in case openDatabase is called
std::list<rtabmap::Statistics> rtabmapEvents; std::list<rtabmap::Statistics> rtabmapEvents;
{ {
@@ -464,11 +469,11 @@ int RTABMapApp::Render()
main_scene_.addCloud(id, outputCloud, outputPolygons, iter->second, data.imageRaw()); main_scene_.addCloud(id, outputCloud, outputPolygons, iter->second, data.imageRaw());
// protect createdMeshes_ used also by exportMesh() method // protect createdMeshes_ used also by exportMesh() method
std::pair<std::map<int, Mesh>::iterator, bool> inserted = createdMeshes_.insert(std::make_pair(id, Mesh())); std::pair<std::map<int, Mesh>::iterator, bool> inserted = createdMeshes_.insert(std::make_pair(id, Mesh()));
UASSERT(inserted.second); UASSERT(inserted.second);
inserted.first->second.cloud = outputCloud; inserted.first->second.cloud = outputCloud;
inserted.first->second.indices = indices;
inserted.first->second.polygons = outputPolygons; inserted.first->second.polygons = outputPolygons;
inserted.first->second.pose = iter->second; inserted.first->second.pose = iter->second;
inserted.first->second.texture = data.imageCompressed(); inserted.first->second.texture = data.imageCompressed();
@@ -545,8 +550,7 @@ int RTABMapApp::Render()
event.data().imageRaw().cols, event.data().imageRaw().rows, event.data().imageRaw().cols, event.data().imageRaw().rows,
event.data().depthRaw().cols, event.data().depthRaw().rows, event.data().depthRaw().cols, event.data().depthRaw().rows,
(int)cloud->width, (int)cloud->height); (int)cloud->width, (int)cloud->height);
std::vector<pcl::Vertices> polygons = rtabmap::util3d::organizedFastMesh(cloud, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_); main_scene_.addCloud(-1, cloud, std::vector<pcl::Vertices>(), opengl_world_T_rtabmap_world*event.pose());
main_scene_.addCloud(-1, cloud, polygons, opengl_world_T_rtabmap_world*event.pose(), event.data().imageRaw());
main_scene_.setCloudVisible(-1, true); main_scene_.setCloudVisible(-1, true);
} }
else else
@@ -562,6 +566,39 @@ int RTABMapApp::Render()
} }
} }
if(gainCompensationOnNextRender_)
{
gainCompensationOnNextRender_ = false;
rtabmap::GainCompensator compensator;
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > clouds;
std::map<int, pcl::IndicesPtr> indices;
for(std::map<int, Mesh>::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter)
{
clouds.insert(std::make_pair(iter->first, iter->second.cloud));
indices.insert(std::make_pair(iter->first, iter->second.indices));
}
std::map<int, rtabmap::Transform> poses;
std::multimap<int, rtabmap::Link> links;
rtabmap_->getGraph(poses, links, false, true);
compensator.feed(clouds, indices, links);
for(std::map<int, Mesh>::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter)
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudCpy(new pcl::PointCloud<pcl::PointXYZRGB>);
*cloudCpy = *iter->second.cloud;
cv::Mat imageCpy = rtabmap::uncompressImage(iter->second.texture);
if(!cloudCpy->empty())
{
compensator.apply(iter->first, cloudCpy, iter->second.indices);
if(!imageCpy.empty())
{
compensator.apply(iter->first, imageCpy);
}
}
main_scene_.updateCloudColors(iter->first, cloudCpy, imageCpy);
}
notifyDataLoaded = true;
}
if(filterPolygonsOnNextRender_) if(filterPolygonsOnNextRender_)
{ {
filterPolygonsOnNextRender_ = false; filterPolygonsOnNextRender_ = false;
@@ -1021,7 +1058,7 @@ int RTABMapApp::postProcessing(int approach)
LOGE("g2o not available!"); LOGE("g2o not available!");
} }
} }
else else if(approach!=4 || approach!=5)
{ {
// simple graph optmimization // simple graph optmimization
rtabmap_->getGraph(poses, links, true, true); rtabmap_->getGraph(poses, links, true, true);
@@ -1038,18 +1075,29 @@ int RTABMapApp::postProcessing(int approach)
rtabmap_->setOptimizedPoses(poses); rtabmap_->setOptimizedPoses(poses);
} }
else else if(approach!=4 || approach!=5)
{ {
returnedValue = -1; returnedValue = -1;
} }
}
if(returnedValue >=0)
{
// filter polygons // filter polygons
if(approach == 4) if(approach == 4)
{ {
boost::mutex::scoped_lock lock(renderingMutex_);
filterPolygonsOnNextRender_ = true; filterPolygonsOnNextRender_ = true;
} }
// gain compensation
if(approach == -1 || approach == 5)
{
boost::mutex::scoped_lock lock(renderingMutex_);
gainCompensationOnNextRender_ = true;
}
}
}
return returnedValue; return returnedValue;
} }

View File

@@ -161,6 +161,7 @@ class RTABMapApp : public UEventsHandler {
bool clearSceneOnNextRender_; bool clearSceneOnNextRender_;
bool filterPolygonsOnNextRender_; bool filterPolygonsOnNextRender_;
bool gainCompensationOnNextRender_;
int totalPoints_; int totalPoints_;
int totalPolygons_; int totalPolygons_;
int lastDrawnCloudsCount_; int lastDrawnCloudsCount_;
@@ -178,10 +179,12 @@ class RTABMapApp : public UEventsHandler {
boost::mutex meshesMutex_; boost::mutex meshesMutex_;
boost::mutex odomMutex_; boost::mutex odomMutex_;
boost::mutex poseMutex_; boost::mutex poseMutex_;
boost::mutex renderingMutex_;
struct Mesh struct Mesh
{ {
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud; pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
pcl::IndicesPtr indices;
std::vector<pcl::Vertices> polygons; std::vector<pcl::Vertices> polygons;
rtabmap::Transform pose; rtabmap::Transform pose;
cv::Mat texture; cv::Mat texture;

View File

@@ -48,8 +48,74 @@ PointCloudDrawable::PointCloudDrawable(
visible_(true), visible_(true),
cloud_shader_program_(cloudShaderProgram), cloud_shader_program_(cloudShaderProgram),
texture_shader_program_(textureShaderProgram) texture_shader_program_(textureShaderProgram)
{
updateCloud(cloud, image);
updatePolygons(polygons);
}
PointCloudDrawable::~PointCloudDrawable()
{
LOGI("Freeing cloud buffer %d", vertex_buffers_);
if (vertex_buffers_)
{
glDeleteBuffers(1, &vertex_buffers_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
vertex_buffers_ = 0;
}
if (textures_)
{
glDeleteTextures(1, &textures_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
textures_ = 0;
}
}
void PointCloudDrawable::updatePolygons(const std::vector<pcl::Vertices> & polygons)
{
polygons_.clear();
if(polygons.size())
{
int polygonSize = polygons[0].vertices.size();
UASSERT(polygonSize == 3);
polygons_.resize(polygons.size() * polygonSize);
int oi = 0;
for(unsigned int i=0; i<polygons.size(); ++i)
{
UASSERT((int)polygons[i].vertices.size() == polygonSize);
for(int j=0; j<polygonSize; ++j)
{
polygons_[oi++] = (unsigned short)polygons[i].vertices[j];
}
}
}
}
void PointCloudDrawable::updateCloud(const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const cv::Mat & image)
{ {
UASSERT(!cloud->empty()); UASSERT(!cloud->empty());
if(nPoints_)
{
UASSERT((int)cloud->size() == nPoints_);
}
nPoints_ = 0;
if (vertex_buffers_)
{
glDeleteBuffers(1, &vertex_buffers_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
vertex_buffers_ = 0;
}
if(!image.empty())
{
if (textures_)
{
glDeleteTextures(1, &textures_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
textures_ = 0;
}
}
glGenBuffers(1, &vertex_buffers_); glGenBuffers(1, &vertex_buffers_);
if(!vertex_buffers_) if(!vertex_buffers_)
@@ -61,7 +127,7 @@ PointCloudDrawable::PointCloudDrawable(
if(!cloud->is_dense && !image.empty()) if(!cloud->is_dense && !image.empty())
{ {
LOGI("cloud=%dx%d image=%dx%d\n", (int)cloud->width, (int)cloud->height, image.cols, image.rows); LOGI("cloud=%dx%d image=%dx%d\n", (int)cloud->width, (int)cloud->height, image.cols, image.rows);
UASSERT(polygons.size() && !cloud->is_dense && !image.empty() && image.type() == CV_8UC3); UASSERT(!cloud->is_dense && !image.empty() && image.type() == CV_8UC3);
glGenTextures(1, &textures_); glGenTextures(1, &textures_);
if(!textures_) if(!textures_)
{ {
@@ -114,12 +180,12 @@ PointCloudDrawable::PointCloudDrawable(
return; return;
} }
if(textures_) if(textures_ && !image.empty())
{ {
// gen texture from image // gen texture from image
glBindTexture(GL_TEXTURE_2D, textures_); glBindTexture(GL_TEXTURE_2D, textures_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
cv::Mat rgbImage; cv::Mat rgbImage;
cv::cvtColor(image, rgbImage, CV_BGR2RGB); cv::cvtColor(image, rgbImage, CV_BGR2RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rgbImage.cols, rgbImage.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, rgbImage.data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rgbImage.cols, rgbImage.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, rgbImage.data);
@@ -137,46 +203,6 @@ PointCloudDrawable::PointCloudDrawable(
} }
nPoints_ = cloud->size(); nPoints_ = cloud->size();
updatePolygons(polygons);
}
PointCloudDrawable::~PointCloudDrawable()
{
LOGI("Freeing cloud buffer %d", vertex_buffers_);
if (vertex_buffers_)
{
glDeleteBuffers(1, &vertex_buffers_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
vertex_buffers_ = 0;
}
if (textures_)
{
glDeleteTextures(1, &textures_);
tango_gl::util::CheckGlError("PointCloudDrawable::~PointCloudDrawable()");
textures_ = 0;
}
}
void PointCloudDrawable::updatePolygons(const std::vector<pcl::Vertices> & polygons)
{
polygons_.clear();
if(polygons.size())
{
int polygonSize = polygons[0].vertices.size();
UASSERT(polygonSize == 3);
polygons_.resize(polygons.size() * polygonSize);
int oi = 0;
for(unsigned int i=0; i<polygons.size(); ++i)
{
UASSERT((int)polygons[i].vertices.size() == polygonSize);
for(int j=0; j<polygonSize; ++j)
{
polygons_[oi++] = (unsigned short)polygons[i].vertices[j];
}
}
}
} }
void PointCloudDrawable::setPose(const rtabmap::Transform & pose) void PointCloudDrawable::setPose(const rtabmap::Transform & pose)

View File

@@ -50,6 +50,7 @@ class PointCloudDrawable {
virtual ~PointCloudDrawable(); virtual ~PointCloudDrawable();
void updatePolygons(const std::vector<pcl::Vertices> & polygons); void updatePolygons(const std::vector<pcl::Vertices> & polygons);
void updateCloud(const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const cv::Mat & image = cv::Mat());
void setPose(const rtabmap::Transform & pose); void setPose(const rtabmap::Transform & pose);
void setVisible(bool visible) {visible_=visible;} void setVisible(bool visible) {visible_=visible;}
rtabmap::Transform getPose() const {return glmToTransform(pose_);} rtabmap::Transform getPose() const {return glmToTransform(pose_);}

View File

@@ -480,3 +480,12 @@ void Scene::updateCloudPolygons(int id, const std::vector<pcl::Vertices> & polyg
iter->second->updatePolygons(polygons); iter->second->updatePolygons(polygons);
} }
} }
void Scene::updateCloudColors(int id, const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const cv::Mat & image)
{
std::map<int, PointCloudDrawable*>::iterator iter=pointClouds_.find(id);
if(iter != pointClouds_.end())
{
iter->second->updateCloud(cloud, image);
}
}

View File

@@ -109,6 +109,7 @@ class Scene {
bool hasCloud(int id) const; bool hasCloud(int id) const;
std::set<int> getAddedClouds() const; std::set<int> getAddedClouds() const;
void updateCloudPolygons(int id, const std::vector<pcl::Vertices> & polygons); void updateCloudPolygons(int id, const std::vector<pcl::Vertices> & polygons);
void updateCloudColors(int id, const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, const cv::Mat & image = cv::Mat());
void setMapRendering(bool enabled) {mapRendering_ = enabled;} void setMapRendering(bool enabled) {mapRendering_ = enabled;}
void setMeshRendering(bool enabled, bool withTexture) {meshRendering_ = enabled; meshRenderingTexture_ = withTexture;} void setMeshRendering(bool enabled, bool withTexture) {meshRendering_ = enabled; meshRenderingTexture_ = withTexture;}

View File

@@ -39,6 +39,9 @@ class LogHandler : public UEventsHandler
public: public:
LogHandler() LogHandler()
{ {
ULogger::setEventLevel(ULogger::kWarning);
ULogger::setPrintThreadId(true);
registerToEventsManager(); registerToEventsManager();
} }
protected: protected:

View File

@@ -14,6 +14,7 @@
<item android:id="@+id/global_graph_optimization" android:title="Global Graph Optimization" /> <item android:id="@+id/global_graph_optimization" android:title="Global Graph Optimization" />
<item android:id="@+id/detect_more_loop_closures" android:title="Detect More Loop Closures" /> <item android:id="@+id/detect_more_loop_closures" android:title="Detect More Loop Closures" />
<item android:id="@+id/icp_refining" android:title="ICP Refining" /> <item android:id="@+id/icp_refining" android:title="ICP Refining" />
<item android:id="@+id/gain_compensation" android:title="Adjust Colors" />
<item android:id="@+id/sba" android:title="Bundle Adjustement" /> <item android:id="@+id/sba" android:title="Bundle Adjustement" />
<item android:id="@+id/polygons_filtering" android:title="Noise Filtering" /> <item android:id="@+id/polygons_filtering" android:title="Noise Filtering" />
</menu> </menu>

View File

@@ -670,11 +670,10 @@ public class RTABMapActivity extends Activity implements OnClickListener {
final int loopDetected = RTABMapLib.postProcessing(-1); final int loopDetected = RTABMapLib.postProcessing(-1);
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
mProgressDialog.dismiss();
if(loopDetected >= 0) if(loopDetected >= 0)
{ {
mTotalLoopClosures+=loopDetected; mTotalLoopClosures+=loopDetected;
mToast.makeText(getActivity(), String.format("Optimization done!"), mToast.LENGTH_SHORT).show(); mToast.makeText(getActivity(), String.format("Optimization done! Adjusting colors..."), mToast.LENGTH_SHORT).show();
} }
else if(loopDetected < 0) else if(loopDetected < 0)
{ {
@@ -772,6 +771,13 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mProgressDialog.show(); mProgressDialog.show();
RTABMapLib.postProcessing(4); RTABMapLib.postProcessing(4);
} }
else if (itemId == R.id.gain_compensation)
{
mProgressDialog.setTitle("Post-Processing");
mProgressDialog.setMessage(String.format("Gain compensation..."));
mProgressDialog.show();
RTABMapLib.postProcessing(5);
}
else if (itemId == R.id.sba) else if (itemId == R.id.sba)
{ {
mProgressDialog.setTitle("Post-Processing"); mProgressDialog.setTitle("Post-Processing");

View File

@@ -247,6 +247,7 @@ private:
int _imagePreDecimation; int _imagePreDecimation;
int _imagePostDecimation; int _imagePostDecimation;
float _laserScanDownsampleStepSize; float _laserScanDownsampleStepSize;
int _laserScanNormalK;
bool _reextractLoopClosureFeatures; bool _reextractLoopClosureFeatures;
float _rehearsalMaxDistance; float _rehearsalMaxDistance;
float _rehearsalMaxAngle; float _rehearsalMaxAngle;

View File

@@ -210,6 +210,7 @@ class RTABMAP_EXP Parameters
RTABMAP_PARAM(Mem, ImagePreDecimation, int, 1, "Image decimation (>=1) before features extraction."); RTABMAP_PARAM(Mem, ImagePreDecimation, int, 1, "Image decimation (>=1) before features extraction.");
RTABMAP_PARAM(Mem, ImagePostDecimation, int, 1, "Image decimation (>=1) of saved data in created signatures (after features extraction). Decimation is done from the original image."); RTABMAP_PARAM(Mem, ImagePostDecimation, int, 1, "Image decimation (>=1) of saved data in created signatures (after features extraction). Decimation is done from the original image.");
RTABMAP_PARAM(Mem, LaserScanDownsampleStepSize, int, 1, "If > 1, downsample the laser scans when creating a signature."); RTABMAP_PARAM(Mem, LaserScanDownsampleStepSize, int, 1, "If > 1, downsample the laser scans when creating a signature.");
RTABMAP_PARAM(Mem, LaserScanNormalK, int, 0, "If > 0 and laser scans are 3D without normals, normals will be computed with K search neighbors when creating a signature.");
RTABMAP_PARAM(Mem, UseOdomFeatures, bool, false, "Use odometry features."); RTABMAP_PARAM(Mem, UseOdomFeatures, bool, false, "Use odometry features.");
// KeypointMemory (Keypoint-based) // KeypointMemory (Keypoint-based)

View File

@@ -127,7 +127,8 @@ class RTABMAP_EXP Statistics
RTABMAP_STATS(TimingMem, Add_new_words, ms); RTABMAP_STATS(TimingMem, Add_new_words, ms);
RTABMAP_STATS(TimingMem, Compressing_data, ms); RTABMAP_STATS(TimingMem, Compressing_data, ms);
RTABMAP_STATS(TimingMem, Post_decimation, ms); RTABMAP_STATS(TimingMem, Post_decimation, ms);
RTABMAP_STATS(TimingMem, Downsampling_scan, ms); RTABMAP_STATS(TimingMem, Scan_downsampling, ms);
RTABMAP_STATS(TimingMem, Scan_normals, ms);
RTABMAP_STATS(TimingMem, Occupancy_grid, ms); RTABMAP_STATS(TimingMem, Occupancy_grid, ms);
RTABMAP_STATS(Keypoint, Dictionary_size, words); RTABMAP_STATS(Keypoint, Dictionary_size, words);

View File

@@ -186,6 +186,7 @@ pcl::PointCloud<pcl::PointXYZ> RTABMAP_EXP laserScanFromDepthImages(
cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud, const Transform & transform = Transform()); cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud, const Transform & transform = Transform());
// return CV_32FC6 // return CV_32FC6
cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointNormal> & cloud, const Transform & transform = Transform()); cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointNormal> & cloud, const Transform & transform = Transform());
cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud, const pcl::PointCloud<pcl::Normal> & normals, const Transform & transform = Transform());
// return CV_32FC4 // return CV_32FC4
cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZRGB> & cloud, const Transform & transform = Transform()); cv::Mat RTABMAP_EXP laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZRGB> & cloud, const Transform & transform = Transform());
// return CV_32FC2 // return CV_32FC2

View File

@@ -86,6 +86,7 @@ Memory::Memory(const ParametersMap & parameters) :
_imagePreDecimation(Parameters::defaultMemImagePreDecimation()), _imagePreDecimation(Parameters::defaultMemImagePreDecimation()),
_imagePostDecimation(Parameters::defaultMemImagePostDecimation()), _imagePostDecimation(Parameters::defaultMemImagePostDecimation()),
_laserScanDownsampleStepSize(Parameters::defaultMemLaserScanDownsampleStepSize()), _laserScanDownsampleStepSize(Parameters::defaultMemLaserScanDownsampleStepSize()),
_laserScanNormalK(Parameters::defaultMemLaserScanNormalK()),
_reextractLoopClosureFeatures(Parameters::defaultRGBDLoopClosureReextractFeatures()), _reextractLoopClosureFeatures(Parameters::defaultRGBDLoopClosureReextractFeatures()),
_rehearsalMaxDistance(Parameters::defaultRGBDLinearUpdate()), _rehearsalMaxDistance(Parameters::defaultRGBDLinearUpdate()),
_rehearsalMaxAngle(Parameters::defaultRGBDAngularUpdate()), _rehearsalMaxAngle(Parameters::defaultRGBDAngularUpdate()),
@@ -407,6 +408,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
Parameters::parse(parameters, Parameters::kMemImagePreDecimation(), _imagePreDecimation); Parameters::parse(parameters, Parameters::kMemImagePreDecimation(), _imagePreDecimation);
Parameters::parse(parameters, Parameters::kMemImagePostDecimation(), _imagePostDecimation); Parameters::parse(parameters, Parameters::kMemImagePostDecimation(), _imagePostDecimation);
Parameters::parse(parameters, Parameters::kMemLaserScanDownsampleStepSize(), _laserScanDownsampleStepSize); Parameters::parse(parameters, Parameters::kMemLaserScanDownsampleStepSize(), _laserScanDownsampleStepSize);
Parameters::parse(parameters, Parameters::kMemLaserScanNormalK(), _laserScanNormalK);
Parameters::parse(parameters, Parameters::kRGBDLoopClosureReextractFeatures(), _reextractLoopClosureFeatures); Parameters::parse(parameters, Parameters::kRGBDLoopClosureReextractFeatures(), _reextractLoopClosureFeatures);
Parameters::parse(parameters, Parameters::kRGBDLinearUpdate(), _rehearsalMaxDistance); Parameters::parse(parameters, Parameters::kRGBDLinearUpdate(), _rehearsalMaxDistance);
Parameters::parse(parameters, Parameters::kRGBDAngularUpdate(), _rehearsalMaxAngle); Parameters::parse(parameters, Parameters::kRGBDAngularUpdate(), _rehearsalMaxAngle);
@@ -3495,9 +3497,20 @@ Signature * Memory::createSignature(const SensorData & data, const Transform & p
maxLaserScanMaxPts /= _laserScanDownsampleStepSize; maxLaserScanMaxPts /= _laserScanDownsampleStepSize;
t = timer.ticks(); t = timer.ticks();
if(stats) stats->addStatistic(Statistics::kTimingMemDownsampling_scan(), t*1000.0f); if(stats) stats->addStatistic(Statistics::kTimingMemScan_downsampling(), t*1000.0f);
UDEBUG("time downsampling scan = %fs", t); UDEBUG("time downsampling scan = %fs", t);
} }
if(!laserScan.empty() && _laserScanNormalK > 0 && laserScan.channels() == 3)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud = util3d::laserScanToPointCloud(laserScan);
float x,y,z;
data.laserScanInfo().localTransform().getTranslation(x,y,z);
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloud, _laserScanNormalK, Eigen::Vector3f(x,y,z));
laserScan = util3d::laserScanFromPointCloud(*cloud, *normals);
t = timer.ticks();
if(stats) stats->addStatistic(Statistics::kTimingMemScan_normals(), t*1000.0f);
UDEBUG("time normals scan = %fs", t);
}
Signature * s; Signature * s;
if(this->isBinDataKept()) if(this->isBinDataKept())

View File

@@ -1047,18 +1047,19 @@ cv::Mat laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud, co
Eigen::Affine3f transform3f = transform.toEigen3f(); Eigen::Affine3f transform3f = transform.toEigen3f();
for(unsigned int i=0; i<cloud.size(); ++i) for(unsigned int i=0; i<cloud.size(); ++i)
{ {
float * ptr = laserScan.ptr<float>(0, i);
if(!nullTransform) if(!nullTransform)
{ {
pcl::PointXYZ pt = pcl::transformPoint(cloud.at(i), transform3f); pcl::PointXYZ pt = pcl::transformPoint(cloud.at(i), transform3f);
laserScan.at<cv::Vec3f>(i)[0] = pt.x; ptr[0] = pt.x;
laserScan.at<cv::Vec3f>(i)[1] = pt.y; ptr[1] = pt.y;
laserScan.at<cv::Vec3f>(i)[2] = pt.z; ptr[2] = pt.z;
} }
else else
{ {
laserScan.at<cv::Vec3f>(i)[0] = cloud.at(i).x; ptr[0] = cloud.at(i).x;
laserScan.at<cv::Vec3f>(i)[1] = cloud.at(i).y; ptr[1] = cloud.at(i).y;
laserScan.at<cv::Vec3f>(i)[2] = cloud.at(i).z; ptr[2] = cloud.at(i).z;
} }
} }
@@ -1071,24 +1072,63 @@ cv::Mat laserScanFromPointCloud(const pcl::PointCloud<pcl::PointNormal> & cloud,
bool nullTransform = transform.isNull() || transform.isIdentity(); bool nullTransform = transform.isNull() || transform.isIdentity();
for(unsigned int i=0; i<cloud.size(); ++i) for(unsigned int i=0; i<cloud.size(); ++i)
{ {
float * ptr = laserScan.ptr<float>(0, i);
if(!nullTransform) if(!nullTransform)
{ {
pcl::PointNormal pt = util3d::transformPoint(cloud.at(i), transform); pcl::PointNormal pt = util3d::transformPoint(cloud.at(i), transform);
laserScan.at<cv::Vec6f>(i)[0] = pt.x; ptr[0] = pt.x;
laserScan.at<cv::Vec6f>(i)[1] = pt.y; ptr[1] = pt.y;
laserScan.at<cv::Vec6f>(i)[2] = pt.z; ptr[2] = pt.z;
laserScan.at<cv::Vec6f>(i)[3] = pt.normal_x; ptr[3] = pt.normal_x;
laserScan.at<cv::Vec6f>(i)[4] = pt.normal_y; ptr[4] = pt.normal_y;
laserScan.at<cv::Vec6f>(i)[5] = pt.normal_z; ptr[5] = pt.normal_z;
} }
else else
{ {
laserScan.at<cv::Vec6f>(i)[0] = cloud.at(i).x; ptr[0] = cloud.at(i).x;
laserScan.at<cv::Vec6f>(i)[1] = cloud.at(i).y; ptr[1] = cloud.at(i).y;
laserScan.at<cv::Vec6f>(i)[2] = cloud.at(i).z; ptr[2] = cloud.at(i).z;
laserScan.at<cv::Vec6f>(i)[3] = cloud.at(i).normal_x; ptr[3] = cloud.at(i).normal_x;
laserScan.at<cv::Vec6f>(i)[4] = cloud.at(i).normal_y; ptr[4] = cloud.at(i).normal_y;
laserScan.at<cv::Vec6f>(i)[5] = cloud.at(i).normal_z; ptr[5] = cloud.at(i).normal_z;
}
}
return laserScan;
}
cv::Mat laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud, const pcl::PointCloud<pcl::Normal> & normals, const Transform & transform)
{
UASSERT(cloud.size() == normals.size());
cv::Mat laserScan(1, (int)cloud.size(), CV_32FC(6));
bool nullTransform = transform.isNull() || transform.isIdentity();
for(unsigned int i=0; i<cloud.size(); ++i)
{
float * ptr = laserScan.ptr<float>(0, i);
if(!nullTransform)
{
pcl::PointNormal pt;
pt.x = cloud.at(i).x;
pt.y = cloud.at(i).y;
pt.z = cloud.at(i).z;
pt.normal_x = normals.at(i).normal_x;
pt.normal_y = normals.at(i).normal_y;
pt.normal_z = normals.at(i).normal_z;
pt = util3d::transformPoint(pt, transform);
ptr[0] = pt.x;
ptr[1] = pt.y;
ptr[2] = pt.z;
ptr[3] = pt.normal_x;
ptr[4] = pt.normal_y;
ptr[5] = pt.normal_z;
}
else
{
ptr[0] = cloud.at(i).x;
ptr[1] = cloud.at(i).y;
ptr[2] = cloud.at(i).z;
ptr[3] = normals.at(i).normal_x;
ptr[4] = normals.at(i).normal_y;
ptr[5] = normals.at(i).normal_z;
} }
} }
return laserScan; return laserScan;
@@ -1101,20 +1141,22 @@ cv::Mat laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZRGB> & cloud,
Eigen::Affine3f transform3f = transform.toEigen3f(); Eigen::Affine3f transform3f = transform.toEigen3f();
for(unsigned int i=0; i<cloud.size(); ++i) for(unsigned int i=0; i<cloud.size(); ++i)
{ {
float * ptr = laserScan.ptr<float>(0, i);
if(!nullTransform) if(!nullTransform)
{ {
pcl::PointXYZRGB pt = pcl::transformPoint(cloud.at(i), transform3f); pcl::PointXYZRGB pt = pcl::transformPoint(cloud.at(i), transform3f);
laserScan.at<cv::Vec4f>(i)[0] = pt.x; ptr[0] = pt.x;
laserScan.at<cv::Vec4f>(i)[1] = pt.y; ptr[1] = pt.y;
laserScan.at<cv::Vec4f>(i)[2] = pt.z; ptr[2] = pt.z;
} }
else else
{ {
laserScan.at<cv::Vec4f>(i)[0] = cloud.at(i).x; ptr[0] = cloud.at(i).x;
laserScan.at<cv::Vec4f>(i)[1] = cloud.at(i).y; ptr[1] = cloud.at(i).y;
laserScan.at<cv::Vec4f>(i)[2] = cloud.at(i).z; ptr[2] = cloud.at(i).z;
} }
laserScan.at<cv::Vec4i>(i)[3] = int(cloud.at(i).b) | (int(cloud.at(i).g) << 8) | (int(cloud.at(i).r) << 16); int * ptrInt = (int*)ptr;
ptrInt[3] = int(cloud.at(i).b) | (int(cloud.at(i).g) << 8) | (int(cloud.at(i).r) << 16);
} }
return laserScan; return laserScan;
} }
@@ -1126,16 +1168,17 @@ cv::Mat laserScan2dFromPointCloud(const pcl::PointCloud<pcl::PointXYZ> & cloud,
Eigen::Affine3f transform3f = transform.toEigen3f(); Eigen::Affine3f transform3f = transform.toEigen3f();
for(unsigned int i=0; i<cloud.size(); ++i) for(unsigned int i=0; i<cloud.size(); ++i)
{ {
float * ptr = laserScan.ptr<float>(0, i);
if(!nullTransform) if(!nullTransform)
{ {
pcl::PointXYZ pt = pcl::transformPoint(cloud.at(i), transform3f); pcl::PointXYZ pt = pcl::transformPoint(cloud.at(i), transform3f);
laserScan.at<cv::Vec2f>(i)[0] = pt.x; ptr[0] = pt.x;
laserScan.at<cv::Vec2f>(i)[1] = pt.y; ptr[1] = pt.y;
} }
else else
{ {
laserScan.at<cv::Vec2f>(i)[0] = cloud.at(i).x; ptr[0] = cloud.at(i).x;
laserScan.at<cv::Vec2f>(i)[1] = cloud.at(i).y; ptr[1] = cloud.at(i).y;
} }
} }
@@ -1203,28 +1246,12 @@ pcl::PointXYZ laserScanToPoint(const cv::Mat & laserScan, int index)
UASSERT(!laserScan.empty() && index < laserScan.cols); UASSERT(!laserScan.empty() && index < laserScan.cols);
UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6)); UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6));
pcl::PointXYZ output; pcl::PointXYZ output;
if(laserScan.type() == CV_32FC2) const float * ptr = laserScan.ptr<float>(0, index);
output.x = ptr[0];
output.y = ptr[1];
if(laserScan.channels() >= 3)
{ {
output.x = laserScan.at<cv::Vec2f>(index)[0]; output.z = ptr[2];
output.y = laserScan.at<cv::Vec2f>(index)[1];
}
else if(laserScan.type() == CV_32FC3)
{
output.x = laserScan.at<cv::Vec3f>(index)[0];
output.y = laserScan.at<cv::Vec3f>(index)[1];
output.z = laserScan.at<cv::Vec3f>(index)[2];
}
else if(laserScan.type() == CV_32FC(4))
{
output.x = laserScan.at<cv::Vec4f>(index)[0];
output.y = laserScan.at<cv::Vec4f>(index)[1];
output.z = laserScan.at<cv::Vec4f>(index)[2];
}
else
{
output.x = laserScan.at<cv::Vec6f>(index)[0];
output.y = laserScan.at<cv::Vec6f>(index)[1];
output.z = laserScan.at<cv::Vec6f>(index)[2];
} }
return output; return output;
} }
@@ -1234,31 +1261,18 @@ pcl::PointNormal laserScanToPointNormal(const cv::Mat & laserScan, int index)
UASSERT(!laserScan.empty() && index < laserScan.cols); UASSERT(!laserScan.empty() && index < laserScan.cols);
UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6)); UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6));
pcl::PointNormal output; pcl::PointNormal output;
if(laserScan.type() == CV_32FC2) const float * ptr = laserScan.ptr<float>(0, index);
output.x = ptr[0];
output.y = ptr[1];
if(laserScan.channels() >= 3)
{ {
output.x = laserScan.at<cv::Vec2f>(index)[0]; output.z = ptr[2];
output.y = laserScan.at<cv::Vec2f>(index)[1];
} }
else if(laserScan.type() == CV_32FC3) if(laserScan.channels() == 6)
{ {
output.x = laserScan.at<cv::Vec3f>(index)[0]; output.normal_x = ptr[3];
output.y = laserScan.at<cv::Vec3f>(index)[1]; output.normal_y = ptr[4];
output.z = laserScan.at<cv::Vec3f>(index)[2]; output.normal_z = ptr[5];
}
else if(laserScan.type() == CV_32FC(4))
{
output.x = laserScan.at<cv::Vec4f>(index)[0];
output.y = laserScan.at<cv::Vec4f>(index)[1];
output.z = laserScan.at<cv::Vec4f>(index)[2];
}
else
{
output.x = laserScan.at<cv::Vec6f>(index)[0];
output.y = laserScan.at<cv::Vec6f>(index)[1];
output.z = laserScan.at<cv::Vec6f>(index)[2];
output.normal_x = laserScan.at<cv::Vec6f>(index)[3];
output.normal_y = laserScan.at<cv::Vec6f>(index)[4];
output.normal_z = laserScan.at<cv::Vec6f>(index)[5];
} }
return output; return output;
} }
@@ -1268,31 +1282,19 @@ pcl::PointXYZRGB laserScanToPointRGB(const cv::Mat & laserScan, int index)
UASSERT(!laserScan.empty() && index < laserScan.cols); UASSERT(!laserScan.empty() && index < laserScan.cols);
UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6)); UASSERT(laserScan.type() == CV_32FC2 || laserScan.type() == CV_32FC3 || laserScan.type() == CV_32FC(4) || laserScan.type() == CV_32FC(6));
pcl::PointXYZRGB output; pcl::PointXYZRGB output;
if(laserScan.type() == CV_32FC2) const float * ptr = laserScan.ptr<float>(0, index);
output.x = ptr[0];
output.y = ptr[1];
if(laserScan.type() >= 3)
{ {
output.x = laserScan.at<cv::Vec2f>(index)[0]; output.z = ptr[2];
output.y = laserScan.at<cv::Vec2f>(index)[1];
} }
else if(laserScan.type() == CV_32FC3) if(laserScan.channels() == 4)
{ {
output.x = laserScan.at<cv::Vec3f>(index)[0]; int * ptrInt = (int*)ptr;
output.y = laserScan.at<cv::Vec3f>(index)[1]; output.b = (unsigned char)(ptrInt[3] & 0xFF);
output.z = laserScan.at<cv::Vec3f>(index)[2]; output.g = (unsigned char)((ptrInt[3] >> 8) & 0xFF);
} output.r = (unsigned char)((ptrInt[3] >> 16) & 0xFF);
else if(laserScan.type() == CV_32FC(4))
{
output.x = laserScan.at<cv::Vec4f>(index)[0];
output.y = laserScan.at<cv::Vec4f>(index)[1];
output.z = laserScan.at<cv::Vec4f>(index)[2];
output.b = (unsigned char)(laserScan.at<cv::Vec4i>(index)[3] & 0xFF);
output.g = (unsigned char)((laserScan.at<cv::Vec4i>(index)[3] >> 8) & 0xFF);
output.r = (unsigned char)((laserScan.at<cv::Vec4i>(index)[3] >> 16) & 0xFF);
}
else
{
output.x = laserScan.at<cv::Vec6f>(index)[0];
output.y = laserScan.at<cv::Vec6f>(index)[1];
output.z = laserScan.at<cv::Vec6f>(index)[2];
} }
return output; return output;
} }

View File

@@ -573,6 +573,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->spinBox_imagePreDecimation->setObjectName(Parameters::kMemImagePreDecimation().c_str()); _ui->spinBox_imagePreDecimation->setObjectName(Parameters::kMemImagePreDecimation().c_str());
_ui->spinBox_imagePostDecimation->setObjectName(Parameters::kMemImagePostDecimation().c_str()); _ui->spinBox_imagePostDecimation->setObjectName(Parameters::kMemImagePostDecimation().c_str());
_ui->general_spinBox_laserScanDownsample->setObjectName(Parameters::kMemLaserScanDownsampleStepSize().c_str()); _ui->general_spinBox_laserScanDownsample->setObjectName(Parameters::kMemLaserScanDownsampleStepSize().c_str());
_ui->general_spinBox_laserScanNormalK->setObjectName(Parameters::kMemLaserScanNormalK().c_str());
_ui->checkBox_useOdomFeatures->setObjectName(Parameters::kMemUseOdomFeatures().c_str()); _ui->checkBox_useOdomFeatures->setObjectName(Parameters::kMemUseOdomFeatures().c_str());
// Database // Database

View File

@@ -63,7 +63,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>-279</y>
<width>673</width> <width>673</width>
<height>2496</height> <height>2496</height>
</rect> </rect>
@@ -86,7 +86,7 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>8</number>
</property> </property>
<widget class="QWidget" name="page_22"> <widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,1"> <layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,1">
@@ -5356,19 +5356,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
<layout class="QVBoxLayout" name="verticalLayout_10"> <layout class="QVBoxLayout" name="verticalLayout_10">
<item> <item>
<layout class="QGridLayout" name="gridLayout_42" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout_42" columnstretch="0,1">
<item row="4" column="1">
<widget class="QLabel" name="label_retrieved_2">
<property name="text">
<string>True=Generate location Ids, False=use input image ids.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QSpinBox" name="general_spinBox_maxStMemSize"> <widget class="QSpinBox" name="general_spinBox_maxStMemSize">
<property name="minimum"> <property name="minimum">
@@ -5382,6 +5369,208 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QLabel" name="label_retrieved_10">
<property name="text">
<string>Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="general_doubleSpinBox_recentWmRatio">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_retrieved_4">
<property name="text">
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="general_spinBox_maxRetrieved">
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_retrieved_3">
<property name="text">
<string>Bad signatures are ignored.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="general_checkBox_initWMWithAllNodes">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_retrieved_5">
<property name="text">
<string>Keep raw sensor data. Only useful to save loop closure computation time when features re-extraction is enabled. Disable to save RAM memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="general_checkBox_keepRawData">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="general_checkBox_reduceGraph">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_retrieved_11">
<property name="text">
<string>Create map labels. The first node of a map will be labelled as &quot;map#&quot; where # is the map ID.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="general_checkBox_createMapLabels">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QSpinBox" name="general_spinBox_laserScanDownsample">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="label_retrieved_12">
<property name="text">
<string>Raw descriptors kept in memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="general_checkBox_keepDescriptors">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="label_retrieved_13">
<property name="text">
<string>Image pre decimation. This option can be used to reduce image size before features extraction.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_retrieved_2">
<property name="text">
<string>True=Generate location Ids, False=use input image ids.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLabel" name="label_retrieved"> <widget class="QLabel" name="label_retrieved">
<property name="text"> <property name="text">
@@ -5438,51 +5627,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QLabel" name="label_retrieved_10">
<property name="text">
<string>Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="general_doubleSpinBox_recentWmRatio">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.200000000000000</double>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_retrieved_4">
<property name="text">
<string>Initialize the Woking Memory with all nodes from Long-Term memory, instead of only nodes of the last session. This may be useful in localization mode, where less processing time is required than in SLAM mode, so more nodes can be kept in Working Memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLabel" name="label_29"> <widget class="QLabel" name="label_29">
<property name="text"> <property name="text">
@@ -5509,19 +5653,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QSpinBox" name="general_spinBox_maxRetrieved">
<property name="maximum">
<number>999</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="6" column="0"> <item row="6" column="0">
<widget class="QCheckBox" name="general_checkBox_badSignaturesIgnored"> <widget class="QCheckBox" name="general_checkBox_badSignaturesIgnored">
<property name="text"> <property name="text">
@@ -5532,52 +5663,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1">
<widget class="QLabel" name="label_retrieved_3">
<property name="text">
<string>Bad signatures are ignored.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="general_checkBox_initWMWithAllNodes">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_retrieved_5">
<property name="text">
<string>Keep raw sensor data. Only useful to save loop closure computation time when features re-extraction is enabled. Disable to save RAM memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="general_checkBox_keepRawData">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="13" column="1"> <item row="13" column="1">
<widget class="QLabel" name="label_retrieved_6"> <widget class="QLabel" name="label_retrieved_6">
<property name="text"> <property name="text">
@@ -5617,55 +5702,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0">
<widget class="QCheckBox" name="general_checkBox_reduceGraph">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_retrieved_11">
<property name="text">
<string>Create map labels. The first node of a map will be labelled as &quot;map#&quot; where # is the map ID.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="general_checkBox_createMapLabels">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QSpinBox" name="general_spinBox_laserScanDownsample">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="11" column="0"> <item row="11" column="0">
<widget class="QCheckBox" name="general_checkBox_saveDepth16bits"> <widget class="QCheckBox" name="general_checkBox_saveDepth16bits">
<property name="text"> <property name="text">
@@ -5676,29 +5712,6 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1">
<widget class="QLabel" name="label_retrieved_12">
<property name="text">
<string>Raw descriptors kept in memory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="general_checkBox_keepDescriptors">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="12" column="0"> <item row="12" column="0">
<widget class="QSpinBox" name="spinBox_imagePreDecimation"> <widget class="QSpinBox" name="spinBox_imagePreDecimation">
<property name="minimum"> <property name="minimum">
@@ -5709,10 +5722,10 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="15" column="1">
<widget class="QLabel" name="label_retrieved_13"> <widget class="QLabel" name="label_retrieved_14">
<property name="text"> <property name="text">
<string>Image pre decimation. This option can be used to reduce image size before features extraction.</string> <string>If &gt; 0 and laser scans are 3D without normals, normals will be computed with K search neighbors when creating a signature.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@@ -5722,6 +5735,25 @@ If set to false, classic RTAB-Map loop closure detection is done using only imag
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="0">
<widget class="QSpinBox" name="general_spinBox_laserScanNormalK">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>