Refactored texture projection (avoid saving image on disk at this step)

This commit is contained in:
matlabbe
2017-01-16 18:29:14 -05:00
parent 2b8f40e505
commit a011a6af64
7 changed files with 208 additions and 127 deletions

View File

@@ -73,18 +73,18 @@ public:
void apply( void apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud); pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud) const;
void apply( void apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const pcl::IndicesPtr & indices); const pcl::IndicesPtr & indices) const;
void apply( void apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
const pcl::IndicesPtr & indices); const pcl::IndicesPtr & indices) const;
void apply( void apply(
int id, int id,
cv::Mat & image); cv::Mat & image) const;
double getGain(int id) const; double getGain(int id) const;
int getIndex(int id) const; int getIndex(int id) const;

View File

@@ -136,8 +136,6 @@ pcl::TextureMesh::Ptr RTABMAP_EXP createTextureMesh(
const pcl::PolygonMesh::Ptr & mesh, const pcl::PolygonMesh::Ptr & mesh,
const std::map<int, Transform> & poses, const std::map<int, Transform> & poses,
const std::map<int, CameraModel> & cameraModels, const std::map<int, CameraModel> & cameraModels,
const std::map<int, cv::Mat> & images,
const std::string & tmpDirectory = ".",
int kNormalSearch = 20); // if mesh doesn't have normals, compute them with k neighbors int kNormalSearch = 20); // if mesh doesn't have normals, compute them with k neighbors
pcl::PointCloud<pcl::Normal>::Ptr RTABMAP_EXP computeNormals( pcl::PointCloud<pcl::Normal>::Ptr RTABMAP_EXP computeNormals(

View File

@@ -375,7 +375,7 @@ void applyImpl(
void GainCompensator::apply( void GainCompensator::apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud) pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud) const
{ {
pcl::IndicesPtr indices(new std::vector<int>); pcl::IndicesPtr indices(new std::vector<int>);
apply(id, cloud, indices); apply(id, cloud, indices);
@@ -383,7 +383,7 @@ void GainCompensator::apply(
void GainCompensator::apply( void GainCompensator::apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud, pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
const pcl::IndicesPtr & indices) const pcl::IndicesPtr & indices) const
{ {
UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str()); UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str());
applyImpl<pcl::PointXYZRGB>(idToIndex_.at(id), cloud, indices, gains_); applyImpl<pcl::PointXYZRGB>(idToIndex_.at(id), cloud, indices, gains_);
@@ -391,7 +391,7 @@ void GainCompensator::apply(
void GainCompensator::apply( void GainCompensator::apply(
int id, int id,
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
const pcl::IndicesPtr & indices) const pcl::IndicesPtr & indices) const
{ {
UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str()); UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str());
applyImpl<pcl::PointXYZRGBNormal>(idToIndex_.at(id), cloud, indices, gains_); applyImpl<pcl::PointXYZRGBNormal>(idToIndex_.at(id), cloud, indices, gains_);
@@ -399,7 +399,7 @@ void GainCompensator::apply(
void GainCompensator::apply( void GainCompensator::apply(
int id, int id,
cv::Mat & image) cv::Mat & image) const
{ {
UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str()); UASSERT_MSG(uContains(idToIndex_, id), uFormat("id=%d idToIndex_.size()=%d", id, (int)idToIndex_.size()).c_str());
cv::multiply(image, gains_(idToIndex_.at(id), 0), image); cv::multiply(image, gains_(idToIndex_.at(id), 0), image);

View File

@@ -569,21 +569,16 @@ pcl::PolygonMesh::Ptr createMesh(
pcl::texture_mapping::CameraVector createTextureCameras( pcl::texture_mapping::CameraVector createTextureCameras(
const std::map<int, Transform> & poses, const std::map<int, Transform> & poses,
const std::map<int, CameraModel> & cameraModels, const std::map<int, CameraModel> & cameraModels)
const std::map<int, cv::Mat> & images,
const std::string & tmpDirectory)
{ {
UASSERT(poses.size() == cameraModels.size() && poses.size() == images.size()); UASSERT(poses.size() == cameraModels.size());
UASSERT(UDirectory::exists(tmpDirectory));
pcl::texture_mapping::CameraVector cameras(poses.size()); pcl::texture_mapping::CameraVector cameras(poses.size());
std::map<int, Transform>::const_iterator poseIter=poses.begin(); std::map<int, Transform>::const_iterator poseIter=poses.begin();
std::map<int, CameraModel>::const_iterator modelIter=cameraModels.begin(); std::map<int, CameraModel>::const_iterator modelIter=cameraModels.begin();
std::map<int, cv::Mat>::const_iterator imageIter=images.begin();
int oi=0; int oi=0;
for(; poseIter!=poses.end(); ++poseIter, ++modelIter, ++imageIter) for(; poseIter!=poses.end(); ++poseIter, ++modelIter)
{ {
UASSERT(poseIter->first == modelIter->first); UASSERT(poseIter->first == modelIter->first);
UASSERT(poseIter->first == imageIter->first);
pcl::TextureMapping<pcl::PointXYZ>::Camera cam; pcl::TextureMapping<pcl::PointXYZ>::Camera cam;
// transform into optical referential // transform into optical referential
@@ -595,22 +590,17 @@ pcl::texture_mapping::CameraVector createTextureCameras(
cam.pose = t.toEigen3f(); cam.pose = t.toEigen3f();
UASSERT(modelIter->second.fx()>0 && imageIter->second.rows>0 && imageIter->second.cols>0); if(modelIter->second.imageHeight() <=0 || modelIter->second.imageWidth() <=0)
{
UERROR("Should have camera models with width/height set to create texture cameras!");
return pcl::texture_mapping::CameraVector();
}
UASSERT(modelIter->second.fx()>0 && modelIter->second.imageHeight()>0 && modelIter->second.imageWidth()>0);
cam.focal_length=modelIter->second.fx(); cam.focal_length=modelIter->second.fx();
cam.height=imageIter->second.rows; cam.height=modelIter->second.imageHeight();
cam.width=imageIter->second.cols; cam.width=modelIter->second.imageWidth();
cam.texture_file = uFormat("%d.png", poseIter->first);
std::string fileName = uFormat("%s/%s%d.png", tmpDirectory.c_str(), "texture_", poseIter->first);
if(!cv::imwrite(fileName, imageIter->second))
{
UERROR("Cannot save texture of image %d", poseIter->first);
}
else
{
UINFO("Saved temporary texture: \"%s\"", fileName.c_str());
}
cam.texture_file = fileName;
cameras[oi++] = cam; cameras[oi++] = cam;
} }
return cameras; return cameras;
@@ -620,8 +610,6 @@ pcl::TextureMesh::Ptr createTextureMesh(
const pcl::PolygonMesh::Ptr & mesh, const pcl::PolygonMesh::Ptr & mesh,
const std::map<int, Transform> & poses, const std::map<int, Transform> & poses,
const std::map<int, CameraModel> & cameraModels, const std::map<int, CameraModel> & cameraModels,
const std::map<int, cv::Mat> & images,
const std::string & tmpDirectory,
int kNormalSearch) int kNormalSearch)
{ {
pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh); pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh);
@@ -636,9 +624,7 @@ pcl::TextureMesh::Ptr createTextureMesh(
// create cameras // create cameras
pcl::texture_mapping::CameraVector cameras = createTextureCameras( pcl::texture_mapping::CameraVector cameras = createTextureCameras(
poses, poses,
cameraModels, cameraModels);
images,
tmpDirectory);
// Create materials for each texture (and one extra for occluded faces) // Create materials for each texture (and one extra for occluded faces)
textureMesh->tex_materials.resize (cameras.size () + 1); textureMesh->tex_materials.resize (cameras.size () + 1);
@@ -671,17 +657,7 @@ pcl::TextureMesh::Ptr createTextureMesh(
} }
else else
{ {
mesh_material.tex_file = tmpDirectory+UDirectory::separator()+"occluded.png"; mesh_material.tex_file = "occluded.png";
cv::Mat emptyImage;
if(i>0)
{
emptyImage = cv::Mat::ones(cameras[i-1].height,cameras[i-1].width, CV_8UC1)*255;
}
else
{
emptyImage = cv::Mat::ones(480, 640, CV_8UC1)*255;
}
cv::imwrite(mesh_material.tex_file, emptyImage);
} }
textureMesh->tex_materials[i] = mesh_material; textureMesh->tex_materials[i] = mesh_material;

View File

@@ -1056,6 +1056,7 @@ bool CloudViewer::addTextureMesh (
coordinates->SetName (this_coordinates_name.c_str ()); coordinates->SetName (this_coordinates_name.c_str ());
for (std::size_t t = 0 ; t < mesh.tex_coordinates.size (); ++t) for (std::size_t t = 0 ; t < mesh.tex_coordinates.size (); ++t)
{
if (t == tex_id) if (t == tex_id)
for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc)
coordinates->InsertNextTuple2 ((double)mesh.tex_coordinates[t][tc][0], coordinates->InsertNextTuple2 ((double)mesh.tex_coordinates[t][tc][0],
@@ -1063,7 +1064,7 @@ bool CloudViewer::addTextureMesh (
else else
for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc) for (std::size_t tc = 0; tc < mesh.tex_coordinates[t].size (); ++tc)
coordinates->InsertNextTuple2 (-1.0, -1.0); coordinates->InsertNextTuple2 (-1.0, -1.0);
}
mapper->MapDataArrayToMultiTextureAttribute(tu, mapper->MapDataArrayToMultiTextureAttribute(tu,
this_coordinates_name.c_str (), this_coordinates_name.c_str (),
vtkDataObject::FIELD_ASSOCIATION_POINTS); vtkDataObject::FIELD_ASSOCIATION_POINTS);

View File

@@ -64,7 +64,8 @@ namespace rtabmap {
ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) : ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
QDialog(parent), QDialog(parent),
_canceled(false) _canceled(false),
_compensator(0)
{ {
_ui = new Ui_ExportCloudsDialog(); _ui = new Ui_ExportCloudsDialog();
_ui->setupUi(this); _ui->setupUi(this);
@@ -161,6 +162,10 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
ExportCloudsDialog::~ExportCloudsDialog() ExportCloudsDialog::~ExportCloudsDialog()
{ {
delete _ui; delete _ui;
if(_compensator)
{
delete _compensator;
}
} }
void ExportCloudsDialog::updateMLSGrpVisibility() void ExportCloudsDialog::updateMLSGrpVisibility()
@@ -174,13 +179,13 @@ void ExportCloudsDialog::updateMLSGrpVisibility()
void ExportCloudsDialog::updateTexturingAvailability() void ExportCloudsDialog::updateTexturingAvailability()
{ {
updateTexturingAvailability(_ui->checkBox_binary->isEnabled()); updateTexturingAvailability(_ui->checkBox_binary->isEnabled());
_ui->comboBox_meshingApproach->setCurrentIndex(_ui->checkBox_assemble->isChecked()?1:0);
_ui->comboBox_meshingApproach->setItemData(1, _ui->checkBox_assemble->isChecked()?1 | 32:0,Qt::UserRole - 1);
} }
void ExportCloudsDialog::updateTexturingAvailability(bool isExporting) void ExportCloudsDialog::updateTexturingAvailability(bool isExporting)
{ {
_ui->checkBox_textureMapping->setEnabled(!_ui->checkBox_assemble->isChecked() || isExporting); _ui->checkBox_textureMapping->setEnabled(!_ui->checkBox_assemble->isChecked() || isExporting);
_ui->label_textureMapping->setEnabled(_ui->checkBox_textureMapping->isEnabled()); _ui->label_textureMapping->setEnabled(_ui->checkBox_textureMapping->isEnabled());
_ui->comboBox_meshingApproach->setCurrentIndex(_ui->checkBox_assemble->isChecked()?1:0);
_ui->comboBox_meshingApproach->setItemData(1, _ui->checkBox_assemble->isChecked()?1 | 32:0,Qt::UserRole - 1);
} }
void ExportCloudsDialog::cancel() void ExportCloudsDialog::cancel()
@@ -538,7 +543,7 @@ void ExportCloudsDialog::exportClouds(
{ {
if(textureMeshes.size()) if(textureMeshes.size())
{ {
saveTextureMeshes(workingDirectory, poses, textureMeshes); saveTextureMeshes(workingDirectory, poses, textureMeshes, cachedSignatures);
} }
else if(meshes.size()) else if(meshes.size())
{ {
@@ -629,10 +634,61 @@ void ExportCloudsDialog::viewClouds(
if(textureMeshes.size()) if(textureMeshes.size())
{ {
QString prefix = "tmp_textures";
removeDirRecursively(workingDirectory+QDir::separator()+prefix);
QDir(workingDirectory).mkdir(prefix);
cv::Size imageSize;
for(std::map<int, pcl::TextureMesh::Ptr>::iterator iter = textureMeshes.begin(); iter!=textureMeshes.end(); ++iter) for(std::map<int, pcl::TextureMesh::Ptr>::iterator iter = textureMeshes.begin(); iter!=textureMeshes.end(); ++iter)
{ {
_progressDialog->appendText(tr("Viewing the mesh %1 (%2 polygons)...").arg(iter->first).arg(iter->second->tex_polygons.size()?iter->second->tex_polygons[0].size():0)); _progressDialog->appendText(tr("Viewing the mesh %1 (%2 polygons)...").arg(iter->first).arg(iter->second->tex_polygons.size()?iter->second->tex_polygons[0].size():0));
_progressDialog->incrementStep(); _progressDialog->incrementStep();
// save tmp textures
for(unsigned int i=0;i<iter->second->tex_materials.size(); ++i)
{
if(!iter->second->tex_materials[i].tex_file.empty())
{
// absolute path
QString fullPath = workingDirectory+QDir::separator()+prefix+QDir::separator()+QString(iter->second->tex_materials[i].tex_file.c_str());
if(!QFileInfo(fullPath).exists())
{
std::list<std::string> values = uSplit(iter->second->tex_materials[i].tex_file, '.');
if(values.size() == 2 && uIsInteger(*values.begin(), false))
{
int textureId = uStr2Int(*values.begin());
UASSERT(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty());
cv::Mat image;
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
UASSERT(!image.empty());
imageSize = image.size();
if(_ui->groupBox_gain->isChecked() && _compensator && _compensator->getIndex(textureId) >= 0)
{
_compensator->apply(textureId, image);
}
if(!cv::imwrite(fullPath.toStdString(), image))
{
_progressDialog->appendText(tr("Failed saving texture \"%1\" to \"%2\".")
.arg(iter->second->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
_progressDialog->setAutoClose(false);
}
}
else if(imageSize.height && imageSize.width)
{
// make a blank texture
cv::Mat image = cv::Mat::ones(imageSize, CV_8UC1)*255;
cv::imwrite(fullPath.toStdString(), image);
}
else
{
UWARN("Ignored texture %s (no image size set yet)", iter->second->tex_materials[i].tex_file.c_str());
}
}
iter->second->tex_materials[i].tex_file=fullPath.toStdString();
}
}
bool isRGB = false; bool isRGB = false;
for(unsigned int i=0; i<iter->second->cloud.fields.size(); ++i) for(unsigned int i=0; i<iter->second->cloud.fields.size(); ++i)
{ {
@@ -750,6 +806,11 @@ bool ExportCloudsDialog::getExportedClouds(
_canceled = false; _canceled = false;
_workingDirectory = workingDirectory; _workingDirectory = workingDirectory;
enableRegeneration(cachedSignatures.size()); enableRegeneration(cachedSignatures.size());
if(_compensator)
{
delete _compensator;
_compensator = 0;
}
if(this->exec() == QDialog::Accepted) if(this->exec() == QDialog::Accepted)
{ {
if(poses.empty()) if(poses.empty())
@@ -815,9 +876,11 @@ bool ExportCloudsDialog::getExportedClouds(
return false; return false;
} }
GainCompensator compensator(_ui->doubleSpinBox_gainRadius->value(), _ui->doubleSpinBox_gainOverlap->value(), _ui->doubleSpinBox_gainAlpha->value(), _ui->doubleSpinBox_gainBeta->value());
if(_ui->groupBox_gain->isChecked() && clouds.size() > 1) if(_ui->groupBox_gain->isChecked() && clouds.size() > 1)
{ {
UASSERT(_compensator == 0);
_compensator = new GainCompensator(_ui->doubleSpinBox_gainRadius->value(), _ui->doubleSpinBox_gainOverlap->value(), _ui->doubleSpinBox_gainAlpha->value(), _ui->doubleSpinBox_gainBeta->value());
if(!_ui->checkBox_gainLinkedLocationsOnly->isChecked()) if(!_ui->checkBox_gainLinkedLocationsOnly->isChecked())
{ {
_progressDialog->appendText(tr("Full gain compensation of %1 clouds...").arg(clouds.size())); _progressDialog->appendText(tr("Full gain compensation of %1 clouds...").arg(clouds.size()));
@@ -844,11 +907,11 @@ bool ExportCloudsDialog::getExportedClouds(
} }
} }
compensator.feed(clouds, allLinks); _compensator->feed(clouds, allLinks);
} }
else else
{ {
compensator.feed(clouds, links); _compensator->feed(clouds, links);
} }
_progressDialog->appendText(tr("Applying gain compensation...")); _progressDialog->appendText(tr("Applying gain compensation..."));
@@ -856,8 +919,8 @@ bool ExportCloudsDialog::getExportedClouds(
{ {
if(jter!=clouds.end()) if(jter!=clouds.end())
{ {
double gain = compensator.getGain(jter->first);; double gain = _compensator->getGain(jter->first);;
compensator.apply(jter->first, jter->second.first, jter->second.second); _compensator->apply(jter->first, jter->second.first, jter->second.second);
_progressDialog->appendText(tr("Cloud %1 has gain %2").arg(jter->first).arg(gain)); _progressDialog->appendText(tr("Cloud %1 has gain %2").arg(jter->first).arg(gain));
_progressDialog->incrementStep(); _progressDialog->incrementStep();
@@ -1448,9 +1511,6 @@ bool ExportCloudsDialog::getExportedClouds(
UDEBUG("texture mapping=%d", _ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked()?1:0); UDEBUG("texture mapping=%d", _ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked()?1:0);
if(_ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked()) if(_ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked())
{ {
QDir dir(workingDirectory);
removeDirRecursively(workingDirectory+QDir::separator()+"tmp_textures");
dir.mkdir("tmp_textures");
int i=0; int i=0;
for(std::map<int, pcl::PolygonMesh::Ptr>::iterator iter=meshes.begin(); for(std::map<int, pcl::PolygonMesh::Ptr>::iterator iter=meshes.begin();
iter!= meshes.end(); iter!= meshes.end();
@@ -1468,7 +1528,6 @@ bool ExportCloudsDialog::getExportedClouds(
} }
std::map<int, Transform> cameraPoses; std::map<int, Transform> cameraPoses;
std::map<int, CameraModel> cameraModels; std::map<int, CameraModel> cameraModels;
std::map<int, cv::Mat> images;
for(std::map<int, Transform>::iterator jter=cameras.begin(); jter!=cameras.end(); ++jter) for(std::map<int, Transform>::iterator jter=cameras.begin(); jter!=cameras.end(); ++jter)
{ {
if(cachedSignatures.contains(jter->first)) if(cachedSignatures.contains(jter->first))
@@ -1483,20 +1542,10 @@ bool ExportCloudsDialog::getExportedClouds(
{ {
model = s.sensorData().cameraModels()[0]; model = s.sensorData().cameraModels()[0];
} }
cv::Mat image = s.sensorData().imageRaw(); if(!jter->second.isNull() && model.isValidForProjection() && !s.sensorData().imageCompressed().empty())
if(image.empty() && !s.sensorData().imageCompressed().empty())
{
s.sensorData().uncompressDataConst(&image, 0, 0, 0);
}
if(!jter->second.isNull() && model.isValidForProjection() && !image.empty())
{ {
cameraPoses.insert(std::make_pair(jter->first, jter->second)); cameraPoses.insert(std::make_pair(jter->first, jter->second));
cameraModels.insert(std::make_pair(jter->first, model)); cameraModels.insert(std::make_pair(jter->first, model));
if(_ui->groupBox_gain->isChecked() && compensator.getIndex(jter->first) >= 0)
{
compensator.apply(jter->first, image);
}
images.insert(std::make_pair(jter->first, image));
} }
} }
} }
@@ -1563,17 +1612,7 @@ bool ExportCloudsDialog::getExportedClouds(
tex_name << "material_" << iter->first; tex_name << "material_" << iter->first;
tex_name >> mesh_material.tex_name; tex_name >> mesh_material.tex_name;
std::string tmpDirectory = dir.filePath("tmp_textures").toStdString(); mesh_material.tex_file = uFormat("%d.png", iter->first);
mesh_material.tex_file = uFormat("%s/%s%d.png", tmpDirectory.c_str(), "texture_", iter->first);
if(!cv::imwrite(mesh_material.tex_file, images.at(iter->first)))
{
UERROR("Cannot save texture of image %d", iter->first);
}
else
{
UINFO("Saved temporary texture: \"%s\"", mesh_material.tex_file.c_str());
}
textureMesh->tex_materials.push_back(mesh_material); textureMesh->tex_materials.push_back(mesh_material);
} }
else else
@@ -1588,8 +1627,6 @@ bool ExportCloudsDialog::getExportedClouds(
iter->second, iter->second,
cameraPoses, cameraPoses,
cameraModels, cameraModels,
images,
dir.filePath("tmp_textures").toStdString(),
_ui->spinBox_normalKSearch->value()); _ui->spinBox_normalKSearch->value());
// Remove occluded polygons (polygons with no texture) // Remove occluded polygons (polygons with no texture)
@@ -1684,6 +1721,14 @@ bool ExportCloudsDialog::getExportedClouds(
QApplication::processEvents(); QApplication::processEvents();
} }
} }
else if(!_ui->checkBox_binary->isEnabled() && // not enabled -> we are not exporting to file
textureMesh->tex_coordinates.size() == 2)
{
// Remove occluded texture to avoid doing multi-texturing (buggy)
textureMesh->tex_coordinates.pop_back();
textureMesh->tex_polygons.pop_back();
textureMesh->tex_materials.pop_back();
}
if(!_ui->checkBox_binary->isEnabled() && // not enabled -> we are not exporting to file if(!_ui->checkBox_binary->isEnabled() && // not enabled -> we are not exporting to file
textureMesh->tex_coordinates.size()) textureMesh->tex_coordinates.size())
@@ -1699,7 +1744,8 @@ bool ExportCloudsDialog::getExportedClouds(
#else #else
std::vector<Eigen::Vector2f> tmpCoordinates = textureMesh->tex_coordinates[t]; std::vector<Eigen::Vector2f> tmpCoordinates = textureMesh->tex_coordinates[t];
#endif #endif
textureMesh->tex_coordinates[t].resize(nPoints); textureMesh->tex_coordinates[t].clear();
textureMesh->tex_coordinates[t].resize(nPoints, Eigen::Vector2f(-1.0f, -1.0f));
int polygonSize = textureMesh->tex_polygons[t][0].vertices.size(); int polygonSize = textureMesh->tex_polygons[t][0].vertices.size();
UASSERT(textureMesh->tex_polygons[t].size() == tmpCoordinates.size()/polygonSize); UASSERT(textureMesh->tex_polygons[t].size() == tmpCoordinates.size()/polygonSize);
@@ -2274,7 +2320,8 @@ void ExportCloudsDialog::saveMeshes(
void ExportCloudsDialog::saveTextureMeshes( void ExportCloudsDialog::saveTextureMeshes(
const QString & workingDirectory, const QString & workingDirectory,
const std::map<int, Transform> & poses, const std::map<int, Transform> & poses,
const std::map<int, pcl::TextureMesh::Ptr> & meshes) std::map<int, pcl::TextureMesh::Ptr> & meshes,
const QMap<int, Signature> & cachedSignatures)
{ {
if(meshes.size() == 1) if(meshes.size() == 1)
{ {
@@ -2294,32 +2341,61 @@ void ExportCloudsDialog::saveTextureMeshes(
path += ".obj"; path += ".obj";
} }
pcl::TextureMesh mesh; pcl::TextureMesh::Ptr mesh = meshes.begin()->second;
mesh.tex_coordinates = meshes.begin()->second->tex_coordinates;
mesh.tex_materials = meshes.begin()->second->tex_materials;
removeDirRecursively(QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName()); removeDirRecursively(QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName());
QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName()); QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName());
for(unsigned int i=0;i<meshes.begin()->second->tex_materials.size(); ++i) cv::Size imageSize;
for(unsigned int i=0; i<mesh->tex_materials.size(); ++i)
{ {
QFileInfo info(mesh.tex_materials[i].tex_file.c_str()); if(!mesh->tex_materials[i].tex_file.empty())
QString fullPath = QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName()+QDir::separator()+info.fileName();
// relative path
mesh.tex_materials[i].tex_file=(QFileInfo(path).baseName()+QDir::separator()+info.fileName()).toStdString();
if(!QFile::copy(meshes.begin()->second->tex_materials[i].tex_file.c_str(), fullPath))
{ {
_progressDialog->appendText(tr("Failed copying texture \"%1\" to \"%2\".") // absolute path
.arg(meshes.begin()->second->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed); QString fullPath = QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str());
_progressDialog->setAutoClose(false);
if(!QFileInfo(fullPath).exists())
{
std::list<std::string> values = uSplit(mesh->tex_materials[i].tex_file, '.');
if(values.size() == 2 && uIsInteger(*values.begin(), false))
{
int textureId = uStr2Int(*values.begin());
UASSERT(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty());
cv::Mat image;
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
UASSERT(!image.empty());
imageSize = image.size();
if(_ui->groupBox_gain->isChecked() && _compensator && _compensator->getIndex(textureId) >= 0)
{
_compensator->apply(textureId, image);
}
if(!cv::imwrite(fullPath.toStdString(), image))
{
_progressDialog->appendText(tr("Failed saving texture \"%1\" to \"%2\".")
.arg(mesh->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
_progressDialog->setAutoClose(false);
}
}
else if(imageSize.height && imageSize.width)
{
// make a blank texture
cv::Mat image = cv::Mat::ones(imageSize, CV_8UC1)*255;
cv::imwrite(fullPath.toStdString(), image);
}
else
{
UWARN("Ignored texture %s (no image size set yet)", mesh->tex_materials[i].tex_file.c_str());
}
}
// relative path
mesh->tex_materials[i].tex_file=(QFileInfo(path).baseName()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())).toStdString();
} }
} }
mesh.tex_polygons = meshes.begin()->second->tex_polygons;
mesh.cloud = meshes.begin()->second->cloud;
success = pcl::io::saveOBJFile(path.toStdString(), mesh) == 0; success = pcl::io::saveOBJFile(path.toStdString(), *mesh) == 0;
if(success) if(success)
{ {
_progressDialog->incrementStep(); _progressDialog->incrementStep();
_progressDialog->appendText(tr("Saving the mesh (with %1 textures)... done.").arg(mesh.tex_materials.size())); _progressDialog->appendText(tr("Saving the mesh (with %1 textures)... done.").arg(mesh->tex_materials.size()));
QMessageBox::information(this, tr("Save successful!"), tr("Mesh saved to \"%1\"").arg(path)); QMessageBox::information(this, tr("Save successful!"), tr("Mesh saved to \"%1\"").arg(path));
} }
@@ -2345,41 +2421,69 @@ void ExportCloudsDialog::saveTextureMeshes(
if(ok) if(ok)
{ {
for(std::map<int, pcl::TextureMesh::Ptr>::const_iterator iter=meshes.begin(); iter!=meshes.end(); ++iter) for(std::map<int, pcl::TextureMesh::Ptr>::iterator iter=meshes.begin(); iter!=meshes.end(); ++iter)
{ {
QString currentPrefix=prefix+QString::number(iter->first); QString currentPrefix=prefix+QString::number(iter->first);
if(iter->second->tex_materials.size()) if(iter->second->tex_materials.size())
{ {
pcl::TextureMesh mesh; pcl::TextureMesh::Ptr mesh = iter->second;
mesh.tex_coordinates = iter->second->tex_coordinates; removeDirRecursively(path+QDir::separator()+currentPrefix);
mesh.tex_materials = iter->second->tex_materials;
QDir(path).rmdir(currentPrefix);
QDir(path).mkdir(currentPrefix); QDir(path).mkdir(currentPrefix);
for(unsigned int i=0;i<iter->second->tex_materials.size(); ++i) cv::Size imageSize;
for(unsigned int i=0;i<mesh->tex_materials.size(); ++i)
{ {
QFileInfo info(mesh.tex_materials[i].tex_file.c_str()); if(!mesh->tex_materials[i].tex_file.empty())
QString fullPath = path+QDir::separator()+currentPrefix+QDir::separator()+info.fileName();
// relative path
mesh.tex_materials[i].tex_file=(currentPrefix+QDir::separator()+info.fileName()).toStdString();
if(!QFile::copy(iter->second->tex_materials[i].tex_file.c_str(), fullPath) &&
info.fileName().compare("occluded.png") != 0)
{ {
_progressDialog->appendText(tr("Failed copying texture \"%1\" to \"%2\".") // absolute path
.arg(iter->second->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed); QString fullPath = path+QDir::separator()+currentPrefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str());
_progressDialog->setAutoClose(false); if(!QFileInfo(fullPath).exists())
{
std::list<std::string> values = uSplit(mesh->tex_materials[i].tex_file, '.');
if(values.size() == 2 && uIsInteger(*values.begin(), false))
{
int textureId = uStr2Int(*values.begin());
UASSERT(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty());
cv::Mat image;
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
UASSERT(!image.empty());
imageSize = image.size();
if(_ui->groupBox_gain->isChecked() && _compensator && _compensator->getIndex(textureId) >= 0)
{
_compensator->apply(textureId, image);
}
if(!cv::imwrite(fullPath.toStdString(), image))
{
_progressDialog->appendText(tr("Failed saving texture \"%1\" to \"%2\".")
.arg(mesh->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
_progressDialog->setAutoClose(false);
}
}
else if(imageSize.height && imageSize.width)
{
// make a blank texture
cv::Mat image = cv::Mat::ones(imageSize, CV_8UC1)*255;
cv::imwrite(fullPath.toStdString(), image);
}
else
{
UWARN("Ignored texture %s (no image size set yet)", mesh->tex_materials[i].tex_file.c_str());
}
}
// relative path
mesh->tex_materials[i].tex_file=(currentPrefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())).toStdString();
} }
} }
mesh.tex_polygons = iter->second->tex_polygons;
pcl::PointCloud<pcl::PointNormal>::Ptr tmp(new pcl::PointCloud<pcl::PointNormal>); pcl::PointCloud<pcl::PointNormal>::Ptr tmp(new pcl::PointCloud<pcl::PointNormal>);
pcl::fromPCLPointCloud2(iter->second->cloud, *tmp); pcl::fromPCLPointCloud2(mesh->cloud, *tmp);
tmp = util3d::transformPointCloud(tmp, poses.at(iter->first)); tmp = util3d::transformPointCloud(tmp, poses.at(iter->first));
pcl::toPCLPointCloud2(*tmp, mesh.cloud); pcl::toPCLPointCloud2(*tmp, mesh->cloud);
QString pathFile = path+QDir::separator()+QString("%1.%3").arg(currentPrefix).arg(suffix); QString pathFile = path+QDir::separator()+QString("%1.%3").arg(currentPrefix).arg(suffix);
bool success =false; bool success =false;
if(suffix == "obj") if(suffix == "obj")
{ {
success = pcl::io::saveOBJFile(pathFile.toStdString(), mesh) == 0; success = pcl::io::saveOBJFile(pathFile.toStdString(), *mesh) == 0;
} }
else else
{ {
@@ -2388,12 +2492,12 @@ void ExportCloudsDialog::saveTextureMeshes(
if(success) if(success)
{ {
_progressDialog->appendText(tr("Saved mesh %1 (%2 textures) to %3.") _progressDialog->appendText(tr("Saved mesh %1 (%2 textures) to %3.")
.arg(iter->first).arg(iter->second->tex_materials.size()-1).arg(pathFile)); .arg(iter->first).arg(mesh->tex_materials.size()).arg(pathFile));
} }
else else
{ {
_progressDialog->appendText(tr("Failed saving mesh %1 (%2 textures) to %3.") _progressDialog->appendText(tr("Failed saving mesh %1 (%2 textures) to %3.")
.arg(iter->first).arg(iter->second->tex_materials.size()-1).arg(pathFile), Qt::darkRed); .arg(iter->first).arg(mesh->tex_materials.size()).arg(pathFile), Qt::darkRed);
_progressDialog->setAutoClose(false); _progressDialog->setAutoClose(false);
} }
} }

View File

@@ -46,6 +46,7 @@ class QAbstractButton;
namespace rtabmap { namespace rtabmap {
class ProgressDialog; class ProgressDialog;
class GainCompensator;
class ExportCloudsDialog : public QDialog class ExportCloudsDialog : public QDialog
{ {
@@ -112,7 +113,7 @@ private:
std::map<int, pcl::TextureMesh::Ptr> & textureMeshes); std::map<int, pcl::TextureMesh::Ptr> & textureMeshes);
void saveClouds(const QString & workingDirectory, const std::map<int, Transform> & poses, const std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds, bool binaryMode = true); void saveClouds(const QString & workingDirectory, const std::map<int, Transform> & poses, const std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds, bool binaryMode = true);
void saveMeshes(const QString & workingDirectory, const std::map<int, Transform> & poses, const std::map<int, pcl::PolygonMesh::Ptr> & meshes, bool binaryMode = true); void saveMeshes(const QString & workingDirectory, const std::map<int, Transform> & poses, const std::map<int, pcl::PolygonMesh::Ptr> & meshes, bool binaryMode = true);
void saveTextureMeshes(const QString & workingDirectory, const std::map<int, Transform> & poses, const std::map<int, pcl::TextureMesh::Ptr> & meshes); void saveTextureMeshes(const QString & workingDirectory, const std::map<int, Transform> & poses, std::map<int, pcl::TextureMesh::Ptr> & textureMeshes, const QMap<int, Signature> & cachedSignatures);
void setSaveButton(); void setSaveButton();
void setOkButton(); void setOkButton();
@@ -124,6 +125,7 @@ private:
ProgressDialog * _progressDialog; ProgressDialog * _progressDialog;
QString _workingDirectory; QString _workingDirectory;
bool _canceled; bool _canceled;
GainCompensator * _compensator;
}; };
} }