mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-03 10:00:23 +08:00
GUI: Added export mesh with textures, refactored Export dialog (more MLS options)
This commit is contained in:
@@ -98,7 +98,7 @@ public:
|
|||||||
|
|
||||||
const cv::Size & imageSize() const {return imageSize_;}
|
const cv::Size & imageSize() const {return imageSize_;}
|
||||||
int imageWidth() const {return imageSize_.width;}
|
int imageWidth() const {return imageSize_.width;}
|
||||||
int imageWeight() const {return imageSize_.height;}
|
int imageHeight() const {return imageSize_.height;}
|
||||||
|
|
||||||
bool load(const std::string & directory, const std::string & cameraName);
|
bool load(const std::string & directory, const std::string & cameraName);
|
||||||
bool save(const std::string & directory) const;
|
bool save(const std::string & directory) const;
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <pcl/PolygonMesh.h>
|
#include <pcl/PolygonMesh.h>
|
||||||
#include <pcl/point_cloud.h>
|
#include <pcl/point_cloud.h>
|
||||||
#include <pcl/point_types.h>
|
#include <pcl/point_types.h>
|
||||||
|
#include <pcl/TextureMesh.h>
|
||||||
|
#include <rtabmap/core/Transform.h>
|
||||||
|
#include <rtabmap/core/CameraModel.h>
|
||||||
|
|
||||||
namespace rtabmap
|
namespace rtabmap
|
||||||
{
|
{
|
||||||
@@ -50,6 +53,13 @@ pcl::PolygonMesh::Ptr RTABMAP_EXP createMesh(
|
|||||||
float gp3MaximumAngle = 2*M_PI/3,
|
float gp3MaximumAngle = 2*M_PI/3,
|
||||||
bool gp3NormalConsistency = true);
|
bool gp3NormalConsistency = true);
|
||||||
|
|
||||||
|
pcl::TextureMesh::Ptr RTABMAP_EXP createTextureMesh(
|
||||||
|
const pcl::PolygonMesh::Ptr & mesh,
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
const std::map<int, CameraModel> & cameraModels,
|
||||||
|
const std::map<int, cv::Mat> & images,
|
||||||
|
const std::string & tmpDirectory = ".");
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr RTABMAP_EXP computeNormals(
|
pcl::PointCloud<pcl::PointNormal>::Ptr RTABMAP_EXP computeNormals(
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
|
||||||
int normalKSearch = 20);
|
int normalKSearch = 20);
|
||||||
@@ -58,15 +68,21 @@ pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr RTABMAP_EXP computeNormals(
|
|||||||
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
||||||
int normalKSearch = 20);
|
int normalKSearch = 20);
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr RTABMAP_EXP computeNormalsSmoothed(
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr RTABMAP_EXP mls(
|
||||||
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
||||||
float smoothingSearchRadius = 0.025,
|
float searchRadius = 0.0f,
|
||||||
bool smoothingPolynomialFit = true,
|
int polygonialOrder = 2,
|
||||||
float voxelSize = 0.0f);
|
int upsamplingMethod = 0, // NONE, DISTINCT_CLOUD, SAMPLE_LOCAL_PLANE, RANDOM_UNIFORM_DENSITY, VOXEL_GRID_DILATION
|
||||||
|
float upsamplingRadius = 0.0f, // SAMPLE_LOCAL_PLANE
|
||||||
|
float upsamplingStep = 0.0f, // SAMPLE_LOCAL_PLANE
|
||||||
|
int pointDensity = 0, // RANDOM_UNIFORM_DENSITY
|
||||||
|
float dilationVoxelSize = 1.0f, // VOXEL_GRID_DILATION
|
||||||
|
int dilationIterations = 0); // VOXEL_GRID_DILATION
|
||||||
|
|
||||||
void RTABMAP_EXP adjustNormalsToViewPoints(
|
void RTABMAP_EXP adjustNormalsToViewPoints(
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & viewpoints,
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & viewpoints,
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal> & cloud);
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
||||||
|
int k = 0); // optional: recompute normal with k neighbors (min k=3)
|
||||||
|
|
||||||
template<typename pointT>
|
template<typename pointT>
|
||||||
std::vector<pcl::Vertices> normalizePolygonsSide(
|
std::vector<pcl::Vertices> normalizePolygonsSide(
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ pcl::PointCloud<pcl::PointXYZ>::Ptr RTABMAP_EXP transformPointCloud(
|
|||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP transformPointCloud(
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr RTABMAP_EXP transformPointCloud(
|
||||||
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
||||||
const Transform & transform);
|
const Transform & transform);
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr RTABMAP_EXP transformPointCloud(
|
||||||
|
const pcl::PointCloud<pcl::PointNormal>::Ptr & cloud,
|
||||||
|
const Transform & transform);
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr RTABMAP_EXP transformPointCloud(
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr RTABMAP_EXP transformPointCloud(
|
||||||
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
||||||
const Transform & transform);
|
const Transform & transform);
|
||||||
|
|||||||
@@ -28,10 +28,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include "rtabmap/core/util3d_surface.h"
|
#include "rtabmap/core/util3d_surface.h"
|
||||||
#include "rtabmap/core/util3d_filtering.h"
|
#include "rtabmap/core/util3d_filtering.h"
|
||||||
#include "rtabmap/utilite/ULogger.h"
|
#include "rtabmap/utilite/ULogger.h"
|
||||||
|
#include "rtabmap/utilite/UDirectory.h"
|
||||||
|
#include "rtabmap/utilite/UConversion.h"
|
||||||
#include <pcl/search/kdtree.h>
|
#include <pcl/search/kdtree.h>
|
||||||
#include <pcl/surface/gp3.h>
|
#include <pcl/surface/gp3.h>
|
||||||
#include <pcl/features/normal_3d_omp.h>
|
#include <pcl/features/normal_3d_omp.h>
|
||||||
|
#include <pcl/features/normal_3d.h>
|
||||||
#include <pcl/surface/mls.h>
|
#include <pcl/surface/mls.h>
|
||||||
|
#include <pcl/surface/texture_mapping.h>
|
||||||
|
|
||||||
|
#include <pcl/visualization/pcl_visualizer.h>
|
||||||
|
|
||||||
namespace rtabmap
|
namespace rtabmap
|
||||||
{
|
{
|
||||||
@@ -82,6 +88,147 @@ pcl::PolygonMesh::Ptr createMesh(
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcl::texture_mapping::CameraVector createTextureCameras(
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
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(UDirectory::exists(tmpDirectory));
|
||||||
|
pcl::texture_mapping::CameraVector cameras(poses.size());
|
||||||
|
std::map<int, Transform>::const_iterator poseIter=poses.begin();
|
||||||
|
std::map<int, CameraModel>::const_iterator modelIter=cameraModels.begin();
|
||||||
|
std::map<int, cv::Mat>::const_iterator imageIter=images.begin();
|
||||||
|
int oi=0;
|
||||||
|
for(; poseIter!=poses.end(); ++poseIter, ++modelIter, ++imageIter)
|
||||||
|
{
|
||||||
|
UASSERT(poseIter->first == modelIter->first);
|
||||||
|
UASSERT(poseIter->first == imageIter->first);
|
||||||
|
pcl::TextureMapping<pcl::PointXYZ>::Camera cam;
|
||||||
|
|
||||||
|
// transform into optical referential
|
||||||
|
Transform rotation(0,-1,0,0,
|
||||||
|
0,0,-1,0,
|
||||||
|
1,0,0,0);
|
||||||
|
|
||||||
|
Transform t = poseIter->second*rotation.inverse();
|
||||||
|
|
||||||
|
cam.pose = t.toEigen3f();
|
||||||
|
|
||||||
|
UASSERT(modelIter->second.fx()>0 && imageIter->second.rows>0 && imageIter->second.cols>0);
|
||||||
|
cam.focal_length=modelIter->second.fx();
|
||||||
|
cam.height=imageIter->second.rows;
|
||||||
|
cam.width=imageIter->second.cols;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return cameras;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcl::TextureMesh::Ptr createTextureMesh(
|
||||||
|
const pcl::PolygonMesh::Ptr & mesh,
|
||||||
|
const std::map<int, Transform> & poses,
|
||||||
|
const std::map<int, CameraModel> & cameraModels,
|
||||||
|
const std::map<int, cv::Mat> & images,
|
||||||
|
const std::string & tmpDirectory)
|
||||||
|
{
|
||||||
|
// Original from pcl/gpu/kinfu_large_scale/tools/standalone_texture_mapping.cpp:
|
||||||
|
// Author: Raphael Favier, Technical University Eindhoven, (r.mysurname <aT> tue.nl)
|
||||||
|
|
||||||
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
|
||||||
|
pcl::fromPCLPointCloud2(mesh->cloud, *cloud);
|
||||||
|
|
||||||
|
// Create the texturemesh object that will contain our UV-mapped mesh
|
||||||
|
pcl::TextureMesh::Ptr textureMesh(new pcl::TextureMesh);
|
||||||
|
textureMesh->cloud = mesh->cloud;
|
||||||
|
std::vector< pcl::Vertices> polygons;
|
||||||
|
|
||||||
|
// push faces into the texturemesh object
|
||||||
|
polygons.resize (mesh->polygons.size ());
|
||||||
|
for(size_t i =0; i < mesh->polygons.size (); ++i)
|
||||||
|
{
|
||||||
|
polygons[i] = mesh->polygons[i];
|
||||||
|
}
|
||||||
|
textureMesh->tex_polygons.push_back(polygons);
|
||||||
|
|
||||||
|
// create cameras
|
||||||
|
pcl::texture_mapping::CameraVector cameras = createTextureCameras(
|
||||||
|
poses,
|
||||||
|
cameraModels,
|
||||||
|
images,
|
||||||
|
tmpDirectory);
|
||||||
|
|
||||||
|
// Create materials for each texture (and one extra for occluded faces)
|
||||||
|
textureMesh->tex_materials.resize (cameras.size () + 1);
|
||||||
|
for(int i = 0 ; i <= cameras.size() ; ++i)
|
||||||
|
{
|
||||||
|
pcl::TexMaterial mesh_material;
|
||||||
|
mesh_material.tex_Ka.r = 0.2f;
|
||||||
|
mesh_material.tex_Ka.g = 0.2f;
|
||||||
|
mesh_material.tex_Ka.b = 0.2f;
|
||||||
|
|
||||||
|
mesh_material.tex_Kd.r = 0.8f;
|
||||||
|
mesh_material.tex_Kd.g = 0.8f;
|
||||||
|
mesh_material.tex_Kd.b = 0.8f;
|
||||||
|
|
||||||
|
mesh_material.tex_Ks.r = 1.0f;
|
||||||
|
mesh_material.tex_Ks.g = 1.0f;
|
||||||
|
mesh_material.tex_Ks.b = 1.0f;
|
||||||
|
|
||||||
|
mesh_material.tex_d = 1.0f;
|
||||||
|
mesh_material.tex_Ns = 75.0f;
|
||||||
|
mesh_material.tex_illum = 2;
|
||||||
|
|
||||||
|
std::stringstream tex_name;
|
||||||
|
tex_name << "material_" << i;
|
||||||
|
tex_name >> mesh_material.tex_name;
|
||||||
|
|
||||||
|
if(i < cameras.size ())
|
||||||
|
{
|
||||||
|
mesh_material.tex_file = cameras[i].texture_file;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mesh_material.tex_file = tmpDirectory+UDirectory::separator()+"occluded.png";
|
||||||
|
cv::Mat emptyImage;
|
||||||
|
if(i>0)
|
||||||
|
{
|
||||||
|
emptyImage = cv::Mat::zeros(cameras[i-1].height,cameras[i-1].width, CV_8UC1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emptyImage = cv::Mat::zeros(480, 640, CV_8UC1);
|
||||||
|
}
|
||||||
|
cv::imwrite(mesh_material.tex_file, emptyImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
textureMesh->tex_materials[i] = mesh_material;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort faces
|
||||||
|
pcl::TextureMapping<pcl::PointXYZ> tm; // TextureMapping object that will perform the sort
|
||||||
|
tm.textureMeshwithMultipleCameras(*textureMesh, cameras);
|
||||||
|
|
||||||
|
// compute normals for the mesh
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr cloudWithNormals = computeNormals(cloud, 20);
|
||||||
|
|
||||||
|
pcl::toPCLPointCloud2 (*cloudWithNormals, textureMesh->cloud);
|
||||||
|
|
||||||
|
return textureMesh;
|
||||||
|
}
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointNormal>::Ptr computeNormals(
|
pcl::PointCloud<pcl::PointNormal>::Ptr computeNormals(
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & cloud,
|
||||||
int normalKSearch)
|
int normalKSearch)
|
||||||
@@ -130,33 +277,48 @@ pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr computeNormals(
|
|||||||
return cloud_with_normals;
|
return cloud_with_normals;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr computeNormalsSmoothed(
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr mls(
|
||||||
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGB>::Ptr & cloud,
|
||||||
float smoothingSearchRadius,
|
float searchRadius,
|
||||||
bool smoothingPolynomialFit,
|
int polygonialOrder,
|
||||||
float voxelSize)
|
int upsamplingMethod, // NONE, DISTINCT_CLOUD, SAMPLE_LOCAL_PLANE, RANDOM_UNIFORM_DENSITY, VOXEL_GRID_DILATION
|
||||||
|
float upsamplingRadius, // SAMPLE_LOCAL_PLANE
|
||||||
|
float upsamplingStep, // SAMPLE_LOCAL_PLANE
|
||||||
|
int pointDensity, // RANDOM_UNIFORM_DENSITY
|
||||||
|
float dilationVoxelSize, // VOXEL_GRID_DILATION
|
||||||
|
int dilationIterations) // VOXEL_GRID_DILATION
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
||||||
pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGB>);
|
pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGB>);
|
||||||
tree->setInputCloud (cloud);
|
tree->setInputCloud (cloud);
|
||||||
|
|
||||||
// Init object (second point type is for the normals, even if unused)
|
// Init object (second point type is for the normals)
|
||||||
pcl::MovingLeastSquares<pcl::PointXYZRGB, pcl::PointXYZRGBNormal> mls;
|
pcl::MovingLeastSquares<pcl::PointXYZRGB, pcl::PointXYZRGBNormal> mls;
|
||||||
|
|
||||||
mls.setComputeNormals (true);
|
|
||||||
|
|
||||||
// Set parameters
|
// Set parameters
|
||||||
mls.setInputCloud (cloud);
|
mls.setComputeNormals (true);
|
||||||
mls.setPolynomialFit (smoothingPolynomialFit);
|
if(polygonialOrder > 0)
|
||||||
mls.setSearchMethod (tree);
|
|
||||||
mls.setSearchRadius (smoothingSearchRadius);
|
|
||||||
if(voxelSize > 0.0f)
|
|
||||||
{
|
{
|
||||||
mls.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZRGB, pcl::PointXYZRGBNormal>::VOXEL_GRID_DILATION);
|
mls.setPolynomialFit (true);
|
||||||
mls.setDilationVoxelSize(voxelSize);
|
mls.setPolynomialOrder(polygonialOrder);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mls.setPolynomialFit (false);
|
||||||
|
}
|
||||||
|
UASSERT(upsamplingMethod >= mls.NONE &&
|
||||||
|
upsamplingMethod <= mls.VOXEL_GRID_DILATION);
|
||||||
|
mls.setUpsamplingMethod((pcl::MovingLeastSquares<pcl::PointXYZRGB, pcl::PointXYZRGBNormal>::UpsamplingMethod)upsamplingMethod);
|
||||||
|
mls.setSearchRadius(searchRadius);
|
||||||
|
mls.setUpsamplingRadius(upsamplingRadius);
|
||||||
|
mls.setUpsamplingStepSize(upsamplingStep);
|
||||||
|
mls.setPointDensity(pointDensity);
|
||||||
|
mls.setDilationVoxelSize(dilationVoxelSize);
|
||||||
|
mls.setDilationIterations(dilationIterations);
|
||||||
|
|
||||||
// Reconstruct
|
// Reconstruct
|
||||||
|
mls.setInputCloud (cloud);
|
||||||
|
mls.setSearchMethod (tree);
|
||||||
mls.process (*cloud_with_normals);
|
mls.process (*cloud_with_normals);
|
||||||
|
|
||||||
return cloud_with_normals;
|
return cloud_with_normals;
|
||||||
@@ -164,30 +326,56 @@ pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr computeNormalsSmoothed(
|
|||||||
|
|
||||||
void adjustNormalsToViewPoints(
|
void adjustNormalsToViewPoints(
|
||||||
const pcl::PointCloud<pcl::PointXYZ>::Ptr & viewpoints,
|
const pcl::PointCloud<pcl::PointXYZ>::Ptr & viewpoints,
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal> & cloud)
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
||||||
|
int k)
|
||||||
{
|
{
|
||||||
if(viewpoints->size() && cloud.size())
|
// FIXME: maybe better to project points in camera planes to know if they are visible from a specified viewpoint
|
||||||
|
if(viewpoints->size() && cloud->size())
|
||||||
{
|
{
|
||||||
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
|
pcl::search::KdTree<pcl::PointXYZ>::Ptr viewpointsTree (new pcl::search::KdTree<pcl::PointXYZ>);
|
||||||
tree->setInputCloud (viewpoints);
|
viewpointsTree->setInputCloud (viewpoints);
|
||||||
|
|
||||||
for(unsigned int i=0; i<cloud.size(); ++i)
|
pcl::search::KdTree<pcl::PointXYZRGBNormal>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGBNormal>);
|
||||||
|
tree->setInputCloud (cloud);
|
||||||
|
|
||||||
|
for(unsigned int i=0; i<cloud->size(); ++i)
|
||||||
{
|
{
|
||||||
std::vector<int> indices;
|
std::vector<int> indices;
|
||||||
std::vector<float> dist;
|
std::vector<float> dist;
|
||||||
tree->nearestKSearch(pcl::PointXYZ(cloud.points[i].x, cloud.points[i].y, cloud.points[i].z), 1, indices, dist);
|
viewpointsTree->nearestKSearch(pcl::PointXYZ(cloud->points[i].x, cloud->points[i].y, cloud->points[i].z), 1, indices, dist);
|
||||||
UASSERT(indices.size() == 1);
|
UASSERT(indices.size() == 1);
|
||||||
|
if(indices.size() && indices[0]>=0)
|
||||||
Eigen::Vector3f v = viewpoints->at(indices[0]).getVector3fMap() - cloud.points[i].getVector3fMap();
|
|
||||||
Eigen::Vector3f n(cloud.points[i].normal_x, cloud.points[i].normal_y, cloud.points[i].normal_z);
|
|
||||||
|
|
||||||
float result = v.dot(n);
|
|
||||||
if(result < 0)
|
|
||||||
{
|
{
|
||||||
//reverse normal
|
|
||||||
cloud.points[i].normal_x *= -1.0f;
|
Eigen::Vector3f v = viewpoints->at(indices[0]).getVector3fMap() - cloud->points[i].getVector3fMap();
|
||||||
cloud.points[i].normal_y *= -1.0f;
|
|
||||||
cloud.points[i].normal_z *= -1.0f;
|
//compute point normal
|
||||||
|
if(k >= 3)
|
||||||
|
{
|
||||||
|
tree->nearestKSearch(cloud->points[i], k, indices, dist);
|
||||||
|
if(indices.size() >= 3)
|
||||||
|
{
|
||||||
|
Eigen::Vector4f planeParameters;
|
||||||
|
float curvature;
|
||||||
|
pcl::computePointNormal(*cloud, indices, planeParameters, curvature);
|
||||||
|
|
||||||
|
//update normal
|
||||||
|
cloud->points[i].normal_x = planeParameters[0];
|
||||||
|
cloud->points[i].normal_y = planeParameters[1];
|
||||||
|
cloud->points[i].normal_z = planeParameters[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Vector3f n(cloud->points[i].normal_x, cloud->points[i].normal_y, cloud->points[i].normal_z);
|
||||||
|
|
||||||
|
float result = v.dot(n);
|
||||||
|
if(result < 0)
|
||||||
|
{
|
||||||
|
//reverse normal
|
||||||
|
cloud->points[i].normal_x *= -1.0f;
|
||||||
|
cloud->points[i].normal_y *= -1.0f;
|
||||||
|
cloud->points[i].normal_z *= -1.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,14 @@ pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformPointCloud(
|
|||||||
pcl::transformPointCloud(*cloud, *output, transform.toEigen4f());
|
pcl::transformPointCloud(*cloud, *output, transform.toEigen4f());
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr transformPointCloud(
|
||||||
|
const pcl::PointCloud<pcl::PointNormal>::Ptr & cloud,
|
||||||
|
const Transform & transform)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr output(new pcl::PointCloud<pcl::PointNormal>);
|
||||||
|
pcl::transformPointCloudWithNormals(*cloud, *output, transform.toEigen4f());
|
||||||
|
return output;
|
||||||
|
}
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr transformPointCloud(
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr transformPointCloud(
|
||||||
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr & cloud,
|
||||||
const Transform & transform)
|
const Transform & transform)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <pcl/point_types.h>
|
#include <pcl/point_types.h>
|
||||||
#include <pcl/point_cloud.h>
|
#include <pcl/point_cloud.h>
|
||||||
#include <pcl/PolygonMesh.h>
|
#include <pcl/PolygonMesh.h>
|
||||||
|
#include <pcl/TextureMesh.h>
|
||||||
#include "rtabmap/core/Transform.h"
|
#include "rtabmap/core/Transform.h"
|
||||||
#include <QtCore/QMap>
|
#include <QtCore/QMap>
|
||||||
#include <QtCore/QSet>
|
#include <QtCore/QSet>
|
||||||
@@ -145,6 +146,11 @@ public:
|
|||||||
const pcl::PolygonMesh::Ptr & mesh,
|
const pcl::PolygonMesh::Ptr & mesh,
|
||||||
const Transform & pose = Transform::getIdentity());
|
const Transform & pose = Transform::getIdentity());
|
||||||
|
|
||||||
|
bool addCloudTextureMesh(
|
||||||
|
const std::string & id,
|
||||||
|
const pcl::TextureMesh::Ptr & textureMesh,
|
||||||
|
const Transform & pose = Transform::getIdentity());
|
||||||
|
|
||||||
bool addOccupancyGridMap(
|
bool addOccupancyGridMap(
|
||||||
const cv::Mat & map8U,
|
const cv::Mat & map8U,
|
||||||
float resolution, // cell size
|
float resolution, // cell size
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <pcl/point_cloud.h>
|
#include <pcl/point_cloud.h>
|
||||||
#include <pcl/point_types.h>
|
#include <pcl/point_types.h>
|
||||||
#include <pcl/PolygonMesh.h>
|
#include <pcl/PolygonMesh.h>
|
||||||
|
#include <pcl/TextureMesh.h>
|
||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
class CameraThread;
|
class CameraThread;
|
||||||
@@ -226,20 +227,22 @@ private:
|
|||||||
void exportPoses(int format);
|
void exportPoses(int format);
|
||||||
QString captureScreen();
|
QString captureScreen();
|
||||||
|
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr > getClouds(
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > getClouds(
|
||||||
const std::map<int, Transform> & poses,
|
const std::map<int, Transform> & poses,
|
||||||
bool regenerateClouds,
|
bool regenerateClouds,
|
||||||
int regenerateDecimation,
|
int regenerateDecimation,
|
||||||
float regenerateVoxelSize,
|
float regenerateVoxelSize,
|
||||||
float regenerateMaxDepth,
|
float regenerateMaxDepth) const;
|
||||||
int normalKSearch,
|
|
||||||
bool mls,
|
|
||||||
float mlsRadius) const;
|
|
||||||
|
|
||||||
bool getExportedScans(std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans);
|
bool getExportedScans(std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr > & scans);
|
||||||
bool getExportedClouds(std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds, std::map<int, pcl::PolygonMesh::Ptr> & meshes, bool toSave);
|
bool getExportedClouds(
|
||||||
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds,
|
||||||
|
std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
||||||
|
std::map<int, pcl::TextureMesh::Ptr> & textureMeshes,
|
||||||
|
bool toSave);
|
||||||
void saveClouds(const std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds, bool binaryMode = true);
|
void saveClouds(const std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds, bool binaryMode = true);
|
||||||
void saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes, bool binaryMode = true);
|
void saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes, bool binaryMode = true);
|
||||||
|
void saveTextureMeshes(const std::map<int, pcl::TextureMesh::Ptr> & meshes);
|
||||||
void saveScans(const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr> & clouds, bool binaryMode = true);
|
void saveScans(const std::map<int, pcl::PointCloud<pcl::PointXYZ>::Ptr> & clouds, bool binaryMode = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -573,6 +573,24 @@ bool CloudViewer::addCloudMesh(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CloudViewer::addCloudTextureMesh(
|
||||||
|
const std::string & id,
|
||||||
|
const pcl::TextureMesh::Ptr & textureMesh,
|
||||||
|
const Transform & pose)
|
||||||
|
{
|
||||||
|
if(!_addedClouds.contains(id))
|
||||||
|
{
|
||||||
|
UDEBUG("Adding %s", id.c_str());
|
||||||
|
if(_visualizer->addTextureMesh(*textureMesh, id))
|
||||||
|
{
|
||||||
|
_visualizer->updatePointCloudPose(id, pose.toEigen3f());
|
||||||
|
_addedClouds.insert(id, pose);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CloudViewer::addOccupancyGridMap(
|
bool CloudViewer::addOccupancyGridMap(
|
||||||
const cv::Mat & map8U,
|
const cv::Mat & map8U,
|
||||||
float resolution, // cell size
|
float resolution, // cell size
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ DetailedProgressDialog::DetailedProgressDialog(QWidget *parent, Qt::WindowFlags
|
|||||||
hLayout->addWidget(_closeWhenDoneCheckBox);
|
hLayout->addWidget(_closeWhenDoneCheckBox);
|
||||||
hLayout->addWidget(_closeButton);
|
hLayout->addWidget(_closeButton);
|
||||||
this->setLayout(layout);
|
this->setLayout(layout);
|
||||||
|
|
||||||
|
this->setModal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
DetailedProgressDialog::~DetailedProgressDialog()
|
DetailedProgressDialog::~DetailedProgressDialog()
|
||||||
@@ -78,7 +80,10 @@ DetailedProgressDialog::~DetailedProgressDialog()
|
|||||||
|
|
||||||
void DetailedProgressDialog::setAutoClose(bool on, int delayedClosingTimeSec)
|
void DetailedProgressDialog::setAutoClose(bool on, int delayedClosingTimeSec)
|
||||||
{
|
{
|
||||||
_delayedClosingTime = delayedClosingTimeSec;
|
if(delayedClosingTimeSec >= 0)
|
||||||
|
{
|
||||||
|
_delayedClosingTime = delayedClosingTimeSec;
|
||||||
|
}
|
||||||
_closeWhenDoneCheckBox->setChecked(on);
|
_closeWhenDoneCheckBox->setChecked(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +151,6 @@ void DetailedProgressDialog::closeEvent(QCloseEvent *event)
|
|||||||
if(_progressBar->value() == _progressBar->maximum())
|
if(_progressBar->value() == _progressBar->maximum())
|
||||||
{
|
{
|
||||||
event->accept();
|
event->accept();
|
||||||
_closeWhenDoneCheckBox->setChecked(true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
void setValue(int value);
|
void setValue(int value);
|
||||||
int maximumSteps() const;
|
int maximumSteps() const;
|
||||||
void setMaximumSteps(int steps);
|
void setMaximumSteps(int steps);
|
||||||
void setAutoClose(bool on, int delayedClosingTimeMsec = 0);
|
void setAutoClose(bool on, int delayedClosingTimeMsec = -1);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void closeEvent(QCloseEvent * event);
|
virtual void closeEvent(QCloseEvent * event);
|
||||||
|
|||||||
@@ -40,19 +40,37 @@ ExportCloudsDialog::ExportCloudsDialog(QWidget *parent) :
|
|||||||
|
|
||||||
connect(_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults()));
|
connect(_ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults()));
|
||||||
|
|
||||||
connect(_ui->groupBox_assemble, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
restoreDefaults();
|
||||||
connect(_ui->doubleSpinBox_voxelSize_assembled, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
_ui->comboBox_upsamplingMethod->setItemData(1, 0, Qt::UserRole - 1); // disable DISTINCT_CLOUD
|
||||||
|
|
||||||
|
connect(_ui->checkBox_binary, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->spinBox_normalKSearch, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
|
||||||
connect(_ui->groupBox_regenerate, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
connect(_ui->groupBox_regenerate, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->spinBox_decimation, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
connect(_ui->spinBox_decimation, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
connect(_ui->doubleSpinBox_voxelSize, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->doubleSpinBox_maxDepth, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
connect(_ui->doubleSpinBox_maxDepth, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->checkBox_binary, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
|
||||||
|
connect(_ui->groupBox_assemble, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->doubleSpinBox_voxelSize_assembled, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
|
|
||||||
connect(_ui->groupBox_mls, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
connect(_ui->groupBox_mls, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->doubleSpinBox_mlsRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
connect(_ui->doubleSpinBox_mlsRadius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->spinBox_polygonialOrder, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->comboBox_upsamplingMethod, SIGNAL(currentIndexChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->doubleSpinBox_sampleStep, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->spinBox_randomPoints, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->doubleSpinBox_dilationVoxelSize, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->spinBox_dilationSteps, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
||||||
|
_ui->stackedWidget_upsampling->setCurrentIndex(_ui->comboBox_upsamplingMethod->currentIndex());
|
||||||
|
connect(_ui->comboBox_upsamplingMethod, SIGNAL(currentIndexChanged(int)), _ui->stackedWidget_upsampling, SLOT(setCurrentIndex(int)));
|
||||||
|
connect(_ui->comboBox_upsamplingMethod, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMLSGrpVisibility()));
|
||||||
|
updateMLSGrpVisibility();
|
||||||
|
|
||||||
connect(_ui->groupBox_gp3, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
connect(_ui->groupBox_gp3, SIGNAL(clicked(bool)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->spinBox_normalKSearch, SIGNAL(valueChanged(int)), this, SIGNAL(configChanged()));
|
|
||||||
connect(_ui->doubleSpinBox_gp3Radius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
connect(_ui->doubleSpinBox_gp3Radius, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
connect(_ui->doubleSpinBox_gp3Mu, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
connect(_ui->doubleSpinBox_gp3Mu, SIGNAL(valueChanged(double)), this, SIGNAL(configChanged()));
|
||||||
|
connect(_ui->checkBox_textureMapping, SIGNAL(stateChanged(int)), this, SIGNAL(configChanged()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportCloudsDialog::~ExportCloudsDialog()
|
ExportCloudsDialog::~ExportCloudsDialog()
|
||||||
@@ -60,25 +78,48 @@ ExportCloudsDialog::~ExportCloudsDialog()
|
|||||||
delete _ui;
|
delete _ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportCloudsDialog::updateMLSGrpVisibility()
|
||||||
|
{
|
||||||
|
_ui->groupBox->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 0);
|
||||||
|
_ui->groupBox_2->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 1);
|
||||||
|
_ui->groupBox_3->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 2);
|
||||||
|
_ui->groupBox_4->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 3);
|
||||||
|
_ui->groupBox_5->setVisible(_ui->comboBox_upsamplingMethod->currentIndex() == 4);
|
||||||
|
}
|
||||||
|
|
||||||
void ExportCloudsDialog::saveSettings(QSettings & settings, const QString & group) const
|
void ExportCloudsDialog::saveSettings(QSettings & settings, const QString & group) const
|
||||||
{
|
{
|
||||||
if(!group.isEmpty())
|
if(!group.isEmpty())
|
||||||
{
|
{
|
||||||
settings.beginGroup(group);
|
settings.beginGroup(group);
|
||||||
}
|
}
|
||||||
settings.setValue("assemble", this->getAssemble());
|
settings.setValue("binary", this->getBinaryFile());
|
||||||
settings.setValue("assemble_voxel", this->getAssembleVoxel());
|
settings.setValue("normals_k", this->getNormalKSearch());
|
||||||
|
|
||||||
settings.setValue("regenerate", this->getGenerate());
|
settings.setValue("regenerate", this->getGenerate());
|
||||||
settings.setValue("regenerate_decimation", this->getGenerateDecimation());
|
settings.setValue("regenerate_decimation", this->getGenerateDecimation());
|
||||||
settings.setValue("regenerate_voxel", this->getGenerateVoxel());
|
settings.setValue("regenerate_voxel", this->getGenerateVoxel());
|
||||||
settings.setValue("regenerate_max_depth", this->getGenerateMaxDepth());
|
settings.setValue("regenerate_max_depth", this->getGenerateMaxDepth());
|
||||||
settings.setValue("binary", this->getBinaryFile());
|
|
||||||
|
settings.setValue("assemble", this->getAssemble());
|
||||||
|
settings.setValue("assemble_voxel", this->getAssembleVoxel());
|
||||||
|
|
||||||
settings.setValue("mls", this->getMLS());
|
settings.setValue("mls", this->getMLS());
|
||||||
settings.setValue("mls_radius", this->getMLSRadius());
|
settings.setValue("mls_radius", this->getMLSRadius());
|
||||||
|
settings.setValue("mls_polygonial_order", this->getMLSPolygonialOrder());
|
||||||
|
settings.setValue("mls_upsampling_method", this->getMLSUpsamplingMethod());
|
||||||
|
settings.setValue("mls_upsampling_radius", this->getMLSUpsamplingRadius());
|
||||||
|
settings.setValue("mls_upsampling_step", this->getMLSUpsamplingStep());
|
||||||
|
settings.setValue("mls_point_density", this->getMLSPointDensity());
|
||||||
|
settings.setValue("mls_dilation_voxel_size", this->getMLSDilationVoxelSize());
|
||||||
|
settings.setValue("mls_dilation_iterations", this->getMLSDilationIterations());
|
||||||
|
|
||||||
settings.setValue("mesh", this->getMesh());
|
settings.setValue("mesh", this->getMesh());
|
||||||
settings.setValue("mesh_k", this->getMeshNormalKSearch());
|
|
||||||
settings.setValue("mesh_radius", this->getMeshGp3Radius());
|
settings.setValue("mesh_radius", this->getMeshGp3Radius());
|
||||||
settings.setValue("mesh_mu", this->getMeshGp3Mu());
|
settings.setValue("mesh_mu", this->getMeshGp3Mu());
|
||||||
|
|
||||||
|
settings.setValue("mesh_texture", this->getMeshTexture());
|
||||||
|
|
||||||
if(!group.isEmpty())
|
if(!group.isEmpty())
|
||||||
{
|
{
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
@@ -91,19 +132,34 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
|
|||||||
{
|
{
|
||||||
settings.beginGroup(group);
|
settings.beginGroup(group);
|
||||||
}
|
}
|
||||||
this->setAssemble(settings.value("assemble", this->getAssemble()).toBool());
|
|
||||||
this->setAssembleVoxel(settings.value("assemble_voxel", this->getAssembleVoxel()).toDouble());
|
_ui->checkBox_binary->setChecked(settings.value("binary", this->getBinaryFile()).toBool());
|
||||||
this->setGenerate(settings.value("regenerate", this->getGenerate()).toBool());
|
_ui->spinBox_normalKSearch->setValue(settings.value("normals_k", this->getNormalKSearch()).toInt());
|
||||||
this->setGenerateDecimation(settings.value("regenerate_decimation", this->getGenerateDecimation()).toInt());
|
|
||||||
this->setGenerateVoxel(settings.value("regenerate_voxel", this->getGenerateVoxel()).toDouble());
|
_ui->groupBox_regenerate->setChecked(settings.value("regenerate", this->getGenerate()).toBool());
|
||||||
this->setGenerateMaxDepth(settings.value("regenerate_max_depth", this->getGenerateMaxDepth()).toDouble());
|
_ui->spinBox_decimation->setValue(settings.value("regenerate_decimation", this->getGenerateDecimation()).toInt());
|
||||||
this->setBinaryFile(settings.value("binary", this->getBinaryFile()).toBool());
|
_ui->doubleSpinBox_voxelSize->setValue(settings.value("regenerate_voxel", this->getGenerateVoxel()).toDouble());
|
||||||
this->setMLS(settings.value("mls", this->getMLS()).toBool());
|
_ui->doubleSpinBox_maxDepth->setValue(settings.value("regenerate_max_depth", this->getGenerateMaxDepth()).toDouble());
|
||||||
this->setMLSRadius(settings.value("mls_radius", this->getMLSRadius()).toDouble());
|
|
||||||
this->setMesh(settings.value("mesh", this->getMesh()).toBool());
|
_ui->groupBox_assemble->setChecked(settings.value("assemble", this->getAssemble()).toBool());
|
||||||
this->setMeshNormalKSearch(settings.value("mesh_k", this->getMeshNormalKSearch()).toInt());
|
_ui->doubleSpinBox_voxelSize_assembled->setValue(settings.value("assemble_voxel", this->getAssembleVoxel()).toDouble());
|
||||||
this->setMeshGp3Radius(settings.value("mesh_radius", this->getMeshGp3Radius()).toDouble());
|
|
||||||
this->setMeshGp3Mu(settings.value("mesh_mu", this->getMeshGp3Mu()).toDouble());
|
_ui->groupBox_mls->setChecked(settings.value("mls", this->getMLS()).toBool());
|
||||||
|
_ui->doubleSpinBox_mlsRadius->setValue(settings.value("mls_radius", this->getMLSRadius()).toDouble());
|
||||||
|
_ui->spinBox_polygonialOrder->setValue(settings.value("mls_polygonial_order", this->getMLSPolygonialOrder()).toInt());
|
||||||
|
_ui->comboBox_upsamplingMethod->setCurrentIndex(settings.value("mls_upsampling_method", this->getMLSUpsamplingMethod()).toInt());
|
||||||
|
_ui->doubleSpinBox_sampleRadius->setValue(settings.value("mls_upsampling_radius", this->getMLSRadius()).toDouble());
|
||||||
|
_ui->doubleSpinBox_sampleStep->setValue(settings.value("mls_upsampling_step", this->getMLSUpsamplingStep()).toDouble());
|
||||||
|
_ui->spinBox_randomPoints->setValue(settings.value("mls_point_density", this->getMLSPointDensity()).toInt());
|
||||||
|
_ui->doubleSpinBox_dilationVoxelSize->setValue(settings.value("mls_dilation_voxel_size", this->getMLSDilationVoxelSize()).toDouble());
|
||||||
|
_ui->spinBox_dilationSteps->setValue(settings.value("mls_dilation_iterations", this->getMLSDilationIterations()).toInt());
|
||||||
|
|
||||||
|
_ui->groupBox_gp3->setChecked(settings.value("mesh", this->getMesh()).toBool());
|
||||||
|
_ui->doubleSpinBox_gp3Radius->setValue(settings.value("mesh_radius", this->getMeshGp3Radius()).toDouble());
|
||||||
|
_ui->doubleSpinBox_gp3Mu->setValue(settings.value("mesh_mu", this->getMeshGp3Mu()).toDouble());
|
||||||
|
|
||||||
|
_ui->checkBox_textureMapping->setChecked(settings.value("mesh_texture", this->getGenerate()).toBool());
|
||||||
|
|
||||||
if(!group.isEmpty())
|
if(!group.isEmpty())
|
||||||
{
|
{
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
@@ -112,22 +168,34 @@ void ExportCloudsDialog::loadSettings(QSettings & settings, const QString & grou
|
|||||||
|
|
||||||
void ExportCloudsDialog::restoreDefaults()
|
void ExportCloudsDialog::restoreDefaults()
|
||||||
{
|
{
|
||||||
setAssemble(true);
|
_ui->checkBox_binary->setChecked(true);
|
||||||
setAssembleVoxel(0.005);
|
_ui->spinBox_normalKSearch->setValue(20);
|
||||||
|
|
||||||
if(_ui->groupBox_regenerate->isEnabled())
|
if(_ui->groupBox_regenerate->isEnabled())
|
||||||
{
|
{
|
||||||
setGenerate(true);
|
_ui->groupBox_regenerate->setChecked(true);
|
||||||
}
|
}
|
||||||
setGenerateDecimation(1);
|
_ui->spinBox_decimation->setValue(1);
|
||||||
setGenerateVoxel(0.005);
|
_ui->doubleSpinBox_voxelSize->setValue(0.01);
|
||||||
setGenerateMaxDepth(4);
|
_ui->doubleSpinBox_maxDepth->setValue(4);
|
||||||
setBinaryFile(true);
|
|
||||||
setMLS(false);
|
_ui->groupBox_assemble->setChecked(true);
|
||||||
setMLSRadius(0.04);
|
_ui->doubleSpinBox_voxelSize_assembled->setValue(0.01);
|
||||||
setMesh(false);
|
|
||||||
setMeshNormalKSearch(20);
|
_ui->groupBox_mls->setChecked(false);
|
||||||
setMeshGp3Radius(0.04);
|
_ui->doubleSpinBox_mlsRadius->setValue(0.04);
|
||||||
setMeshGp3Radius(2.5);
|
_ui->spinBox_polygonialOrder->setValue(2);
|
||||||
|
_ui->comboBox_upsamplingMethod->setCurrentIndex(0);
|
||||||
|
_ui->doubleSpinBox_sampleRadius->setValue(0.01);
|
||||||
|
_ui->doubleSpinBox_sampleStep->setValue(0.0);
|
||||||
|
_ui->spinBox_randomPoints->setValue(0);
|
||||||
|
_ui->doubleSpinBox_dilationVoxelSize->setValue(0.01);
|
||||||
|
_ui->spinBox_dilationSteps->setValue(0);
|
||||||
|
|
||||||
|
_ui->groupBox_gp3->setChecked(false);
|
||||||
|
_ui->doubleSpinBox_gp3Radius->setValue(0.04);
|
||||||
|
_ui->doubleSpinBox_gp3Mu->setValue(2.5);
|
||||||
|
_ui->checkBox_textureMapping->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportCloudsDialog::setSaveButton()
|
void ExportCloudsDialog::setSaveButton()
|
||||||
@@ -135,6 +203,9 @@ void ExportCloudsDialog::setSaveButton()
|
|||||||
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(false);
|
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(false);
|
||||||
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(true);
|
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(true);
|
||||||
_ui->checkBox_binary->setVisible(true);
|
_ui->checkBox_binary->setVisible(true);
|
||||||
|
_ui->label_binaryFile->setVisible(true);
|
||||||
|
_ui->checkBox_textureMapping->setVisible(true);
|
||||||
|
_ui->label_textureMapping->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportCloudsDialog::setOkButton()
|
void ExportCloudsDialog::setOkButton()
|
||||||
@@ -142,6 +213,9 @@ void ExportCloudsDialog::setOkButton()
|
|||||||
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
|
_ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(true);
|
||||||
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(false);
|
_ui->buttonBox->button(QDialogButtonBox::Save)->setVisible(false);
|
||||||
_ui->checkBox_binary->setVisible(false);
|
_ui->checkBox_binary->setVisible(false);
|
||||||
|
_ui->label_binaryFile->setVisible(false);
|
||||||
|
_ui->checkBox_textureMapping->setVisible(false);
|
||||||
|
_ui->label_textureMapping->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportCloudsDialog::enableRegeneration(bool enabled)
|
void ExportCloudsDialog::enableRegeneration(bool enabled)
|
||||||
@@ -154,14 +228,13 @@ void ExportCloudsDialog::enableRegeneration(bool enabled)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//getters
|
//getters
|
||||||
bool ExportCloudsDialog::getAssemble() const
|
bool ExportCloudsDialog::getBinaryFile() const
|
||||||
{
|
{
|
||||||
return _ui->groupBox_assemble->isChecked();
|
return _ui->checkBox_binary->isChecked();
|
||||||
}
|
}
|
||||||
|
int ExportCloudsDialog::getNormalKSearch() const
|
||||||
double ExportCloudsDialog::getAssembleVoxel() const
|
|
||||||
{
|
{
|
||||||
return _ui->doubleSpinBox_voxelSize_assembled->value();
|
return _ui->spinBox_normalKSearch->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportCloudsDialog::getGenerate() const
|
bool ExportCloudsDialog::getGenerate() const
|
||||||
@@ -184,31 +257,57 @@ double ExportCloudsDialog::getGenerateMaxDepth() const
|
|||||||
return _ui->doubleSpinBox_maxDepth->value();
|
return _ui->doubleSpinBox_maxDepth->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportCloudsDialog::getBinaryFile() const
|
bool ExportCloudsDialog::getAssemble() const
|
||||||
{
|
{
|
||||||
return _ui->checkBox_binary->isChecked();
|
return _ui->groupBox_assemble->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExportCloudsDialog::getAssembleVoxel() const
|
||||||
|
{
|
||||||
|
return _ui->doubleSpinBox_voxelSize_assembled->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportCloudsDialog::getMLS() const
|
bool ExportCloudsDialog::getMLS() const
|
||||||
{
|
{
|
||||||
return _ui->groupBox_mls->isChecked();
|
return _ui->groupBox_mls->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
double ExportCloudsDialog::getMLSRadius() const
|
double ExportCloudsDialog::getMLSRadius() const
|
||||||
{
|
{
|
||||||
return _ui->doubleSpinBox_mlsRadius->value();
|
return _ui->doubleSpinBox_mlsRadius->value();
|
||||||
}
|
}
|
||||||
|
int ExportCloudsDialog::getMLSPolygonialOrder() const
|
||||||
|
{
|
||||||
|
return _ui->spinBox_polygonialOrder->value();
|
||||||
|
}
|
||||||
|
int ExportCloudsDialog::getMLSUpsamplingMethod() const
|
||||||
|
{
|
||||||
|
return _ui->comboBox_upsamplingMethod->currentIndex();
|
||||||
|
}
|
||||||
|
double ExportCloudsDialog::getMLSUpsamplingRadius() const
|
||||||
|
{
|
||||||
|
return _ui->doubleSpinBox_sampleRadius->value();
|
||||||
|
}
|
||||||
|
double ExportCloudsDialog::getMLSUpsamplingStep() const
|
||||||
|
{
|
||||||
|
return _ui->doubleSpinBox_sampleStep->value();
|
||||||
|
}
|
||||||
|
int ExportCloudsDialog::getMLSPointDensity() const
|
||||||
|
{
|
||||||
|
return _ui->spinBox_randomPoints->value();
|
||||||
|
}
|
||||||
|
double ExportCloudsDialog::getMLSDilationVoxelSize() const
|
||||||
|
{
|
||||||
|
return _ui->doubleSpinBox_dilationVoxelSize->value();
|
||||||
|
}
|
||||||
|
int ExportCloudsDialog::getMLSDilationIterations() const
|
||||||
|
{
|
||||||
|
return _ui->spinBox_dilationSteps->value();
|
||||||
|
}
|
||||||
|
|
||||||
bool ExportCloudsDialog::getMesh() const
|
bool ExportCloudsDialog::getMesh() const
|
||||||
{
|
{
|
||||||
return _ui->groupBox_gp3->isChecked();
|
return _ui->groupBox_gp3->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExportCloudsDialog::getMeshNormalKSearch() const
|
|
||||||
{
|
|
||||||
return _ui->spinBox_normalKSearch->value();
|
|
||||||
}
|
|
||||||
|
|
||||||
double ExportCloudsDialog::getMeshGp3Radius() const
|
double ExportCloudsDialog::getMeshGp3Radius() const
|
||||||
{
|
{
|
||||||
return _ui->doubleSpinBox_gp3Radius->value();
|
return _ui->doubleSpinBox_gp3Radius->value();
|
||||||
@@ -217,59 +316,9 @@ double ExportCloudsDialog::getMeshGp3Mu() const
|
|||||||
{
|
{
|
||||||
return _ui->doubleSpinBox_gp3Mu->value();
|
return _ui->doubleSpinBox_gp3Mu->value();
|
||||||
}
|
}
|
||||||
|
bool ExportCloudsDialog::getMeshTexture() const
|
||||||
//setters
|
|
||||||
void ExportCloudsDialog::setAssemble(bool on)
|
|
||||||
{
|
{
|
||||||
_ui->groupBox_assemble->setChecked(on);
|
return _ui->checkBox_textureMapping->isChecked();
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setAssembleVoxel(double voxel)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_voxelSize_assembled->setValue(voxel);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setGenerate(bool on)
|
|
||||||
{
|
|
||||||
_ui->groupBox_regenerate->setChecked(on);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setGenerateDecimation(int decimation)
|
|
||||||
{
|
|
||||||
_ui->spinBox_decimation->setValue(decimation);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setGenerateVoxel(double voxel)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_voxelSize->setValue(voxel);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setGenerateMaxDepth(double maxDepth)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_maxDepth->setValue(maxDepth);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setBinaryFile(bool on)
|
|
||||||
{
|
|
||||||
_ui->checkBox_binary->setChecked(on);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMLS(bool on)
|
|
||||||
{
|
|
||||||
_ui->groupBox_mls->setChecked(on);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMLSRadius(double radius)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_mlsRadius->setValue(radius);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMesh(bool on)
|
|
||||||
{
|
|
||||||
_ui->groupBox_gp3->setChecked(on);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMeshNormalKSearch(int k)
|
|
||||||
{
|
|
||||||
_ui->spinBox_normalKSearch->setValue(k);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMeshGp3Radius(double radius)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_gp3Radius->setValue(radius);
|
|
||||||
}
|
|
||||||
void ExportCloudsDialog::setMeshGp3Mu(double mu)
|
|
||||||
{
|
|
||||||
_ui->doubleSpinBox_gp3Mu->setValue(mu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,34 +53,31 @@ public:
|
|||||||
void enableRegeneration(bool enabled);
|
void enableRegeneration(bool enabled);
|
||||||
|
|
||||||
//getters
|
//getters
|
||||||
bool getAssemble() const;
|
bool getBinaryFile() const;
|
||||||
double getAssembleVoxel() const;
|
int getNormalKSearch() const;
|
||||||
|
|
||||||
bool getGenerate() const;
|
bool getGenerate() const;
|
||||||
int getGenerateDecimation() const;
|
int getGenerateDecimation() const;
|
||||||
double getGenerateVoxel() const;
|
double getGenerateVoxel() const;
|
||||||
double getGenerateMaxDepth() const;
|
double getGenerateMaxDepth() const;
|
||||||
bool getBinaryFile() const;
|
|
||||||
|
bool getAssemble() const;
|
||||||
|
double getAssembleVoxel() const;
|
||||||
|
|
||||||
bool getMLS() const;
|
bool getMLS() const;
|
||||||
double getMLSRadius() const;
|
double getMLSRadius() const;
|
||||||
|
int getMLSPolygonialOrder() const;
|
||||||
|
int getMLSUpsamplingMethod() const;
|
||||||
|
double getMLSUpsamplingRadius() const;
|
||||||
|
double getMLSUpsamplingStep() const;
|
||||||
|
int getMLSPointDensity() const;
|
||||||
|
double getMLSDilationVoxelSize() const;
|
||||||
|
int getMLSDilationIterations() const;
|
||||||
|
|
||||||
bool getMesh() const;
|
bool getMesh() const;
|
||||||
int getMeshNormalKSearch() const;
|
|
||||||
double getMeshGp3Radius() const;
|
double getMeshGp3Radius() const;
|
||||||
double getMeshGp3Mu() const;
|
double getMeshGp3Mu() const;
|
||||||
|
bool getMeshTexture() const;
|
||||||
//setters
|
|
||||||
void setAssemble(bool on);
|
|
||||||
void setAssembleVoxel(double voxel);
|
|
||||||
void setGenerate(bool on);
|
|
||||||
void setGenerateDecimation(int decimation);
|
|
||||||
void setGenerateVoxel(double voxel);
|
|
||||||
void setGenerateMaxDepth(double maxDepth);
|
|
||||||
void setBinaryFile(bool on);
|
|
||||||
void setMLS(bool on);
|
|
||||||
void setMLSRadius(double radius);
|
|
||||||
void setMesh(bool on);
|
|
||||||
void setMeshNormalKSearch(int k);
|
|
||||||
void setMeshGp3Radius(double radius);
|
|
||||||
void setMeshGp3Mu(double mu);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void configChanged();
|
void configChanged();
|
||||||
@@ -88,6 +85,9 @@ signals:
|
|||||||
public slots:
|
public slots:
|
||||||
void restoreDefaults();
|
void restoreDefaults();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateMLSGrpVisibility();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui_ExportCloudsDialog * _ui;
|
Ui_ExportCloudsDialog * _ui;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <pcl/io/pcd_io.h>
|
#include <pcl/io/pcd_io.h>
|
||||||
#include <pcl/io/ply_io.h>
|
#include <pcl/io/ply_io.h>
|
||||||
#include <pcl/io/vtk_io.h>
|
#include <pcl/io/vtk_io.h>
|
||||||
|
#include <pcl/io/obj_io.h>
|
||||||
#include <pcl/filters/filter.h>
|
#include <pcl/filters/filter.h>
|
||||||
#include <pcl/search/kdtree.h>
|
#include <pcl/search/kdtree.h>
|
||||||
|
|
||||||
@@ -1764,7 +1765,7 @@ void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int
|
|||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
|
||||||
if(_preferencesDialog->getMeshSmoothing())
|
if(_preferencesDialog->getMeshSmoothing())
|
||||||
{
|
{
|
||||||
cloudWithNormals = util3d::computeNormalsSmoothed(
|
cloudWithNormals = util3d::mls(
|
||||||
cloudFiltered,
|
cloudFiltered,
|
||||||
(float)_preferencesDialog->getMeshSmoothingRadius(),
|
(float)_preferencesDialog->getMeshSmoothingRadius(),
|
||||||
false,
|
false,
|
||||||
@@ -1796,7 +1797,7 @@ void MainWindow::createAndAddCloudToMap(int nodeId, const Transform & pose, int
|
|||||||
if(_preferencesDialog->getMeshSmoothing())
|
if(_preferencesDialog->getMeshSmoothing())
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals;
|
||||||
cloudWithNormals = util3d::computeNormalsSmoothed(
|
cloudWithNormals = util3d::mls(
|
||||||
cloudFiltered,
|
cloudFiltered,
|
||||||
(float)_preferencesDialog->getMeshSmoothingRadius(),
|
(float)_preferencesDialog->getMeshSmoothingRadius(),
|
||||||
false,
|
false,
|
||||||
@@ -3321,7 +3322,6 @@ void MainWindow::postProcessing()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_initProgressDialog->setAutoClose(true, 1);
|
|
||||||
_initProgressDialog->resetProgress();
|
_initProgressDialog->resetProgress();
|
||||||
_initProgressDialog->clear();
|
_initProgressDialog->clear();
|
||||||
_initProgressDialog->show();
|
_initProgressDialog->show();
|
||||||
@@ -3708,7 +3708,7 @@ void MainWindow::postProcessing()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_initProgressDialog->appendText(tr("SBA... failed!"));
|
_initProgressDialog->appendText(tr("SBA... failed!"));
|
||||||
_initProgressDialog->setAutoClose(false, 1);
|
_initProgressDialog->setAutoClose(false);
|
||||||
}
|
}
|
||||||
_initProgressDialog->incrementStep();
|
_initProgressDialog->incrementStep();
|
||||||
}
|
}
|
||||||
@@ -4437,10 +4437,15 @@ void MainWindow::exportClouds()
|
|||||||
{
|
{
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
|
||||||
std::map<int, pcl::PolygonMesh::Ptr> meshes;
|
std::map<int, pcl::PolygonMesh::Ptr> meshes;
|
||||||
|
std::map<int, pcl::TextureMesh::Ptr> textureMeshes;
|
||||||
|
|
||||||
if(getExportedClouds(clouds, meshes, true))
|
if(getExportedClouds(clouds, meshes, textureMeshes, true))
|
||||||
{
|
{
|
||||||
if(meshes.size())
|
if(textureMeshes.size())
|
||||||
|
{
|
||||||
|
saveTextureMeshes(textureMeshes);
|
||||||
|
}
|
||||||
|
else if(meshes.size())
|
||||||
{
|
{
|
||||||
saveMeshes(meshes, _exportDialog->getBinaryFile());
|
saveMeshes(meshes, _exportDialog->getBinaryFile());
|
||||||
}
|
}
|
||||||
@@ -4456,8 +4461,9 @@ void MainWindow::viewClouds()
|
|||||||
{
|
{
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
|
||||||
std::map<int, pcl::PolygonMesh::Ptr> meshes;
|
std::map<int, pcl::PolygonMesh::Ptr> meshes;
|
||||||
|
std::map<int, pcl::TextureMesh::Ptr> textureMeshes;
|
||||||
|
|
||||||
if(getExportedClouds(clouds, meshes, false))
|
if(getExportedClouds(clouds, meshes, textureMeshes, false))
|
||||||
{
|
{
|
||||||
QDialog * window = new QDialog(this, Qt::Window);
|
QDialog * window = new QDialog(this, Qt::Window);
|
||||||
if(meshes.size())
|
if(meshes.size())
|
||||||
@@ -4519,8 +4525,9 @@ void MainWindow::viewClouds()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::getExportedClouds(
|
bool MainWindow::getExportedClouds(
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & clouds,
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> & cloudsWithNormals,
|
||||||
std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
||||||
|
std::map<int, pcl::TextureMesh::Ptr> & textureMeshes,
|
||||||
bool toSave)
|
bool toSave)
|
||||||
{
|
{
|
||||||
if(_exportDialog->isVisible())
|
if(_exportDialog->isVisible())
|
||||||
@@ -4551,6 +4558,11 @@ bool MainWindow::getExportedClouds(
|
|||||||
{
|
{
|
||||||
mul+=1;
|
mul+=1;
|
||||||
}
|
}
|
||||||
|
mul+=1; // normals
|
||||||
|
if(_exportDialog->getMeshTexture())
|
||||||
|
{
|
||||||
|
mul+=1;
|
||||||
|
}
|
||||||
_initProgressDialog->setMaximumSteps(int(poses.size())*mul+1);
|
_initProgressDialog->setMaximumSteps(int(poses.size())*mul+1);
|
||||||
|
|
||||||
if(_exportDialog->getMLS())
|
if(_exportDialog->getMLS())
|
||||||
@@ -4559,17 +4571,14 @@ bool MainWindow::getExportedClouds(
|
|||||||
"[search radius=%1m voxel=%2m]").arg(_exportDialog->getMLSRadius()).arg(_exportDialog->getGenerateVoxel()));
|
"[search radius=%1m voxel=%2m]").arg(_exportDialog->getMLSRadius()).arg(_exportDialog->getGenerateVoxel()));
|
||||||
}
|
}
|
||||||
_initProgressDialog->appendText(tr("Computing surface normals... "
|
_initProgressDialog->appendText(tr("Computing surface normals... "
|
||||||
"[K neighbors=%1]").arg(_exportDialog->getMeshNormalKSearch()));
|
"[K neighbors=%1]").arg(_exportDialog->getNormalKSearch()));
|
||||||
|
|
||||||
clouds = this->getClouds(
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr> clouds = this->getClouds(
|
||||||
poses,
|
poses,
|
||||||
_exportDialog->getGenerate(),
|
_exportDialog->getGenerate(),
|
||||||
_exportDialog->getGenerateDecimation(),
|
_exportDialog->getGenerateDecimation(),
|
||||||
_exportDialog->getGenerateVoxel(),
|
_exportDialog->getGenerateVoxel(),
|
||||||
_exportDialog->getGenerateMaxDepth(),
|
_exportDialog->getGenerateMaxDepth());
|
||||||
_exportDialog->getMeshNormalKSearch(),
|
|
||||||
_exportDialog->getAssemble()?false:_exportDialog->getMLS(),
|
|
||||||
(float)_exportDialog->getMLSRadius());
|
|
||||||
|
|
||||||
if(_exportDialog->getAssemble())
|
if(_exportDialog->getAssemble())
|
||||||
{
|
{
|
||||||
@@ -4577,62 +4586,75 @@ bool MainWindow::getExportedClouds(
|
|||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
int i =0;
|
int i =0;
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr assembledCloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||||
for(std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr>::iterator iter=clouds.begin();
|
for(std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr>::iterator iter=clouds.begin();
|
||||||
iter!= clouds.end();
|
iter!= clouds.end();
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr transformed = util3d::transformPointCloud(iter->second, poses.at(iter->first));
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed = util3d::transformPointCloud(iter->second, poses.at(iter->first));
|
||||||
*assembledCloud += *transformed;
|
*assembledCloud += *transformed;
|
||||||
|
|
||||||
_initProgressDialog->appendText(tr("Assembled cloud %1 (%2/%3).").arg(iter->first).arg(++i).arg(clouds.size()));
|
_initProgressDialog->appendText(tr("Assembled cloud %1 (%2/%3).").arg(iter->first).arg(++i).arg(clouds.size()));
|
||||||
_initProgressDialog->incrementStep();
|
_initProgressDialog->incrementStep();
|
||||||
|
QApplication::processEvents();
|
||||||
if(i % 100 == 0)
|
|
||||||
{
|
|
||||||
QApplication::processEvents();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_initProgressDialog->appendText(tr("Voxelize assembled cloud (%1 points, voxel size = %2 m)...")
|
||||||
|
.arg(assembledCloud->size())
|
||||||
|
.arg(_exportDialog->getGenerateVoxel()));
|
||||||
|
QApplication::processEvents();
|
||||||
|
if(_exportDialog->getGenerateVoxel())
|
||||||
|
{
|
||||||
|
assembledCloud = util3d::voxelize(
|
||||||
|
assembledCloud,
|
||||||
|
_exportDialog->getGenerateVoxel());
|
||||||
|
}
|
||||||
|
clouds.clear();
|
||||||
|
clouds.insert(std::make_pair(0, assembledCloud));
|
||||||
|
}
|
||||||
|
|
||||||
|
// normals
|
||||||
|
for(std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr>::iterator iter=clouds.begin();
|
||||||
|
iter!= clouds.end();
|
||||||
|
++iter)
|
||||||
|
{
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
|
||||||
if(_exportDialog->getMLS())
|
if(_exportDialog->getMLS())
|
||||||
{
|
{
|
||||||
_initProgressDialog->appendText(tr("Voxelize assembled cloud (%1 points, voxel size = %2 m)...")
|
_initProgressDialog->appendText(tr("Smoothing (MLS) of the assembled cloud (%1 points)...").arg(iter->second->size()));
|
||||||
.arg(assembledCloud->size())
|
|
||||||
.arg(_exportDialog->getGenerateVoxel()));
|
|
||||||
QApplication::processEvents();
|
|
||||||
if(_exportDialog->getGenerateVoxel())
|
|
||||||
{
|
|
||||||
assembledCloud = util3d::voxelize(
|
|
||||||
assembledCloud,
|
|
||||||
_exportDialog->getGenerateVoxel());
|
|
||||||
}
|
|
||||||
|
|
||||||
_initProgressDialog->appendText(tr("Smoothing (MLS) of the assembled cloud (%1 points)...").arg(assembledCloud->size()));
|
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledCloudXYZRGB(new pcl::PointCloud<pcl::PointXYZRGB>);
|
cloudWithNormals = util3d::mls(
|
||||||
pcl::copyPointCloud(*assembledCloud, *assembledCloudXYZRGB);
|
iter->second,
|
||||||
assembledCloud = util3d::computeNormalsSmoothed(
|
|
||||||
assembledCloudXYZRGB,
|
|
||||||
(float)_exportDialog->getMLSRadius(),
|
(float)_exportDialog->getMLSRadius(),
|
||||||
true,
|
_exportDialog->getMLSPolygonialOrder(),
|
||||||
_exportDialog->getGenerateVoxel());
|
_exportDialog->getMLSUpsamplingMethod(),
|
||||||
|
(float)_exportDialog->getMLSUpsamplingRadius(),
|
||||||
|
(float)_exportDialog->getMLSUpsamplingStep(),
|
||||||
|
_exportDialog->getMLSPointDensity(),
|
||||||
|
(float)_exportDialog->getMLSDilationVoxelSize(),
|
||||||
|
_exportDialog->getMLSDilationIterations());
|
||||||
|
|
||||||
if(_exportDialog->getAssembleVoxel())
|
// Re-voxelize to make sure to have uniform density
|
||||||
{
|
_initProgressDialog->appendText(tr("Voxelize assembled cloud (%1 points, voxel size = %2 m)...")
|
||||||
_initProgressDialog->appendText(tr("Voxelize assembled cloud (%1 points, voxel size = %2 m)...")
|
.arg(cloudWithNormals->size())
|
||||||
.arg(assembledCloud->size())
|
.arg(_exportDialog->getAssemble()?_exportDialog->getAssembleVoxel():_exportDialog->getGenerateVoxel()));
|
||||||
.arg(_exportDialog->getAssembleVoxel()));
|
|
||||||
QApplication::processEvents();
|
|
||||||
|
|
||||||
assembledCloud = util3d::voxelize(
|
|
||||||
assembledCloud,
|
|
||||||
_exportDialog->getAssembleVoxel());
|
|
||||||
}
|
|
||||||
|
|
||||||
_initProgressDialog->appendText(tr("Update %1 normals with %2 camera views...").arg(assembledCloud->size()).arg(poses.size()));
|
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
|
cloudWithNormals = util3d::voxelize(
|
||||||
|
cloudWithNormals,
|
||||||
|
_exportDialog->getAssemble()?_exportDialog->getAssembleVoxel():_exportDialog->getGenerateVoxel());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//compute normals
|
||||||
|
cloudWithNormals = util3d::computeNormals(iter->second, _exportDialog->getNormalKSearch());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_exportDialog->getAssemble())
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Update %1 normals with %2 camera views...").arg(cloudWithNormals->size()).arg(poses.size()));
|
||||||
pcl::PointCloud<pcl::PointXYZ>::Ptr viewpoints(new pcl::PointCloud<pcl::PointXYZ>);
|
pcl::PointCloud<pcl::PointXYZ>::Ptr viewpoints(new pcl::PointCloud<pcl::PointXYZ>);
|
||||||
viewpoints->resize(poses.size());
|
viewpoints->resize(poses.size());
|
||||||
int oi=0;
|
int oi=0;
|
||||||
@@ -4642,30 +4664,23 @@ bool MainWindow::getExportedClouds(
|
|||||||
(*viewpoints)[oi].y = iter->second.y();
|
(*viewpoints)[oi].y = iter->second.y();
|
||||||
(*viewpoints)[oi++].z = iter->second.z();
|
(*viewpoints)[oi++].z = iter->second.z();
|
||||||
}
|
}
|
||||||
util3d::adjustNormalsToViewPoints(viewpoints, *assembledCloud);
|
util3d::adjustNormalsToViewPoints(viewpoints, cloudWithNormals, _exportDialog->getNormalKSearch());
|
||||||
}
|
|
||||||
else if(_exportDialog->getAssembleVoxel())
|
|
||||||
{
|
|
||||||
_initProgressDialog->appendText(tr("Voxelize assembled cloud (%1 points)...").arg(assembledCloud->size()));
|
|
||||||
QApplication::processEvents();
|
|
||||||
assembledCloud = util3d::voxelize(
|
|
||||||
assembledCloud,
|
|
||||||
_exportDialog->getAssembleVoxel());
|
|
||||||
_initProgressDialog->appendText(tr("Voxelized assembled cloud (%1 points)").arg(assembledCloud->size()));
|
|
||||||
}
|
}
|
||||||
|
cloudsWithNormals.insert(std::make_pair(iter->first, cloudWithNormals));
|
||||||
|
|
||||||
clouds.clear();
|
_initProgressDialog->incrementStep();
|
||||||
clouds.insert(std::make_pair(0, assembledCloud));
|
QApplication::processEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//mesh
|
||||||
if(_exportDialog->getMesh())
|
if(_exportDialog->getMesh())
|
||||||
{
|
{
|
||||||
_initProgressDialog->appendText(tr("Greedy projection triangulation... [radius=%1m]").arg(_exportDialog->getMeshGp3Radius()));
|
_initProgressDialog->appendText(tr("Greedy projection triangulation... [radius=%1m]").arg(_exportDialog->getMeshGp3Radius()));
|
||||||
QApplication::processEvents();
|
QApplication::processEvents();
|
||||||
|
|
||||||
int i=0;
|
int i=0;
|
||||||
for(std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr>::iterator iter=clouds.begin();
|
for(std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr>::iterator iter=cloudsWithNormals.begin();
|
||||||
iter!= clouds.end();
|
iter!= cloudsWithNormals.end();
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
pcl::PolygonMesh::Ptr mesh = util3d::createMesh(iter->second, _exportDialog->getMeshGp3Radius(), _exportDialog->getMeshGp3Mu());
|
pcl::PolygonMesh::Ptr mesh = util3d::createMesh(iter->second, _exportDialog->getMeshGp3Radius(), _exportDialog->getMeshGp3Mu());
|
||||||
@@ -4673,13 +4688,78 @@ bool MainWindow::getExportedClouds(
|
|||||||
|
|
||||||
_initProgressDialog->appendText(tr("Mesh %1 created with %2 polygons (%3/%4).").arg(iter->first).arg(mesh->polygons.size()).arg(++i).arg(clouds.size()));
|
_initProgressDialog->appendText(tr("Mesh %1 created with %2 polygons (%3/%4).").arg(iter->first).arg(mesh->polygons.size()).arg(++i).arg(clouds.size()));
|
||||||
_initProgressDialog->incrementStep();
|
_initProgressDialog->incrementStep();
|
||||||
if(i % 100 == 0)
|
QApplication::processEvents();
|
||||||
{
|
|
||||||
QApplication::processEvents();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(toSave && _exportDialog->getMeshTexture())
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
for(std::map<int, pcl::PolygonMesh::Ptr>::iterator iter=meshes.begin();
|
||||||
|
iter!= meshes.end();
|
||||||
|
++iter)
|
||||||
|
{
|
||||||
|
std::map<int, Transform> cameras;
|
||||||
|
if(iter->first == 0)
|
||||||
|
{
|
||||||
|
cameras = poses;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UASSERT(uContains(poses, iter->first));
|
||||||
|
cameras.insert(std::make_pair(iter->first, Transform::getIdentity()));
|
||||||
|
}
|
||||||
|
std::map<int, Transform> cameraPoses;
|
||||||
|
std::map<int, CameraModel> cameraModels;
|
||||||
|
std::map<int, cv::Mat> images;
|
||||||
|
for(std::map<int, Transform>::iterator iter=cameras.begin(); iter!=cameras.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(_cachedSignatures.contains(iter->first))
|
||||||
|
{
|
||||||
|
const Signature & s = _cachedSignatures.value(iter->first);
|
||||||
|
CameraModel model;
|
||||||
|
if(s.sensorData().stereoCameraModel().isValid())
|
||||||
|
{
|
||||||
|
model = s.sensorData().stereoCameraModel().left();
|
||||||
|
}
|
||||||
|
else if(s.sensorData().cameraModels().size() == 1 && s.sensorData().cameraModels()[0].isValid())
|
||||||
|
{
|
||||||
|
model = s.sensorData().cameraModels()[0];
|
||||||
|
}
|
||||||
|
cv::Mat image = s.sensorData().imageRaw();
|
||||||
|
if(image.empty() && !s.sensorData().imageCompressed().empty())
|
||||||
|
{
|
||||||
|
s.sensorData().uncompressDataConst(&image, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if(!iter->second.isNull() && model.isValid() && !image.empty())
|
||||||
|
{
|
||||||
|
cameraPoses.insert(std::make_pair(iter->first, iter->second));
|
||||||
|
cameraModels.insert(std::make_pair(iter->first, model));
|
||||||
|
images.insert(std::make_pair(iter->first, image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cameraPoses.size())
|
||||||
|
{
|
||||||
|
QDir dir(_preferencesDialog->getWorkingDirectory());
|
||||||
|
dir.mkdir("tmp_textures");
|
||||||
|
pcl::TextureMesh::Ptr textureMesh = util3d::createTextureMesh(
|
||||||
|
iter->second,
|
||||||
|
cameraPoses,
|
||||||
|
cameraModels,
|
||||||
|
images,
|
||||||
|
dir.filePath("tmp_textures").toStdString());
|
||||||
|
|
||||||
|
textureMeshes.insert(std::make_pair(iter->first, textureMesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
_initProgressDialog->appendText(tr("TextureMesh %1 created [cameras=%2] (%3/%4).").arg(iter->first).arg(cameraPoses.size()).arg(++i).arg(clouds.size()));
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
QApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -4782,14 +4862,9 @@ void MainWindow::exportBundlerFormat()
|
|||||||
out << t.x() << " " << t.y() << " " << t.z() << "\n";
|
out << t.x() << " " << t.y() << " " << t.z() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::Button b = QMessageBox::question(this,
|
QMessageBox::question(this,
|
||||||
tr("Exporting cameras in Bundler format..."),
|
tr("Exporting cameras in Bundler format..."),
|
||||||
tr("%1 cameras/images exported to directory \"%2\".\nDo you want to export the cloud/mesh (PLY)?").arg(poses.size()).arg(path),
|
tr("%1 cameras/images exported to directory \"%2\".").arg(poses.size()).arg(path));
|
||||||
QMessageBox::Yes | QMessageBox::No);
|
|
||||||
if(b == QMessageBox::Yes)
|
|
||||||
{
|
|
||||||
this->exportClouds();
|
|
||||||
}
|
|
||||||
fileList.close();
|
fileList.close();
|
||||||
}
|
}
|
||||||
fileOut.close();
|
fileOut.close();
|
||||||
@@ -4987,7 +5062,11 @@ void MainWindow::saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
|||||||
_initProgressDialog->appendText(tr("Saving the mesh (%1 polygons)...").arg(meshes.begin()->second->polygons.size()));
|
_initProgressDialog->appendText(tr("Saving the mesh (%1 polygons)...").arg(meshes.begin()->second->polygons.size()));
|
||||||
|
|
||||||
bool success =false;
|
bool success =false;
|
||||||
if(QFileInfo(path).suffix() == "ply")
|
if(QFileInfo(path).suffix() == "")
|
||||||
|
{
|
||||||
|
path += ".ply";
|
||||||
|
}
|
||||||
|
else if(QFileInfo(path).suffix() == "ply")
|
||||||
{
|
{
|
||||||
if(binaryMode)
|
if(binaryMode)
|
||||||
{
|
{
|
||||||
@@ -4998,18 +5077,9 @@ void MainWindow::saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
|||||||
success = pcl::io::savePLYFile(path.toStdString(), *meshes.begin()->second) == 0;
|
success = pcl::io::savePLYFile(path.toStdString(), *meshes.begin()->second) == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(QFileInfo(path).suffix() == "")
|
else if(QFileInfo(path).suffix() == "obj")
|
||||||
{
|
{
|
||||||
//default ply
|
success = pcl::io::saveOBJFile(path.toStdString(), *meshes.begin()->second) == 0;
|
||||||
path += ".ply";
|
|
||||||
if(binaryMode)
|
|
||||||
{
|
|
||||||
success = pcl::io::savePLYFileBinary(path.toStdString(), *meshes.begin()->second) == 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
success = pcl::io::savePLYFile(path.toStdString(), *meshes.begin()->second) == 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -5035,38 +5105,177 @@ void MainWindow::saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
|||||||
}
|
}
|
||||||
else if(meshes.size())
|
else if(meshes.size())
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getExistingDirectory(this, tr("Save to (*.ply)..."), _preferencesDialog->getWorkingDirectory(), 0);
|
QString path = QFileDialog::getExistingDirectory(this, tr("Save to (*.ply *.obj)..."), _preferencesDialog->getWorkingDirectory(), 0);
|
||||||
|
if(!path.isEmpty())
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
QStringList items;
|
||||||
|
items.push_back("ply");
|
||||||
|
items.push_back("obj");
|
||||||
|
QString suffix = QInputDialog::getItem(this, tr("File format"), tr("Which format?"), items, 0, false, &ok);
|
||||||
|
|
||||||
|
if(ok)
|
||||||
|
{
|
||||||
|
QString prefix = QInputDialog::getText(this, tr("File prefix"), tr("Prefix:"), QLineEdit::Normal, "mesh", &ok);
|
||||||
|
|
||||||
|
if(ok)
|
||||||
|
{
|
||||||
|
for(std::map<int, pcl::PolygonMesh::Ptr>::const_iterator iter=meshes.begin(); iter!=meshes.end(); ++iter)
|
||||||
|
{
|
||||||
|
if(iter->second->polygons.size())
|
||||||
|
{
|
||||||
|
pcl::PolygonMesh mesh;
|
||||||
|
mesh.polygons = iter->second->polygons;
|
||||||
|
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||||
|
pcl::fromPCLPointCloud2(iter->second->cloud, *tmp);
|
||||||
|
tmp = util3d::transformPointCloud(tmp, _currentPosesMap.at(iter->first));
|
||||||
|
pcl::toPCLPointCloud2(*tmp, mesh.cloud);
|
||||||
|
|
||||||
|
QString pathFile = path+QDir::separator()+QString("%1%2.%3").arg(prefix).arg(iter->first).arg(suffix);
|
||||||
|
bool success =false;
|
||||||
|
if(suffix == "ply")
|
||||||
|
{
|
||||||
|
if(binaryMode)
|
||||||
|
{
|
||||||
|
success = pcl::io::savePLYFileBinary(pathFile.toStdString(), mesh) == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
success = pcl::io::savePLYFile(pathFile.toStdString(), mesh) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(suffix == "obj")
|
||||||
|
{
|
||||||
|
success = pcl::io::saveOBJFile(pathFile.toStdString(), mesh) == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UFATAL("Extension not recognized! (%s)", suffix.toStdString().c_str());
|
||||||
|
}
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Saved mesh %1 (%2 polygons) to %3.")
|
||||||
|
.arg(iter->first).arg(iter->second->polygons.size()).arg(pathFile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Failed saving mesh %1 (%2 polygons) to %3.")
|
||||||
|
.arg(iter->first).arg(iter->second->polygons.size()).arg(pathFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Mesh %1 is empty!").arg(iter->first));
|
||||||
|
}
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
QApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::saveTextureMeshes(const std::map<int, pcl::TextureMesh::Ptr> & meshes)
|
||||||
|
{
|
||||||
|
if(meshes.size() == 1)
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getSaveFileName(this, tr("Save to ..."), _preferencesDialog->getWorkingDirectory()+QDir::separator()+"mesh.obj", tr("Mesh (*.obj)"));
|
||||||
|
if(!path.isEmpty())
|
||||||
|
{
|
||||||
|
if(meshes.begin()->second->tex_materials.size())
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Saving the mesh (with %1 textures)...").arg(meshes.begin()->second->tex_materials.size()));
|
||||||
|
|
||||||
|
bool success =false;
|
||||||
|
if(QFileInfo(path).suffix() == "")
|
||||||
|
{
|
||||||
|
path += ".obj";
|
||||||
|
}
|
||||||
|
|
||||||
|
pcl::TextureMesh mesh;
|
||||||
|
mesh.tex_coordinates = meshes.begin()->second->tex_coordinates;
|
||||||
|
mesh.tex_materials = meshes.begin()->second->tex_materials;
|
||||||
|
QDir(QFileInfo(path).absoluteDir().absolutePath()).mkdir(QFileInfo(path).baseName());
|
||||||
|
for(unsigned int i=0;i<meshes.begin()->second->tex_materials.size(); ++i)
|
||||||
|
{
|
||||||
|
QFileInfo info(mesh.tex_materials[i].tex_file.c_str());
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Failed copying texture \"%1\" to \"%2\".")
|
||||||
|
.arg(meshes.begin()->second->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
|
||||||
|
_initProgressDialog->setAutoClose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesh.tex_polygons = meshes.begin()->second->tex_polygons;
|
||||||
|
mesh.cloud = meshes.begin()->second->cloud;
|
||||||
|
|
||||||
|
success = pcl::io::saveOBJFile(path.toStdString(), mesh) == 0;
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
_initProgressDialog->incrementStep();
|
||||||
|
_initProgressDialog->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));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Save failed!"), tr("Failed to save to \"%1\"").arg(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Save failed!"), tr("No textures..."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(meshes.size())
|
||||||
|
{
|
||||||
|
QString path = QFileDialog::getExistingDirectory(this, tr("Save to (*.obj)..."), _preferencesDialog->getWorkingDirectory(), 0);
|
||||||
if(!path.isEmpty())
|
if(!path.isEmpty())
|
||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
QString prefix = QInputDialog::getText(this, tr("File prefix"), tr("Prefix:"), QLineEdit::Normal, "mesh", &ok);
|
QString prefix = QInputDialog::getText(this, tr("File prefix"), tr("Prefix:"), QLineEdit::Normal, "mesh", &ok);
|
||||||
QString suffix = "ply";
|
QString suffix = "obj";
|
||||||
|
|
||||||
if(ok)
|
if(ok)
|
||||||
{
|
{
|
||||||
for(std::map<int, pcl::PolygonMesh::Ptr>::const_iterator iter=meshes.begin(); iter!=meshes.end(); ++iter)
|
for(std::map<int, pcl::TextureMesh::Ptr>::const_iterator iter=meshes.begin(); iter!=meshes.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(iter->second->polygons.size())
|
QString currentPrefix=prefix+QString::number(iter->first);
|
||||||
|
if(iter->second->tex_materials.size())
|
||||||
{
|
{
|
||||||
pcl::PolygonMesh mesh;
|
pcl::TextureMesh mesh;
|
||||||
mesh.polygons = iter->second->polygons;
|
mesh.tex_coordinates = iter->second->tex_coordinates;
|
||||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr tmp(new pcl::PointCloud<pcl::PointXYZRGB>);
|
mesh.tex_materials = iter->second->tex_materials;
|
||||||
|
QDir(path).mkdir(currentPrefix);
|
||||||
|
for(unsigned int i=0;i<iter->second->tex_materials.size(); ++i)
|
||||||
|
{
|
||||||
|
QFileInfo info(mesh.tex_materials[i].tex_file.c_str());
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
_initProgressDialog->appendText(tr("Failed copying texture \"%1\" to \"%2\".")
|
||||||
|
.arg(iter->second->tex_materials[i].tex_file.c_str()).arg(fullPath), Qt::darkRed);
|
||||||
|
_initProgressDialog->setAutoClose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mesh.tex_polygons = iter->second->tex_polygons;
|
||||||
|
pcl::PointCloud<pcl::PointNormal>::Ptr tmp(new pcl::PointCloud<pcl::PointNormal>);
|
||||||
pcl::fromPCLPointCloud2(iter->second->cloud, *tmp);
|
pcl::fromPCLPointCloud2(iter->second->cloud, *tmp);
|
||||||
tmp = util3d::transformPointCloud(tmp, _currentPosesMap.at(iter->first));
|
tmp = util3d::transformPointCloud(tmp, _currentPosesMap.at(iter->first));
|
||||||
pcl::toPCLPointCloud2(*tmp, mesh.cloud);
|
pcl::toPCLPointCloud2(*tmp, mesh.cloud);
|
||||||
|
|
||||||
QString pathFile = path+QDir::separator()+QString("%1%2.%3").arg(prefix).arg(iter->first).arg(suffix);
|
QString pathFile = path+QDir::separator()+QString("%1.%3").arg(currentPrefix).arg(suffix);
|
||||||
bool success =false;
|
bool success =false;
|
||||||
if(suffix == "ply")
|
if(suffix == "obj")
|
||||||
{
|
{
|
||||||
if(binaryMode)
|
success = pcl::io::saveOBJFile(pathFile.toStdString(), mesh) == 0;
|
||||||
{
|
|
||||||
success = pcl::io::savePLYFileBinary(pathFile.toStdString(), mesh) == 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
success = pcl::io::savePLYFile(pathFile.toStdString(), mesh) == 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -5074,13 +5283,13 @@ void MainWindow::saveMeshes(const std::map<int, pcl::PolygonMesh::Ptr> & meshes,
|
|||||||
}
|
}
|
||||||
if(success)
|
if(success)
|
||||||
{
|
{
|
||||||
_initProgressDialog->appendText(tr("Saved mesh %1 (%2 polygons) to %3.")
|
_initProgressDialog->appendText(tr("Saved mesh %1 (%2 textures) to %3.")
|
||||||
.arg(iter->first).arg(iter->second->polygons.size()).arg(pathFile));
|
.arg(iter->first).arg(iter->second->tex_materials.size()-1).arg(pathFile));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_initProgressDialog->appendText(tr("Failed saving mesh %1 (%2 polygons) to %3.")
|
_initProgressDialog->appendText(tr("Failed saving mesh %1 (%2 textures) to %3.")
|
||||||
.arg(iter->first).arg(iter->second->polygons.size()).arg(pathFile));
|
.arg(iter->first).arg(iter->second->tex_materials.size()-1).arg(pathFile), Qt::darkRed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -5203,17 +5412,14 @@ void MainWindow::saveScans(const std::map<int, pcl::PointCloud<pcl::PointXYZ>::P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr > MainWindow::getClouds(
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr > MainWindow::getClouds(
|
||||||
const std::map<int, Transform> & poses,
|
const std::map<int, Transform> & poses,
|
||||||
bool regenerateClouds,
|
bool regenerateClouds,
|
||||||
int regenerateDecimation,
|
int regenerateDecimation,
|
||||||
float regenerateVoxelSize,
|
float regenerateVoxelSize,
|
||||||
float regenerateMaxDepth,
|
float regenerateMaxDepth) const
|
||||||
int normalKSearch,
|
|
||||||
bool mls,
|
|
||||||
float mlsRadius) const
|
|
||||||
{
|
{
|
||||||
std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr> clouds;
|
std::map<int, pcl::PointCloud<pcl::PointXYZRGB>::Ptr> clouds;
|
||||||
int i=0;
|
int i=0;
|
||||||
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
|
||||||
{
|
{
|
||||||
@@ -5265,28 +5471,7 @@ std::map<int, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr > MainWindow::getClou
|
|||||||
|
|
||||||
if(cloud->size())
|
if(cloud->size())
|
||||||
{
|
{
|
||||||
if(mls)
|
clouds.insert(std::make_pair(iter->first, cloud));
|
||||||
{
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals = util3d::computeNormalsSmoothed(
|
|
||||||
cloud,
|
|
||||||
mlsRadius,
|
|
||||||
true,
|
|
||||||
regenerateVoxelSize);
|
|
||||||
|
|
||||||
if(regenerateVoxelSize)
|
|
||||||
{
|
|
||||||
cloudWithNormals = util3d::voxelize(
|
|
||||||
cloudWithNormals,
|
|
||||||
regenerateVoxelSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
cloud->clear();
|
|
||||||
pcl::copyPointCloud(*cloudWithNormals, *cloud);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudWithNormals = util3d::computeNormals(cloud, _exportDialog->getMeshNormalKSearch());
|
|
||||||
|
|
||||||
clouds.insert(std::make_pair(iter->first, cloudWithNormals));
|
|
||||||
inserted = true;
|
inserted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1012</width>
|
<width>1012</width>
|
||||||
<height>25</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
@@ -987,7 +987,7 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionSave_point_cloud">
|
<action name="actionSave_point_cloud">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Export 3D clouds (*.ply *.pcd)...</string>
|
<string>Export 3D clouds (*.ply *.pcd *.obj)...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDownload_all_clouds">
|
<action name="actionDownload_all_clouds">
|
||||||
|
|||||||
Reference in New Issue
Block a user