Export Clouds: export scans integrated in the same dialog (remove export scans dialog)

This commit is contained in:
matlabbe
2017-05-11 13:20:51 -04:00
parent d2255adc4e
commit 58e6424bf7
14 changed files with 487 additions and 1519 deletions

View File

@@ -22,7 +22,6 @@ SET(headers_ui
./ExportDialog.h
./PostProcessingDialog.h
./ExportCloudsDialog.h
./ExportScansDialog.h
./MapVisibilityWidget.h
../include/${PROJECT_PREFIX}/gui/GraphViewer.h
./CreateSimpleCalibrationDialog.h
@@ -43,7 +42,6 @@ SET(uis
./ui/exportDialog.ui
./ui/postProcessingDialog.ui
./ui/exportCloudsDialog.ui
./ui/exportScansDialog.ui
./ui/calibrationDialog.ui
./ui/createSimpleCalibrationDialog.ui
./ui/depthCalibrationDialog.ui
@@ -91,7 +89,6 @@ SET(SRC_FILES
./ExportDialog.cpp
./PostProcessingDialog.cpp
./ExportCloudsDialog.cpp
./ExportScansDialog.cpp
./MapVisibilityWidget.cpp
./GraphViewer.cpp
./EditDepthArea.cpp

View File

@@ -230,9 +230,7 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
connect(ui_->actionTORO_graph, SIGNAL(triggered()), this , SLOT(exportPosesTORO()));
connect(ui_->actionG2o_g2o, SIGNAL(triggered()), this , SLOT(exportPosesG2O()));
connect(ui_->actionView_3D_map, SIGNAL(triggered()), this, SLOT(view3DMap()));
connect(ui_->actionView_3D_laser_scans, SIGNAL(triggered()), this, SLOT(view3DLaserScans()));
connect(ui_->actionGenerate_3D_map_pcd, SIGNAL(triggered()), this, SLOT(generate3DMap()));
connect(ui_->actionExport_3D_laser_scans_ply_pcd, SIGNAL(triggered()), this, SLOT(generate3DLaserScans()));
connect(ui_->actionDetect_more_loop_closures, SIGNAL(triggered()), this, SLOT(detectMoreLoopClosures()));
connect(ui_->actionRefine_all_neighbor_links, SIGNAL(triggered()), this, SLOT(refineAllNeighborLinks()));
connect(ui_->actionRefine_all_loop_closure_links, SIGNAL(triggered()), this, SLOT(refineAllLoopClosureLinks()));
@@ -2079,6 +2077,7 @@ void DatabaseViewer::view3DMap()
mapIds_,
QMap<int, Signature>(),
std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
std::map<int, cv::Mat>(),
pathDatabase_,
ui_->parameters_toolbox->getParameters());
}
@@ -2088,118 +2087,6 @@ void DatabaseViewer::view3DMap()
}
}
void DatabaseViewer::view3DLaserScans()
{
if(!ids_.size() || !dbDriver_)
{
QMessageBox::warning(this, tr("Cannot view 3D laser scans"), tr("The database is empty..."));
return;
}
if(graphes_.empty())
{
this->updateGraphView();
if(graphes_.empty() || ui_->horizontalSlider_iterations->maximum() != (int)graphes_.size()-1)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("No graph in database?!"));
return;
}
}
bool ok = false;
int downsamplingStepSize = QInputDialog::getInt(this, tr("Downsampling?"), tr("Downsample step size (1 = no filtering)"), 1, 1, 99999, 1, &ok);
if(ok)
{
std::map<int, Transform> optimizedPoses = uValueAt(graphes_, ui_->horizontalSlider_iterations->value());
if(ui_->groupBox_posefiltering->isChecked())
{
optimizedPoses = graph::radiusPosesFiltering(optimizedPoses,
ui_->doubleSpinBox_posefilteringRadius->value(),
ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
}
if(optimizedPoses.size() > 0)
{
rtabmap::ProgressDialog progressDialog(this);
progressDialog.setMaximumSteps((int)optimizedPoses.size());
progressDialog.show();
// create a window
QDialog * window = new QDialog(this, Qt::Window);
window->setModal(this->isModal());
window->setWindowTitle(tr("3D Laser Scans"));
window->setMinimumWidth(800);
window->setMinimumHeight(600);
rtabmap::CloudViewer * viewer = new rtabmap::CloudViewer(window);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(viewer);
viewer->setCameraLockZ(false);
window->setLayout(layout);
connect(window, SIGNAL(finished(int)), viewer, SLOT(clear()));
window->show();
for(std::map<int, Transform>::const_iterator iter = optimizedPoses.begin(); iter!=optimizedPoses.end(); ++iter)
{
rtabmap::Transform pose = iter->second;
if(!pose.isNull())
{
SensorData data;
dbDriver_->getNodeData(iter->first, data);
cv::Mat scan;
data.uncompressDataConst(0, 0, &scan);
if(!scan.empty())
{
if(downsamplingStepSize>1)
{
scan = util3d::downsample(scan, downsamplingStepSize);
}
QColor color = Qt::red;
int mapId, weight;
Transform odomPose, groundTruth;
std::string label;
double stamp;
std::vector<float> velocity;
if(dbDriver_->getNodeInfo(iter->first, odomPose, mapId, weight, label, stamp, groundTruth, velocity))
{
color = (Qt::GlobalColor)(mapId % 12 + 7 );
}
if(scan.channels() == 6)
{
pcl::PointCloud<pcl::PointNormal>::Ptr cloud;
cloud = util3d::laserScanToPointCloudNormal(scan, data.laserScanInfo().localTransform());
viewer->addCloud(uFormat("cloud%d", iter->first), cloud, pose, color);
}
else
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
cloud = util3d::laserScanToPointCloud(scan, data.laserScanInfo().localTransform());
viewer->addCloud(uFormat("cloud%d", iter->first), cloud, pose, color);
}
UINFO("Generated %d (%d points)", iter->first, scan.cols);
progressDialog.appendText(QString("Generated %1 (%2 points)").arg(iter->first).arg(scan.cols));
}
else
{
UINFO("Empty scan %d", iter->first);
progressDialog.appendText(QString("Empty scan %1").arg(iter->first));
}
progressDialog.incrementStep();
QApplication::processEvents();
}
}
progressDialog.setValue(progressDialog.maximumSteps());
}
else
{
QMessageBox::critical(this, tr("Error"), tr("No neighbors found for node %1.").arg(ui_->spinBox_optimizationsFrom->value()));
}
}
}
void DatabaseViewer::generate3DMap()
{
if(!ids_.size() || !dbDriver_)
@@ -2233,6 +2120,7 @@ void DatabaseViewer::generate3DMap()
mapIds_,
QMap<int, Signature>(),
std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> >(),
std::map<int, cv::Mat>(),
pathDatabase_,
ui_->parameters_toolbox->getParameters());
}
@@ -2242,103 +2130,6 @@ void DatabaseViewer::generate3DMap()
}
}
void DatabaseViewer::generate3DLaserScans()
{
if(!ids_.size() || !dbDriver_)
{
QMessageBox::warning(this, tr("Cannot generate a graph"), tr("The database is empty..."));
return;
}
bool ok = false;
int downsamplingStepSize = QInputDialog::getInt(this, tr("Downsampling?"), tr("Downsample step size (1 = no filtering)"), 1, 1, 99999, 1, &ok);
if(ok)
{
QString path = QFileDialog::getSaveFileName(this, tr("Save point cloud"),
pathDatabase_+QDir::separator()+"cloud.ply",
tr("Point Cloud (*.ply *.pcd)"));
if(!path.isEmpty())
{
std::map<int, Transform> optimizedPoses = uValueAt(graphes_, ui_->horizontalSlider_iterations->value());
if(ui_->groupBox_posefiltering->isChecked())
{
optimizedPoses = graph::radiusPosesFiltering(optimizedPoses,
ui_->doubleSpinBox_posefilteringRadius->value(),
ui_->doubleSpinBox_posefilteringAngle->value()*CV_PI/180.0);
}
if(optimizedPoses.size() > 0)
{
rtabmap::ProgressDialog progressDialog;
progressDialog.setMaximumSteps((int)optimizedPoses.size());
progressDialog.show();
pcl::PointCloud<pcl::PointXYZ>::Ptr assembledCloud(new pcl::PointCloud<pcl::PointXYZ>);
for(std::map<int, Transform>::const_iterator iter = optimizedPoses.begin(); iter!=optimizedPoses.end(); ++iter)
{
const rtabmap::Transform & pose = iter->second;
if(!pose.isNull())
{
SensorData data;
dbDriver_->getNodeData(iter->first, data);
cv::Mat scan;
data.uncompressDataConst(0, 0, &scan);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
UASSERT(scan.empty() || scan.type()==CV_32FC2 || scan.type() == CV_32FC3);
if(downsamplingStepSize > 1)
{
scan = util3d::downsample(scan, downsamplingStepSize);
}
cloud = util3d::laserScanToPointCloud(scan, data.laserScanInfo().localTransform());
if(cloud->size())
{
cloud = rtabmap::util3d::transformPointCloud(cloud, pose);
if(assembledCloud->size() == 0)
{
*assembledCloud = *cloud;
}
else
{
*assembledCloud += *cloud;
}
}
UINFO("Created cloud %d (%d points)", iter->first, (int)cloud->size());
progressDialog.appendText(QString("Created cloud %1 (%2 points)").arg(iter->first).arg(cloud->size()));
progressDialog.incrementStep();
QApplication::processEvents();
}
}
if(assembledCloud->size())
{
//voxelize by default to 1 cm
progressDialog.appendText(QString("Voxelize assembled cloud (%1 points)").arg(assembledCloud->size()));
QApplication::processEvents();
assembledCloud = util3d::voxelize(assembledCloud, 0.01);
if(QFileInfo(path).suffix() == "ply")
{
pcl::io::savePLYFile(path.toStdString(), *assembledCloud);
}
else
{
pcl::io::savePCDFile(path.toStdString(), *assembledCloud);
}
progressDialog.appendText(QString("Saved %1 (%2 points)").arg(path).arg(assembledCloud->size()));
QApplication::processEvents();
}
QMessageBox::information(this, tr("Finished"), tr("%1 clouds generated to %2.").arg(optimizedPoses.size()).arg(path));
progressDialog.setValue(progressDialog.maximumSteps());
}
else
{
QMessageBox::critical(this, tr("Error"), tr("No neighbors found for node %1.").arg(ui_->spinBox_optimizationsFrom->value()));
}
}
}
}
void DatabaseViewer::detectMoreLoopClosures()
{
if(graphes_.empty())

View File

@@ -84,6 +84,8 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
restoreDefaults();
_ui->comboBox_upsamplingMethod->setItemData(1, 0, Qt::UserRole - 1); // disable DISTINCT_CLOUD
connect(_ui->checkBox_fromDepth, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_fromDepth, SIGNAL(stateChanged(int)), this, SLOT(updateReconstructionFlavor()));
connect(_ui->checkBox_binary, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_normalKSearch, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->comboBox_pipeline, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
@@ -243,6 +245,7 @@ void ExportCloudsDialog::saveSettings(QSettings & settings, const QString & grou
settings.beginGroup(group);
}
settings.setValue("pipeline", _ui->comboBox_pipeline->currentIndex());
settings.setValue("from_depth", _ui->checkBox_fromDepth->isChecked());
settings.setValue("binary", _ui->checkBox_binary->isChecked());
settings.setValue("normals_k", _ui->spinBox_normalKSearch->value());
@@ -351,6 +354,7 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
}
_ui->comboBox_pipeline->setCurrentIndex(settings.value("pipeline", _ui->comboBox_pipeline->currentIndex()).toInt());
_ui->checkBox_fromDepth->setChecked(settings.value("from_depth", _ui->checkBox_fromDepth->isChecked()).toBool());
_ui->checkBox_binary->setChecked(settings.value("binary", _ui->checkBox_binary->isChecked()).toBool());
_ui->spinBox_normalKSearch->setValue(settings.value("normals_k", _ui->spinBox_normalKSearch->value()).toInt());
@@ -459,6 +463,7 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
void ExportCloudsDialog::restoreDefaults()
{
_ui->comboBox_pipeline->setCurrentIndex(1);
_ui->checkBox_fromDepth->setChecked(true);
_ui->checkBox_binary->setChecked(true);
_ui->spinBox_normalKSearch->setValue(20);
@@ -560,6 +565,28 @@ void ExportCloudsDialog::restoreDefaults()
void ExportCloudsDialog::updateReconstructionFlavor()
{
if(!_ui->checkBox_fromDepth->isChecked())
{
_ui->comboBox_pipeline->setCurrentIndex(1);
_ui->comboBox_pipeline->setEnabled(false);
_ui->comboBox_frame->setItemData(2, 0,Qt::UserRole - 1);
_ui->comboBox_frame->setItemData(3, 1|32,Qt::UserRole - 1);
if(_ui->comboBox_frame->currentIndex() == 2)
{
_ui->comboBox_frame->setCurrentIndex(0);
}
}
else
{
_ui->comboBox_pipeline->setEnabled(true);
_ui->comboBox_frame->setItemData(2, 1|32,Qt::UserRole - 1);
_ui->comboBox_frame->setItemData(3, 0,Qt::UserRole - 1);
if(_ui->comboBox_frame->currentIndex() == 3)
{
_ui->comboBox_frame->setCurrentIndex(0);
}
}
_ui->checkBox_smoothing->setVisible(_ui->comboBox_pipeline->currentIndex() == 1);
_ui->checkBox_smoothing->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
@@ -570,7 +597,8 @@ void ExportCloudsDialog::updateReconstructionFlavor()
_ui->checkBox_gainCompensation->setVisible(_ui->checkBox_gainCompensation->isEnabled());
_ui->label_gainCompensation->setVisible(_ui->checkBox_gainCompensation->isEnabled());
_ui->groupBox_regenerate->setVisible(_ui->checkBox_regenerate->isChecked());
_ui->groupBox_regenerate->setVisible(_ui->checkBox_regenerate->isChecked() && _ui->checkBox_fromDepth->isChecked());
_ui->groupBox_regenerateScans->setVisible(_ui->checkBox_regenerate->isChecked() && !_ui->checkBox_fromDepth->isChecked());
_ui->groupBox_bilateral->setVisible(_ui->checkBox_bilateral->isChecked());
_ui->groupBox_filtering->setVisible(_ui->checkBox_filtering->isChecked());
_ui->groupBox_gain->setVisible(_ui->checkBox_gainCompensation->isEnabled() && _ui->checkBox_gainCompensation->isChecked());
@@ -668,21 +696,13 @@ void ExportCloudsDialog::setOkButton()
updateReconstructionFlavor();
}
void ExportCloudsDialog::enableRegeneration(bool enabled)
{
if(!enabled)
{
_ui->checkBox_regenerate->setChecked(false);
}
_ui->checkBox_regenerate->setEnabled(enabled);
}
void ExportCloudsDialog::exportClouds(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters)
{
@@ -699,6 +719,7 @@ void ExportCloudsDialog::exportClouds(
mapIds,
cachedSignatures,
cachedClouds,
cachedScans,
workingDirectory,
parameters,
clouds,
@@ -743,6 +764,7 @@ void ExportCloudsDialog::viewClouds(
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters)
{
@@ -759,6 +781,7 @@ void ExportCloudsDialog::viewClouds(
mapIds,
cachedSignatures,
cachedClouds,
cachedScans,
workingDirectory,
parameters,
clouds,
@@ -981,6 +1004,7 @@ bool ExportCloudsDialog::getExportedClouds(
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters,
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & cloudsWithNormals,
@@ -990,10 +1014,11 @@ bool ExportCloudsDialog::getExportedClouds(
{
_canceled = false;
_workingDirectory = workingDirectory;
enableRegeneration(_dbDriver || cachedSignatures.size());
_ui->checkBox_regenerate->setEnabled(true);
if(cachedSignatures.empty() && _dbDriver)
{
_ui->checkBox_regenerate->setChecked(true);
_ui->checkBox_regenerate->setEnabled(false);
}
if(_compensator)
{
@@ -1037,12 +1062,16 @@ bool ExportCloudsDialog::getExportedClouds(
}
_progressDialog->setMaximumSteps(int(poses.size())*mul+1);
bool has2dScans = false;
std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::IndicesPtr> > clouds = this->getClouds(
poses,
cachedSignatures,
cachedClouds,
parameters);
cachedScans,
parameters,
has2dScans);
UDEBUG("");
if(_canceled)
{
return false;
@@ -1064,7 +1093,8 @@ bool ExportCloudsDialog::getExportedClouds(
return false;
}
if(_ui->checkBox_gainCompensation->isChecked() && clouds.size() > 1 &&
UDEBUG("");
if(_ui->checkBox_gainCompensation->isChecked() && _ui->checkBox_fromDepth->isChecked() && clouds.size() > 1 &&
// Do compensation later if we are merging textures on a dense assembled cloud
!(_ui->checkBox_meshing->isChecked() &&
_ui->checkBox_textureMapping->isEnabled() &&
@@ -1137,6 +1167,59 @@ bool ExportCloudsDialog::getExportedClouds(
}
}
UDEBUG("");
std::map<int, Transform> normalViewpoints = poses;
if(_ui->checkBox_assemble->isChecked())
{
// Adjust view points with local transforms
for(std::map<int, Transform>::iterator iter= normalViewpoints.begin(); iter!=normalViewpoints.end(); ++iter)
{
if(_ui->checkBox_fromDepth->isChecked())
{
std::vector<CameraModel> models;
StereoCameraModel stereoModel;
if(cachedSignatures.contains(iter->first))
{
const SensorData & data = cachedSignatures.find(iter->first)->sensorData();
models = data.cameraModels();
stereoModel = data.stereoCameraModel();
}
else if(_dbDriver)
{
_dbDriver->getCalibration(iter->first, models, stereoModel);
}
if(models.size() && !models[0].localTransform().isNull())
{
iter->second *= models[0].localTransform();
}
else if(!stereoModel.localTransform().isNull())
{
iter->second *= stereoModel.localTransform();
}
}
else
{
LaserScanInfo info;
if(cachedSignatures.contains(iter->first))
{
const SensorData & data = cachedSignatures.find(iter->first)->sensorData();
info = data.laserScanInfo();
}
else if(_dbDriver)
{
_dbDriver->getLaserScanInfo(iter->first, info);
}
if(!info.localTransform().isNull())
{
iter->second *= info.localTransform();
}
}
}
}
UDEBUG("");
pcl::PointCloud<pcl::PointXYZ>::Ptr rawAssembledCloud(new pcl::PointCloud<pcl::PointXYZ>);
std::vector<int> rawCameraIndices;
if(_ui->checkBox_assemble->isChecked() &&
@@ -1202,61 +1285,55 @@ bool ExportCloudsDialog::getExportedClouds(
{
indices->at(i) = i;
}
if(!_ui->checkBox_fromDepth->isChecked() && !has2dScans)
{
// recompute normals
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudWithoutNormals(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*assembledCloud, *cloudWithoutNormals);
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloudWithoutNormals, indices, _ui->spinBox_normalKSearch->value());
UASSERT(assembledCloud->size() == normals->size());
for(unsigned int i=0; i<normals->size(); ++i)
{
assembledCloud->points[i].normal_x = normals->points[i].normal_x;
assembledCloud->points[i].normal_y = normals->points[i].normal_y;
assembledCloud->points[i].normal_z = normals->points[i].normal_z;
}
// adjust with point of views
util3d::adjustNormalsToViewPoints(
normalViewpoints,
rawAssembledCloud,
rawCameraIndices,
assembledCloud);
}
clouds.insert(std::make_pair(0, std::make_pair(assembledCloud, indices)));
}
UDEBUG("");
if(_canceled)
{
return false;
}
std::map<int, Transform> mlsViewPoints = poses;
if(_ui->checkBox_smoothing->isEnabled() && _ui->checkBox_smoothing->isChecked())
if(_ui->checkBox_smoothing->isEnabled() && _ui->checkBox_smoothing->isChecked() && !has2dScans)
{
_progressDialog->appendText(tr("Smoothing the surface using Moving Least Squares (MLS) algorithm... "
"[search radius=%1m voxel=%2m]").arg(_ui->doubleSpinBox_mlsRadius->value()).arg(_ui->doubleSpinBox_voxelSize_assembled->value()));
QApplication::processEvents();
uSleep(100);
QApplication::processEvents();
if(_ui->checkBox_assemble->isChecked())
{
// Adjust view points with local transforms
for(std::map<int, Transform>::iterator iter= mlsViewPoints.begin(); iter!=mlsViewPoints.end(); ++iter)
{
std::vector<CameraModel> models;
StereoCameraModel stereoModel;
if(cachedSignatures.contains(iter->first))
{
const SensorData & data = cachedSignatures.find(iter->first)->sensorData();
models = data.cameraModels();
stereoModel = data.stereoCameraModel();
}
else if(_dbDriver)
{
_dbDriver->getCalibration(iter->first, models, stereoModel);
}
if(models.size() && !models[0].localTransform().isNull())
{
iter->second *= models[0].localTransform();
}
else if(!stereoModel.localTransform().isNull())
{
iter->second *= stereoModel.localTransform();
}
}
}
}
//fill cloudWithNormals
for(std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::IndicesPtr> >::iterator iter=clouds.begin();
iter!= clouds.end();
++iter)
iter!= clouds.end();)
{
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals = iter->second.first;
if(_ui->checkBox_smoothing->isEnabled() && _ui->checkBox_smoothing->isChecked())
if(_ui->checkBox_smoothing->isEnabled() && _ui->checkBox_smoothing->isChecked() && !has2dScans)
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudWithoutNormals(new pcl::PointCloud<pcl::PointXYZRGB>);
if(iter->second.second->size())
@@ -1306,7 +1383,7 @@ bool ExportCloudsDialog::getExportedClouds(
_progressDialog->appendText(tr("Update %1 normals with %2 camera views...").arg(cloudWithNormals->size()).arg(poses.size()));
util3d::adjustNormalsToViewPoints(
mlsViewPoints,
normalViewpoints,
rawAssembledCloud,
rawCameraIndices,
cloudWithNormals);
@@ -1319,6 +1396,9 @@ bool ExportCloudsDialog::getExportedClouds(
cloudsWithNormals.insert(std::make_pair(iter->first, cloudWithNormals));
// clear memory
clouds.erase(iter++);
_progressDialog->incrementStep();
QApplication::processEvents();
if(_canceled)
@@ -1327,6 +1407,7 @@ bool ExportCloudsDialog::getExportedClouds(
}
}
UDEBUG("");
#ifdef RTABMAP_CPUTSDF
cpu_tsdf::TSDFVolumeOctree::Ptr tsdf;
#endif
@@ -1337,7 +1418,7 @@ bool ExportCloudsDialog::getExportedClouds(
//mesh
UDEBUG("Meshing=%d", _ui->checkBox_meshing->isChecked()?1:0);
if(_ui->checkBox_meshing->isChecked())
if(_ui->checkBox_meshing->isChecked() && !has2dScans)
{
if(_ui->comboBox_pipeline->currentIndex() == 0)
{
@@ -1521,7 +1602,7 @@ bool ExportCloudsDialog::getExportedClouds(
if(polygons.size() == 0)
{
std::string msg = uFormat("All %d polygons filtered after polygon cluster filtering. Cluster minimum size is %d.", before, _ui->spinBox_mesh_minClusterSize->value());
_progressDialog->appendText(msg.c_str());
_progressDialog->appendText(msg.c_str(), Qt::darkYellow);
UWARN(msg.c_str());
}
@@ -1529,7 +1610,7 @@ bool ExportCloudsDialog::getExportedClouds(
QApplication::processEvents();
}
_progressDialog->appendText(tr("Mesh %1 created with %2 polygons (%3/%4).").arg(iter->first).arg(polygons.size()).arg(++i).arg(clouds.size()));
_progressDialog->appendText(tr("Mesh %1 created with %2 polygons (%3/%4).").arg(iter->first).arg(polygons.size()).arg(++i).arg(cloudsWithNormals.size()));
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr denseCloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
std::vector<pcl::Vertices> densePolygons;
@@ -1571,12 +1652,25 @@ bool ExportCloudsDialog::getExportedClouds(
}
else
{
_progressDialog->appendText(tr("Mesh %1 not created (no valid points) (%2/%3).").arg(iter->first).arg(++i).arg(clouds.size()));
_progressDialog->appendText(tr("Mesh %1 not created (no valid points) (%2/%3).").arg(iter->first).arg(++i).arg(cloudsWithNormals.size()));
}
}
else
{
_progressDialog->appendText(tr("Mesh %1 not created (cloud is not organized). You may want to check cloud regeneration option (%2/%3).").arg(iter->first).arg(++i).arg(clouds.size()));
int weight = 0;
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
weight = s.getWeight();
}
else if(_dbDriver)
{
_dbDriver->getWeight(iter->first, weight);
}
if(weight>=0) // don't show error for intermediate nodes
{
_progressDialog->appendText(tr("Mesh %1 not created (cloud is not organized). You may want to check cloud regeneration option (%2/%3).").arg(iter->first).arg(++i).arg(cloudsWithNormals.size()));
}
}
_progressDialog->incrementStep();
@@ -1694,7 +1788,14 @@ bool ExportCloudsDialog::getExportedClouds(
}
}
}
else if(_ui->checkBox_meshing->isChecked())
{
std::string msg = uFormat("Some clouds are 2D laser scans. Meshing can be done only from RGB-D clouds or 3D laser scans.");
_progressDialog->appendText(msg.c_str(), Qt::darkYellow);
UWARN(msg.c_str());
}
UDEBUG("");
#ifdef RTABMAP_CPUTSDF
if(tsdf.get())
{
@@ -1780,6 +1881,7 @@ bool ExportCloudsDialog::getExportedClouds(
}
#endif
UDEBUG("");
if(_canceled)
{
return false;
@@ -1787,7 +1889,7 @@ bool ExportCloudsDialog::getExportedClouds(
// texture mesh
UDEBUG("texture mapping=%d", _ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked()?1:0);
if(_ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked())
if(!has2dScans && _ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked())
{
_progressDialog->appendText(tr("Texturing..."));
QApplication::processEvents();
@@ -2083,7 +2185,7 @@ bool ExportCloudsDialog::getExportedClouds(
if(validPolygons.size() == 0)
{
std::string msg = uFormat("All %d polygons filtered after polygon cluster filtering. Cluster minimum size is %d.",totalSize, _ui->spinBox_mesh_minClusterSize->value());
_progressDialog->appendText(msg.c_str());
_progressDialog->appendText(msg.c_str(), Qt::darkYellow);
UWARN(msg.c_str());
}
@@ -2190,8 +2292,11 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
const std::map<int, Transform> & poses,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const ParametersMap & parameters) const
const std::map<int, cv::Mat> & cachedScans,
const ParametersMap & parameters,
bool & has2dScans) const
{
has2dScans = false;
std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::IndicesPtr> > clouds;
int index=1;
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr previousCloud;
@@ -2209,20 +2314,26 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
if(_ui->checkBox_regenerate->isChecked())
{
SensorData data;
cv::Mat image, depth;
cv::Mat image, depth, scan;
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
data = s.sensorData();
data.uncompressData(&image, &depth, 0);
data.uncompressData(
_ui->checkBox_fromDepth->isChecked()?&image:0,
_ui->checkBox_fromDepth->isChecked()?&depth:0,
!_ui->checkBox_fromDepth->isChecked()?&scan:0);
}
else if(_dbDriver)
{
_dbDriver->getNodeData(iter->first, data, true, false, false, false);
data.uncompressData(&image, &depth, 0);
_dbDriver->getNodeData(iter->first, data, _ui->checkBox_fromDepth->isChecked(), !_ui->checkBox_fromDepth->isChecked(), false, false);
data.uncompressData(
_ui->checkBox_fromDepth->isChecked()?&image:0,
_ui->checkBox_fromDepth->isChecked()?&depth:0,
!_ui->checkBox_fromDepth->isChecked()?&scan:0);
}
if(!image.empty() && !depth.empty())
if(_ui->checkBox_fromDepth->isChecked() && !image.empty() && !depth.empty())
{
if(_ui->spinBox_fillDepthHoles->value() > 0)
{
@@ -2332,10 +2443,63 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
}
}
}
else if(!_ui->checkBox_fromDepth->isChecked() && !scan.empty())
{
bool is2D = scan.channels() == 2;
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudWithoutNormals;
localTransform = Transform::getIdentity();
Eigen::Vector3f viewPoint(0.0f,0.0f,0.0f);
if(_ui->comboBox_frame->isEnabled() &&
_ui->comboBox_frame->currentIndex()!=3 &&
!data.laserScanInfo().localTransform().isNull())
{
localTransform = data.laserScanInfo().localTransform();
viewPoint[0] = localTransform.x();
viewPoint[1] = localTransform.y();
viewPoint[2] = localTransform.z();
}
cloudWithoutNormals = util3d::laserScanToPointCloudRGB(scan, localTransform);
if(cloudWithoutNormals->size())
{
if(_ui->doubleSpinBox_voxelSize_assembled->value()>0.0)
{
cloudWithoutNormals = util3d::voxelize(cloudWithoutNormals, _ui->doubleSpinBox_voxelSize_assembled->value());
}
indices->resize(cloudWithoutNormals->size());
for(unsigned int i=0; i<indices->size(); ++i)
{
indices->at(i) = i;
}
pcl::PointCloud<pcl::Normal>::Ptr normals;
if(is2D)
{
// set nan normals
normals.reset(new pcl::PointCloud<pcl::Normal>);
normals->resize(cloudWithoutNormals->size());
for(unsigned int i=0;i<cloudWithoutNormals->size(); ++i)
{
normals->points[i].normal_x =std::numeric_limits<float>::quiet_NaN();
normals->points[i].normal_y =std::numeric_limits<float>::quiet_NaN();
normals->points[i].normal_z =std::numeric_limits<float>::quiet_NaN();
}
has2dScans = true;
}
else
{
normals = util3d::computeNormals(cloudWithoutNormals, indices, _ui->spinBox_normalKSearch->value(), viewPoint);
}
pcl::concatenateFields(*cloudWithoutNormals, *normals, *cloud);
}
}
else
{
int weight = 0;
if(_dbDriver)
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
weight = s.getWeight();
}
else if(_dbDriver)
{
_dbDriver->getWeight(iter->first, weight);
}
@@ -2345,7 +2509,7 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
}
}
}
else if(uContains(cachedClouds, iter->first))
else if(_ui->checkBox_fromDepth->isChecked() && uContains(cachedClouds, iter->first))
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudWithoutNormals;
if(!_ui->checkBox_meshing->isChecked() &&
@@ -2406,9 +2570,88 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloudWithoutNormals, indices, _ui->spinBox_normalKSearch->value(), viewPoint);
pcl::concatenateFields(*cloudWithoutNormals, *normals, *cloud);
}
else if(!_ui->checkBox_fromDepth->isChecked() && uContains(cachedScans, iter->first))
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudWithoutNormals;
localTransform = Transform::getIdentity();
Eigen::Vector3f viewPoint(0.0f,0.0f,0.0f);
LaserScanInfo info;
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
info = s.sensorData().laserScanInfo();
}
else if(_dbDriver)
{
_dbDriver->getLaserScanInfo(iter->first, info);
}
if(!info.localTransform().isNull())
{
if(_ui->comboBox_frame->isEnabled() && _ui->comboBox_frame->currentIndex()!=3)
{
viewPoint[0] = localTransform.x();
viewPoint[1] = localTransform.y();
viewPoint[2] = localTransform.z();
}
else
{
localTransform = info.localTransform().inverse();
}
}
bool is2D = cachedScans.at(iter->first).channels() == 2;
cloudWithoutNormals = util3d::laserScanToPointCloudRGB(cachedScans.at(iter->first), localTransform);
if(cloudWithoutNormals->size())
{
if(_ui->doubleSpinBox_voxelSize_assembled->value()>0.0)
{
cloudWithoutNormals = util3d::voxelize(cloudWithoutNormals, _ui->doubleSpinBox_voxelSize_assembled->value());
}
indices->resize(cloudWithoutNormals->size());
for(unsigned int i=0; i<indices->size(); ++i)
{
indices->at(i) = i;
}
pcl::PointCloud<pcl::Normal>::Ptr normals;
if(is2D)
{
// set nan normals
normals.reset(new pcl::PointCloud<pcl::Normal>);
normals->resize(cloudWithoutNormals->size());
for(unsigned int i=0;i<cloudWithoutNormals->size(); ++i)
{
normals->points[i].normal_x =std::numeric_limits<float>::quiet_NaN();
normals->points[i].normal_y =std::numeric_limits<float>::quiet_NaN();
normals->points[i].normal_z =std::numeric_limits<float>::quiet_NaN();
}
has2dScans = true;
}
else
{
normals = util3d::computeNormals(cloudWithoutNormals, indices, _ui->spinBox_normalKSearch->value(), viewPoint);
}
pcl::concatenateFields(*cloudWithoutNormals, *normals, *cloud);
}
}
else
{
_progressDialog->appendText(tr("Cached cloud %1 not found. You may want to regenerate the clouds (%2/%3).").arg(iter->first).arg(index).arg(poses.size()), Qt::darkYellow);
int weight = 0;
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
weight = s.getWeight();
}
else if(_dbDriver)
{
_dbDriver->getWeight(iter->first, weight);
}
if(weight>=0) // don't show error for intermediate nodes
{
_progressDialog->appendText(tr("Cached cloud %1 not found. You may want to regenerate the clouds (%2/%3).").arg(iter->first).arg(index).arg(poses.size()), Qt::darkYellow);
}
}
if(indices->size())

View File

@@ -69,6 +69,7 @@ public:
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters);
@@ -78,6 +79,7 @@ public:
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters);
@@ -100,13 +102,16 @@ private:
const std::map<int, Transform> & poses,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const ParametersMap & parameters) const;
const std::map<int, cv::Mat> & cachedScans,
const ParametersMap & parameters,
bool & has2dScans) const;
bool getExportedClouds(
const std::map<int, Transform> & poses,
const std::multimap<int, Link> & links,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGB>::Ptr, pcl::IndicesPtr> > & cachedClouds,
const std::map<int, cv::Mat> & cachedScans,
const QString & workingDirectory,
const ParametersMap & parameters,
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds,
@@ -120,7 +125,6 @@ private:
void setSaveButton();
void setOkButton();
void enableRegeneration(bool enabled);
void denseMeshPostProcessing(
int id,

View File

@@ -1,597 +0,0 @@
/*
Copyright (c) 2010-2016, 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 "ExportScansDialog.h"
#include "ui_exportScansDialog.h"
#include "rtabmap/gui/ProgressDialog.h"
#include "rtabmap/gui/CloudViewer.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UConversion.h"
#include "rtabmap/utilite/UThread.h"
#include "rtabmap/utilite/UStl.h"
#include "rtabmap/core/util3d_filtering.h"
#include "rtabmap/core/util3d_surface.h"
#include "rtabmap/core/util3d_transforms.h"
#include "rtabmap/core/util3d.h"
#include "rtabmap/core/Graph.h"
#include <pcl/conversions.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <QPushButton>
#include <QDir>
#include <QFileInfo>
#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
namespace rtabmap {
ExportScansDialog::ExportScansDialog(QWidget *parent) :
QDialog(parent)
{
_ui = new Ui_ExportScansDialog();
_ui->setupUi(this);
connect(_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults()));
restoreDefaults();
connect(_ui->checkBox_binary, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_normalKSearch, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->groupBox_regenerate, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_decimation, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->groupBox_filtering, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_filteringRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
connect(_ui->spinBox_filteringMinNeighbors, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
connect(_ui->checkBox_assemble, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
connect(_ui->doubleSpinBox_voxelSize_assembled, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
_progressDialog = new ProgressDialog(this);
_progressDialog->setVisible(false);
_progressDialog->setAutoClose(true, 2);
_progressDialog->setMinimumWidth(600);
}
ExportScansDialog::~ExportScansDialog()
{
delete _ui;
}
void ExportScansDialog::saveSettings(QSettings & settings, const QString & group) const
{
if(!group.isEmpty())
{
settings.beginGroup(group);
}
settings.setValue("binary", _ui->checkBox_binary->isChecked());
settings.setValue("normals_k", _ui->spinBox_normalKSearch->value());
settings.setValue("regenerate", _ui->groupBox_regenerate->isChecked());
settings.setValue("regenerate_decimation", _ui->spinBox_decimation->value());
settings.setValue("filtering", _ui->groupBox_filtering->isChecked());
settings.setValue("filtering_radius", _ui->doubleSpinBox_filteringRadius->value());
settings.setValue("filtering_min_neighbors", _ui->spinBox_filteringMinNeighbors->value());
settings.setValue("assemble", _ui->checkBox_assemble->isChecked());
settings.setValue("assemble_voxel",_ui->doubleSpinBox_voxelSize_assembled->value());
if(!group.isEmpty())
{
settings.endGroup();
}
}
void ExportScansDialog::loadSettings(QSettings & settings, const QString & group)
{
if(!group.isEmpty())
{
settings.beginGroup(group);
}
_ui->checkBox_binary->setChecked(settings.value("binary", _ui->checkBox_binary->isChecked()).toBool());
_ui->spinBox_normalKSearch->setValue(settings.value("normals_k", _ui->spinBox_normalKSearch->value()).toInt());
_ui->groupBox_regenerate->setChecked(settings.value("regenerate", _ui->groupBox_regenerate->isChecked()).toBool());
_ui->spinBox_decimation->setValue(settings.value("regenerate_decimation", _ui->spinBox_decimation->value()).toInt());
_ui->groupBox_filtering->setChecked(settings.value("filtering", _ui->groupBox_filtering->isChecked()).toBool());
_ui->doubleSpinBox_filteringRadius->setValue(settings.value("filtering_radius", _ui->doubleSpinBox_filteringRadius->value()).toDouble());
_ui->spinBox_filteringMinNeighbors->setValue(settings.value("filtering_min_neighbors", _ui->spinBox_filteringMinNeighbors->value()).toInt());
_ui->checkBox_assemble->setChecked(settings.value("assemble", _ui->checkBox_assemble->isChecked()).toBool());
_ui->doubleSpinBox_voxelSize_assembled->setValue(settings.value("assemble_voxel", _ui->doubleSpinBox_voxelSize_assembled->value()).toDouble());
if(!group.isEmpty())
{
settings.endGroup();
}
}
void ExportScansDialog::restoreDefaults()
{
_ui->checkBox_binary->setChecked(true);
_ui->spinBox_normalKSearch->setValue(20);
_ui->groupBox_regenerate->setChecked(false);
_ui->spinBox_decimation->setValue(1);
_ui->groupBox_filtering->setChecked(false);
_ui->doubleSpinBox_filteringRadius->setValue(0.02);
_ui->spinBox_filteringMinNeighbors->setValue(2);
_ui->checkBox_assemble->setChecked(true);
_ui->doubleSpinBox_voxelSize_assembled->setValue(0.01);
this->update();
}
void ExportScansDialog::setSaveButton()
{
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(false);
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(true);
_ui->checkBox_binary->setVisible(true);
_ui->label_binaryFile->setVisible(true);
}
void ExportScansDialog::setOkButton()
{
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(false);
_ui->checkBox_binary->setVisible(false);
_ui->label_binaryFile->setVisible(false);
}
void ExportScansDialog::enableRegeneration(bool enabled)
{
if(!enabled)
{
_ui->groupBox_regenerate->setChecked(false);
}
_ui->groupBox_regenerate->setEnabled(enabled);
}
void ExportScansDialog::exportScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans,
const QString & workingDirectory)
{
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> clouds;
setSaveButton();
if(getExportedScans(
poses,
mapIds,
cachedSignatures,
createdScans,
workingDirectory,
clouds))
{
saveScans(workingDirectory, poses, clouds, _ui->checkBox_binary->isChecked());
_progressDialog->setValue(_progressDialog->maximumSteps());
}
}
void ExportScansDialog::viewScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans,
const QString & workingDirectory)
{
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> clouds;
setOkButton();
if(getExportedScans(
poses,
mapIds,
cachedSignatures,
createdScans,
workingDirectory,
clouds))
{
QDialog * window = new QDialog(this->parentWidget()?this->parentWidget():this, Qt::Window);
window->setAttribute(Qt::WA_DeleteOnClose, true);
window->setWindowTitle(tr("Scans (%1 nodes)").arg(clouds.size()));
window->setMinimumWidth(800);
window->setMinimumHeight(600);
CloudViewer * viewer = new CloudViewer(window);
viewer->setCameraLockZ(false);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(viewer);
window->setLayout(layout);
connect(window, SIGNAL(finished(int)), viewer, SLOT(clear()));
window->show();
uSleep(500);
if(clouds.size())
{
for(std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr>::iterator iter = clouds.begin(); iter!=clouds.end(); ++iter)
{
_progressDialog->appendText(tr("Viewing the cloud %1 (%2 points)...").arg(iter->first).arg(iter->second->size()));
_progressDialog->incrementStep();
QColor color = Qt::gray;
int mapId = uValue(mapIds, iter->first, -1);
if(mapId >= 0)
{
color = (Qt::GlobalColor)(mapId % 12 + 7 );
}
viewer->addCloud(uFormat("cloud%d",iter->first), iter->second, iter->first>0?poses.at(iter->first):Transform::getIdentity());
_progressDialog->appendText(tr("Viewing the cloud %1 (%2 points)... done.").arg(iter->first).arg(iter->second->size()));
}
}
_progressDialog->setValue(_progressDialog->maximumSteps());
viewer->update();
}
}
bool ExportScansDialog::getExportedScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdClouds,
const QString & workingDirectory,
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> & cloudsWithNormals)
{
enableRegeneration(cachedSignatures.size());
if(this->exec() == QDialog::Accepted)
{
_progressDialog->resetProgress();
_progressDialog->show();
int mul = 1;
if(_ui->checkBox_assemble->isChecked())
{
mul+=1;
}
mul+=1; // normals
_progressDialog->setMaximumSteps(int(poses.size())*mul+1);
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> clouds = this->getScans(
poses,
cachedSignatures,
createdClouds);
if(_ui->checkBox_assemble->isChecked())
{
_progressDialog->appendText(tr("Assembling %1 clouds...").arg(clouds.size()));
QApplication::processEvents();
pcl::PointCloud<pcl::PointXYZ>::Ptr rawAssembledCloud(new pcl::PointCloud<pcl::PointXYZ>);
std::vector<int> rawCameraIndices;
int i =0;
pcl::PointCloud<pcl::PointNormal>::Ptr assembledCloud(new pcl::PointCloud<pcl::PointNormal>);
for(std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr>::iterator iter=clouds.begin();
iter!= clouds.end();
++iter)
{
pcl::PointCloud<pcl::PointNormal>::Ptr transformed(new pcl::PointCloud<pcl::PointNormal>);
transformed = util3d::transformPointCloud(iter->second, poses.at(iter->first));
*assembledCloud += *transformed;
rawCameraIndices.resize(assembledCloud->size(), iter->first);
_progressDialog->appendText(tr("Assembled cloud %1, total=%2 (%3/%4).").arg(iter->first).arg(assembledCloud->size()).arg(++i).arg(clouds.size()));
_progressDialog->incrementStep();
QApplication::processEvents();
}
pcl::copyPointCloud(*assembledCloud, *rawAssembledCloud);
if(_ui->doubleSpinBox_voxelSize_assembled->value())
{
_progressDialog->appendText(tr("Voxelize cloud (%1 points, voxel size = %2 m)...")
.arg(assembledCloud->size())
.arg(_ui->doubleSpinBox_voxelSize_assembled->value()));
QApplication::processEvents();
assembledCloud = util3d::voxelize(
assembledCloud,
_ui->doubleSpinBox_voxelSize_assembled->value());
if(_ui->spinBox_normalKSearch->value() > 0)
{
_progressDialog->appendText(tr("Compute normals (%1 points)...")
.arg(assembledCloud->size()));
QApplication::processEvents();
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudXYZ(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*assembledCloud, *cloudXYZ);
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloudXYZ, _ui->spinBox_normalKSearch->value());
pcl::concatenateFields(*cloudXYZ, *normals, *assembledCloud);
_progressDialog->appendText(tr("Update %1 normals with %2 camera views...")
.arg(assembledCloud->size()).arg(poses.size()));
util3d::adjustNormalsToViewPoints(
poses,
rawAssembledCloud,
rawCameraIndices,
assembledCloud);
}
}
clouds.clear();
clouds.insert(std::make_pair(0, assembledCloud));
}
//fill cloudWithNormals
for(std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr>::iterator iter=clouds.begin();
iter!= clouds.end();
++iter)
{
pcl::PointCloud<pcl::PointNormal>::Ptr cloudWithNormals = iter->second;
cloudsWithNormals.insert(std::make_pair(iter->first, cloudWithNormals));
_progressDialog->incrementStep();
QApplication::processEvents();
}
return true;
}
return false;
}
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> ExportScansDialog::getScans(
const std::map<int, Transform> & poses,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans) const
{
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> clouds;
int i=0;
pcl::PointCloud<pcl::PointNormal>::Ptr previousCloud;
pcl::IndicesPtr previousIndices;
Transform previousPose;
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
{
int points = 0;
if(!iter->second.isNull())
{
cv::Mat scan;
if(_ui->groupBox_regenerate->isChecked())
{
if(cachedSignatures.contains(iter->first))
{
const Signature & s = cachedSignatures.find(iter->first).value();
SensorData d = s.sensorData();
d.uncompressData(0, 0, &scan);
if(!scan.empty())
{
if(_ui->spinBox_decimation->value() > 1)
{
scan = util3d::downsample(scan, _ui->spinBox_decimation->value());
}
}
scan = util3d::transformLaserScan(scan, s.sensorData().laserScanInfo().localTransform());
}
else
{
UERROR("Scan %d not found in cache!", iter->first);
}
}
else
{
scan = uValue(createdScans, iter->first, cv::Mat());
}
if(!scan.empty())
{
pcl::PointCloud<pcl::PointNormal>::Ptr cloud(new pcl::PointCloud<pcl::PointNormal>);
if(scan.channels() == 6 && _ui->doubleSpinBox_voxelSize_assembled->value() == 0.0)
{
cloud = util3d::laserScanToPointCloudNormal(scan);
}
else
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudXYZ = util3d::laserScanToPointCloud(scan);
if(_ui->doubleSpinBox_voxelSize_assembled->value() > 0.0)
{
cloudXYZ = util3d::voxelize(
cloudXYZ,
_ui->doubleSpinBox_voxelSize_assembled->value());
}
if(!_ui->checkBox_assemble->isChecked() && _ui->spinBox_normalKSearch->value() > 0)
{
pcl::PointCloud<pcl::Normal>::Ptr normals = util3d::computeNormals(cloudXYZ, _ui->spinBox_normalKSearch->value());
pcl::concatenateFields(*cloudXYZ, *normals, *cloud);
}
else
{
pcl::copyPointCloud(*cloudXYZ, *cloud);
}
}
if(cloud->size())
{
if(_ui->groupBox_filtering->isChecked() &&
_ui->doubleSpinBox_filteringRadius->value() > 0.0f &&
_ui->spinBox_filteringMinNeighbors->value() > 0)
{
pcl::IndicesPtr indices = util3d::radiusFiltering(cloud, _ui->doubleSpinBox_filteringRadius->value(), _ui->spinBox_filteringMinNeighbors->value());
pcl::PointCloud<pcl::PointNormal>::Ptr tmp(new pcl::PointCloud<pcl::PointNormal>);
pcl::copyPointCloud(*cloud, *indices, *tmp);
cloud = tmp;
}
clouds.insert(std::make_pair(iter->first, cloud));
points = cloud->size();
}
}
}
else
{
UERROR("transform is null!?");
}
if(points>0)
{
_progressDialog->appendText(tr("Generated cloud %1 with %2 points (%3/%4).")
.arg(iter->first).arg(points).arg(++i).arg(poses.size()));
}
else
{
_progressDialog->appendText(tr("Ignored cloud %1 (%2/%3).").arg(iter->first).arg(++i).arg(poses.size()));
}
_progressDialog->incrementStep();
QApplication::processEvents();
}
return clouds;
}
void ExportScansDialog::saveScans(
const QString & workingDirectory,
const std::map<int, Transform> & poses,
const std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> & clouds,
bool binaryMode)
{
if(clouds.size() == 1)
{
QString path = QFileDialog::getSaveFileName(this, tr("Save scan to ..."), workingDirectory+QDir::separator()+"scan.ply", tr("Point cloud data (*.ply *.pcd)"));
if(!path.isEmpty())
{
if(clouds.begin()->second->size())
{
_progressDialog->appendText(tr("Saving the scan (%1 points)...").arg(clouds.begin()->second->size()));
bool success =false;
if(QFileInfo(path).suffix() == "pcd")
{
success = pcl::io::savePCDFile(path.toStdString(), *clouds.begin()->second, binaryMode) == 0;
}
else if(QFileInfo(path).suffix() == "ply")
{
success = pcl::io::savePLYFile(path.toStdString(), *clouds.begin()->second, binaryMode) == 0;
}
else if(QFileInfo(path).suffix() == "")
{
//use ply by default
path += ".ply";
success = pcl::io::savePLYFile(path.toStdString(), *clouds.begin()->second, binaryMode) == 0;
}
else
{
UERROR("Extension not recognized! (%s) Should be one of (*.ply *.pcd).", QFileInfo(path).suffix().toStdString().c_str());
}
if(success)
{
_progressDialog->incrementStep();
_progressDialog->appendText(tr("Saving the scan (%1 points)... done.").arg(clouds.begin()->second->size()));
QMessageBox::information(this, tr("Save successful!"), tr("Scan saved to \"%1\"").arg(path));
}
else
{
QMessageBox::warning(this, tr("Save failed!"), tr("Failed to save to \"%1\"").arg(path));
}
}
else
{
QMessageBox::warning(this, tr("Save failed!"), tr("Scan is empty..."));
}
}
}
else if(clouds.size())
{
QString path = QFileDialog::getExistingDirectory(this, tr("Save scans to (*.ply *.pcd)..."), workingDirectory, 0);
if(!path.isEmpty())
{
bool ok = false;
QStringList items;
items.push_back("ply");
items.push_back("pcd");
QString suffix = QInputDialog::getItem(this, tr("File format"), tr("Which format?"), items, 0, false, &ok);
if(ok)
{
QString prefix = QInputDialog::getText(this, tr("File prefix"), tr("Prefix:"), QLineEdit::Normal, "scan", &ok);
if(ok)
{
for(std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr >::const_iterator iter=clouds.begin(); iter!=clouds.end(); ++iter)
{
if(iter->second->size())
{
pcl::PointCloud<pcl::PointNormal>::Ptr transformedCloud;
transformedCloud = util3d::transformPointCloud(iter->second, poses.at(iter->first));
QString pathFile = path+QDir::separator()+QString("%1%2.%3").arg(prefix).arg(iter->first).arg(suffix);
bool success =false;
if(suffix == "pcd")
{
success = pcl::io::savePCDFile(pathFile.toStdString(), *transformedCloud, binaryMode) == 0;
}
else if(suffix == "ply")
{
success = pcl::io::savePLYFile(pathFile.toStdString(), *transformedCloud, binaryMode) == 0;
}
else
{
UFATAL("Extension not recognized! (%s)", suffix.toStdString().c_str());
}
if(success)
{
_progressDialog->appendText(tr("Saved scan %1 (%2 points) to %3.").arg(iter->first).arg(iter->second->size()).arg(pathFile));
}
else
{
_progressDialog->appendText(tr("Failed saving scan %1 (%2 points) to %3.").arg(iter->first).arg(iter->second->size()).arg(pathFile));
}
}
else
{
_progressDialog->appendText(tr("Scan %1 is empty!").arg(iter->first));
}
_progressDialog->incrementStep();
QApplication::processEvents();
}
}
}
}
}
}
}

View File

@@ -1,109 +0,0 @@
/*
Copyright (c) 2010-2016, 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 EXPORTSCANSDIALOG_H_
#define EXPORTSCANSDIALOG_H_
#include <QDialog>
#include <QMap>
#include <QtCore/QSettings>
#include <rtabmap/core/Signature.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/PolygonMesh.h>
#include <pcl/TextureMesh.h>
#include <pcl/pcl_base.h>
class Ui_ExportScansDialog;
class QAbstractButton;
namespace rtabmap {
class ProgressDialog;
class ExportScansDialog : public QDialog
{
Q_OBJECT
public:
ExportScansDialog(QWidget *parent = 0);
virtual ~ExportScansDialog();
void saveSettings(QSettings & settings, const QString & group = "") const;
void loadSettings(QSettings & settings, const QString & group = "");
void exportScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans,
const QString & workingDirectory);
void viewScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans,
const QString & workingDirectory);
signals:
void configChanged();
public slots:
void restoreDefaults();
private:
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> getScans(
const std::map<int, Transform> & poses,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans) const;
bool getExportedScans(
const std::map<int, Transform> & poses,
const std::map<int, int> & mapIds,
const QMap<int, Signature> & cachedSignatures,
const std::map<int, cv::Mat> & createdScans,
const QString & workingDirectory,
std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> & clouds);
void saveScans(const QString & workingDirectory,
const std::map<int, Transform> & poses,
const std::map<int, pcl::PointCloud<pcl::PointNormal>::Ptr> & clouds,
bool binaryMode = true);
void setSaveButton();
void setOkButton();
void enableRegeneration(bool enabled);
private:
Ui_ExportScansDialog * _ui;
ProgressDialog * _progressDialog;
};
}
#endif /* EXPORTSCANSDIALOG_H_ */

View File

@@ -62,7 +62,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/UCv2Qt.h"
#include "ExportCloudsDialog.h"
#include "ExportScansDialog.h"
#include "AboutDialog.h"
#include "PostProcessingDialog.h"
#include "DepthCalibrationDialog.h"
@@ -137,7 +136,6 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_preferencesDialog(0),
_aboutDialog(0),
_exportCloudsDialog(0),
_exportScansDialog(0),
_dataRecorder(0),
_lastId(0),
_firstStamp(0.0f),
@@ -185,8 +183,6 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_aboutDialog->setObjectName("AboutDialog");
_exportCloudsDialog = new ExportCloudsDialog(this);
_exportCloudsDialog->setObjectName("ExportCloudsDialog");
_exportScansDialog = new ExportScansDialog(this);
_exportScansDialog->setObjectName("ExportScansDialog");
_postProcessingDialog = new PostProcessingDialog(this);
_postProcessingDialog->setObjectName("PostProcessingDialog");
_depthCalibrationDialog = new DepthCalibrationDialog(this);
@@ -238,7 +234,6 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
_preferencesDialog->loadMainWindowState(this, _savedMaximized, statusBarShown);
_preferencesDialog->loadWindowGeometry(_preferencesDialog);
_preferencesDialog->loadWindowGeometry(_exportCloudsDialog);
_preferencesDialog->loadWindowGeometry(_exportScansDialog);
_preferencesDialog->loadWindowGeometry(_postProcessingDialog);
_preferencesDialog->loadWindowGeometry(_depthCalibrationDialog);
_preferencesDialog->loadWindowGeometry(_aboutDialog);
@@ -372,11 +367,9 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
connect(_ui->action1080p, SIGNAL(triggered()), this, SLOT(setAspectRatio1080p()));
connect(_ui->actionCustom, SIGNAL(triggered()), this, SLOT(setAspectRatioCustom()));
connect(_ui->actionSave_point_cloud, SIGNAL(triggered()), this, SLOT(exportClouds()));
connect(_ui->actionExport_2D_scans_ply_pcd, SIGNAL(triggered()), this, SLOT(exportScans()));
connect(_ui->actionExport_2D_Grid_map_bmp_png, SIGNAL(triggered()), this, SLOT(exportGridMap()));
connect(_ui->actionExport_images_RGB_jpg_Depth_png, SIGNAL(triggered()), this , SLOT(exportImages()));
connect(_ui->actionExport_cameras_in_Bundle_format_out, SIGNAL(triggered()), SLOT(exportBundlerFormat()));
connect(_ui->actionView_scans, SIGNAL(triggered()), this, SLOT(viewScans()));
connect(_ui->actionExport_octomap, SIGNAL(triggered()), this, SLOT(exportOctomap()));
connect(_ui->actionView_high_res_point_cloud, SIGNAL(triggered()), this, SLOT(viewClouds()));
connect(_ui->actionReset_Odometry, SIGNAL(triggered()), this, SLOT(resetOdometry()));
@@ -467,7 +460,6 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
connect(_ui->graphicsView_graphView, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_cloudViewer, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_exportCloudsDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_exportScansDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_postProcessingDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_depthCalibrationDialog, SIGNAL(configChanged()), this, SLOT(configGUIModified()));
connect(_ui->toolBar->toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(configGUIModified()));
@@ -528,7 +520,6 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent) :
//dialog states
_preferencesDialog->loadWidgetState(_exportCloudsDialog);
_preferencesDialog->loadWidgetState(_exportScansDialog);
_preferencesDialog->loadWidgetState(_postProcessingDialog);
_preferencesDialog->loadWidgetState(_depthCalibrationDialog);
@@ -2494,9 +2485,7 @@ void MainWindow::updateMapCloud(
{
_ui->actionSave_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionView_high_res_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionExport_2D_scans_ply_pcd->setEnabled(!_createdScans.empty());
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(!_occupancyGrid->addedNodes().empty());
_ui->actionView_scans->setEnabled(!_createdScans.empty());
#ifdef RTABMAP_OCTOMAP
_ui->actionExport_octomap->setEnabled(_octomap->octree()->size());
#else
@@ -2990,7 +2979,14 @@ void MainWindow::createAndAddScanToMap(int nodeId, const Transform & pose, int m
if(filtered)
{
//reconvert the voxelized cloud
scan = util3d::laserScanFromPointCloud(*cloud);
if(scan.channels() == 2)
{
scan = util3d::laserScan2dFromPointCloud(*cloud);
}
else
{
scan = util3d::laserScanFromPointCloud(*cloud);
}
}
else
{
@@ -4106,7 +4102,6 @@ void MainWindow::saveConfigGUI()
_preferencesDialog->saveWidgetState(_ui->imageView_loopClosure);
_preferencesDialog->saveWidgetState(_ui->imageView_odometry);
_preferencesDialog->saveWidgetState(_exportCloudsDialog);
_preferencesDialog->saveWidgetState(_exportScansDialog);
_preferencesDialog->saveWidgetState(_postProcessingDialog);
_preferencesDialog->saveWidgetState(_depthCalibrationDialog);
_preferencesDialog->saveWidgetState(_ui->graphicsView_graphView);
@@ -5800,13 +5795,11 @@ void MainWindow::clearTheCache()
_ui->statsToolBox->clear();
//disable save cloud action
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
_ui->actionPost_processing->setEnabled(false);
_ui->actionSave_point_cloud->setEnabled(false);
_ui->actionExport_cameras_in_Bundle_format_out->setEnabled(false);
_ui->actionDepth_Calibration->setEnabled(false);
_ui->actionExport_images_RGB_jpg_Depth_png->setEnabled(false);
_ui->actionView_scans->setEnabled(false);
_ui->actionExport_octomap->setEnabled(false);
_ui->actionView_high_res_point_cloud->setEnabled(false);
_likelihoodCurve->clear();
@@ -6162,37 +6155,6 @@ void MainWindow::exportGridMap()
}
}
void MainWindow::exportScans()
{
if(_exportScansDialog->isVisible())
{
return;
}
_exportScansDialog->exportScans(
_currentPosesMap,
_currentMapIds,
_cachedSignatures,
_createdScans,
_preferencesDialog->getWorkingDirectory());
}
void MainWindow::viewScans()
{
if(_exportScansDialog->isVisible())
{
return;
}
_exportScansDialog->viewScans(
_ui->widget_mapVisibility->getVisiblePoses(),
_currentMapIds,
_cachedSignatures,
_createdScans,
_preferencesDialog->getWorkingDirectory());
}
void MainWindow::exportClouds()
{
if(_exportCloudsDialog->isVisible())
@@ -6225,6 +6187,7 @@ void MainWindow::exportClouds()
_currentMapIds,
_cachedSignatures,
_cachedClouds,
_createdScans,
_preferencesDialog->getWorkingDirectory(),
_preferencesDialog->getAllParameters());
}
@@ -6261,6 +6224,7 @@ void MainWindow::viewClouds()
_currentMapIds,
_cachedSignatures,
_cachedClouds,
_createdScans,
_preferencesDialog->getWorkingDirectory(),
_preferencesDialog->getAllParameters());
@@ -6780,9 +6744,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(!_currentPosesMap.empty());
_ui->actionSave_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionView_high_res_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionExport_2D_scans_ply_pcd->setEnabled(!_createdScans.empty());
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(!_occupancyGrid->addedNodes().empty());
_ui->actionView_scans->setEnabled(!_createdScans.empty());
#ifdef RTABMAP_OCTOMAP
_ui->actionExport_octomap->setEnabled(_octomap->octree()->size());
#else
@@ -6842,9 +6804,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(!_currentPosesMap.empty());
_ui->actionSave_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionView_high_res_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionExport_2D_scans_ply_pcd->setEnabled(!_createdScans.empty());
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(!_occupancyGrid->addedNodes().empty());
_ui->actionView_scans->setEnabled(!_createdScans.empty());
#ifdef RTABMAP_OCTOMAP
_ui->actionExport_octomap->setEnabled(_octomap->octree()->size());
#else
@@ -6893,9 +6853,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(false);
_ui->actionSave_point_cloud->setEnabled(false);
_ui->actionView_high_res_point_cloud->setEnabled(false);
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
_ui->actionView_scans->setEnabled(false);
_ui->actionExport_octomap->setEnabled(false);
_ui->actionExport_cameras_in_Bundle_format_out->setEnabled(false);
_ui->actionDepth_Calibration->setEnabled(false);
@@ -6937,9 +6895,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(false);
_ui->actionSave_point_cloud->setEnabled(false);
_ui->actionView_high_res_point_cloud->setEnabled(false);
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
_ui->actionView_scans->setEnabled(false);
_ui->actionExport_octomap->setEnabled(false);
_ui->actionExport_cameras_in_Bundle_format_out->setEnabled(false);
_ui->actionDepth_Calibration->setEnabled(false);
@@ -6968,9 +6924,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(!_currentPosesMap.empty());
_ui->actionSave_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionView_high_res_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionExport_2D_scans_ply_pcd->setEnabled(!_createdScans.empty());
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(!_occupancyGrid->addedNodes().empty());
_ui->actionView_scans->setEnabled(!_createdScans.empty());
#ifdef RTABMAP_OCTOMAP
_ui->actionExport_octomap->setEnabled(_octomap->octree()->size());
#else
@@ -7003,9 +6957,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(false);
_ui->actionSave_point_cloud->setEnabled(false);
_ui->actionView_high_res_point_cloud->setEnabled(false);
_ui->actionExport_2D_scans_ply_pcd->setEnabled(false);
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(false);
_ui->actionView_scans->setEnabled(false);
_ui->actionExport_octomap->setEnabled(false);
_ui->actionExport_cameras_in_Bundle_format_out->setEnabled(false);
_ui->actionDepth_Calibration->setEnabled(false);
@@ -7035,9 +6987,7 @@ void MainWindow::changeState(MainWindow::State newState)
_ui->menuExport_poses->setEnabled(!_currentPosesMap.empty());
_ui->actionSave_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionView_high_res_point_cloud->setEnabled(!_cachedSignatures.empty() || !_cachedClouds.empty());
_ui->actionExport_2D_scans_ply_pcd->setEnabled(!_createdScans.empty());
_ui->actionExport_2D_Grid_map_bmp_png->setEnabled(!_occupancyGrid->addedNodes().empty());
_ui->actionView_scans->setEnabled(!_createdScans.empty());
#ifdef RTABMAP_OCTOMAP
_ui->actionExport_octomap->setEnabled(_octomap->octree()->size());
#else

View File

@@ -69,7 +69,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/gui/ImageView.h"
#include "rtabmap/gui/GraphViewer.h"
#include "ExportCloudsDialog.h"
#include "ExportScansDialog.h"
#include "PostProcessingDialog.h"
#include "CreateSimpleCalibrationDialog.h"
#include "DepthCalibrationDialog.h"
@@ -2697,7 +2696,6 @@ void PreferencesDialog::saveWidgetState(const QWidget * widget)
const CloudViewer * cloudViewer = qobject_cast<const CloudViewer*>(widget);
const ImageView * imageView = qobject_cast<const ImageView*>(widget);
const ExportCloudsDialog * exportCloudsDialog = qobject_cast<const ExportCloudsDialog*>(widget);
const ExportScansDialog * exportScansDialog = qobject_cast<const ExportScansDialog*>(widget);
const PostProcessingDialog * postProcessingDialog = qobject_cast<const PostProcessingDialog *>(widget);
const GraphViewer * graphViewer = qobject_cast<const GraphViewer *>(widget);
const CalibrationDialog * calibrationDialog = qobject_cast<const CalibrationDialog *>(widget);
@@ -2718,11 +2716,6 @@ void PreferencesDialog::saveWidgetState(const QWidget * widget)
exportCloudsDialog->saveSettings(settings);
exportCloudsDialog->saveSettings(settingsTmp);
}
else if(exportScansDialog)
{
exportScansDialog->saveSettings(settings);
exportScansDialog->saveSettings(settingsTmp);
}
else if(postProcessingDialog)
{
postProcessingDialog->saveSettings(settings);
@@ -2771,7 +2764,6 @@ void PreferencesDialog::loadWidgetState(QWidget * widget)
CloudViewer * cloudViewer = qobject_cast<CloudViewer*>(widget);
ImageView * imageView = qobject_cast<ImageView*>(widget);
ExportCloudsDialog * exportCloudsDialog = qobject_cast<ExportCloudsDialog*>(widget);
ExportScansDialog * exportScansDialog = qobject_cast<ExportScansDialog*>(widget);
PostProcessingDialog * postProcessingDialog = qobject_cast<PostProcessingDialog *>(widget);
GraphViewer * graphViewer = qobject_cast<GraphViewer *>(widget);
CalibrationDialog * calibrationDialog = qobject_cast<CalibrationDialog *>(widget);
@@ -2792,11 +2784,6 @@ void PreferencesDialog::loadWidgetState(QWidget * widget)
exportCloudsDialog->loadSettings(settings);
exportCloudsDialog->saveSettings(settingsTmp);
}
else if(exportScansDialog)
{
exportScansDialog->loadSettings(settings);
exportScansDialog->saveSettings(settingsTmp);
}
else if(postProcessingDialog)
{
postProcessingDialog->loadSettings(settings);

View File

@@ -506,7 +506,6 @@
<addaction name="actionSave_config"/>
<addaction name="separator"/>
<addaction name="actionGenerate_3D_map_pcd"/>
<addaction name="actionExport_3D_laser_scans_ply_pcd"/>
<addaction name="actionExport"/>
<addaction name="actionExtract_images"/>
<addaction name="menuExport_poses"/>
@@ -533,7 +532,6 @@
<addaction name="actionRestore_default_GUI_settings"/>
<addaction name="separator"/>
<addaction name="actionView_3D_map"/>
<addaction name="actionView_3D_laser_scans"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@@ -2119,31 +2117,11 @@
<string>Generate graph (*.dot) ...</string>
</property>
</action>
<action name="actionGenerate_graph_only_weighted_locations">
<property name="text">
<string>Generate graph (only weighted locations) ...</string>
</property>
</action>
<action name="actionClean_database">
<property name="text">
<string>Clean database</string>
</property>
</action>
<action name="actionGenerate_local_graph_dot">
<property name="text">
<string>Generate local graph (.dot) ...</string>
</property>
</action>
<action name="actionClean_local_graph">
<property name="text">
<string>Clean local graph ...</string>
</property>
</action>
<action name="actionUpdate_base_ids">
<property name="text">
<string>Update base ids</string>
</property>
</action>
<action name="actionGenerate_3D_map_pcd">
<property name="text">
<string>Export 3D map (*.ply *.pcd) ...</string>
@@ -2195,16 +2173,6 @@
<string>Reset link and grid map changes</string>
</property>
</action>
<action name="actionView_3D_laser_scans">
<property name="text">
<string>View 2D-3D laser scans...</string>
</property>
</action>
<action name="actionExport_3D_laser_scans_ply_pcd">
<property name="text">
<string>Export 2D-3D laser scans (*.ply *.pcd) ...</string>
</property>
</action>
<action name="actionRegenerate_local_grid_maps">
<property name="text">
<string>Regenerate local grid maps...</string>

View File

@@ -25,23 +25,58 @@
<x>0</x>
<y>0</y>
<width>773</width>
<height>3795</height>
<height>3895</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<layout class="QGridLayout" name="gridLayout_8" columnstretch="0,1">
<item row="10" column="1">
<widget class="QLabel" name="label_binaryFile_12">
<item row="1" column="0">
<widget class="QComboBox" name="comboBox_pipeline">
<item>
<property name="text">
<string>Organized Point Cloud</string>
</property>
</item>
<item>
<property name="text">
<string>Dense Point Cloud</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Meshing.</string>
<string>Reconstruction flavor.</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="checkBox_regenerate">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="checkBox_filtering">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_regenerate">
<property name="text">
<string>Regenerate clouds. This can be used to regenerate the point clouds at higher density than those used for online visualization.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="7" column="1">
<widget class="QLabel" name="label_voxel">
<property name="text">
<string>Voxel size. Set 0 to disable. When organized meshes are assembled, this is the radius in which the vertices of the polygons are merged.</string>
@@ -51,7 +86,44 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="11" column="0">
<widget class="QCheckBox" name="checkBox_gainCompensation">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_binaryFile_2">
<property name="text">
<string>Assemble clouds/meshes to a single output cloud/mesh.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="label_gainCompensation">
<property name="text">
<string>Gain compensation. Normalize brightness of images.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_frame">
<property name="text">
<string>Output frame.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QSpinBox" name="spinBox_normalKSearch">
<property name="minimum">
<number>3</number>
@@ -61,14 +133,17 @@
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="checkBox_meshing">
<item row="12" column="1">
<widget class="QLabel" name="label_binaryFile_12">
<property name="text">
<string/>
<string>Meshing.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="9" column="1">
<widget class="QLabel" name="label_binaryFile_9">
<property name="text">
<string>Cloud filtering. Remove sparse points that are far from surfaces.</string>
@@ -78,28 +153,31 @@
</property>
</widget>
</item>
<item row="8" column="0">
<item row="10" column="0">
<widget class="QCheckBox" name="checkBox_smoothing">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_filtering">
<item row="3" column="1">
<widget class="QLabel" name="label_binaryFile">
<property name="text">
<string>Binary file (for ply and pcd outputs).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QCheckBox" name="checkBox_meshing">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBox_regenerate">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="10" column="1">
<widget class="QLabel" name="label_binaryFile_10">
<property name="text">
<string>Cloud smoothing using Moving Least Squares algorithm (MLS).</string>
@@ -109,7 +187,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="7" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize_assembled">
<property name="suffix">
<string> m</string>
@@ -128,55 +206,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Reconstruction flavor.</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="comboBox_pipeline">
<item>
<property name="text">
<string>Organized Point Cloud</string>
</property>
</item>
<item>
<property name="text">
<string>Dense Point Cloud</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_binaryFile">
<property name="text">
<string>Binary file.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_binaryFile_8">
<property name="text">
<string>Regenerate clouds. This can be used to regenerate the point clouds at higher density than those used for online visualization.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="checkBox_gainCompensation">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_normal">
<property name="text">
<string>Set the number of k nearest neighbors to use for the normal estimation.</string>
@@ -186,7 +216,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="3" column="0">
<widget class="QCheckBox" name="checkBox_binary">
<property name="text">
<string/>
@@ -196,44 +226,14 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_binaryFile_2">
<property name="text">
<string>Assemble clouds/meshes to a single output cloud/mesh.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="4" column="0">
<widget class="QCheckBox" name="checkBox_assemble">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_gainCompensation">
<property name="text">
<string>Gain compensation. Normalize brightness of images.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_frame">
<property name="text">
<string>Output frame.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QComboBox" name="comboBox_frame">
<item>
<property name="text">
@@ -250,10 +250,67 @@
<string>Camera</string>
</property>
</item>
<item>
<property name="text">
<string>Scan</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_binaryFile_11">
<property name="text">
<string>From RGB-D images. If not checked, clouds will be generated from laser scans.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_fromDepth">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_regenerateScans">
<property name="title">
<string>Regenerate clouds</string>
</property>
<layout class="QGridLayout" name="gridLayout_18" columnstretch="0,1">
<item row="0" column="0">
<widget class="QSpinBox" name="spinBox_decimation_scan">
<property name="minimum">
<number>-32</number>
</property>
<property name="maximum">
<number>32</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_109">
<property name="text">
<string>Downsampling step.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_regenerate">
<property name="title">

View File

@@ -1,305 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportScansDialog</class>
<widget class="QDialog" name="ExportScansDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>545</width>
<height>439</height>
</rect>
</property>
<property name="windowTitle">
<string>Export Scans</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0,0">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>363</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<layout class="QGridLayout" name="gridLayout_8" columnstretch="0,1">
<item row="1" column="1">
<widget class="QLabel" name="label_binaryFile_2">
<property name="text">
<string>Assemble scans to a single output cloud.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_binary">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_assemble">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_109">
<property name="text">
<string>Voxel size. Set 0 to disable.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize_assembled">
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.005000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_111">
<property name="text">
<string>Set the number of k nearest neighbors to use for the normal estimation. Set 0 to disable normal estimation.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="spinBox_normalKSearch">
<property name="minimum">
<number>0</number>
</property>
<property name="value">
<number>20</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_binaryFile">
<property name="text">
<string>Binary file.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_regenerate">
<property name="title">
<string>Regenerate Scans</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item row="0" column="1">
<widget class="QLabel" name="label_108">
<property name="text">
<string>Downsampling step.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QSpinBox" name="spinBox_decimation">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_filtering">
<property name="title">
<string>Cloud Filtering (remove noisy points)</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_9" columnstretch="0,1">
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_filteringRadius">
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_110">
<property name="text">
<string>Radius search.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="spinBox_filteringMinNeighbors">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_112">
<property name="text">
<string>Minimum neighbors in the search radius.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</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|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ExportScansDialog</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>ExportScansDialog</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>

View File

@@ -27,7 +27,7 @@
<x>0</x>
<y>0</y>
<width>1012</width>
<height>25</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -50,7 +50,6 @@
<addaction name="actionSave_GUI_config"/>
<addaction name="separator"/>
<addaction name="actionSave_point_cloud"/>
<addaction name="actionExport_2D_scans_ply_pcd"/>
<addaction name="actionExport_2D_Grid_map_bmp_png"/>
<addaction name="actionExport_octomap"/>
<addaction name="menuExport_poses"/>
@@ -83,7 +82,6 @@
<addaction name="actionClear_cache"/>
<addaction name="separator"/>
<addaction name="actionView_high_res_point_cloud"/>
<addaction name="actionView_scans"/>
<addaction name="separator"/>
<addaction name="actionDelete_memory"/>
<addaction name="separator"/>
@@ -1074,7 +1072,7 @@
</action>
<action name="actionView_high_res_point_cloud">
<property name="text">
<string>View high-res point clouds...</string>
<string>View point clouds...</string>
</property>
</action>
<action name="actionTrigger_a_new_map">
@@ -1150,16 +1148,6 @@
<string>Export 2D grid map (*.png *.bmp)...</string>
</property>
</action>
<action name="actionExport_2D_scans_ply_pcd">
<property name="text">
<string>Export 2D-3D scans (*.ply *.pcd)...</string>
</property>
</action>
<action name="actionView_scans">
<property name="text">
<string>View scans...</string>
</property>
</action>
<action name="actionOpen_database">
<property name="icon">
<iconset resource="../GuiLib.qrc">