How do you access the individual vertices inside of a pcl::PolygonMesh? - c++

I'm currently using visual studio 2022
I'm using vcpkg pcl:x64 library install.
pcl version: 1.9.1-12
I'm expecting to be able to access 3 vertices per polygon.
Unfortunately, I can't seem to access the vertices associated with each triangle.
#include <Eigen/Dense>
#include <pcl/common/io.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/passthrough.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/PolygonMesh.h>
#include <pcl/TextureMesh.h>
int main
{
pcl::PolygonMesh mesh;
pcl::io::loadPolygonFileOBJ("pathtomesh.meshfile.obj", mesh);
pcl::PointXYZ v = mesh.polygons[0].vertices[0];
}
the error i recieve is:
no suitable constructor exists to convert from "boost::random::seed_seq::result_type" to "pcl::PointXYZ"

It seems as though vertices is an unsigned int and not a pcl::PointXYZ. This is kinda weird to me because I was expecting a double or a floating point to store the vertices coordinates. It turns out that mesh.polygons[0].vertices[0] returns the indices of each point in the mesh that are stored in the point cloud. So i was able to find the points that the function mesh.polygons[0].vertices[0] were pointing at by using converting the mesh to a pcl::PointCloudpcl::PointXYZ and putting the indexes into that function.
pcl::PolygonMesh mesh;
pcl::io::loadPolygonFileOBJ("D:\\testOBJs\\cube.obj", mesh);
pcl::PointCloud<pcl::PointXYZ>::Ptr allVertices(new pcl::PointCloud<pcl::PointXYZ>);
pcl::fromPCLPointCloud2(mesh.cloud, *allVertices);
std::cout << "All Vertices" << std::endl;
for (int i = 0; i < allVertices->size(); i++)
{
std::cout << std::to_string(i) + " " << allVertices->points[i] << std::endl;
}
std::cout << "All Polygons" << std::endl;
for (int i = 0; i < mesh.polygons.size(); i++)
{
std::cout << std::endl;
std::cout << mesh.polygons[i].vertices[0] << std::endl;
std::cout << mesh.polygons[i].vertices[1] << std::endl;
std::cout << mesh.polygons[i].vertices[2] << std::endl;
std::cout << std::endl;
}

Related

Trouble when using Efficient_Ransac in CGAL

