Skip to content

Instantly share code, notes, and snippets.

@hfaulds
Created March 22, 2012 17:42
Show Gist options
  • Select an option

  • Save hfaulds/2160724 to your computer and use it in GitHub Desktop.

Select an option

Save hfaulds/2160724 to your computer and use it in GitHub Desktop.
Dissertation - Meshing - Loading/Saving - Kinect not required
#include <boost/thread/thread.hpp>
#include <boost/make_shared.hpp>
#include <pcl/io/openni_grabber.h>
#include <pcl/visualization/point_cloud_handlers.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/image_viewer.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>
#include <pcl/console/time.h>
#include <pcl/common/transforms.h>
#include <pcl/filters/conditional_removal.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/gp3.h>
#include <pcl/io/vtk_io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/io/obj_io.h>
typedef pcl::PointCloud<pcl::PointXYZRGB> PointCloud;
const int WIDTH = 1600, HEIGHT = 800;
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
boost::mutex cld_mutex;
PointCloud::ConstPtr g_cloud;
pcl::Grabber* grabber;
const int NUM_CAPTURES = 6;
PointCloud::ConstPtr clouds[NUM_CAPTURES];
const float ANGLE_STEP = 2 * M_PI / NUM_CAPTURES;
int currentCloud = 0;
const float X_STEP = 0.005;
const float Z_STEP = 0.005;
float xTranslation = -0.034999996;
float zTranslation = -0.71499950;
const std::string LOAD_DIRECTORY = "E:\\University\\Year 3\\Dissertation\\C++\\PCL\\visualization\\tools\\";
const std::string SAVE_DIRECTORY = "E:\\University\\Year 3\\Dissertation\\Meshes\\";
void cloud_callback (const PointCloud::ConstPtr& cloud)
{
cld_mutex.lock();
g_cloud = cloud;
cld_mutex.unlock();
}
PointCloud::ConstPtr filterCloudByDepth(const PointCloud::ConstPtr c_in) {
pcl::ConditionAnd<pcl::PointXYZRGB>::Ptr range_cond (new pcl::ConditionAnd<pcl::PointXYZRGB>());
pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr zCondition(new pcl::FieldComparison<pcl::PointXYZRGB>("z", pcl::ComparisonOps::LT, 1));
pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr xCondition1(new pcl::FieldComparison<pcl::PointXYZRGB>("x", pcl::ComparisonOps::LT, 0.2));
pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr xCondition2(new pcl::FieldComparison<pcl::PointXYZRGB>("x", pcl::ComparisonOps::GT, -0.2));
range_cond->addComparison(zCondition);
range_cond->addComparison(xCondition1);
range_cond->addComparison(xCondition2);
pcl::ConditionalRemoval<pcl::PointXYZRGB> conditional_remove(range_cond);
conditional_remove.setInputCloud(c_in);
PointCloud c_out;
conditional_remove.filter(c_out);
return c_out.makeShared();
}
/* pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
icp.setInputCloud(cloud_in);
icp.setInputTarget(cloud_out);*/
PointCloud::ConstPtr transformCloud(const PointCloud::ConstPtr c_in, Eigen::Affine3f transform)
{
PointCloud c_out;
pcl::transformPointCloud<pcl::PointXYZRGB>(*c_in, c_out, transform);
return c_out.makeShared();
}
PointCloud::ConstPtr xTranslateCloud(const PointCloud::ConstPtr c_in)
{
Eigen::Affine3f transform;
transform = Eigen::Translation3f(xTranslation, 0, 0);
return transformCloud(c_in, transform);
}
PointCloud::ConstPtr zTranslateCloud(const PointCloud::ConstPtr c_in)
{
Eigen::Affine3f transform;
transform = Eigen::Translation3f(0, 0, zTranslation);
return transformCloud(c_in, transform);
}
PointCloud::ConstPtr rotateCloud(const PointCloud::ConstPtr c_in, int cloudID)
{
float angle = cloudID * ANGLE_STEP;
Eigen::Affine3f transform;
transform = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitY());
return transformCloud(c_in, transform);
}
std::string cloudName(int cloudNum, std::string filetype = "")
{
std::stringstream cloud_name_stream;
cloud_name_stream << "cloud" << cloudNum << filetype;
return cloud_name_stream.str();
}
void updateCloud(PointCloud::ConstPtr cloud, int cloudID)
{
PointCloud::ConstPtr transformedCloud = rotateCloud(zTranslateCloud(xTranslateCloud(cloud)), cloudID);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> handler(transformedCloud);
std::string name = cloudName(cloudID);
if (!viewer->updatePointCloud(transformedCloud, handler, name))
{
viewer->addPointCloud(transformedCloud, handler, name);
}
}
void updateClouds()
{
for(int i=0; i < NUM_CAPTURES; i++)
{
if(clouds[i]) {
updateCloud(clouds[i], i);
}
}
}
void capture()
{
if (g_cloud && cld_mutex.try_lock ())
{
PointCloud::ConstPtr c_in = filterCloudByDepth(g_cloud);
clouds[currentCloud] = c_in;
updateCloud(c_in, currentCloud);
cld_mutex.unlock();
}
}
void createMesh() {
pcl::PointCloud<pcl::PointXYZ> cloud_xyz;
int point_count = 0;
for(int i=0; i < NUM_CAPTURES; i++) {
point_count += clouds[i]->points.size();
}
cloud_xyz.points.resize(point_count);
point_count = 0;
for(int i=0; i < NUM_CAPTURES; i++) {
if(clouds[i]) {
PointCloud::ConstPtr cloud = rotateCloud(zTranslateCloud(xTranslateCloud(clouds[i])), i);
for (size_t j = 0; j < cloud->points.size(); j++) {
pcl::PointXYZRGB point = cloud->points[j];
if(point.x == point.x) // Gaurd against NaN
{
cloud_xyz.points[point_count].x = point.x;
cloud_xyz.points[point_count].y = point.y;
cloud_xyz.points[point_count++].z = point.z;
}
}
}
}
pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud = cloud_xyz.makeShared();
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud (cloud);
n.setInputCloud (cloud);
n.setSearchMethod (tree);
n.setKSearch (20);
n.compute (*normals);
//* normals should not contain the point normals + surface curvatures
// Concatenate the XYZ and normal fields*
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals (new pcl::PointCloud<pcl::PointNormal>);
pcl::concatenateFields (*cloud, *normals, *cloud_with_normals);
//* cloud_with_normals = cloud + normals
// Create search tree*
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2 (new pcl::search::KdTree<pcl::PointNormal>);
tree2->setInputCloud (cloud_with_normals);
// Initialize objects
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
pcl::PolygonMesh triangles;
// Set the maximum distance between connected points (maximum edge length)
gp3.setSearchRadius (0.2);
// Set typical values for the parameters
const int mu = 2;
const int maxNearestNeighbors = 200;
const double maxSurfaceAngle = 2 * M_PI / 3;
const double minAngle = M_PI / 18;
const double maxAngle = 2 * M_PI / 3;
const bool normalConsistency = false;
gp3.setMu (mu);
gp3.setMaximumNearestNeighbors(maxNearestNeighbors);
gp3.setMaximumSurfaceAngle(maxSurfaceAngle);
gp3.setMinimumAngle(minAngle);
gp3.setMaximumAngle(maxAngle);
gp3.setNormalConsistency(normalConsistency);
// Get result
gp3.setInputCloud (cloud_with_normals);
gp3.setSearchMethod (tree2);
gp3.reconstruct (triangles);
/*Create Filename*/
time_t t = time(0);
struct tm * date = localtime( & t );
std::stringstream mesh_name_stream;
mesh_name_stream << (date->tm_year + 1900) << '-'
<< (date->tm_mon + 1) << '-'
<< date->tm_mday << "-"
<< date->tm_hour << "-"
<< date->tm_min << "-mesh";
std::string mesh_name = mesh_name_stream.str();
ofstream detailsFile(SAVE_DIRECTORY + mesh_name + ".txt");
detailsFile << "mu = " << mu << std::endl
<< "maximum nearest neighbors = " << maxNearestNeighbors << endl
<< "maximum surface angle = " << maxSurfaceAngle << endl
<< "minimum angle = " << minAngle << endl
<< "maximum angle = " << maxAngle << endl
<< "normal consistency = " << normalConsistency << endl;
detailsFile.close();
pcl::io::saveVTKFile(SAVE_DIRECTORY + mesh_name + ".vtk", triangles);
pcl::io::saveOBJFile(SAVE_DIRECTORY + mesh_name + ".obj", triangles);
viewer->addPolygonMesh(triangles);
}
void loadPointClouds()
{
for(int i = 0; i < NUM_CAPTURES; i++) {
PointCloud::Ptr cloud (new PointCloud);
std::string filename = cloudName(i, ".pcd");
int error = pcl::io::loadPCDFile<pcl::PointXYZRGB>(LOAD_DIRECTORY + filename, *cloud);
if(error != -1)
{
xTranslation = 0;
zTranslation = 0;
clouds[i] = cloud;
}
}
updateClouds();
}
void savePointClouds()
{
for(int i = 0; i < NUM_CAPTURES; i++) {
if(clouds[i]) {
PointCloud::ConstPtr cloud = zTranslateCloud(xTranslateCloud(clouds[i]));
pcl::io::savePCDFileASCII (SAVE_DIRECTORY + cloudName(i, ".pcd"), *cloud);
pcl::io::savePLYFile (SAVE_DIRECTORY + cloudName(i, ".ply"), *cloud);
}
}
}
void initializeGrabber() {
grabber = new pcl::OpenNIGrabber ();
boost::function<void(const PointCloud::ConstPtr&)> callback = boost::bind(&cloud_callback, _1);
boost::signals2::connection c1 = grabber->registerCallback(callback);
grabber->start();
}
void keyboard_callback (const pcl::visualization::KeyboardEvent& event)
{
if(event.keyDown())
{
char keyCode = event.getKeyCode();
switch(keyCode)
{
case 32: // SPACE
if(!grabber)
initializeGrabber();
capture();
break;
case 13: // ENTER
createMesh();
break;
case 111: // o
savePointClouds();
break;
case 79: // O
loadPointClouds();
break;
case 45: // -
currentCloud--;
break;
case 61: // +
currentCloud= currentCloud + 1 % NUM_CAPTURES;
break;
case 97: //a
xTranslation += X_STEP;
updateClouds();
break;
case 100: //d
xTranslation -= X_STEP;
updateClouds();
break;
case 119: //w
zTranslation += Z_STEP;
updateClouds();
break;
case 115: //s
zTranslation -= Z_STEP;
updateClouds();
break;
}
}
}
void initializeViewer() {
viewer.reset(new pcl::visualization::PCLVisualizer ("OpenNI Viewer"));
viewer->registerKeyboardCallback(&keyboard_callback);
viewer->getRenderWindow()->SetSize(WIDTH, HEIGHT);
viewer->addLine(pcl::PointXYZ(0,0,0), pcl::PointXYZ(1, 0, 0), 255, 0, 0, "xaxis");
viewer->addLine(pcl::PointXYZ(0,0,0), pcl::PointXYZ(0,-1, 0), 0, 255, 0, "yaxis");
viewer->addLine(pcl::PointXYZ(0,0,0), pcl::PointXYZ(0, 0, 1), 0, 0, 255, "zaxis");
viewer->spinOnce();
}
int main (int argc, char** argv)
{
initializeViewer();
while (!viewer->wasStopped ())
{
viewer->spinOnce();
boost::this_thread::sleep (boost::posix_time::microseconds(200));
}
if(grabber);
grabber->stop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment