mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 01:20:25 +08:00
Export: Added merging textures option (default true)
This commit is contained in:
@@ -246,6 +246,9 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP concatenateClouds(
|
||||
pcl::TextureMesh::Ptr RTABMAP_EXP concatenateTextureMeshes(
|
||||
const std::list<pcl::TextureMesh::Ptr> & meshes);
|
||||
|
||||
void RTABMAP_EXP concatenateTextureMaterials(
|
||||
pcl::TextureMesh & mesh, const cv::Size & imageSize, int textureSize, float & scale, std::vector<bool> * materialsKept=0);
|
||||
|
||||
/**
|
||||
* @brief Concatenate a vector of indices to a single vector.
|
||||
*
|
||||
|
||||
@@ -1694,59 +1694,186 @@ pcl::TextureMesh::Ptr concatenateTextureMeshes(const std::list<pcl::TextureMesh:
|
||||
std::map<std::string, int> addedMaterials; //<file, index>
|
||||
for(std::list<pcl::TextureMesh::Ptr>::const_iterator iter = meshes.begin(); iter!=meshes.end(); ++iter)
|
||||
{
|
||||
// append point cloud
|
||||
int polygonStep = output->cloud.height * output->cloud.width;
|
||||
pcl::PCLPointCloud2 tmp;
|
||||
pcl::concatenatePointCloud(output->cloud, iter->get()->cloud, tmp);
|
||||
output->cloud = tmp;
|
||||
|
||||
UASSERT((*iter)->tex_polygons.size() == (*iter)->tex_coordinates.size() &&
|
||||
(*iter)->tex_polygons.size() == (*iter)->tex_materials.size());
|
||||
|
||||
int materialCount = (*iter)->tex_polygons.size();
|
||||
for(int i=0; i<materialCount; ++i)
|
||||
if((*iter)->cloud.point_step &&
|
||||
(*iter)->cloud.data.size()/(*iter)->cloud.point_step &&
|
||||
(*iter)->tex_polygons.size() &&
|
||||
(*iter)->tex_coordinates.size())
|
||||
{
|
||||
std::map<std::string, int>::iterator jter = addedMaterials.find((*iter)->tex_materials[i].tex_file);
|
||||
int index;
|
||||
if(jter != addedMaterials.end())
|
||||
{
|
||||
index = jter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
addedMaterials.insert(std::make_pair((*iter)->tex_materials[i].tex_file, output->tex_materials.size()));
|
||||
index = output->tex_materials.size();
|
||||
output->tex_materials.push_back((*iter)->tex_materials[i]);
|
||||
output->tex_materials.back().tex_name = uFormat("material_%d", index);
|
||||
output->tex_polygons.resize(output->tex_polygons.size() + 1);
|
||||
output->tex_coordinates.resize(output->tex_coordinates.size() + 1);
|
||||
}
|
||||
// append point cloud
|
||||
int polygonStep = output->cloud.height * output->cloud.width;
|
||||
pcl::PCLPointCloud2 tmp;
|
||||
pcl::concatenatePointCloud(output->cloud, iter->get()->cloud, tmp);
|
||||
output->cloud = tmp;
|
||||
|
||||
// update and append polygon indices
|
||||
int oi = output->tex_polygons[index].size();
|
||||
output->tex_polygons[index].resize(output->tex_polygons[index].size() + (*iter)->tex_polygons[i].size());
|
||||
for(unsigned int j=0; j<(*iter)->tex_polygons[i].size(); ++j)
|
||||
UASSERT((*iter)->tex_polygons.size() == (*iter)->tex_coordinates.size() &&
|
||||
(*iter)->tex_polygons.size() == (*iter)->tex_materials.size());
|
||||
|
||||
int materialCount = (*iter)->tex_polygons.size();
|
||||
for(int i=0; i<materialCount; ++i)
|
||||
{
|
||||
pcl::Vertices polygon = (*iter)->tex_polygons[i][j];
|
||||
for(unsigned int k=0; k<polygon.vertices.size(); ++k)
|
||||
std::map<std::string, int>::iterator jter = addedMaterials.find((*iter)->tex_materials[i].tex_file);
|
||||
int index;
|
||||
if(jter != addedMaterials.end())
|
||||
{
|
||||
polygon.vertices[k] += polygonStep;
|
||||
index = jter->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
addedMaterials.insert(std::make_pair((*iter)->tex_materials[i].tex_file, output->tex_materials.size()));
|
||||
index = output->tex_materials.size();
|
||||
output->tex_materials.push_back((*iter)->tex_materials[i]);
|
||||
output->tex_materials.back().tex_name = uFormat("material_%d", index);
|
||||
output->tex_polygons.resize(output->tex_polygons.size() + 1);
|
||||
output->tex_coordinates.resize(output->tex_coordinates.size() + 1);
|
||||
}
|
||||
output->tex_polygons[index][oi+j] = polygon;
|
||||
}
|
||||
|
||||
// append uv coordinates
|
||||
oi = output->tex_coordinates[index].size();
|
||||
output->tex_coordinates[index].resize(output->tex_coordinates[index].size() + (*iter)->tex_coordinates[i].size());
|
||||
for(unsigned int j=0; j<(*iter)->tex_coordinates[i].size(); ++j)
|
||||
{
|
||||
output->tex_coordinates[index][oi+j] = (*iter)->tex_coordinates[i][j];
|
||||
// update and append polygon indices
|
||||
int oi = output->tex_polygons[index].size();
|
||||
output->tex_polygons[index].resize(output->tex_polygons[index].size() + (*iter)->tex_polygons[i].size());
|
||||
for(unsigned int j=0; j<(*iter)->tex_polygons[i].size(); ++j)
|
||||
{
|
||||
pcl::Vertices polygon = (*iter)->tex_polygons[i][j];
|
||||
for(unsigned int k=0; k<polygon.vertices.size(); ++k)
|
||||
{
|
||||
polygon.vertices[k] += polygonStep;
|
||||
}
|
||||
output->tex_polygons[index][oi+j] = polygon;
|
||||
}
|
||||
|
||||
// append uv coordinates
|
||||
oi = output->tex_coordinates[index].size();
|
||||
output->tex_coordinates[index].resize(output->tex_coordinates[index].size() + (*iter)->tex_coordinates[i].size());
|
||||
for(unsigned int j=0; j<(*iter)->tex_coordinates[i].size(); ++j)
|
||||
{
|
||||
output->tex_coordinates[index][oi+j] = (*iter)->tex_coordinates[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
int gcd(int a, int b) {
|
||||
return b == 0 ? a : gcd(b, a % b);
|
||||
}
|
||||
|
||||
void concatenateTextureMaterials(pcl::TextureMesh & mesh, const cv::Size & imageSize, int textureSize, float & scale, std::vector<bool> * materialsKept)
|
||||
{
|
||||
UASSERT(textureSize>0 && imageSize.width>0 && imageSize.height>0);
|
||||
int materials = 0;
|
||||
for(unsigned int i=0; i<mesh.tex_materials.size(); ++i)
|
||||
{
|
||||
if(mesh.tex_polygons.size())
|
||||
{
|
||||
++materials;
|
||||
}
|
||||
}
|
||||
if(materials)
|
||||
{
|
||||
int w = imageSize.width; // 640
|
||||
int h = imageSize.height; // 480
|
||||
int g = gcd(w,h); // 160
|
||||
int a = w/g; // 4=640/160
|
||||
int b = h/g; // 3=480/160
|
||||
UDEBUG("w=%d h=%d g=%d a=%d b=%d", w, h, g, a, b);
|
||||
int colCount = 0;
|
||||
int rowCount = 0;
|
||||
float factor = 0.1f;
|
||||
float epsilon = 0.001f;
|
||||
scale = 1.0f;
|
||||
while(colCount*rowCount < materials || (factor == 0.1f || scale > 1.0f))
|
||||
{
|
||||
// first run try scale = 1 (no scaling)
|
||||
if(factor!=0.1f)
|
||||
{
|
||||
scale = float(textureSize)/float(w*b*factor);
|
||||
}
|
||||
colCount = float(textureSize)/(scale*float(w));
|
||||
rowCount = float(textureSize)/(scale*float(h));
|
||||
factor+=epsilon; // search the maximum perfect fit
|
||||
}
|
||||
UDEBUG("materials=%d col=%d row=%d factor=%f scale=%f", materials, colCount, rowCount, factor-epsilon, scale);
|
||||
|
||||
UASSERT(mesh.tex_coordinates.size() == mesh.tex_materials.size() && mesh.tex_polygons.size() == mesh.tex_materials.size());
|
||||
|
||||
// prepare size
|
||||
int totalPolygons = 0;
|
||||
int totalCoordinates = 0;
|
||||
for(unsigned int i=0; i<mesh.tex_materials.size(); ++i)
|
||||
{
|
||||
if(mesh.tex_polygons[i].size())
|
||||
{
|
||||
totalPolygons+=mesh.tex_polygons[i].size();
|
||||
totalCoordinates+=mesh.tex_coordinates[i].size();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<pcl::Vertices> newPolygons(totalPolygons);
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
|
||||
std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > newCoordinates(totalCoordinates); // UV coordinates
|
||||
#else
|
||||
std::vector<Eigen::Vector2f> newCoordinates(totalCoordinates); // UV coordinates
|
||||
#endif
|
||||
|
||||
int pi = 0;
|
||||
int ci = 0;
|
||||
int ti=0;
|
||||
float scaledHeight = float(int(scale*float(h)))/float(textureSize);
|
||||
float scaledWidth = float(int(scale*float(w)))/float(textureSize);
|
||||
float lowerBorderSize = 1.0f - scaledHeight*float(rowCount);
|
||||
UDEBUG("scaledWidth=%f scaledHeight=%f lowerBorderSize=%f", scaledWidth, scaledHeight, lowerBorderSize);
|
||||
if(materialsKept)
|
||||
{
|
||||
materialsKept->resize(mesh.tex_materials.size(), 0);
|
||||
}
|
||||
for(unsigned int t=0; t<mesh.tex_materials.size(); ++t)
|
||||
{
|
||||
if(mesh.tex_polygons[t].size())
|
||||
{
|
||||
int row = ti/colCount;
|
||||
int col = ti%colCount;
|
||||
float offsetU = scaledWidth * float(col);
|
||||
float offsetV = scaledHeight * float((rowCount - 1) - row) + lowerBorderSize;
|
||||
// Texture coords have lower-left origin
|
||||
|
||||
for(unsigned int i=0; i<mesh.tex_polygons[t].size(); ++i)
|
||||
{
|
||||
newPolygons[pi++] = mesh.tex_polygons[t].at(i);
|
||||
}
|
||||
|
||||
for(unsigned int i=0; i<mesh.tex_coordinates[t].size(); ++i)
|
||||
{
|
||||
const Eigen::Vector2f & v = mesh.tex_coordinates[t].at(i);
|
||||
if(v[0] >= 0 && v[1] >=0)
|
||||
{
|
||||
newCoordinates[ci][0] = v[0]*scaledWidth + offsetU;
|
||||
newCoordinates[ci][1] = v[1]*scaledHeight + offsetV;
|
||||
}
|
||||
else
|
||||
{
|
||||
newCoordinates[ci] = v;
|
||||
}
|
||||
++ci;
|
||||
}
|
||||
++ti;
|
||||
if(materialsKept)
|
||||
{
|
||||
materialsKept->at(t) = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
pcl::TexMaterial m = mesh.tex_materials.front();
|
||||
mesh.tex_materials.clear();
|
||||
m.tex_file = "texture";
|
||||
m.tex_name = "material";
|
||||
mesh.tex_materials.push_back(m);
|
||||
mesh.tex_coordinates.clear();
|
||||
mesh.tex_coordinates.push_back(newCoordinates);
|
||||
mesh.tex_polygons.clear();
|
||||
mesh.tex_polygons.push_back(newPolygons);
|
||||
}
|
||||
}
|
||||
|
||||
pcl::IndicesPtr concatenate(const std::vector<pcl::IndicesPtr> & indices)
|
||||
{
|
||||
//compute total size
|
||||
|
||||
@@ -80,7 +80,7 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
|
||||
connect(_ui->comboBox_pipeline, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->comboBox_pipeline, SIGNAL(currentIndexChanged(int)), this, SLOT(updateReconstructionFlavor()));
|
||||
connect(_ui->comboBox_meshingApproach, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->comboBox_meshingApproach, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDenseReconstruction()));
|
||||
connect(_ui->comboBox_meshingApproach, SIGNAL(currentIndexChanged(int)), this, SLOT(updateReconstructionFlavor()));
|
||||
|
||||
connect(_ui->groupBox_regenerate, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->spinBox_decimation, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||
@@ -101,7 +101,7 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
|
||||
connect(_ui->spinBox_filteringMinNeighbors, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||
|
||||
connect(_ui->checkBox_assemble, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->checkBox_assemble, SIGNAL(clicked(bool)), this, SLOT(updateTexturingAvailability()));
|
||||
connect(_ui->checkBox_assemble, SIGNAL(clicked(bool)), this, SLOT(updateReconstructionFlavor()));
|
||||
connect(_ui->doubleSpinBox_voxelSize_assembled, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
|
||||
connect(_ui->groupBox_subtraction, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||
@@ -129,15 +129,17 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
|
||||
connect(_ui->checkBox_gainLinkedLocationsOnly, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
|
||||
connect(_ui->groupBox_meshing, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->groupBox_meshing, SIGNAL(toggled(bool)), this, SIGNAL(updateReconstructionFlavor()));
|
||||
connect(_ui->doubleSpinBox_gp3Radius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->doubleSpinBox_gp3Mu, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->doubleSpinBox_meshDecimationFactor, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->doubleSpinBox_meshDecimationFactor, SIGNAL(valueChanged(double)), this, SLOT(updatePoissonOutputPolygonsAvailability()));
|
||||
connect(_ui->doubleSpinBox_meshDecimationFactor, SIGNAL(valueChanged(double)), this, SLOT(updateReconstructionFlavor()));
|
||||
connect(_ui->doubleSpinBox_transferColorRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->checkBox_cleanMesh, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->checkBox_textureMapping, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->checkBox_textureMapping, SIGNAL(stateChanged(int)), this, SLOT(updateTexturingAvailability()));
|
||||
connect(_ui->checkBox_textureMapping, SIGNAL(stateChanged(int)), this, SLOT(updateReconstructionFlavor()));
|
||||
connect(_ui->comboBox_meshingTextureFormat, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->comboBox_meshingTextureSize, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
|
||||
|
||||
connect(_ui->checkBox_poisson_outputPolygons, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
connect(_ui->checkBox_poisson_manifold, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||
@@ -178,26 +180,6 @@ void ExportCloudsDialog::updateMLSGrpVisibility()
|
||||
_ui->groupBox_4->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 3);
|
||||
_ui->groupBox_5->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 4);
|
||||
}
|
||||
void ExportCloudsDialog::updateTexturingAvailability()
|
||||
{
|
||||
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)
|
||||
{
|
||||
_ui->checkBox_textureMapping->setEnabled(!_ui->checkBox_assemble->isChecked() || isExporting);
|
||||
_ui->label_textureMapping->setEnabled(_ui->checkBox_textureMapping->isEnabled());
|
||||
_ui->comboBox_meshingTextureFormat->setEnabled(_ui->label_textureMapping->isEnabled());
|
||||
_ui->label_meshingTextureFormat->setEnabled(_ui->label_textureMapping->isEnabled());
|
||||
updatePoissonOutputPolygonsAvailability();
|
||||
}
|
||||
void ExportCloudsDialog::updatePoissonOutputPolygonsAvailability()
|
||||
{
|
||||
_ui->checkBox_poisson_outputPolygons->setDisabled(
|
||||
_ui->checkBox_binary->isEnabled() ||
|
||||
_ui->doubleSpinBox_meshDecimationFactor->value()!=0.0);
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::cancel()
|
||||
{
|
||||
@@ -269,6 +251,7 @@ void ExportCloudsDialog::saveSettings(QSettings & settings, const QString & grou
|
||||
|
||||
settings.setValue("mesh_texture", _ui->checkBox_textureMapping->isChecked());
|
||||
settings.setValue("mesh_textureFormat", _ui->comboBox_meshingTextureFormat->currentIndex());
|
||||
settings.setValue("mesh_textureSize", _ui->comboBox_meshingTextureSize->currentIndex());
|
||||
|
||||
settings.setValue("mesh_angle_tolerance", _ui->doubleSpinBox_mesh_angleTolerance->value());
|
||||
settings.setValue("mesh_quad", _ui->checkBox_mesh_quad->isChecked());
|
||||
@@ -355,6 +338,7 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
|
||||
|
||||
_ui->checkBox_textureMapping->setChecked(settings.value("mesh_texture", _ui->checkBox_textureMapping->isChecked()).toBool());
|
||||
_ui->comboBox_meshingTextureFormat->setCurrentIndex(settings.value("mesh_textureFormat", _ui->comboBox_meshingTextureFormat->currentIndex()).toInt());
|
||||
_ui->comboBox_meshingTextureSize->setCurrentIndex(settings.value("mesh_textureSize", _ui->comboBox_meshingTextureSize->currentIndex()).toInt());
|
||||
|
||||
_ui->doubleSpinBox_mesh_angleTolerance->setValue(settings.value("mesh_angle_tolerance", _ui->doubleSpinBox_mesh_angleTolerance->value()).toDouble());
|
||||
_ui->checkBox_mesh_quad->setChecked(settings.value("mesh_quad", _ui->checkBox_mesh_quad->isChecked()).toBool());
|
||||
@@ -371,7 +355,6 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
|
||||
_ui->doubleSpinBox_poisson_scale->setValue(settings.value("poisson_scale", _ui->doubleSpinBox_poisson_scale->value()).toDouble());
|
||||
|
||||
updateReconstructionFlavor();
|
||||
updateTexturingAvailability();
|
||||
updateMLSGrpVisibility();
|
||||
|
||||
if(!group.isEmpty())
|
||||
@@ -429,16 +412,17 @@ void ExportCloudsDialog::restoreDefaults()
|
||||
_ui->checkBox_gainLinkedLocationsOnly->setChecked(true);
|
||||
|
||||
_ui->groupBox_meshing->setChecked(false);
|
||||
_ui->doubleSpinBox_gp3Radius->setValue(0.04);
|
||||
_ui->doubleSpinBox_gp3Radius->setValue(0.2);
|
||||
_ui->doubleSpinBox_gp3Mu->setValue(2.5);
|
||||
_ui->doubleSpinBox_meshDecimationFactor->setValue(0.0);
|
||||
_ui->doubleSpinBox_transferColorRadius->setValue(0.05);
|
||||
_ui->doubleSpinBox_transferColorRadius->setValue(0.2);
|
||||
_ui->checkBox_cleanMesh->setChecked(true);
|
||||
|
||||
_ui->comboBox_meshingApproach->setCurrentIndex(1);
|
||||
|
||||
_ui->checkBox_textureMapping->setChecked(false);
|
||||
_ui->comboBox_meshingTextureFormat->setCurrentIndex(0);
|
||||
_ui->comboBox_meshingTextureSize->setCurrentIndex(4); // 2048
|
||||
|
||||
_ui->doubleSpinBox_mesh_angleTolerance->setValue(15.0);
|
||||
_ui->checkBox_mesh_quad->setChecked(false);
|
||||
@@ -455,7 +439,6 @@ void ExportCloudsDialog::restoreDefaults()
|
||||
_ui->doubleSpinBox_poisson_scale->setValue(1.1);
|
||||
|
||||
updateReconstructionFlavor();
|
||||
updateTexturingAvailability();
|
||||
updateMLSGrpVisibility();
|
||||
|
||||
this->update();
|
||||
@@ -465,18 +448,28 @@ void ExportCloudsDialog::updateReconstructionFlavor()
|
||||
{
|
||||
_ui->groupBox_mls->setVisible(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->groupBox_mls->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->comboBox_meshingApproach->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->label_denseReconstruction->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->doubleSpinBox_meshDecimationFactor->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->label_meshDecimation->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->groupBox_organized->setVisible(_ui->comboBox_pipeline->currentIndex() == 0);
|
||||
updateDenseReconstruction();
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::updateDenseReconstruction()
|
||||
{
|
||||
// dense texturing options
|
||||
_ui->groupBox_gp3->setVisible(_ui->comboBox_pipeline->currentIndex() == 1 && _ui->comboBox_meshingApproach->currentIndex()==0);
|
||||
_ui->groupBox_poisson->setVisible(_ui->comboBox_pipeline->currentIndex() == 1 && _ui->comboBox_meshingApproach->currentIndex()==1);
|
||||
if(_ui->groupBox_meshing->isChecked())
|
||||
{
|
||||
_ui->comboBox_meshingApproach->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_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);
|
||||
|
||||
_ui->checkBox_poisson_outputPolygons->setDisabled(
|
||||
_ui->checkBox_binary->isEnabled() ||
|
||||
_ui->doubleSpinBox_meshDecimationFactor->value()!=0.0 ||
|
||||
_ui->checkBox_textureMapping->isChecked());
|
||||
|
||||
_ui->spinBox_mesh_minClusterSize->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
_ui->label_meshClean->setEnabled(_ui->comboBox_pipeline->currentIndex() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::selectDistortionModel()
|
||||
@@ -503,7 +496,7 @@ void ExportCloudsDialog::setSaveButton()
|
||||
_ui->checkBox_mesh_quad->setVisible(false);
|
||||
_ui->checkBox_mesh_quad->setEnabled(false);
|
||||
_ui->label_quad->setVisible(false);
|
||||
updateTexturingAvailability(true);
|
||||
updateReconstructionFlavor();
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::setOkButton()
|
||||
@@ -516,7 +509,7 @@ void ExportCloudsDialog::setOkButton()
|
||||
_ui->checkBox_mesh_quad->setVisible(true);
|
||||
_ui->checkBox_mesh_quad->setEnabled(true);
|
||||
_ui->label_quad->setVisible(true);
|
||||
updateTexturingAvailability(false);
|
||||
updateReconstructionFlavor();
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::enableRegeneration(bool enabled)
|
||||
@@ -644,32 +637,77 @@ void ExportCloudsDialog::viewClouds(
|
||||
|
||||
window->show();
|
||||
|
||||
_progressDialog->appendText(tr("Opening visualizer..."));
|
||||
QApplication::processEvents();
|
||||
uSleep(500);
|
||||
QApplication::processEvents();
|
||||
|
||||
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)
|
||||
{
|
||||
_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();
|
||||
|
||||
// save tmp textures
|
||||
for(unsigned int i=0;i<iter->second->tex_materials.size(); ++i)
|
||||
pcl::TextureMesh::Ptr mesh = iter->second;
|
||||
|
||||
// As CloudViewer is not supporting more than one texture per mesh, merge them all by default
|
||||
cv::Mat globalTexture;
|
||||
if(mesh->tex_materials.size() > 1)
|
||||
{
|
||||
if(!iter->second->tex_materials[i].tex_file.empty())
|
||||
globalTexture = mergeTextures(*mesh, cachedSignatures);
|
||||
}
|
||||
|
||||
// VTK issue:
|
||||
// tex_coordinates should be linked to points, not
|
||||
// polygon vertices. Points linked to multiple different TCoords (different textures) should
|
||||
// be duplicated.
|
||||
for(unsigned int t=0; t<mesh->tex_coordinates.size(); ++t)
|
||||
{
|
||||
UASSERT(mesh->tex_polygons[t].size());
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromPCLPointCloud2(mesh->cloud, *originalCloud);
|
||||
|
||||
// make a cloud with as many points than polygon vertices
|
||||
unsigned int nPoints = mesh->tex_coordinates[t].size();
|
||||
UASSERT(nPoints== mesh->tex_polygons[t].size()*mesh->tex_polygons[t][0].vertices.size()); // assuming polygon size is constant!
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
cloud->resize(nPoints);
|
||||
|
||||
unsigned int oi=0;
|
||||
for(unsigned int i=0; i<mesh->tex_polygons[t].size(); ++i)
|
||||
{
|
||||
pcl::Vertices & vertices = mesh->tex_polygons[t][i];
|
||||
for(int j=0; j<vertices.vertices.size(); ++j)
|
||||
{
|
||||
UASSERT(oi < cloud->size());
|
||||
UASSERT(vertices.vertices[j] < originalCloud->size());
|
||||
cloud->at(oi) = originalCloud->at(vertices.vertices[j]);
|
||||
vertices.vertices[j] = oi; // new vertice index
|
||||
++oi;
|
||||
}
|
||||
}
|
||||
pcl::toPCLPointCloud2(*cloud, mesh->cloud);
|
||||
}
|
||||
|
||||
// save tmp textures
|
||||
cv::Size imageSize;
|
||||
for(unsigned int i=0;i<mesh->tex_materials.size(); ++i)
|
||||
{
|
||||
if(!mesh->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())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
QString fullPath = workingDirectory+QDir::separator()+prefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
if(!QFileInfo(fullPath).exists())
|
||||
{
|
||||
if(uIsInteger(iter->second->tex_materials[i].tex_file, false))
|
||||
if(uIsInteger(mesh->tex_materials[i].tex_file, false))
|
||||
{
|
||||
int textureId = uStr2Int(iter->second->tex_materials[i].tex_file);
|
||||
int textureId = uStr2Int(mesh->tex_materials[i].tex_file);
|
||||
UASSERT(cachedSignatures.contains(textureId) && !cachedSignatures.value(textureId).sensorData().imageCompressed().empty());
|
||||
cv::Mat image;
|
||||
cachedSignatures.value(textureId).sensorData().uncompressDataConst(&image, 0);
|
||||
@@ -683,7 +721,7 @@ void ExportCloudsDialog::viewClouds(
|
||||
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);
|
||||
.arg(mesh->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
|
||||
_progressDialog->setAutoClose(false);
|
||||
}
|
||||
}
|
||||
@@ -693,19 +731,28 @@ void ExportCloudsDialog::viewClouds(
|
||||
cv::Mat image = cv::Mat::ones(imageSize, CV_8UC1)*255;
|
||||
cv::imwrite(fullPath.toStdString(), image);
|
||||
}
|
||||
else if(!globalTexture.empty())
|
||||
{
|
||||
if(!cv::imwrite(fullPath.toStdString(), globalTexture))
|
||||
{
|
||||
_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
|
||||
{
|
||||
UWARN("Ignored texture %s (no image size set yet)", iter->second->tex_materials[i].tex_file.c_str());
|
||||
UWARN("Ignored texture %s (no image size set yet)", mesh->tex_materials[i].tex_file.c_str());
|
||||
}
|
||||
}
|
||||
iter->second->tex_materials[i].tex_file=fullPath.toStdString();
|
||||
mesh->tex_materials[i].tex_file=fullPath.toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
bool isRGB = false;
|
||||
for(unsigned int i=0; i<iter->second->cloud.fields.size(); ++i)
|
||||
for(unsigned int i=0; i<mesh->cloud.fields.size(); ++i)
|
||||
{
|
||||
if(iter->second->cloud.fields[i].name.compare("rgb") == 0)
|
||||
if(mesh->cloud.fields[i].name.compare("rgb") == 0)
|
||||
{
|
||||
isRGB=true;
|
||||
break;
|
||||
@@ -713,13 +760,13 @@ void ExportCloudsDialog::viewClouds(
|
||||
}
|
||||
if(isRGB)
|
||||
{
|
||||
viewer->addCloudTextureMesh(uFormat("mesh%d",iter->first), iter->second, iter->first>0?poses.at(iter->first):Transform::getIdentity());
|
||||
viewer->addCloudTextureMesh(uFormat("mesh%d",iter->first), mesh, iter->first>0?poses.at(iter->first):Transform::getIdentity());
|
||||
}
|
||||
else
|
||||
{
|
||||
viewer->addCloudTextureMesh(uFormat("mesh%d",iter->first), iter->second, iter->first>0?poses.at(iter->first):Transform::getIdentity());
|
||||
viewer->addCloudTextureMesh(uFormat("mesh%d",iter->first), mesh, iter->first>0?poses.at(iter->first):Transform::getIdentity());
|
||||
}
|
||||
_progressDialog->appendText(tr("Viewing the mesh %1 (%2 polygons)... done.").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)... done.").arg(iter->first).arg(mesh->tex_polygons.size()?mesh->tex_polygons[0].size():0));
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
@@ -991,9 +1038,14 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
.arg(assembledCloud->size())
|
||||
.arg(_ui->doubleSpinBox_voxelSize_assembled->value()));
|
||||
QApplication::processEvents();
|
||||
int before = assembledCloud->size();
|
||||
assembledCloud = util3d::voxelize(
|
||||
assembledCloud,
|
||||
_ui->doubleSpinBox_voxelSize_assembled->value());
|
||||
_progressDialog->appendText(tr("Voxelize cloud (%1 points, voxel size = %2 m)...done! (%3 points)")
|
||||
.arg(before)
|
||||
.arg(_ui->doubleSpinBox_voxelSize_assembled->value())
|
||||
.arg(assembledCloud->size()));
|
||||
}
|
||||
|
||||
clouds.clear();
|
||||
@@ -1052,6 +1104,8 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
|
||||
_progressDialog->appendText(tr("Smoothing (MLS) the cloud (%1 points)...").arg(cloudWithNormals->size()));
|
||||
QApplication::processEvents();
|
||||
uSleep(100);
|
||||
QApplication::processEvents();
|
||||
if(_canceled)
|
||||
{
|
||||
return false;
|
||||
@@ -1119,6 +1173,7 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
{
|
||||
_progressDialog->appendText(tr("Organized fast mesh... "));
|
||||
QApplication::processEvents();
|
||||
uSleep(100);
|
||||
QApplication::processEvents();
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr mergedClouds(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
||||
@@ -1185,6 +1240,13 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
int before = polygons.size();
|
||||
polygons = filteredPolygons;
|
||||
|
||||
if(oi == 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());
|
||||
UWARN(msg.c_str());
|
||||
}
|
||||
|
||||
_progressDialog->appendText(tr("Filtered %1 polygons.").arg(before-oi));
|
||||
QApplication::processEvents();
|
||||
}
|
||||
@@ -1348,6 +1410,7 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
_progressDialog->appendText(tr("Poisson surface reconstruction..."));
|
||||
}
|
||||
QApplication::processEvents();
|
||||
uSleep(100);
|
||||
QApplication::processEvents();
|
||||
|
||||
int i=0;
|
||||
@@ -1391,6 +1454,8 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
unsigned int count = mesh->polygons.size();
|
||||
_progressDialog->appendText(tr("Mesh decimation (factor=%1) from %2 polygons...").arg(_ui->doubleSpinBox_meshDecimationFactor->value()).arg(count));
|
||||
QApplication::processEvents();
|
||||
uSleep(100);
|
||||
QApplication::processEvents();
|
||||
|
||||
mesh = util3d::meshDecimation(mesh, (float)_ui->doubleSpinBox_meshDecimationFactor->value());
|
||||
_progressDialog->appendText(tr("Mesh decimated (factor=%1) from %2 to %3 polygons").arg(_ui->doubleSpinBox_meshDecimationFactor->value()).arg(count).arg(mesh->polygons.size()));
|
||||
@@ -1524,6 +1589,11 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
UDEBUG("texture mapping=%d", _ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked()?1:0);
|
||||
if(_ui->checkBox_textureMapping->isEnabled() && _ui->checkBox_textureMapping->isChecked())
|
||||
{
|
||||
_progressDialog->appendText(tr("Texturing..."));
|
||||
QApplication::processEvents();
|
||||
uSleep(100);
|
||||
QApplication::processEvents();
|
||||
|
||||
int i=0;
|
||||
for(std::map<int, pcl::PolygonMesh::Ptr>::iterator iter=meshes.begin();
|
||||
iter!= meshes.end();
|
||||
@@ -1579,38 +1649,20 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
if(textureMesh->tex_polygons.size() && textureMesh->tex_polygons[0].size())
|
||||
{
|
||||
textureMesh->tex_coordinates.resize(1);
|
||||
if(!_ui->checkBox_mesh_quad->isEnabled()) // disabled -> we are exporting to file
|
||||
|
||||
//tex_coordinates should be linked to polygon vertices
|
||||
int polygonSize = textureMesh->tex_polygons[0][0].vertices.size();
|
||||
textureMesh->tex_coordinates[0].resize(polygonSize*textureMesh->tex_polygons[0].size());
|
||||
for(unsigned int i=0; i<textureMesh->tex_polygons[0].size(); ++i)
|
||||
{
|
||||
UDEBUG("");
|
||||
// When saving to file, tex_coordinates should be linked to polygon vertices, not points
|
||||
int polygonSize = textureMesh->tex_polygons[0][0].vertices.size();
|
||||
textureMesh->tex_coordinates[0].resize(polygonSize*textureMesh->tex_polygons[0].size());
|
||||
for(unsigned int i=0; i<textureMesh->tex_polygons[0].size(); ++i)
|
||||
{
|
||||
const pcl::Vertices & vertices = textureMesh->tex_polygons[0][i];
|
||||
UASSERT(polygonSize == (int)vertices.vertices.size());
|
||||
for(int k=0; k<polygonSize; ++k)
|
||||
{
|
||||
//uv
|
||||
UASSERT(vertices.vertices[k] < oter->second.size());
|
||||
int originalVertex = oter->second[vertices.vertices[k]];
|
||||
textureMesh->tex_coordinates[0][i*polygonSize+k] = Eigen::Vector2f(
|
||||
float(originalVertex % w) / float(w), // u
|
||||
float(h - originalVertex / w) / float(h)); // v
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UDEBUG("");
|
||||
int nPoints = textureMesh->cloud.data.size()/textureMesh->cloud.point_step;
|
||||
textureMesh->tex_coordinates[0].resize(nPoints);
|
||||
for(int i=0; i<nPoints; ++i)
|
||||
const pcl::Vertices & vertices = textureMesh->tex_polygons[0][i];
|
||||
UASSERT(polygonSize == (int)vertices.vertices.size());
|
||||
for(int k=0; k<polygonSize; ++k)
|
||||
{
|
||||
//uv
|
||||
UASSERT(i < (int)oter->second.size());
|
||||
int originalVertex = oter->second[i];
|
||||
textureMesh->tex_coordinates[0][i] = Eigen::Vector2f(
|
||||
UASSERT(vertices.vertices[k] < oter->second.size());
|
||||
int originalVertex = oter->second[vertices.vertices[k]];
|
||||
textureMesh->tex_coordinates[0][i*polygonSize+k] = Eigen::Vector2f(
|
||||
float(originalVertex % w) / float(w), // u
|
||||
float(h - originalVertex / w) / float(h)); // v
|
||||
}
|
||||
@@ -1691,6 +1743,12 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
validPolygons.insert(*jter);
|
||||
}
|
||||
}
|
||||
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());
|
||||
UWARN(msg.c_str());
|
||||
}
|
||||
|
||||
// for each texture
|
||||
unsigned int allPolygonsIndex = 0;
|
||||
@@ -1703,28 +1761,38 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
#else
|
||||
std::vector<Eigen::Vector2f> filteredCoordinates(textureMesh->tex_coordinates[t].size());
|
||||
#endif
|
||||
int oi=0;
|
||||
unsigned int polygonSize = 0;
|
||||
|
||||
if(textureMesh->tex_polygons[t].size())
|
||||
{
|
||||
polygonSize = textureMesh->tex_polygons[t][0].vertices.size();
|
||||
// make index polygon to coordinate
|
||||
std::vector<unsigned int> polygonToCoord(textureMesh->tex_polygons[t].size());
|
||||
unsigned int totalCoord = 0;
|
||||
for(unsigned int i=0; i<textureMesh->tex_polygons[t].size(); ++i)
|
||||
{
|
||||
polygonToCoord[i] = totalCoord;
|
||||
totalCoord+=textureMesh->tex_polygons[t][i].vertices.size();
|
||||
}
|
||||
UASSERT_MSG(totalCoord == textureMesh->tex_coordinates[t].size(), uFormat("%d vs %d", totalCoord, (int)textureMesh->tex_coordinates[t].size()).c_str());
|
||||
|
||||
UASSERT_MSG(filteredCoordinates.size() == textureMesh->tex_polygons[t].size()*polygonSize, uFormat("%d vs %d (polygon size=%d)", (int)filteredCoordinates.size(), (int)textureMesh->tex_polygons[t].size(), (int)polygonSize).c_str());
|
||||
int oi=0;
|
||||
int ci=0;
|
||||
for(unsigned int i=0; i<textureMesh->tex_polygons[t].size(); ++i)
|
||||
{
|
||||
if(validPolygons.find(allPolygonsIndex) != validPolygons.end())
|
||||
{
|
||||
filteredPolygons[oi] = textureMesh->tex_polygons[t].at(i);
|
||||
for(unsigned int j=0; j<polygonSize; ++j)
|
||||
for(unsigned int j=0; j<filteredPolygons[oi].vertices.size(); ++j)
|
||||
{
|
||||
filteredCoordinates[oi*polygonSize + j] = textureMesh->tex_coordinates[t][i*polygonSize + j];
|
||||
UASSERT(polygonToCoord[i] < textureMesh->tex_coordinates[t].size());
|
||||
filteredCoordinates[ci] = textureMesh->tex_coordinates[t][polygonToCoord[i]+j];
|
||||
++ci;
|
||||
}
|
||||
++oi;
|
||||
}
|
||||
++allPolygonsIndex;
|
||||
}
|
||||
filteredPolygons.resize(oi);
|
||||
filteredCoordinates.resize(oi*polygonSize);
|
||||
filteredCoordinates.resize(ci);
|
||||
textureMesh->tex_polygons[t] = filteredPolygons;
|
||||
textureMesh->tex_coordinates[t] = filteredCoordinates;
|
||||
}
|
||||
@@ -1734,48 +1802,6 @@ bool ExportCloudsDialog::getExportedClouds(
|
||||
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
|
||||
textureMesh->tex_coordinates.size())
|
||||
{
|
||||
UDEBUG("");
|
||||
// for each texture
|
||||
for(unsigned int t=0; t<textureMesh->tex_coordinates.size(); ++t)
|
||||
{
|
||||
// When not saving to file, tex_coordinates should be linked to points, not polygon vertices
|
||||
int nPoints = textureMesh->cloud.data.size()/textureMesh->cloud.point_step;
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
|
||||
std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > tmpCoordinates = textureMesh->tex_coordinates[t];
|
||||
#else
|
||||
std::vector<Eigen::Vector2f> tmpCoordinates = textureMesh->tex_coordinates[t];
|
||||
#endif
|
||||
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();
|
||||
UASSERT(textureMesh->tex_polygons[t].size() == tmpCoordinates.size()/polygonSize);
|
||||
for(unsigned int i=0; i<textureMesh->tex_polygons[t].size(); ++i)
|
||||
{
|
||||
const pcl::Vertices & vertices = textureMesh->tex_polygons[t][i];
|
||||
UASSERT(polygonSize == (int)vertices.vertices.size());
|
||||
for(int j=0; j<polygonSize; ++j)
|
||||
{
|
||||
//uv
|
||||
UASSERT((int)vertices.vertices[j] < nPoints);
|
||||
UASSERT(i*polygonSize+j < tmpCoordinates.size());
|
||||
textureMesh->tex_coordinates[t][vertices.vertices[j]] = tmpCoordinates[i*polygonSize+j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textureMeshes.insert(std::make_pair(iter->first, textureMesh));
|
||||
@@ -2330,6 +2356,112 @@ void ExportCloudsDialog::saveMeshes(
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat ExportCloudsDialog::mergeTextures(pcl::TextureMesh & mesh, const QMap<int, Signature> & cachedSignatures) const
|
||||
{
|
||||
//get texture size, if disabled use default 1024
|
||||
int textureSize = 1024;
|
||||
if(_ui->comboBox_meshingTextureSize->currentIndex() > 0)
|
||||
{
|
||||
textureSize = 128 << _ui->comboBox_meshingTextureSize->currentIndex(); // start at 256
|
||||
}
|
||||
UDEBUG("textureSize = %d", textureSize);
|
||||
cv::Mat globalTexture;
|
||||
if(mesh.tex_materials.size() > 1)
|
||||
{
|
||||
std::vector<int> textures(mesh.tex_materials.size(), -1);
|
||||
cv::Size imageSize;
|
||||
int imageType=CV_8UC1;
|
||||
UDEBUG("");
|
||||
bool mergeTextures = true;
|
||||
for(unsigned int i=0; i<mesh.tex_materials.size(); ++i)
|
||||
{
|
||||
if(!mesh.tex_materials[i].tex_file.empty() &&
|
||||
mesh.tex_polygons[i].size() &&
|
||||
uIsInteger(mesh.tex_materials[i].tex_file, false))
|
||||
{
|
||||
int textureId = uStr2Int(mesh.tex_materials[i].tex_file);
|
||||
textures[i] = textureId;
|
||||
|
||||
QMap<int, Signature>::const_iterator iter = cachedSignatures.find(textureId);
|
||||
UASSERT(iter!=cachedSignatures.end() && !iter->sensorData().imageCompressed().empty());
|
||||
|
||||
cv::Size tmpImageSize;
|
||||
if(iter->sensorData().cameraModels().size()==1 &&
|
||||
iter->sensorData().cameraModels()[0].imageHeight()>0 &&
|
||||
iter->sensorData().cameraModels()[0].imageWidth()>0)
|
||||
{
|
||||
tmpImageSize = iter->sensorData().cameraModels()[0].imageSize();
|
||||
if(imageSize.height == 0 && imageSize.width == 0)
|
||||
{
|
||||
// just for the first image, get the type, assuming all others have the same type
|
||||
cv::Mat image;
|
||||
iter->sensorData().uncompressDataConst(&image, 0);
|
||||
UASSERT(!image.empty());
|
||||
imageType = image.type();
|
||||
}
|
||||
}
|
||||
else // backward compatibility for image size not set in CameraModel
|
||||
{
|
||||
cv::Mat image;
|
||||
iter->sensorData().uncompressDataConst(&image, 0);
|
||||
UASSERT(!image.empty());
|
||||
tmpImageSize = image.size();
|
||||
}
|
||||
if(imageSize.width>0 && imageSize.height>0 && imageSize.width != tmpImageSize.width)
|
||||
{
|
||||
UWARN("All images should have the same dimensions to merge the textures!");
|
||||
mergeTextures = false;
|
||||
break;
|
||||
}
|
||||
imageSize = tmpImageSize;
|
||||
}
|
||||
}
|
||||
if(mergeTextures && textures.size() && imageSize.height>0 && imageSize.width>0)
|
||||
{
|
||||
float scale = 0.0f;
|
||||
UDEBUG("");
|
||||
util3d::concatenateTextureMaterials(mesh, imageSize, textureSize, scale);
|
||||
if(scale && mesh.tex_materials.size()==1)
|
||||
{
|
||||
int cols = float(textureSize)/(scale*imageSize.width);
|
||||
|
||||
globalTexture = cv::Mat(textureSize, textureSize, imageType, cv::Scalar::all(255));
|
||||
|
||||
// make a blank texture
|
||||
cv::Mat emptyImage(int(imageSize.height*scale), int(imageSize.width*scale), imageType, cv::Scalar::all(255));
|
||||
for(int i=0; i<(int)textures.size(); ++i)
|
||||
{
|
||||
int u = i%cols * emptyImage.cols;
|
||||
int v = i/cols * emptyImage.rows;
|
||||
UASSERT(u < textureSize-emptyImage.cols);
|
||||
UASSERT(v < textureSize-emptyImage.rows);
|
||||
if(textures[i]>=0)
|
||||
{
|
||||
QMap<int, Signature>::const_iterator iter = cachedSignatures.find(textures[i]);
|
||||
UASSERT(iter!=cachedSignatures.end() && !iter->sensorData().imageCompressed().empty());
|
||||
cv::Mat image;
|
||||
iter->sensorData().uncompressDataConst(&image, 0);
|
||||
UASSERT(!image.empty());
|
||||
cv::Mat resizedImage;
|
||||
cv::resize(image, resizedImage, emptyImage.size(), 0.0f, 0.0f, cv::INTER_AREA);
|
||||
if(_ui->groupBox_gain->isChecked() && _compensator && _compensator->getIndex(textures[i]) >= 0)
|
||||
{
|
||||
_compensator->apply(textures[i], resizedImage);
|
||||
}
|
||||
UASSERT(resizedImage.type() == globalTexture.type());
|
||||
resizedImage.copyTo(globalTexture(cv::Rect(u, v, resizedImage.cols, resizedImage.rows)));
|
||||
}
|
||||
else
|
||||
{
|
||||
emptyImage.copyTo(globalTexture(cv::Rect(u, v, emptyImage.cols, emptyImage.rows)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return globalTexture;
|
||||
}
|
||||
|
||||
void ExportCloudsDialog::saveTextureMeshes(
|
||||
const QString & workingDirectory,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -2355,16 +2487,35 @@ void ExportCloudsDialog::saveTextureMeshes(
|
||||
}
|
||||
|
||||
pcl::TextureMesh::Ptr mesh = meshes.begin()->second;
|
||||
removeDirRecursively(QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName());
|
||||
QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName());
|
||||
|
||||
cv::Mat globalTexture;
|
||||
bool texturesMerged = _ui->comboBox_meshingTextureSize->isEnabled() && _ui->comboBox_meshingTextureSize->currentIndex() > 0;
|
||||
if(texturesMerged && mesh->tex_materials.size()>1)
|
||||
{
|
||||
globalTexture = mergeTextures(*mesh, cachedSignatures);
|
||||
}
|
||||
bool singleTexture = mesh->tex_materials.size() == 1;
|
||||
if(!singleTexture)
|
||||
{
|
||||
removeDirRecursively(QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName());
|
||||
QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName());
|
||||
}
|
||||
cv::Size imageSize;
|
||||
for(unsigned int i=0; i<mesh->tex_materials.size(); ++i)
|
||||
{
|
||||
if(!mesh->tex_materials[i].tex_file.empty())
|
||||
{
|
||||
// absolute path
|
||||
QString fullPath = QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
|
||||
QString fullPath;
|
||||
if(singleTexture)
|
||||
{
|
||||
mesh->tex_materials[i].tex_file = QFileInfo(path).baseName().toStdString();
|
||||
fullPath = QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
}
|
||||
else
|
||||
{
|
||||
fullPath = QFileInfo(path).absoluteDir().absolutePath()+QDir::separator()+QFileInfo(path).baseName()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
}
|
||||
if(!QFileInfo(fullPath).exists())
|
||||
{
|
||||
if(uIsInteger(mesh->tex_materials[i].tex_file, false))
|
||||
@@ -2393,13 +2544,29 @@ void ExportCloudsDialog::saveTextureMeshes(
|
||||
cv::Mat image = cv::Mat::ones(imageSize, CV_8UC1)*255;
|
||||
cv::imwrite(fullPath.toStdString(), image);
|
||||
}
|
||||
else if(!globalTexture.empty())
|
||||
{
|
||||
if(!cv::imwrite(fullPath.toStdString(), globalTexture))
|
||||
{
|
||||
_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
|
||||
{
|
||||
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())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
if(singleTexture)
|
||||
{
|
||||
mesh->tex_materials[i].tex_file=(QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh->tex_materials[i].tex_file=(QFileInfo(path).baseName()+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2439,50 +2606,82 @@ void ExportCloudsDialog::saveTextureMeshes(
|
||||
if(iter->second->tex_materials.size())
|
||||
{
|
||||
pcl::TextureMesh::Ptr mesh = iter->second;
|
||||
removeDirRecursively(path+QDir::separator()+currentPrefix);
|
||||
QDir(path).mkdir(currentPrefix);
|
||||
cv::Mat globalTexture;
|
||||
bool texturesMerged = _ui->comboBox_meshingTextureSize->isEnabled() && _ui->comboBox_meshingTextureSize->currentIndex() > 0;
|
||||
if(texturesMerged && mesh->tex_materials.size()>1)
|
||||
{
|
||||
globalTexture = mergeTextures(*mesh, cachedSignatures);
|
||||
}
|
||||
bool singleTexture = mesh->tex_materials.size() == 1;
|
||||
if(!singleTexture)
|
||||
{
|
||||
removeDirRecursively(path+QDir::separator()+currentPrefix);
|
||||
QDir(path).mkdir(currentPrefix);
|
||||
}
|
||||
cv::Size imageSize;
|
||||
for(unsigned int i=0;i<mesh->tex_materials.size(); ++i)
|
||||
{
|
||||
if(!mesh->tex_materials[i].tex_file.empty())
|
||||
{
|
||||
// absolute path
|
||||
QString fullPath = path+QDir::separator()+currentPrefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
if(!QFileInfo(fullPath).exists())
|
||||
QString fullPath;
|
||||
if(singleTexture)
|
||||
{
|
||||
if(uIsInteger(mesh->tex_materials[i].tex_file, false))
|
||||
mesh->tex_materials[i].tex_file = uNumber2Str(iter->first);
|
||||
fullPath = path+QDir::separator()+prefix + QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText();
|
||||
}
|
||||
else
|
||||
{
|
||||
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))
|
||||
{
|
||||
int textureId = uStr2Int(mesh->tex_materials[i].tex_file);
|
||||
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)
|
||||
{
|
||||
int textureId = uStr2Int(mesh->tex_materials[i].tex_file);
|
||||
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);
|
||||
}
|
||||
_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)
|
||||
if(!cv::imwrite(fullPath.toStdString(), image))
|
||||
{
|
||||
// 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());
|
||||
_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 if(!globalTexture.empty())
|
||||
{
|
||||
if(!cv::imwrite(fullPath.toStdString(), globalTexture))
|
||||
{
|
||||
_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
|
||||
{
|
||||
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())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
if(singleTexture)
|
||||
{
|
||||
mesh->tex_materials[i].tex_file=(prefix+ QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh->tex_materials[i].tex_file=(currentPrefix+QDir::separator()+QString(mesh->tex_materials[i].tex_file.c_str())+_ui->comboBox_meshingTextureFormat->currentText()).toStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
pcl::PointCloud<pcl::PointNormal>::Ptr tmp(new pcl::PointCloud<pcl::PointNormal>);
|
||||
|
||||
@@ -88,11 +88,8 @@ public slots:
|
||||
|
||||
private slots:
|
||||
void updateReconstructionFlavor();
|
||||
void updateDenseReconstruction();
|
||||
void selectDistortionModel();
|
||||
void updateMLSGrpVisibility();
|
||||
void updateTexturingAvailability();
|
||||
void updatePoissonOutputPolygonsAvailability();
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
@@ -115,11 +112,11 @@ private:
|
||||
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 saveTextureMeshes(const QString & workingDirectory, const std::map<int, Transform> & poses, std::map<int, pcl::TextureMesh::Ptr> & textureMeshes, const QMap<int, Signature> & cachedSignatures);
|
||||
cv::Mat mergeTextures(pcl::TextureMesh & mesh, const QMap<int, Signature> & cachedSignatures) const;
|
||||
|
||||
void setSaveButton();
|
||||
void setOkButton();
|
||||
void enableRegeneration(bool enabled);
|
||||
void updateTexturingAvailability(bool isExporting);
|
||||
|
||||
private:
|
||||
Ui_ExportCloudsDialog * _ui;
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-1430</y>
|
||||
<y>-1356</y>
|
||||
<width>773</width>
|
||||
<height>2774</height>
|
||||
<height>2832</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
@@ -1105,34 +1105,17 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_10" columnstretch="0,1">
|
||||
<item row="6" column="0">
|
||||
<widget class="QSpinBox" name="spinBox_mesh_minClusterSize">
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_meshDecimation">
|
||||
<property name="text">
|
||||
<string>Mesh quadric decimation factor (0=no decimation). Used to reduce the number of polygons. Higher the factor, lower the output resolution (less polygons). Can be used only when dense reconstruction flavor is selected.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_denseReconstruction">
|
||||
<property name="text">
|
||||
<string>Surface reconstruction approach. Can be used only when dense reconstruction flavor is selected. Poisson is available only when clouds are assembled.</string>
|
||||
<string>Surface reconstruction approach. Can be used only when dense reconstruction flavor is selected. Poisson is available when clouds are assembled.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_meshDecimation_2">
|
||||
<property name="text">
|
||||
<string>Radius used to transfer color from original cloud to resampled reconstructed surface (e.g., Poisson or mesh decimation). 0 means disabled.</string>
|
||||
@@ -1142,27 +1125,83 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_textureMapping">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_textureMapping">
|
||||
<property name="text">
|
||||
<string>Texture mapping. Images of the cameras will be projected on the mesh(es). Output is a *.obj format. Only available on Export or when clouds are not assembled.</string>
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_meshDecimationFactor">
|
||||
<property name="decimals">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>0.990000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_meshClean">
|
||||
<property name="text">
|
||||
<string>Clean mesh from polygons without color or texture.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_meshingTextureFormat">
|
||||
<property name="text">
|
||||
<string>Min polygon cluster size</string>
|
||||
<string>Texture format.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_textureMapping">
|
||||
<item row="7" column="0">
|
||||
<widget class="QSpinBox" name="spinBox_mesh_minClusterSize">
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label_meshDecimation">
|
||||
<property name="text">
|
||||
<string/>
|
||||
<string>Mesh quadric decimation factor (0=no decimation). Used to reduce the number of polygons. Higher the factor, lower the output resolution (less polygons). Can be used when dense reconstruction flavor is selected.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_textureMapping">
|
||||
<property name="text">
|
||||
<string>Texture mapping. Images of the cameras will be projected on the mesh(es). Output is a *.obj format. Available on Export or when clouds are not assembled.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>Minimum polygon cluster size.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1180,7 +1219,7 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_transferColorRadius">
|
||||
<property name="suffix">
|
||||
<string> m</string>
|
||||
@@ -1202,52 +1241,13 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox_meshDecimationFactor">
|
||||
<property name="decimals">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>0.990000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="label_meshDecimation_3">
|
||||
<property name="text">
|
||||
<string>Clean mesh from polygons without color or texture.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_cleanMesh">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_meshingTextureFormat">
|
||||
<property name="text">
|
||||
<string>Texture format.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QComboBox" name="comboBox_meshingTextureFormat">
|
||||
<item>
|
||||
@@ -1262,6 +1262,65 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_meshingTextureSize">
|
||||
<property name="text">
|
||||
<string>Output texture size. If not set or when clouds are not assembled, all textures are saved separately. Warning: values higher than 2048 may not be compatible with all GPUs.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QComboBox" name="comboBox_meshingTextureSize">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>256x256</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>512x512</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1024x1024</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2048x2048</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4096x4096</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8192x8192</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16384x16384</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>32768x32768</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -1487,7 +1546,7 @@ Guidelines: 4 times the voxel size, 0.025 for voxel=0.</string>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_outputPolygons">
|
||||
<property name="text">
|
||||
<string>Output polygons flag. Enabling this flag tells the reconstructor to output a polygon mesh (rather than triangulating of the results of Marching Cubes). Disabled when exporting to file or if mesh decimation is set.</string>
|
||||
<string>Output polygons flag. Enabling this flag tells the reconstructor to output a polygon mesh (rather than triangulating of the results of Marching Cubes). Disabled when exporting to file, if mesh decimation is set or when texturing.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
||||
Reference in New Issue
Block a user