I want to use the Efficient Ransac implementation of CGAL, but whenever I try to set my own parameters, the algorithm doesn't detect any shape anymore.
This work is related to the Polyfit implementation in CGAL. I want to fine tune the plane detection to see the influence it has on the algorithm. When I use the standard call to ransac.detect(), it works perfectly. However, when I want to set my own parameters it just doesn't find any plane, even if I set them manually to the default values.
Here is my code, strongly related to this example
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_xyz_points.h>
#include <CGAL/IO/Writer_OFF.h>
#include <CGAL/property_map.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Shape_detection/Efficient_RANSAC.h>
#include <CGAL/Polygonal_surface_reconstruction.h>
#ifdef CGAL_USE_SCIP
#include <CGAL/SCIP_mixed_integer_program_traits.h>
typedef CGAL::SCIP_mixed_integer_program_traits<double> MIP_Solver;
#elif defined(CGAL_USE_GLPK)
#include <CGAL/GLPK_mixed_integer_program_traits.h>
typedef CGAL::GLPK_mixed_integer_program_traits<double> MIP_Solver;
#endif
#if defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
#include <CGAL/Timer.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point;
typedef Kernel::Vector_3 Vector;
// Point with normal, and plane index
typedef boost::tuple<Point, Vector, int> PNI;
typedef std::vector<PNI> Point_vector;
typedef CGAL::Nth_of_tuple_property_map<0, PNI> Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNI> Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNI> Plane_index_map;
typedef CGAL::Shape_detection::Efficient_RANSAC_traits<Kernel, Point_vector, Point_map, Normal_map> Traits;
typedef CGAL::Shape_detection::Efficient_RANSAC<Traits> Efficient_ransac;
typedef CGAL::Shape_detection::Plane<Traits> Plane;
typedef CGAL::Shape_detection::Point_to_shape_index_map<Traits> Point_to_shape_index_map;
typedef CGAL::Polygonal_surface_reconstruction<Kernel> Polygonal_surface_reconstruction;
typedef CGAL::Surface_mesh<Point> Surface_mesh;
int main(int argc, char ** argv)
{
Point_vector points;
// Loads point set from a file.
const std::string &input_file = argv[1];
//const std::string input_file(input);
std::ifstream input_stream(input_file.c_str());
if (input_stream.fail()) {
std::cerr << "failed open file \'" <<input_file << "\'" << std::endl;
return EXIT_FAILURE;
}
std::cout << "Loading point cloud: " << input_file << "...";
CGAL::Timer t;
t.start();
if (!input_stream ||
!CGAL::read_xyz_points(input_stream,
std::back_inserter(points),
CGAL::parameters::point_map(Point_map()).normal_map(Normal_map())))
{
std::cerr << "Error: cannot read file " << input_file << std::endl;
return EXIT_FAILURE;
}
else
std::cout << " Done. " << points.size() << " points. Time: " << t.time() << " sec." << std::endl;
// Shape detection
Efficient_ransac ransac;
ransac.set_input(points);
ransac.add_shape_factory<Plane>();
std::cout << "Extracting planes...";
t.reset();
// Set parameters for shape detection.
Efficient_ransac::Parameters parameters;
// Set probability to miss the largest primitive at each iteration.
parameters.probability = 0.05;
// Detect shapes with at least 500 points.
parameters.min_points = 100;
// Set maximum Euclidean distance between a point and a shape.
parameters.epsilon = 0.01;
// Set maximum Euclidean distance between points to be clustered.
parameters.cluster_epsilon = 0.01;
// Set maximum normal deviation.
// 0.9 < dot(surface_normal, point_normal);
parameters.normal_threshold = 0.9;
// Detect shapes.
ransac.detect(parameters);
//ransac.detect();
Efficient_ransac::Plane_range planes = ransac.planes();
std::size_t num_planes = planes.size();
std::cout << " Done. " << num_planes << " planes extracted. Time: " << t.time() << " sec." << std::endl;
// Stores the plane index of each point as the third element of the tuple.
Point_to_shape_index_map shape_index_map(points, planes);
for (std::size_t i = 0; i < points.size(); ++i) {
// Uses the get function from the property map that accesses the 3rd element of the tuple.
int plane_index = get(shape_index_map, i);
points[i].get<2>() = plane_index;
}
//////////////////////////////////////////////////////////////////////////
std::cout << "Generating candidate faces...";
t.reset();
Polygonal_surface_reconstruction algo(
points,
Point_map(),
Normal_map(),
Plane_index_map()
);
std::cout << " Done. Time: " << t.time() << " sec." << std::endl;
//////////////////////////////////////////////////////////////////////////
Surface_mesh model;
std::cout << "Reconstructing...";
t.reset();
if (!algo.reconstruct<MIP_Solver>(model)) {
std::cerr << " Failed: " << algo.error_message() << std::endl;
return EXIT_FAILURE;
}
const std::string& output_file(input_file+"_result.off");
std::ofstream output_stream(output_file.c_str());
if (output_stream && CGAL::write_off(output_stream, model))
std::cout << " Done. Saved to " << output_file << ". Time: " << t.time() << " sec." << std::endl;
else {
std::cerr << " Failed saving file." << std::endl;
return EXIT_FAILURE;
}
//////////////////////////////////////////////////////////////////////////
// Also stores the candidate faces as a surface mesh to a file
Surface_mesh candidate_faces;
algo.output_candidate_faces(candidate_faces);
const std::string& candidate_faces_file(input_file+"_candidate_faces.off");
std::ofstream candidate_stream(candidate_faces_file.c_str());
if (candidate_stream && CGAL::write_off(candidate_stream, candidate_faces))
std::cout << "Candidate faces saved to " << candidate_faces_file << "." << std::endl;
return EXIT_SUCCESS;
}
#else
int main(int, char**)
{
std::cerr << "This test requires either GLPK or SCIP.\n";
return EXIT_SUCCESS;
}
#endif // defined(CGAL_USE_GLPK) || defined(CGAL_USE_SCIP)
When launched, I have the following message:
Loading point cloud: Scene1/test.xyz... Done. 169064 points. Time: 0.428 sec.
Extracting planes... Done. 0 planes extracted. Time: 8.328 sec.
Generating candidate faces... Done. Time: 0.028 sec.
Reconstructing... Failed: at least 4 planes required to reconstruct a closed surface mesh (only 1 provided)
While I have this when launching the code the ransac detection function without parameters:
Loading point cloud: Scene1/test.xyz... Done. 169064 points. Time: 0.448 sec.
Extracting planes... Done. 18 planes extracted. Time: 3.088 sec.
Generating candidate faces... Done. Time: 94.536 sec.
Reconstructing... Done. Saved to Scene1/test.xyz_result.off. Time: 30.28 sec.
Can someone help me setting my own parameters for the ransac shape detection?
However, when I want to set my own parameters it just doesn't find any
plane, even if I set them manually to the default values.
Just to be sure: "setting them manually to the default values" is not what you are doing in the code you shared.
Default values are documented as:
1% of the total number of points for min_points, which should be around 1700 points in your case, not 100
1% of the bounding box diagonal for epsilon and cluster_epsilon. For that obviously I don't know if that is what you used (0.01) as I don't have access to your point set, but if you want to reproduce default values, you should use the CGAL::Bbox_3 object at some point
If you use these values, there's no reason why it should behave differently than with no parameters given (if it does not work, then please let me know because there may be a bug).

Reading out Pixel Values, png++, C++

I am trying to load the pixel rgb/ga information of a png image into a matrix, using the library png++, to do some computations with them.
My Code (which does not work at the moment):
#include <iostream>
#include <png++/image.hpp>
#include <png++/rgb_pixel.hpp>
int main(int argc, const char * argv[]) {
const std::string path="img_03.png";
png::image< png::basic_rgb_pixel <unsigned char> > pic(path);
pixel=pic.get_pixel(0, 0);
pixelp = &pixel;
std::cout << "value=" << pic[10][10].red << std::endl; //output: '?'
std::cout << "value=" << pixel.red << std::endl; //nothing
std::cout << "pointer=" << pixelp << std::endl; //delivers adress
pic.read(path);
std::cout << "value=" << pic[10][10].red << std::endl; //nothing
pic.write("picOutput.png"); //same picture
return 0;
}
However, none of those methods work to get the rgb values of each pixel.
If there is another way to get rgb/ga information of each pixel, please mention it.
The line pic.write("picOutput.png"); delivers the same png i loaded in the line pic.read(path). This is a personal exercise for me to get more used to C++, criticise my code as much as you can.
Thanks!
Here comes the solution:
change line:
std::cout << "value=" << pic[10][10].red << std::endl; //nothing
with:
std::cout << "value=" << (int) pic[10][10].red << std::endl; //nothing
because std::cout can't output types of unsigned char.
Thanks to Alex!
For in-depth explanation, look here:
cout not printing unsigned char

K-means clustering on point cloud data

I am trying to implement K-means clustering algorithm on a point cloud. I am not sure, however, how to import the data as input for the k-means member of pcl. The documentation has proven to be a little confusing.
So far I have imported the pcd into a point cloud and transferred it into a vector but I dont know how to proceed from here and initialize Kmeans directly.
int main (int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
std::vector<pcl::PointXYZ> cloud;
pcl::io::loadPCDFile ("Scene02 - Cloud.pcd", *cloud_in);
for (int i = 0; i < cloud_in->size(); i++)
{
cloud[i] = cloud_in->points[i];
}
pcl::Kmeans real(300000, 3);
real.setInputData(cloud);
}
I realize that the syntax is wrong but I am not sure what the right one is either.
This function is very odd as compared to how pcl generally does things (centering around custom point types). Basically, the oddity is that you have to enter points via a specified dimension vector rather than a custom point type. Here is tested and functional sample code: (obviously you need to provide your own file name, and you will likely want to adjust cluster size)
int main(int argc, char** argv) {
std::string filePath = "../PointCloudFiles/beaconJR.pcd";
pcl::PointCloud<pcl::PointXYZ>::Ptr tempCloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile(filePath, *tempCloud) == -1) //* load the file
{printf("failed file load!\n");}
else
{
pcl::Kmeans real(static_cast<int> (tempCloud->points.size()), 3);
real.setClusterSize(3); //it is important that you set this term appropriately for your application
for (size_t i = 0; i < tempCloud->points.size(); i++)
{
std::vector<float> data(3);
data[0] = tempCloud->points[i].x;
data[1] = tempCloud->points[i].y;
data[2] = tempCloud->points[i].z;
real.addDataPoint(data);
}
real.kMeans();
// get the cluster centroids
pcl::Kmeans::Centroids centroids = real.get_centroids();
std::cout << "points in total Cloud : " << tempCloud->points.size() << std::endl;
std::cout << "centroid count: " << centroids.size() << std::endl;
for (int i = 0; i<centroids.size(); i++)
{
std::cout << i << "_cent output: x: " << centroids[i][0] << " ,";
std::cout << "y: " << centroids[i][1] << " ,";
std::cout << "z: " << centroids[i][2] << std::endl;
}
}
std::cin.get();
std::cin.get();
}
Cheers!
--edit
As far as visualizing the clusters. I think (untested) that "pcl::Kmeans::PointsToClusters" is going to give you a vector with custer labels per point which you can use to index through the original cloud and separate them.

This predefined function slowing down my program's performance

I am working on a PCL (Point Cloud Library) project. One part of it requires me to clip point clouds, for which I need to know the minimum and maximum coordinates of given point cloud.
PCL provides a predefined function called getminmax3d(). I tried and It works well, The only problem is, It takes a lot of time when I input a large point cloud file. I made my own definition of getminmax3d() and it takes lesser time. I am not understanding why these two behave like this.
I tried with 5 point cloud data files. In all cases, program that uses predefined function takes long time as compare to the program for which I defined the definition.
Here is the code:
First implementation - It uses predefined function getminmax3d()
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/common.h>
int main (int, char**)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud;
cloud = pcl::PointCloud<pcl::PointXYZ>::Ptr (new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile<pcl::PointXYZ> ("your_pcd_file.pcd", *cloud);
pcl::PointXYZ minPt, maxPt;
pcl::getMinMax3D (*cloud, minPt, maxPt);
std::cout << "Max x: " << maxPt.x << std::endl;
std::cout << "Max y: " << maxPt.y << std::endl;
std::cout << "Max z: " << maxPt.z << std::endl;
std::cout << "Min x: " << minPt.x << std::endl;
std::cout << "Min y: " << minPt.y << std::endl;
std::cout << "Min z: " << minPt.z << std::endl;
return (0);
}
Second implementation - This source code uses a user-defined function definition to replace functionality of getminmax3d()
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/time.h>
int main (int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ> ("rhino.pcd", *cloud) == -1) //* load the file
{
PCL_ERROR ("Couldn't read file rhino.pcd \n");
return (-1);
}
float min_x = cloud->points[0].x, min_y = cloud->points[0].y, min_z = cloud->points[0].z, max_x = cloud->points[0].x, max_y = cloud->points[0].y, max_z = cloud->points[0].z;
pcl::StopWatch watch;
for (size_t i = 1; i < cloud->points.size (); ++i){
if(cloud->points[i].x <= min_x )
min_x = cloud->points[i].x;
else if(cloud->points[i].y <= min_y )
min_y = cloud->points[i].y;
else if(cloud->points[i].z <= min_z )
min_z = cloud->points[i].z;
else if(cloud->points[i].x >= max_x )
max_x = cloud->points[i].x;
else if(cloud->points[i].y >= max_y )
max_y = cloud->points[i].y;
else if(cloud->points[i].z >= max_z )
max_z = cloud->points[i].z;
}
pcl::console::print_highlight ("Time taken: %f\n", watch.getTimeSeconds());
std::cout << "Min x: " << min_x <<"\t";
std::cout << "Max x: " << max_x << std::endl;
std::cout << "Min y: " << min_y <<"\t";
std::cout << "Max y: " << max_y << std::endl;
std::cout << "Min z: " << min_z <<"\t";
std::cout << "Max z: " << max_z << std::endl;
return (0);
}
I tried both programs on following 5 point cloud files.
Result obtained:
ttf : Time taken factor
ttf = 15 means user definition is about 15 times faster than predefined functions. ttf value is measured by taking average of 10 trials for both implementations.
PCD file Filetype File size ttf
Rhino.pcd XYZ 2.57 MB 15.260
Bun_zipper XYZCI 1.75 MB 17.422
Armadillo XYZ 5.26 MB 15.847
Dragon_vrip XYZ 14.7 MB 17.013
Happy_vrip XYZ 18.0 MB 14.981
I am wondering why predefined function is taking more time? I want to reduce my program source code lines. I've always believed that using standard header files and their function gives you best performance, But in this case it seems to fail.
This is where you can find standard definition.
Would anyone please help me to find out why second implementation takes less times(approx 15 times), even the standard definition of getminmax3d() is similar to mine.
pcl::getMinMax3D has a very inefficient implementation. To search for the minimum and max point it does the following:
Eigen::Array4f min_p, max_p;
min_p.setConstant (FLT_MAX);
max_p.setConstant (-FLT_MAX);
for (size_t i = 0; i < cloud.points.size (); ++i)
{
// ... (check the validity of the point if it is not a dense cloud)
pcl::Array4fMapConst pt = cloud.points[i].getArray4fMap ();
min_p = min_p.min (pt);
max_p = max_p.max (pt);
}
And if you check for the getArray4fMap() function:
typedef Eigen::Map<Eigen::Array4f, Eigen::Aligned> Array4fMap;
inline pcl::Array4fMap getArray4fMap() const {
return (pcl::Array4fMap(data));
}
For each point in the cloud it is constructing an Eigen::Map and then comparing it against the current minimum and maximum points. This is VERY inefficient.
The predefined function pcl::getMinMax3D is able to be faster with optimization flags set and in Release. Since if SSE intrinsics are used by Eigen, then the operations happen on 4 aligned bytes.
More information at
https://gitter.im/PointCloudLibrary/pcl?at=5e3899d06f9d3d34981c0687

how to iterate through boost unordered_map?

I just want to iterate through the members of an unordered map.
There are many simple examples on the web, including on this site, and yet none of them will compile. Apparently some examples are from a previous non-standard STL version, some are just old, and some are so new that my gcc 4.7.2 can't handle them. Please do not suggest the new auto iterator from C++11. I will get there some day when all my libraries are validated for that. Until then, I just want the old one to work. (see below for what I have tried)
Here is my test code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
//can't get gcc to accept the value_type()...
//umap.insert(boost::unordered_map::value_type("alpha",1));
//umap.insert(boost::unordered_map::value_type("beta",2));
//umap.insert(boost::unordered_map::value_type("gamma",3));
umap["alpha"]=1; //this works
umap["beta"]=2;
umap["gamma"]=3;
//can't get gcc to compile the iterator
//for (boost::unordered_map::iterator it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//gcc does not like it this way either
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x].first << " = " << umap[x].second << std::endl;
//will gcc take this? No it does not
//for (int x=0;x<umap.size();x++)
// std::cout << x << " : " << umap[x] << std::endl;
//this does not work
//boost::unordered::unordered_map::iterator<std::string,int> it;
//this does not work
//boost::unordered::unordered_map::iterator it;
//for (it=umap.begin();it!=umap.end();++it)
// std::cout << it->first <<", " << it->second << std::endl;
//this does not work
//BOOST_FOREACH(boost::unordered_map::value_type value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
//this does not work either
//BOOST_FOREACH(boost::unordered_map::value_type<std::string,int> value, umap) {
// std::cout << value.second;
// }
//std::cout << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
Most of the errors are a variation of this:
error: 'template<class K, class T, class H, class P, class A> class boost::unordered::unordered_map' used without template parameters
Here is my build command:
g++ -I..\boost umap.cpp
I should be embarrassed for getting stuck on such a beginner's question, but from the volume of similar questions I am finding, this is just hard enough to stop a lot of people in their tracks. I have written hash containers before (back when it was recommended NOT to use STL) and I am very tempted to just write my own... but the right thing to do is learn to use as many existing tools as possible... help!
I've looked at the following questions on stackoverflow where I haven't found an answer:
iterate through unordered_map using boost_foreach
I tried:
BOOST_FOREACH(boost::unordered_map::value_type& value, umap) {
but it gives the same error I show below.
Unordered_map iterator invalidation
This one is close, but not quite my issue:
Iterator invalidation in boost::unordered_map
This one uses auto
and I can't switch compilers at this time.
C++ some questions on boost::unordered_map & boost::hash
This one is mostly about the theory of maps:
how to use boost::unordered_map
This is a rather complicated example, but you will see in my code I am already trying to declare iterators... they just won't compile.
How to use BOOST_FOREACH with an Unordered_map?
This is a nice example, but
it just does not compile. I tried a version of this in my code.
IT WORKS !
Here is the working code:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[]) {
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
boost::unordered::unordered_map<std::string,int>::iterator it;
for (it=umap.begin();it!=umap.end();++it)
std::cout << it->first <<", " << it->second << std::endl;
std::cout << "umap size: " << umap.size() << std::endl;
std::cout << "umap max size: " << umap.max_size() << std::endl;
std::cout << "find alpha: " << (umap.find("alpha")!=umap.end()) << std::endl;
std::cout << "count beta: " << umap.count("beta") << std::endl;
}
It was a syntax error. I was putting the type in the wrong place when declaring the iterator.
Thanks everyone for your responses.
try changing boost::unordered::unordered_map::iterator it; it to boost::unordered::unordered_map<std::string,int>::iterator it;
NOTE:
It is also possible, and a good idea in more complex situations, to create a typedef of it, such as typedef boost::unordered::unordered_map<std::string,int>::iterator UMapStringIntIt;, or whatever you may call it.
The answer is in the question, but the simple solution is here for your convenience:
#include <iostream>
#include <boost/unordered_map.hpp>
#include <string>
int main(int argc,char *argv[])
{
boost::unordered::unordered_map<std::string,int> umap;
umap["alpha"]=1;
umap["beta"]=2;
umap["gamma"]=3;
for ( auto it= umap.begin(); it != umap.end(); ++it )
std::cout << it->first <<", " << it->second << std::endl;
}