Export: supporting texturing with multi-camera

This commit is contained in:
matlabbe
2017-04-26 18:39:57 -04:00
parent 6a2fe47b22
commit f023adff4b
8 changed files with 458 additions and 131 deletions

View File

@@ -74,6 +74,7 @@ protected:
virtual void showEvent(QShowEvent* anEvent);
virtual void moveEvent(QMoveEvent* anEvent);
virtual void resizeEvent(QResizeEvent* anEvent);
virtual void keyPressEvent(QKeyEvent *event);
virtual void closeEvent(QCloseEvent* event);
virtual bool eventFilter(QObject *obj, QEvent *event);
@@ -134,7 +135,6 @@ private:
QLabel * label,
QLabel * stamp,
rtabmap::ImageView * view,
rtabmap::CloudViewer * view3D,
QLabel * labelId,
QLabel * labelMapId,
QLabel * labelPose,
@@ -164,8 +164,7 @@ private:
private:
Ui_DatabaseViewer * ui_;
CloudViewer * constraintsViewer_;
CloudViewer * cloudViewerA_;
CloudViewer * cloudViewerB_;
CloudViewer * cloudViewer_;
CloudViewer * stereoViewer_;
CloudViewer * occupancyGridViewer_;
QList<int> ids_;

View File

@@ -118,6 +118,7 @@ protected:
virtual void showEvent(QShowEvent* anEvent);
virtual void moveEvent(QMoveEvent* anEvent);
virtual void resizeEvent(QResizeEvent* anEvent);
virtual void keyPressEvent(QKeyEvent *event);
virtual bool eventFilter(QObject *obj, QEvent *event);
private slots:

View File

@@ -144,18 +144,15 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
// Create cloud viewers
constraintsViewer_ = new CloudViewer(ui_->dockWidgetContents);
cloudViewerA_ = new CloudViewer(ui_->dockWidgetContents_3dviews);
cloudViewerB_ = new CloudViewer(ui_->dockWidgetContents_3dviews);
cloudViewer_ = new CloudViewer(ui_->dockWidgetContents_3dviews);
stereoViewer_ = new CloudViewer(ui_->dockWidgetContents_stereo);
occupancyGridViewer_ = new CloudViewer(ui_->dockWidgetContents_occupancyGrid);
constraintsViewer_->setObjectName("constraintsViewer");
cloudViewerA_->setObjectName("cloudViewerA");
cloudViewerB_->setObjectName("cloudViewerB");
cloudViewer_->setObjectName("cloudViewerA");
stereoViewer_->setObjectName("stereoViewer");
occupancyGridViewer_->setObjectName("occupancyGridView");
ui_->layout_constraintsViewer->addWidget(constraintsViewer_);
ui_->horizontalLayout_3dviews->addWidget(cloudViewerA_, 1);
ui_->horizontalLayout_3dviews->addWidget(cloudViewerB_, 1);
ui_->horizontalLayout_3dviews->addWidget(cloudViewer_, 1);
ui_->horizontalLayout_stereo->addWidget(stereoViewer_, 1);
ui_->layout_occupancyGridView->addWidget(occupancyGridViewer_, 1);
@@ -268,6 +265,7 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
connect(ui_->spinBox_mesh_depthError, SIGNAL(valueChanged(int)), this, SLOT(update3dView()));
connect(ui_->checkBox_mesh_quad, SIGNAL(toggled(bool)), this, SLOT(update3dView()));
connect(ui_->spinBox_mesh_triangleSize, SIGNAL(valueChanged(int)), this, SLOT(update3dView()));
connect(ui_->checkBox_showWords, SIGNAL(toggled(bool)), this, SLOT(update3dView()));
connect(ui_->checkBox_showCloud, SIGNAL(toggled(bool)), this, SLOT(update3dView()));
connect(ui_->checkBox_showMesh, SIGNAL(toggled(bool)), this, SLOT(update3dView()));
connect(ui_->checkBox_showScan, SIGNAL(toggled(bool)), this, SLOT(update3dView()));
@@ -311,6 +309,8 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
connect(ui_->spinBox_grid_depth, SIGNAL(valueChanged(int)), this, SLOT(updateOctomapView()));
connect(ui_->checkBox_grid_empty, SIGNAL(stateChanged(int)), this, SLOT(updateOctomapView()));
connect(ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(double)), this, SLOT(updateConstraintView()));
connect(ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SLOT(updateConstraintView()));
connect(ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SLOT(update3dView()));
connect(ui_->groupBox_posefiltering, SIGNAL(clicked(bool)), this, SLOT(updateGraphView()));
connect(ui_->doubleSpinBox_posefilteringRadius, SIGNAL(editingFinished()), this, SLOT(updateGraphView()));
connect(ui_->doubleSpinBox_posefilteringAngle, SIGNAL(editingFinished()), this, SLOT(updateGraphView()));
@@ -342,6 +342,7 @@ DatabaseViewer::DatabaseViewer(const QString & ini, QWidget * parent) :
connect(ui_->checkBox_gridErode, SIGNAL(stateChanged(int)), this, SLOT(configModified()));
connect(ui_->checkBox_octomap, SIGNAL(stateChanged(int)), this, SLOT(configModified()));
connect(ui_->doubleSpinBox_gainCompensationRadius, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
connect(ui_->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
connect(ui_->doubleSpinBox_gridCellSize, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
connect(ui_->groupBox_posefiltering, SIGNAL(clicked(bool)), this, SLOT(configModified()));
connect(ui_->doubleSpinBox_posefilteringRadius, SIGNAL(valueChanged(double)), this, SLOT(configModified()));
@@ -396,12 +397,10 @@ void DatabaseViewer::setupMainLayout(bool vertical)
if(vertical)
{
qobject_cast<QHBoxLayout *>(ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::TopToBottom);
qobject_cast<QHBoxLayout *>(ui_->horizontalLayout_3dviews->layout())->setDirection(QBoxLayout::TopToBottom);
}
else if(!vertical)
{
qobject_cast<QHBoxLayout *>(ui_->horizontalLayout_imageViews->layout())->setDirection(QBoxLayout::LeftToRight);
qobject_cast<QHBoxLayout *>(ui_->horizontalLayout_3dviews->layout())->setDirection(QBoxLayout::LeftToRight);
}
if(ids_.size())
{
@@ -469,6 +468,8 @@ void DatabaseViewer::readSettings()
ui_->checkBox_ignoreUserLoop->setChecked(settings.value("ignoreUserLoop", ui_->checkBox_ignoreUserLoop->isChecked()).toBool());
ui_->spinBox_optimizationDepth->setValue(settings.value("depth", ui_->spinBox_optimizationDepth->value()).toInt());
ui_->doubleSpinBox_gainCompensationRadius->setValue(settings.value("gainCompensationRadius", ui_->doubleSpinBox_gainCompensationRadius->value()).toDouble());
ui_->doubleSpinBox_voxelSize->setValue(settings.value("voxelSize", ui_->doubleSpinBox_voxelSize->value()).toDouble());
settings.endGroup();
settings.beginGroup("grid");
@@ -561,6 +562,7 @@ void DatabaseViewer::writeSettings()
//settings.setValue("slam2d", ui_->checkBox_2dslam->isChecked());
settings.setValue("depth", ui_->spinBox_optimizationDepth->value());
settings.setValue("gainCompensationRadius", ui_->doubleSpinBox_gainCompensationRadius->value());
settings.setValue("voxelSize", ui_->doubleSpinBox_voxelSize->value());
settings.endGroup();
// save Grid settings
@@ -637,6 +639,7 @@ void DatabaseViewer::restoreDefaultSettings()
ui_->checkBox_ignoreUserLoop->setChecked(false);
ui_->spinBox_optimizationDepth->setValue(0);
ui_->doubleSpinBox_gainCompensationRadius->setValue(0.0);
ui_->doubleSpinBox_voxelSize->setValue(0.0);
ui_->doubleSpinBox_gridCellSize->setValue(0.05);
ui_->groupBox_posefiltering->setChecked(false);
@@ -955,6 +958,15 @@ void DatabaseViewer::resizeEvent(QResizeEvent* anEvent)
}
}
void DatabaseViewer::keyPressEvent(QKeyEvent *event)
{
//catch ctrl-s to save settings
if((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S)
{
this->writeSettings();
}
}
bool DatabaseViewer::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && qobject_cast<QDockWidget*>(obj))
@@ -2370,7 +2382,6 @@ void DatabaseViewer::sliderAValueChanged(int value)
ui_->label_labelA,
ui_->label_stampA,
ui_->graphicsView_A,
cloudViewerA_,
ui_->label_idA,
ui_->label_mapA,
ui_->label_poseA,
@@ -2388,7 +2399,6 @@ void DatabaseViewer::sliderBValueChanged(int value)
ui_->label_labelB,
ui_->label_stampB,
ui_->graphicsView_B,
cloudViewerB_,
ui_->label_idB,
ui_->label_mapB,
ui_->label_poseB,
@@ -2404,7 +2414,6 @@ void DatabaseViewer::update(int value,
QLabel * label,
QLabel * stamp,
rtabmap::ImageView * view,
rtabmap::CloudViewer * view3D,
QLabel * labelId,
QLabel * labelMapId,
QLabel * labelPose,
@@ -2543,7 +2552,7 @@ void DatabaseViewer::update(int value,
}
// 3d view
if(view3D->isVisible())
if(cloudViewer_->isVisible())
{
Transform pose = Transform::getIdentity();
if(signatures.size() && ui_->checkBox_odomFrame_3dview->isChecked())
@@ -2553,14 +2562,15 @@ void DatabaseViewer::update(int value,
pose = Transform(0,0,z,roll,pitch,0);
}
view3D->removeAllFrustums();
view3D->removeCloud("mesh");
view3D->removeCloud("cloud");
view3D->removeCloud("scan");
view3D->removeCloud("map");
view3D->removeCloud("ground");
view3D->removeCloud("obstacles");
view3D->removeOctomap();
cloudViewer_->removeAllFrustums();
cloudViewer_->removeCloud("mesh");
cloudViewer_->removeCloud("cloud");
cloudViewer_->removeCloud("scan");
cloudViewer_->removeCloud("map");
cloudViewer_->removeCloud("ground");
cloudViewer_->removeCloud("obstacles");
cloudViewer_->removeCloud("words");
cloudViewer_->removeOctomap();
if(ui_->checkBox_showCloud->isChecked() || ui_->checkBox_showMesh->isChecked())
{
if(!data.depthOrRightRaw().empty())
@@ -2591,6 +2601,11 @@ void DatabaseViewer::update(int value,
}
if(cloud->size())
{
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
cloud = util3d::voxelize(cloud, ui_->doubleSpinBox_voxelSize->value());
}
if(ui_->checkBox_showMesh->isChecked() && !cloud->is_dense)
{
Eigen::Vector3f viewpoint(0.0f,0.0f,0.0f);
@@ -2638,14 +2653,13 @@ void DatabaseViewer::update(int value,
polygons = filteredPolygons;
}
view3D->addCloudMesh("mesh", cloud, polygons, pose);
cloudViewer_->addCloudMesh("mesh", cloud, polygons, pose);
}
if(ui_->checkBox_showCloud->isChecked())
{
view3D->addCloud("cloud", cloud, pose);
cloudViewer_->addCloud("cloud", cloud, pose);
}
}
view3D->updateCameraFrustums(pose, data.cameraModels());
}
else if(ui_->checkBox_showCloud->isChecked())
{
@@ -2653,25 +2667,68 @@ void DatabaseViewer::update(int value,
cloud = util3d::cloudFromSensorData(data, 1, 0, 0, 0, ui_->parameters_toolbox->getParameters());
if(cloud->size())
{
view3D->addCloud("cloud", cloud, pose);
view3D->updateCameraFrustum(pose, data.stereoCameraModel());
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
cloud = util3d::voxelize(cloud, ui_->doubleSpinBox_voxelSize->value());
}
cloudViewer_->addCloud("cloud", cloud, pose);
cloudViewer_->updateCameraFrustum(pose, data.stereoCameraModel());
}
}
}
}
//frustums
if(cloudViewer_->isFrustumShown())
{
cloudViewer_->updateCameraFrustums(pose, data.cameraModels());
}
//words
if(ui_->checkBox_showWords->isChecked() && signatures.size())
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->resize((*signatures.begin())->getWords3().size());
int i=0;
for(std::multimap<int, cv::Point3f>::const_iterator iter=(*signatures.begin())->getWords3().begin();
iter!=(*signatures.begin())->getWords3().end();
++iter)
{
cloud->at(i++) = pcl::PointXYZ(iter->second.x, iter->second.y, iter->second.z);
}
if(cloud->size())
{
cloud = rtabmap::util3d::removeNaNFromPointCloud(cloud);
}
if(cloud->size())
{
cloudViewer_->addCloud("words", cloud, pose, Qt::red);
}
}
//add scan
if(ui_->checkBox_showScan->isChecked() && data.laserScanRaw().cols)
{
if(data.laserScanRaw().channels() == 6)
{
pcl::PointCloud<pcl::PointNormal>::Ptr scan = util3d::laserScanToPointCloudNormal(data.laserScanRaw(), data.laserScanInfo().localTransform());
view3D->addCloud("scan", scan, pose, Qt::yellow);
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
cloudViewer_->addCloud("scan", scan, pose, Qt::yellow);
}
else
{
pcl::PointCloud<pcl::PointXYZ>::Ptr scan = util3d::laserScanToPointCloud(data.laserScanRaw(), data.laserScanInfo().localTransform());
view3D->addCloud("scan", scan, pose, Qt::yellow);
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
cloudViewer_->addCloud("scan", scan, pose, Qt::yellow);
}
}
@@ -2735,7 +2792,7 @@ void DatabaseViewer::update(int value,
if(!map8S.empty())
{
//convert to gray scaled map
view3D->addOccupancyGridMap(util3d::convertMap2Image8U(map8S), gridCellSize, xMin, yMin, 1);
cloudViewer_->addOccupancyGridMap(util3d::convertMap2Image8U(map8S), gridCellSize, xMin, yMin, 1);
}
}
@@ -2751,36 +2808,36 @@ void DatabaseViewer::update(int value,
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud = octomap->createCloud(0, obstacles.get(), empty.get());
pcl::PointCloud<pcl::PointXYZRGB>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::copyPointCloud(*cloud, *obstacles, *obstaclesCloud);
view3D->addCloud("obstacles", obstaclesCloud);
view3D->setCloudPointSize("obstacles", 5);
cloudViewer_->addCloud("obstacles", obstaclesCloud);
cloudViewer_->setCloudPointSize("obstacles", 5);
if(ui_->checkBox_grid_empty->isChecked())
{
pcl::PointCloud<pcl::PointXYZ>::Ptr emptyCloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::copyPointCloud(*cloud, *empty, *emptyCloud);
view3D->addCloud("ground", emptyCloud, Transform::getIdentity(), Qt::white);
view3D->setCloudOpacity("ground", 0.5);
view3D->setCloudPointSize("ground", 5);
cloudViewer_->addCloud("ground", emptyCloud, Transform::getIdentity(), Qt::white);
cloudViewer_->setCloudOpacity("ground", 0.5);
cloudViewer_->setCloudPointSize("ground", 5);
}
}
else
{
view3D->addOctomap(octomap);
cloudViewer_->addOctomap(octomap);
}
}
else
#endif
{
// occupancy cloud
view3D->addCloud("ground",
cloudViewer_->addCloud("ground",
util3d::laserScanToPointCloud(localMaps.begin()->second.first),
Transform::getIdentity(),
Qt::green);
view3D->addCloud("obstacles",
cloudViewer_->addCloud("obstacles",
util3d::laserScanToPointCloud(localMaps.begin()->second.second),
Transform::getIdentity(),
Qt::red);
view3D->setCloudPointSize("ground", 5);
view3D->setCloudPointSize("obstacles", 5);
cloudViewer_->setCloudPointSize("ground", 5);
cloudViewer_->setCloudPointSize("obstacles", 5);
}
}
#ifdef RTABMAP_OCTOMAP
@@ -2791,7 +2848,7 @@ void DatabaseViewer::update(int value,
#endif
}
}
view3D->update();
cloudViewer_->update();
}
if(signatures.size())
@@ -3381,7 +3438,6 @@ void DatabaseViewer::updateConstraintView(
ui_->label_labelA,
ui_->label_stampA,
ui_->graphicsView_A,
cloudViewerA_,
ui_->label_idA,
ui_->label_mapA,
ui_->label_poseA,
@@ -3395,7 +3451,6 @@ void DatabaseViewer::updateConstraintView(
ui_->label_labelB,
ui_->label_stampB,
ui_->graphicsView_B,
cloudViewerB_,
ui_->label_idB,
ui_->label_mapB,
ui_->label_poseB,
@@ -3472,10 +3527,18 @@ void DatabaseViewer::updateConstraintView(
if(cloudFrom.get() && cloudFrom->size())
{
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
cloudFrom = util3d::voxelize(cloudFrom, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("cloud0", cloudFrom, pose, Qt::red);
}
if(cloudTo.get() && cloudTo->size())
{
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
cloudTo = util3d::voxelize(cloudTo, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("cloud1", cloudTo, pose, Qt::cyan);
}
}
@@ -3699,6 +3762,10 @@ void DatabaseViewer::updateConstraintView(
if(assembledScans->size())
{
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
assembledScans = util3d::voxelize(assembledScans, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("scan2", assembledScans, pose, Qt::cyan);
}
if(graph->size())
@@ -3716,24 +3783,40 @@ void DatabaseViewer::updateConstraintView(
{
pcl::PointCloud<pcl::PointNormal>::Ptr scan;
scan = rtabmap::util3d::laserScanToPointCloudNormal(dataFrom.laserScanRaw(), dataFrom.laserScanInfo().localTransform());
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("scan0", scan, pose, Qt::yellow);
}
else
{
pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
scan = rtabmap::util3d::laserScanToPointCloud(dataFrom.laserScanRaw(), dataFrom.laserScanInfo().localTransform());
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("scan0", scan, pose, Qt::yellow);
}
if(dataTo.laserScanRaw().channels() == 6)
{
pcl::PointCloud<pcl::PointNormal>::Ptr scan;
scan = rtabmap::util3d::laserScanToPointCloudNormal(dataTo.laserScanRaw(), t*dataTo.laserScanInfo().localTransform());
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("scan1", scan, pose, Qt::magenta);
}
else
{
pcl::PointCloud<pcl::PointXYZ>::Ptr scan;
scan = rtabmap::util3d::laserScanToPointCloud(dataTo.laserScanRaw(), t*dataTo.laserScanInfo().localTransform());
if(ui_->doubleSpinBox_voxelSize->value() > 0.0)
{
scan = util3d::voxelize(scan, ui_->doubleSpinBox_voxelSize->value());
}
constraintsViewer_->addCloud("scan1", scan, pose, Qt::magenta);
}
}

View File

@@ -777,7 +777,7 @@ void ExportCloudsDialog::viewClouds(
for(unsigned int j=0; j<vertices.vertices.size(); ++j)
{
UASSERT(oi < cloud->size());
UASSERT(vertices.vertices[j] < originalCloud->size());
UASSERT_MSG(vertices.vertices[j] < originalCloud->size(), uFormat("%d vs %d", vertices.vertices[j], (int)originalCloud->size()).c_str());
cloud->at(oi) = originalCloud->at(vertices.vertices[j]);
vertices.vertices[j] = oi; // new vertice index
++oi;
@@ -1090,7 +1090,10 @@ bool ExportCloudsDialog::getExportedClouds(
}
else
{
transformed = util3d::transformPointCloud(iter->second.first, poses.at(iter->first));
// it looks like that using only transformPointCloud with indices
// flushes the colors, so we should extract points before... maybe a too old PCL version
pcl::copyPointCloud(*iter->second.first, *iter->second.second, *transformed);
transformed = rtabmap::util3d::transformPointCloud(transformed, poses.at(iter->first));
}
*assembledCloud += *transformed;
@@ -1124,7 +1127,13 @@ bool ExportCloudsDialog::getExportedClouds(
}
clouds.clear();
clouds.insert(std::make_pair(0, std::make_pair(assembledCloud, pcl::IndicesPtr(new std::vector<int>))));
pcl::IndicesPtr indices(new std::vector<int>);
indices->resize(assembledCloud->size());
for(unsigned int i=0; i<indices->size(); ++i)
{
indices->at(i) = i;
}
clouds.insert(std::make_pair(0, std::make_pair(assembledCloud, indices)));
}
if(_canceled)
@@ -1803,7 +1812,7 @@ bool ExportCloudsDialog::getExportedClouds(
cameras.insert(std::make_pair(iter->first, _ui->checkBox_assemble->isChecked()?poses.at(iter->first):Transform::getIdentity()));
}
std::map<int, Transform> cameraPoses;
std::map<int, CameraModel> cameraModels;
std::map<int, std::vector<CameraModel> > cameraModels;
for(std::map<int, Transform>::iterator jter=cameras.begin(); jter!=cameras.end(); ++jter)
{
std::vector<CameraModel> models;
@@ -1821,18 +1830,18 @@ bool ExportCloudsDialog::getExportedClouds(
_dbDriver->getCalibration(jter->first, models, stereoModel);
}
CameraModel model;
if(stereoModel.isValidForProjection())
{
model = stereoModel.left();
models.clear();
models.push_back(stereoModel.left());
}
else if(models.size() == 1 && models[0].isValidForProjection())
else if(models.size() == 0 || !models[0].isValidForProjection())
{
model = models[0];
models.clear();
}
if(!jter->second.isNull() && model.isValidForProjection())
if(!jter->second.isNull() && models.size())
{
if(model.imageWidth() == 0 || model.imageHeight() == 0)
if(models[0].imageWidth() == 0 || models[0].imageHeight() == 0)
{
// we are using an old database format (image size not saved in calibrations), we should
// uncompress images to get their size
@@ -1847,13 +1856,19 @@ bool ExportCloudsDialog::getExportedClouds(
_dbDriver->getNodeData(jter->first, data, true, false, false, false);
data.uncompressDataConst(&img, 0);
}
model.setImageSize(img.size());
cv::Size imageSize = img.size();
imageSize.width /= models.size();
for(unsigned int i=0; i<models.size(); ++i)
{
models[i].setImageSize(imageSize);
}
}
if(model.imageWidth() != 0 && model.imageHeight() != 0)
if(models[0].imageWidth() != 0 && models[0].imageHeight() != 0)
{
cameraPoses.insert(std::make_pair(jter->first, jter->second));
cameraModels.insert(std::make_pair(jter->first, model));
cameraModels.insert(std::make_pair(jter->first, models));
}
}
}
@@ -1920,7 +1935,7 @@ bool ExportCloudsDialog::getExportedClouds(
cameraPoses = graph::radiusPosesFiltering(cameraPoses,
_ui->doubleSpinBox_cameraFilterRadius->value(),
_ui->doubleSpinBox_cameraFilterAngle->value());
for(std::map<int, CameraModel>::iterator modelIter = cameraModels.begin(); modelIter!=cameraModels.end();)
for(std::map<int, std::vector<CameraModel> >::iterator modelIter = cameraModels.begin(); modelIter!=cameraModels.end();)
{
if(cameraPoses.find(modelIter->first)==cameraPoses.end())
{
@@ -1944,6 +1959,10 @@ bool ExportCloudsDialog::getExportedClouds(
TexturingState texturingState(_progressDialog);
_progressDialog->setMaximumSteps(_progressDialog->maximumSteps()+iter->second->polygons.size()/10000+1);
if(cameraModels.size() && cameraModels.begin()->second.size()>1)
{
_progressDialog->setMaximumSteps(_progressDialog->maximumSteps()+cameraModels.size()*(cameraModels.begin()->second.size()-1));
}
std::vector<float> roiRatios;
QStringList strings = _ui->lineEdit_meshingTextureRoiRatios->text().split(' ');
@@ -2397,7 +2416,6 @@ std::map<int, std::pair<pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr, pcl::Indic
{
indices = util3d::radiusFiltering(cloud, indices, _ui->doubleSpinBox_filteringRadius->value(), _ui->spinBox_filteringMinNeighbors->value());
}
clouds.insert(std::make_pair(iter->first, std::make_pair(cloud, indices)));
points = (int)cloud->size();
totalIndices = (int)indices->size();
@@ -2728,25 +2746,32 @@ cv::Mat ExportCloudsDialog::mergeTextures(
cv::Mat globalTexture;
if(mesh.tex_materials.size() > 1)
{
std::vector<int> textures(mesh.tex_materials.size(), -1);
std::vector<std::pair<int, int> > textures(mesh.tex_materials.size(), std::pair<int, int>(-1,-1));
cv::Size imageSize;
const int imageType=CV_8UC3;
UDEBUG("");
for(unsigned int i=0; i<mesh.tex_materials.size(); ++i)
{
std::list<std::string> texFileSplit = uSplit(mesh.tex_materials[i].tex_file, '_');
if(!mesh.tex_materials[i].tex_file.empty() &&
mesh.tex_polygons[i].size() &&
uIsInteger(mesh.tex_materials[i].tex_file, false))
uIsInteger(texFileSplit.front(), false))
{
int textureId = uStr2Int(mesh.tex_materials[i].tex_file);
textures[i] = textureId;
textures[i].first = uStr2Int(texFileSplit.front());
if(texFileSplit.size() == 2 &&
uIsInteger(texFileSplit.back(), false) )
{
textures[i].second = uStr2Int(texFileSplit.back());
}
int textureId = textures[i].first;
if(imageSize.width == 0 || imageSize.height == 0)
{
if(cachedSignatures.find(textureId)!=cachedSignatures.end() && !cachedSignatures.find(textureId)->sensorData().imageCompressed().empty())
{
SensorData data = cachedSignatures.find(textureId).value().sensorData();
if(data.cameraModels().size()==1 &&
if(data.cameraModels().size()>=1 &&
data.cameraModels()[0].imageHeight()>0 &&
data.cameraModels()[0].imageWidth()>0)
{
@@ -2763,6 +2788,10 @@ cv::Mat ExportCloudsDialog::mergeTextures(
data.uncompressDataConst(&image, 0);
UASSERT(!image.empty());
imageSize = image.size();
if(data.cameraModels().size()>1)
{
imageSize.width/=data.cameraModels().size();
}
}
}
else if(_dbDriver)
@@ -2770,7 +2799,7 @@ cv::Mat ExportCloudsDialog::mergeTextures(
std::vector<CameraModel> models;
StereoCameraModel stereoModel;
_dbDriver->getCalibration(textureId, models, stereoModel);
if(models.size()==1 &&
if(models.size()>=1 &&
models[0].imageHeight()>0 &&
models[0].imageWidth()>0)
{
@@ -2789,11 +2818,20 @@ cv::Mat ExportCloudsDialog::mergeTextures(
data.uncompressDataConst(&image, 0);
UASSERT(!image.empty());
imageSize = image.size();
if(data.cameraModels().size()>1)
{
imageSize.width/=data.cameraModels().size();
}
}
}
}
}
else
{
UWARN("Failed parsing texture file name: %s", mesh.tex_materials[i].tex_file.c_str());
}
}
UDEBUG("textures=%d imageSize=%dx%d", (int)textures.size(), imageSize.height, imageSize.width);
if(textures.size() && imageSize.height>0 && imageSize.width>0)
{
float scale = 0.0f;
@@ -2807,6 +2845,11 @@ cv::Mat ExportCloudsDialog::mergeTextures(
globalTexture = cv::Mat(textureSize, textureSize, imageType, cv::Scalar::all(255));
cv::Mat globalTextureMask = cv::Mat(textureSize, textureSize, CV_8UC1, cv::Scalar::all(0));
// used for multi camera texturing, to avoid reloading same texture for sub cameras
cv::Mat previousImage;
int previousTextureId = 0;
std::vector<CameraModel> previousCameraModels;
// make a blank texture
cv::Mat emptyImage(int(imageSize.height*scale), int(imageSize.width*scale), imageType, cv::Scalar::all(255));
cv::Mat emptyImageMask(int(imageSize.height*scale), int(imageSize.width*scale), CV_8UC1, cv::Scalar::all(255));
@@ -2824,23 +2867,47 @@ cv::Mat ExportCloudsDialog::mergeTextures(
UASSERT(v < textureSize-emptyImage.rows);
imageOrigin[t].x = u;
imageOrigin[t].y = v;
if(textures[t]>=0)
if(textures[t].first>=0)
{
cv::Mat image;
std::vector<CameraModel> models;
if(cachedSignatures.find(textures[t]) != cachedSignatures.end() &&
!cachedSignatures.find(textures[t])->sensorData().imageCompressed().empty())
if(textures[t].first == previousTextureId)
{
cachedSignatures.find(textures[t])->sensorData().uncompressDataConst(&image, 0);
image = previousImage;
models = previousCameraModels;
}
else if(_dbDriver)
else
{
SensorData data;
_dbDriver->getNodeData(textures[t], data, true, false, false, false);
data.uncompressDataConst(&image, 0);
if(cachedSignatures.find(textures[t].first) != cachedSignatures.end() &&
!cachedSignatures.find(textures[t].first)->sensorData().imageCompressed().empty())
{
cachedSignatures.find(textures[t].first)->sensorData().uncompressDataConst(&image, 0);
models = cachedSignatures.find(textures[t].first)->sensorData().cameraModels();
}
else if(_dbDriver)
{
SensorData data;
_dbDriver->getNodeData(textures[t].first, data, true, false, false, false);
data.uncompressDataConst(&image, 0);
StereoCameraModel stereoModel;
_dbDriver->getCalibration(textures[t].first, models, stereoModel);
}
previousImage = image;
previousCameraModels = models;
previousTextureId = textures[t].first;
}
UASSERT(!image.empty());
if(textures[t].second>=0)
{
UASSERT(textures[t].second < (int)models.size());
int width = image.cols/models.size();
image = image.colRange(width*textures[t].second, width*(textures[t].second+1));
}
cv::Mat resizedImage;
cv::resize(image, resizedImage, emptyImage.size(), 0.0f, 0.0f, cv::INTER_AREA);
UASSERT(resizedImage.type() == CV_8UC1 || resizedImage.type() == CV_8UC3);
@@ -2850,9 +2917,9 @@ cv::Mat ExportCloudsDialog::mergeTextures(
cv::cvtColor(resizedImage, resizedImageColor, CV_GRAY2BGR);
resizedImage = resizedImageColor;
}
if(_ui->checkBox_gainCompensation->isChecked() && _compensator && _compensator->getIndex(textures[t]) >= 0)
if(_ui->checkBox_gainCompensation->isChecked() && _compensator && _compensator->getIndex(textures[t].first) >= 0)
{
_compensator->apply(textures[t], resizedImage);
_compensator->apply(textures[t].first, resizedImage);
}
UASSERT(resizedImage.type() == globalTexture.type());
resizedImage.copyTo(globalTexture(cv::Rect(u, v, resizedImage.cols, resizedImage.rows)));
@@ -3235,6 +3302,12 @@ void ExportCloudsDialog::saveTextureMeshes(
removeDirRecursively(QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName());
QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName());
}
// used for multi camera texturing, to avoid reloading same texture for sub cameras
cv::Mat previousImage;
int previousTextureId = 0;
std::vector<CameraModel> previousCameraModels;
cv::Size imageSize;
for(unsigned int i=0; i<mesh->tex_materials.size(); ++i)
{
@@ -3253,22 +3326,52 @@ void ExportCloudsDialog::saveTextureMeshes(
UDEBUG("Saving %s...", fullPath.toStdString().c_str());
if(singleTexture || !QFileInfo(fullPath).exists())
{
if(uIsInteger(mesh->tex_materials[i].tex_file, false))
std::list<std::string> texFileSplit = uSplit(mesh->tex_materials[i].tex_file, '_');
if(texFileSplit.size() && uIsInteger(texFileSplit.front(), false))
{
int textureId = uStr2Int(mesh->tex_materials[i].tex_file);
cv::Mat image;
if(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty())
int textureId = uStr2Int(texFileSplit.front());
int textureSubCamera = -1;
if(texFileSplit.size() == 2 &&
uIsInteger(texFileSplit.back(), false))
{
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
textureSubCamera = uStr2Int(texFileSplit.back());
}
else if(_dbDriver)
cv::Mat image;
std::vector<CameraModel> cameraModels;
if(textureId == previousTextureId)
{
SensorData data;
_dbDriver->getNodeData(textureId, data, true, false, false, false);
data.uncompressDataConst(&image, 0);
image = previousImage;
cameraModels = previousCameraModels;
}
else
{
if(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty())
{
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
cameraModels = cachedSignatures.value(textureId).sensorData().cameraModels();
}
else if(_dbDriver)
{
SensorData data;
_dbDriver->getNodeData(textureId, data, true, false, false, false);
data.uncompressDataConst(&image, 0);
StereoCameraModel stereoModel;
_dbDriver->getCalibration(textureId, cameraModels, stereoModel);
}
previousImage = image;
previousCameraModels = cameraModels;
previousTextureId = textureId;
}
UASSERT(!image.empty());
imageSize = image.size();
if(textureSubCamera>=0)
{
UASSERT(cameraModels.size());
imageSize.width/=cameraModels.size();
image = image.colRange(imageSize.width*textureSubCamera, imageSize.width*(textureSubCamera+1));
}
if(_ui->checkBox_gainCompensation->isChecked() && _compensator && _compensator->getIndex(textureId) >= 0)
{
_compensator->apply(textureId, image);
@@ -3365,11 +3468,30 @@ void ExportCloudsDialog::saveTextureMeshes(
removeDirRecursively(path+QDir::separator()+currentPrefix);
QDir(path).mkdir(currentPrefix);
}
// used for multi camera texturing, to avoid reloading same texture for sub cameras
cv::Mat previousImage;
int previousTextureId = 0;
std::vector<CameraModel> previousCameraModels;
cv::Size imageSize;
for(unsigned int i=0;i<mesh->tex_materials.size(); ++i)
{
if(!mesh->tex_materials[i].tex_file.empty())
{
std::list<std::string> texFileSplit = uSplit(mesh->tex_materials[i].tex_file, '_');
int textureId = 0;
int textureSubCamera = -1;
if(texFileSplit.size() && uIsInteger(texFileSplit.front(), false))
{
textureId = uStr2Int(texFileSplit.front());
if(texFileSplit.size() == 2 &&
uIsInteger(texFileSplit.back(), false))
{
textureSubCamera = uStr2Int(texFileSplit.back());
}
}
// absolute path
QString fullPath;
if(singleTexture)
@@ -3381,23 +3503,46 @@ void ExportCloudsDialog::saveTextureMeshes(
{
fullPath = path+QDir::separator()+currentPrefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
}
if(uIsInteger(mesh->tex_materials[i].tex_file, false))
if(textureId>0)
{
int textureId = uStr2Int(mesh->tex_materials[i].tex_file);
cv::Mat image;
if(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty())
std::vector<CameraModel> cameraModels;
if(textureId == previousTextureId)
{
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
image = previousImage;
cameraModels = previousCameraModels;
}
else if(_dbDriver)
else
{
SensorData data;
_dbDriver->getNodeData(textureId, data, true, false, false, false);
data.uncompressDataConst(&image, 0);
if(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty())
{
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
cameraModels = cachedSignatures.value(textureId).sensorData().cameraModels();
}
else if(_dbDriver)
{
SensorData data;
_dbDriver->getNodeData(textureId, data, true, false, false, false);
data.uncompressDataConst(&image, 0);
StereoCameraModel stereoModel;
_dbDriver->getCalibration(textureId, cameraModels, stereoModel);
}
previousImage = image;
previousCameraModels = cameraModels;
previousTextureId = textureId;
}
UASSERT(!image.empty());
imageSize = image.size();
if(textureSubCamera>=0)
{
UASSERT(cameraModels.size());
imageSize.width/=cameraModels.size();
image = image.colRange(imageSize.width*textureSubCamera, imageSize.width*(textureSubCamera+1));
}
if(_ui->checkBox_gainCompensation->isChecked() && _compensator && _compensator->getIndex(textureId) >= 0)
{
_compensator->apply(textureId, image);

View File

@@ -3940,6 +3940,15 @@ void MainWindow::resizeEvent(QResizeEvent* anEvent)
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
//catch ctrl-s to save settings
if((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_S)
{
this->saveConfigGUI();
}
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && qobject_cast<QDockWidget*>(obj))

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1465</width>
<width>1547</width>
<height>965</height>
</rect>
</property>
@@ -52,7 +52,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>353</width>
<width>394</width>
<height>196</height>
</rect>
</property>
@@ -210,7 +210,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>353</width>
<width>394</width>
<height>196</height>
</rect>
</property>
@@ -483,7 +483,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>1465</width>
<width>1547</width>
<height>25</height>
</rect>
</property>
@@ -1008,7 +1008,7 @@
<x>0</x>
<y>0</y>
<width>339</width>
<height>186</height>
<height>210</height>
</rect>
</property>
<attribute name="label">
@@ -1144,7 +1144,7 @@
<x>0</x>
<y>0</y>
<width>282</width>
<height>569</height>
<height>626</height>
</rect>
</property>
<attribute name="label">
@@ -1233,6 +1233,38 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_54">
<property name="text">
<string>Voxel size (for clouds and scans)</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voxelSize">
<property name="suffix">
<string> m</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>99.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -1474,8 +1506,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>205</width>
<height>117</height>
<width>295</width>
<height>223</height>
</rect>
</property>
<attribute name="label">
@@ -1574,8 +1606,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>184</width>
<height>487</height>
<width>282</width>
<height>266</height>
</rect>
</property>
<attribute name="label">
@@ -1802,6 +1834,16 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="checkBox_showWords">
<property name="text">
<string>Words</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_showCloud">
<property name="text">