How to get camera's parameters at PCL? - c++

I installed PCL1.7.2. and I am trying use PCL libraries.
I want to show camera's parameters by "const", so, I want to get camera's parameters. But I don't understand how to get the camera's parameters.
I saw the "pcl::visualization::Camera Class Reference".
http://docs.pointclouds.org/trunk/classpcl_1_1visualization_1_1_camera.html
and I understood there are focal, pos, view etc on the "Camera" object.
and now I have confirmed that the following code run.
but I can't understand how to get Camera's member.
this is how to set Camera's member values.
viewer.setCameraPosition(pos_x, pos_y, pos_z, view_x, view_y, view_z, up_x, up_y, up_z, viewport);
so, someone please show me how to get Camera's parameters at the following code.
this is now roading source.
#include "stdafx.h"
#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
int user_data=0;
void
viewerOneOff(pcl::visualization::PCLVisualizer& viewer)
{
viewer.setBackgroundColor(1.0, 0.5, 1.0);
pcl::PointXYZ o;
o.x = 1.0;
o.y = 0;
o.z = 0;
viewer.addSphere(o, 0.25, "sphere", 0);
std::cout << "i only run once" << std::endl;
}
void
viewerPsycho(pcl::visualization::PCLVisualizer& viewer)
{
static unsigned count = 0;
std::stringstream ss;
ss << "Once per viewer loop: " << count++;
viewer.removeShape("text", 0);
viewer.addText(ss.str(), 200, 300, "text", 0);
//FIXME: possible race condition here:
user_data++;
}
int _tmain(int argc, const _TCHAR** argv)
{
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPCDFile("c:\\data\\triceratops\\raw_0.pcd", *cloud);
pcl::visualization::CloudViewer viewer("Cloud Viewer");
//blocks until the cloud is actually rendered
viewer.showCloud(cloud);
//use the following functions to get access to the underlying more advanced/powerful
//PCLVisualizer
//This will only get called once
viewer.runOnVisualizationThreadOnce(viewerOneOff);
//This will get called once per visualization iteration
viewer.runOnVisualizationThread(viewerPsycho);
while (!viewer.wasStopped())
{
//you can also do cool processing here
//FIXME: Note that this is running in a separate thread from viewerPsycho
//and you should guard against race conditions yourself...
user_data++;
}
return 0;
}

This should work:
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
std::vector<pcl::visualization::Camera> cam;
//Save the position of the camera
viewer->getCameras(cam);
//Print recorded points on the screen:
cout << "Cam: " << endl
<< " - pos: (" << cam[0].pos[0] << ", " << cam[0].pos[1] << ", " << cam[0].pos[2] << ")" << endl
<< " - view: (" << cam[0].view[0] << ", " << cam[0].view[1] << ", " << cam[0].view[2] << ")" << endl
<< " - focal: (" << cam[0].focal[0] << ", " << cam[0].focal[1] << ", " << cam[0].focal[2] << ")" << endl;

Related

weird glm::mat2x4 assignment behaviour

I am trying to load freetype chars, stuff them into a texture as subimages and then render them instanced.
While most of it seems to work, right now I have a problem with storing the texture coordinates into a glm::mat2x4 matrix.
As can be seen below each character has a struct with information I right now deem necessary, including a matrix called face, which should store the texture coordinates.
But when it comes to assigning the coordinates, after leaving the loop in which it takes place, suddenly all the values go crazy, without any (wanted/ intended) operation taking place from my side.
After creating the texture atlas with freetype and putting all my structs into the map, I assign the width and height of my texture aw & ah to a storage class called c_atlas.
I calculate the texture coordinates in the loop shown below, make the glm::mat2x4 a 0.0f matrix and then stuff them into it. Couting them into the console gives the values I want.
After leaving the for loop I start another one, browsing over the matrix and cout them into the console, which gives me more or less random values in the range of e^-23 to e^32.
All of this happens in namespace foo and is called in a constructor of a class in the same namespace (sth. like this:)
foo::class::constructor()
{
call_function();
}
int main()
{
foo::class c;
c.call_function();
}
I crafted a minimum working example, but unfortunatly I am not able to replicate the error.
So I have the following loop running (a part of call_function():
namespace foo
{
namespace alphabet
{
const char path_arial[] = "res/font/consola.ttf";
class character
{
public:
glm::vec2 advance;
glm::vec2 bearing;
glm::vec2 size;
glm::vec2 offset;
glm::mat2x4 face;
};
std::map<char, character> char_map;
FT_Library m_ftlib;
FT_Face m_ftface;
GLuint m_VBO, m_VAO;
}
c_atlas ascii;
}
void foo::call_function()
{
//creating all the charactur structs with freetype and store them in the char_map
std::ofstream f("atlas_data.csv", std::ios::openmode::_S_app);
f << "letter;topleft.x;topleft.y;topright.x;topright.y;bottomright.x;bottomright.y;bottomleft.x;bottomleft.y" << std::endl;
for(auto c : alphabet::char_map)
{
std::cout << "b4: " << c.second.offset.x;
c.second.offset /= glm::vec2(aw,ah);
std::cout << "\nafter: " << c.second.offset.x << std::endl;
glm::vec2 ts = c.second.size/glm::vec2(aw,ah);
//couts the right values
uint16_t n = 0;
c.second.face = glm::mat2x4(0.0f);
for(uint16_t i = 0; i < 4; ++i)
{
std::cout << c.first << " at init:\n";
std::cout << c.second.face[0][i] << "\n";
std::cout << c.second.face[1][i] << std::endl;
}
//couts the right values
c.second.face[0][n++] = c.second.offset.x;
c.second.face[0][n++] = c.second.offset.y;
c.second.face[0][n++] = c.second.offset.x+ts.x;
c.second.face[0][n++] = c.second.offset.y;
n = 0;
c.second.face[1][n++]= c.second.offset.x+ts.x;
c.second.face[1][n++] = c.second.offset.y+ts.y;
c.second.face[1][n++] = c.second.offset.x;
c.second.face[1][n++]= c.second.offset.y+ts.y;
for(uint16_t i = 0; i < 4; ++i)
{
std::cout << c.first << " assigned:\n";
std::cout << c.second.face[0][i] << "\n";
std::cout << c.second.face[1][i] << std::endl;
}
//still couts the right values
f << (char)c.first << ";" << c.second.face[0].x << ";" << c.second.face[0].y << ";" << c.second.face[0].z << ";" << c.second.face[0].w << ";" << c.second.face[1].x << ";" << c.second.face[1].y << ";" << c.second.face[1].z << ";" << c.second.face[1].w << std::endl;
//the file also have the right values
}
f.close();
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
//yet here all the values totally off track, i.e. e^32 or e^-23 (while they should all be between 0.01f - 1.0f)
for(auto i : alphabet::char_map)
{
std::cout << "\ntopleft:\n";
std::cout << "X: " << i.second.face[0].x << " | " << "Y: " << i.second.face[0].x;
std::cout << "\ntopright:\n";
std::cout << "X: " << i.second.face[0].z << " | " << "Y: " << i.second.face[0].w;
std::cout << "\nbotleft:\n";
std::cout << "X: " << i.second.face[1].x << " | " << "Y: " << i.second.face[1].x;
std::cout << "\nbotright:\n";
std::cout << "X: " << i.second.face[1].z << " | " << "Y: " << i.second.face[1].w;
}
}
my mwe:
#include <iostream>
#include <string>
#include "glm/glm.hpp"
#include "GL/gl.h"
#include <map>
struct bin
{
glm::mat2x4 mat;
};
int main( int argc, char *argv[] )
{
std::map<char, bin> bucket;
uint16_t r = 0;
for(uint16_t n = 0; n < 7; ++n)
{
glm::vec4 v = glm::vec4(0.12128f, 0.12412f, 0.15532f, 0.23453f);
bin b;
r = 0;
b.mat[0][r++] = v.x;
b.mat[0][r++] = v.y;
b.mat[0][r++] = v.z;
b.mat[0][r++] = v.w;
r = 0;
b.mat[1][r++] = v.x;
b.mat[1][r++] = v.y;
b.mat[1][r++] = v.z;
b.mat[1][r++] = v.w;
bucket[n] = b;
}
for(auto it : bucket)
{
r = 0;
std::cout << "0:\t" << it.second.mat[0][0] << "\t" << it.second.mat[0][1] << "\t" << it.second.mat[0][2] << "\t" << it.second.mat[0][3] << "\n";
r = 0;
std::cout << "1:\t" << it.second.mat[1][0] << "\t" << it.second.mat[1][1] << "\t" << it.second.mat[1][2] << "\t" << it.second.mat[1][3] << std::endl;
}
return 0;
}
Right now I am totally lost, especially as my mwe works fine.
I am clueless what goes wrong after leaving the for-loop, so thanks for any thought on that!
Indeed, I could just rewrite that section and hope it would work - as my mwe does. But I would like to find out/ get help on finding out what exactly happens between the "assign" for loop and the "retrieve" for loop. Any ideas on that?
I made it work for me now:
Appartenly assigning the values this way:
for(auto c : alphabet::char_map)
{
c.second.face[0][n++] = c.second.offset.x;
//and so on
}
Did not work properly (for whatever reason..)
Changing this into a for(uint16_t i = 32; i < 128; ++i) worked for me. Also it was just the assigning loop, the auto-iterating ofer the map elsewhere works just fine.

Is there a function similar MATLAB's 'impixelinfo()' available in OpenCV?

I'm searching for a function in OpenCV that is similar to impixelinfo() in MATLAB.
impixelinfo() shows you
the location of the pixel (x, y) and
the pixel intensity of your cursor hovering in the image,
like:
impixelinfo() in matlab shows you this
Is there any implementation of this in OpenCV already? Does anyone have a personal version of it created?
You can do something like this:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat img;
void
CallBackFunc(int event,int x,int y,int flags,void* userdata)
{
if(event==EVENT_MOUSEMOVE){
cout << "Pixel (" << x << ", " << y << "): " << img.at<Vec3b>(y,x) << endl;
}
}
int main()
{
// Read image from file
img=imread("demo.jpg");
// Check it loaded
if(img.empty())
{
cout << "Error loading the image" << endl;
exit(1);
}
//Create a window
namedWindow("ImageDisplay",1);
// Register a mouse callback
setMouseCallback("ImageDisplay",CallBackFunc,nullptr);
// Main loop
while(true){
imshow("ImageDisplay",img);
waitKey(50);
}
}
As a result of the helpful comments, I (hopefully) improved the code and now handle grayscale images, and have also set the RGB ordering more akin to how non-OpenCV aficionados might expect it - i.e. RGB rather than BGR. The updated function is below:
void
CallBackFunc(int event,int x,int y,int flags,void* userdata)
{
if(event==EVENT_MOUSEMOVE){
// Test if greyscale or color
if(img.channels()==1){
cout << "Grey Pixel (" << x << ", " << y << "): " << (int)img.at<uchar>(y,x) << endl;
} else {
cout << "RGB Pixel (" << x << ", " << y << "): " << (int)img.at<Vec3b>(y,x)[2] << "/" << (int)img.at<Vec3b>(y,x)[1] << "/" << (int)img.at<Vec3b>(y,x)[0] << endl;
}
}
}

Wrong inexact intersection between 3D triangles (CGAL)

I'm having a really weird bug when trying to intersect two triangles inside a 3D space while using the CGAL::Exact_predicates_inexact_constructions_kernel kernel. Essentially, I have two triangles that should not intersect. The function CGAL::do_intersect returns always false when testing them, but the function CGAL::intersection builds an intersection, depending on the order of the vertices of the triangles.
The bug disappears when I use the CGAL::Exact_predicates_exact_constructions_kernel kernel, but I can't afford to use it in the real case scenario.
Below is a minimal code with the bug. Triangles B and C are equal (up to a permutation of the vertices), and should return the same intersection with Triangle A.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Intersections.h>
#include <iostream>
#include <vector>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point_3;
typedef Kernel::Triangle_3 Triangle_3;
int main(int argc, char *argv[])
{
std::vector<Point_3> APoints(3);
std::vector<Point_3> BPoints(3);
APoints[0] = Point_3(2, 2, 0.9423616295572568);
APoints[1] = Point_3(0.9685134704003172, 2, 0.9678422992674797);
APoints[2] = Point_3(2, 1.124710354419025, 1.068692504586136);
BPoints[0] = Point_3(2.5, 2.5, 1.442361629557257);
BPoints[1] = Point_3(1.588259113885977, 2.5, 0.5);
BPoints[2] = Point_3(2.5, 1.624710354419025, 1.568692504586136);
Triangle_3 TriangleA(APoints[0],APoints[1],APoints[2]);
Triangle_3 TriangleB(BPoints[0],BPoints[1],BPoints[2]);
Triangle_3 TriangleC(BPoints[2],BPoints[1],BPoints[0]);
std::cout.precision(16);
std::cout << " - Tried to intersect: " << std::endl;
std::cout << " - Triangle (A) " << " : "
<< "(" << TriangleA.vertex(0) << ") "
<< "(" << TriangleA.vertex(1) << ") "
<< "(" << TriangleA.vertex(2) << ") " << std::endl;
std::cout << " - Triangle (B) " << " : "
<< "(" << TriangleB.vertex(0) << ") "
<< "(" << TriangleB.vertex(1) << ") "
<< "(" << TriangleB.vertex(2) << ") " << std::endl;
std::cout << " - Triangle (C) " << " : "
<< "(" << TriangleC.vertex(0) << ") "
<< "(" << TriangleC.vertex(1) << ") "
<< "(" << TriangleC.vertex(2) << ") " << std::endl;
if( TriangleB.vertex(0)==TriangleC.vertex(2) &&
TriangleB.vertex(1)==TriangleC.vertex(1) &&
TriangleB.vertex(2)==TriangleC.vertex(0))
{
std::cout << " - Triangles (B) and (C) have the same vertices " << std::endl;
}
bool bIntersectAB = CGAL::do_intersect(TriangleA,TriangleB);
bool bIntersectAC = CGAL::do_intersect(TriangleA,TriangleC);
bool bIntersectInexactAB = CGAL::intersection(TriangleA,TriangleB);
bool bIntersectInexactAC = CGAL::intersection(TriangleA,TriangleC);
if(bIntersectAB)
{
std::cout << " --> A and B are intersecting (exact) ..." << std::endl;
}
if(bIntersectAC)
{
std::cout << " --> A and C are intersecting (exact) ..." << std::endl;
}
if(bIntersectInexactAB)
{
std::cout << " --> A and B are intersecting (inexact) ..." << std::endl;
}
if(bIntersectInexactAC)
{
std::cout << " --> A and C are intersecting (inexact) ..." << std::endl;
}
return 0;
}
Here's the output ...
- Tried to intersect:
- Triangle (A) : (2 2 0.9423616295572568) (0.9685134704003172 2 0.9678422992674797) (2 1.124710354419025 1.068692504586136)
- Triangle (B) : (2.5 2.5 1.442361629557257) (1.588259113885977 2.5 0.5) (2.5 1.624710354419025 1.568692504586136)
- Triangle (C) : (2.5 1.624710354419025 1.568692504586136) (1.588259113885977 2.5 0.5) (2.5 2.5 1.442361629557257)
- Triangles (B) and (C) have the same vertices
--> A and C are intersecting (inexact) ...
... and a figure with the two triangles (A: vertices 1, 2, 3 ; B: vertices 11,12,13) and the "intersection" (segment 21 - 22), found using a similar version of this program.
What could be wrong? I'm using CGAL 4.6.1 on OS X 10.10.5 (Yosemite). Thanks in advance!
I've also sent this question to CGAL's mailing list, and the developers answered that this behaviour is not a bug, although
it is unfortunate. intersection is a generic function, implemented the same way for all CGAL kernels, and it uses one step that is not always handled correctly by inexact kernels - hence the intersection error. According to this thread at CGAL's GitHub page,
In order to keep using a kernel with inexact constructions, I usually advice to first call the do_intersect predicate and then call the intersection function using EPECK on primitives converted on the fly using CGAL::Cartesian_converter. You'll have to convert the output using another CGAL::Cartesian_converter. The call to do_intersect is not mandatory, it usually depends on your setting.

Surface parametrization

I am trying to get a parametrized surface on a surface mesh (which is read from a STL format file.). I read some examples about parametrization provided by CGAL examples directory. I get to know that seam line should be provided in order to get a parametric surface on a arbitrary surface. But still I don't get how to make seam line. The below is my code so far. In summary, What I want to know is,
1) When CGAL::Parameterization_mesh_feature_extractor is used, how can I get vertices on the feature curves and make a seam line with the vertices?
2) Does CGAL provide a way to get intersection curve of a given surface and a cutting plane so that I can get a parametrized surface on a part of the given surface?
#include <cstdio>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <CGAL/IO/io.h>
#include <CGAL/IO/STL_reader.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/polygon_soup_to_polyhedron_3.h>
#include <CGAL/Parameterization_polyhedron_adaptor_3.h>
#include <CGAL/Parameterization_mesh_patch_3.h>
#include <CGAL/parameterize.h>
int main(int argc, char* argv[]) {
clock_t time1, time2;
double read_time, write_time, build_time;
if(argc == 1) {
std::cout << "Please, give me a filename" << std::endl;
return 0;
}
std::ifstream infile(argv[1]);
if(infile.bad()) {
std::cout << "Infile not found or file corrupt" << std::endl;
return 1;
}
std::vector<CGAL::cpp11::array<double, 3> > points;
std::vector<CGAL::cpp11::array<int, 3> > triangles;
time1 = clock();
if (!CGAL::read_STL(infile, points, triangles)) {
std::cerr << "Error: invalid STL file" << std::endl;
return 0;
}
time2 = clock();
read_time = float(time2 - time1) / CLOCKS_PER_SEC;
fprintf(stdout, "Read time : %5.2f sec\n", read_time);
// Write polyhedron in Tecplot format
std::ofstream ofs("mesh.dat");
CGAL::set_ascii_mode(ofs);
time1 = clock();
ofs << "TITLE=\"\"" << std::endl;
ofs << "VARIABLES=\"X\" \"Y\" \"Z\"" << std::endl;
ofs << "ZONE T=\"None\" N=" << points.size() << " E=" << triangles.size() << " F=FEPOINT ET=TRIANGLE" << std::endl;
ofs.setf(std::ios::fixed);
ofs.precision(6);
for(std::vector<CGAL::cpp11::array<double, 3> >::iterator i = points.begin(); i != points.end(); ++i) {
ofs << (*i)[0] << " " << (*i)[1] << " " << (*i)[2] << std::endl;
}
for(std::vector<CGAL::cpp11::array<int, 3> >::iterator i = triangles.begin(); i != triangles.end(); ++i) {
ofs << (*i)[0]+1 << " " << (*i)[1]+1 << " " << (*i)[2]+1 << std::endl;
}
time2 = clock();
write_time = float(time2 - time1) / CLOCKS_PER_SEC;
fprintf(stdout, "Write time : %5.2f sec\n", write_time);
// build mesh
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
Polyhedron mesh;
time1 = clock();
try{
// Try building a polyhedron
CGAL::polygon_soup_to_polyhedron_3(mesh, points, triangles);
if(! mesh.is_valid() || mesh.empty()){
std::cerr << "Error: Invalid polyhedron" << std::endl;
}
}
catch(...){}
time2 = clock();
build_time= float(time2 - time1) / CLOCKS_PER_SEC;
fprintf(stdout, "Build time : %5.2f sec\n", build_time);
// parameterization
typedef CGAL::Parameterization_polyhedron_adaptor_3<Polyhedron> Parameterization_polyhedron_adaptor;
// Type describing a border or seam as a vertex list
typedef std::list<Parameterization_polyhedron_adaptor::Vertex_handle> Seam;
//Create a second adaptor that virtually "cuts" the mesh following the 'seam' path
typedef CGAL::Parameterization_mesh_patch_3<Parameterization_polyhedron_adaptor> Mesh_patch_polyhedron;
Parameterization_polyhedron_adaptor mesh_adaptor(mesh);
////////////////////// cut graph ////////////////////////////////
typedef CGAL::Parameterization_mesh_feature_extractor<Parameterization_polyhedron_adaptor>
Mesh_feature_extractor;
Seam seam;
// Get reference to Polyhedron_3 mesh
Polyhedron& mesh_ref = mesh_adaptor.get_adapted_mesh();
// Extract mesh borders and compute genus
Mesh_feature_extractor feature_extractor(mesh_adaptor);
int nb_borders = feature_extractor.get_nb_borders();
int genus = feature_extractor.get_genus(); // genus means a hole inside a surface
std::cout << "# borders: " << nb_borders << " # holes: " << genus << std::endl;
std::cout << feature_extractor.get_borders()[0] << std::endl;
///////////////////// end of cut graph //////////////////////////
/*
Mesh_patch_polyhedron mesh_patch(mesh_adaptor, seam.begin(), seam.end());
if (!mesh_patch.is_valid())
{
std::cerr << "Input mesh not supported: non manifold shape or invalid cutting" << std::endl;
return EXIT_FAILURE;
}
typedef CGAL::Parameterizer_traits_3<Mesh_patch_polyhedron> Parameterizer; // Type that defines the error codes
Parameterizer::Error_code err = CGAL::parameterize(mesh_patch);
switch(err) {
case Parameterizer::OK: // Success
break;
case Parameterizer::ERROR_EMPTY_MESH: // Input mesh not supported
case Parameterizer::ERROR_NON_TRIANGULAR_MESH:
case Parameterizer::ERROR_NO_TOPOLOGICAL_DISC:
case Parameterizer::ERROR_BORDER_TOO_SHORT:
std::cerr << "Input mesh not supported: " << Parameterizer::get_error_message(err) << std::endl;
return EXIT_FAILURE;
break;
default: // Error
std::cerr << "Error: " << Parameterizer::get_error_message(err) << std::endl;
return EXIT_FAILURE;
break;
};
// Raw output: dump (u,v) pairs
Polyhedron::Vertex_const_iterator pVertex;
for (pVertex = mesh.vertices_begin(); pVertex != mesh.vertices_end(); pVertex++)
{
// (u,v) pair is stored in any halfedge
double u = mesh_adaptor.info(pVertex->halfedge())->uv().x();
double v = mesh_adaptor.info(pVertex->halfedge())->uv().y();
std::cout << "(u,v) = (" << u << "," << v << ")" << std::endl;
}
*/
return 0;
}

saving CGAL alpha shape surface mesh

I have never used CGAL and have got almost no C/C++ experience. But following
Google I have however managed to compile the example "Alpha_shapes_3"
(\CGAL-4.1-beta1\examples\Alpha_shapes_3) on a Windows 7 64bit machine using
visual studio 2010.
Now if we check the source code for the program "ex_alpha_shapes_3" we
notice that a data file called "bunny_1000" is red where the 3d point
cluster resides.
Now my question is how can I change the source code so that after the alpha
shape is computed for the given points, surface mesh of the alpha shape is
saved/wrote in an external file. It can be simply the list of polygons and
their respective 3D vertices. I guess these polygons will be defining the
surface mesh of the alpha shape. If I can do that I can see the output of
the alpha shape generation program in an external tool I am familiar with.
I know this is very straightforward but I could not figure this out with my
limited knowledge of CGAL.
I know you gueys have the code but I am pasting it again for completion.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/Alpha_shape_3.h>
#include <fstream>
#include <list>
#include <cassert>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Gt;
typedef CGAL::Alpha_shape_vertex_base_3<Gt> Vb;
typedef CGAL::Alpha_shape_cell_base_3<Gt> Fb;
typedef CGAL::Triangulation_data_structure_3<Vb,Fb> Tds;
typedef CGAL::Delaunay_triangulation_3<Gt,Tds> Triangulation_3;
typedef CGAL::Alpha_shape_3<Triangulation_3> Alpha_shape_3;
typedef Gt::Point_3 Point;
typedef Alpha_shape_3::Alpha_iterator Alpha_iterator;
int main()
{
std::list<Point> lp;
//read input
std::ifstream is("./data/bunny_1000");
int n;
is >> n;
std::cout << "Reading " << n << " points " << std::endl;
Point p;
for( ; n>0 ; n--) {
is >> p;
lp.push_back(p);
}
// compute alpha shape
Alpha_shape_3 as(lp.begin(),lp.end());
std::cout << "Alpha shape computed in REGULARIZED mode by default"
<< std::endl;
// find optimal alpha value
Alpha_iterator opt = as.find_optimal_alpha(1);
std::cout << "Optimal alpha value to get one connected component is "
<< *opt << std::endl;
as.set_alpha(*opt);
assert(as.number_of_solid_components() == 1);
return 0;
}
After searching a lot in the internet I found that probably we need to use something like
std::list<Facet> facets;
alpha_shape.get_alpha_shape_facets
(
std::back_inserter(facets),Alpha_shape::REGULAR
);
But I am still completely clueless how to use this in the above code!
As documented here, a facet is a pair (Cell_handle c,int i) defined as the facet in c opposite to the vertex of index i.
On this page, you have the description of how the vertex indices of a cell are.
In the following code sample, I added a small output that prints an OFF file on cout by duplicating the vertices. To do something clean, you can either use a std::map<Alpha_shape_3::Vertex_handle,int> to associate a unique index per vertex or add an info to the vertices like in those examples.
/// collect all regular facets
std::vector<Alpha_shape_3::Facet> facets;
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
std::stringstream pts;
std::stringstream ind;
std::size_t nbf=facets.size();
for (std::size_t i=0;i<nbf;++i)
{
//To have a consistent orientation of the facet, always consider an exterior cell
if ( as.classify( facets[i].first )!=Alpha_shape_3::EXTERIOR )
facets[i]=as.mirror_facet( facets[i] );
CGAL_assertion( as.classify( facets[i].first )==Alpha_shape_3::EXTERIOR );
int indices[3]={
(facets[i].second+1)%4,
(facets[i].second+2)%4,
(facets[i].second+3)%4,
};
/// according to the encoding of vertex indices, this is needed to get
/// a consistent orienation
if ( facets[i].second%2==0 ) std::swap(indices[0], indices[1]);
pts <<
facets[i].first->vertex(indices[0])->point() << "\n" <<
facets[i].first->vertex(indices[1])->point() << "\n" <<
facets[i].first->vertex(indices[2])->point() << "\n";
ind << "3 " << 3*i << " " << 3*i+1 << " " << 3*i+2 << "\n";
}
std::cout << "OFF "<< 3*nbf << " " << nbf << " 0\n";
std::cout << pts.str();
std::cout << ind.str();
Here is my code, which outputs vtk file for visualization in Paraview. Comparing with slorior's solutions, no duplicated points are saved in the file. But my code is just for the visualization, if you need to figure out the exterior or interior simplexes, you should modify the code to get these results.
void writevtk(Alpha_shape_3 &as, const std::string &asfile) {
// http://cgal-discuss.949826.n4.nabble.com/Help-with-filtration-and-filtration-with-alpha-values-td4659524.html#a4659549
std::cout << "Information of the Alpha_Complex:\n";
std::vector<Alpha_shape_3::Cell_handle> cells;
std::vector<Alpha_shape_3::Facet> facets;
std::vector<Alpha_shape_3::Edge> edges;
// tetrahedron = cell, they should be the interior, it is inside the 3D space
as.get_alpha_shape_cells(std::back_inserter(cells), Alpha_shape_3::INTERIOR);
// triangles
// for the visualiization, don't need regular because tetrahedron will show it
//as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::REGULAR);
as.get_alpha_shape_facets(std::back_inserter(facets), Alpha_shape_3::SINGULAR);
// edges
as.get_alpha_shape_edges(std::back_inserter(edges), Alpha_shape_3::SINGULAR);
std::cout << "The alpha-complex has : " << std::endl;
std::cout << cells.size() << " cells as tetrahedrons" << std::endl;
std::cout << facets.size() << " triangles" << std::endl;
std::cout << edges.size() << " edges" << std::endl;
size_t tetra_num, tri_num, edge_num;
tetra_num = cells.size();
tri_num = facets.size();
edge_num = edges.size();
// vertices: points <-> id
std::map<Point, size_t> points;
size_t index = 0;
// finite_.. is from DT class
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
points[v_it->point()] = index;
index++;
}
// write
std::ofstream of(asfile);
of << "# vtk DataFile Version 2.0\n\nASCII\nDATASET UNSTRUCTURED_GRID\n\n";
of << "POINTS " << index << " float\n";
for (auto v_it = as.finite_vertices_begin(); v_it != as.finite_vertices_end(); v_it++) {
of << v_it->point() << std::endl;
}
of << std::endl;
of << "CELLS " << tetra_num + tri_num + edge_num << " " << 5 * tetra_num + 4 * tri_num + 3 * edge_num << std::endl;
for (auto cell:cells) {
size_t v0 = points.find(cell->vertex(0)->point())->second;
size_t v1 = points.find(cell->vertex(1)->point())->second;
size_t v2 = points.find(cell->vertex(2)->point())->second;
size_t v3 = points.find(cell->vertex(3)->point())->second;
of << "4 " << v0 << " " << v1 << " " << v2 << " " << v3 << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#ad6a20b45e66dfb690bfcdb8438e9fcae
for (auto tri_it = facets.begin(); tri_it != facets.end(); ++tri_it) {
of << "3 ";
auto tmp_tetra = tri_it->first;
for (int i = 0; i < 4; i++) {
if (i != tri_it->second) {
of << points.find(tmp_tetra->vertex(i)->point())->second << " ";
}
}
of << std::endl;
}
// https://doc.cgal.org/latest/TDS_3/classTriangulationDataStructure__3.html#af31db7673a6d7d28c0bb90a3115ac695
for (auto e : edges) {
of << "2 ";
auto tmp_tetra = e.get<0>();
int p1, p2;
p1 = e.get<1>();
p2 = e.get<2>();
of << points.find(tmp_tetra->vertex(p1)->point())->second << " "
<< points.find(tmp_tetra->vertex(p2)->point())->second << std::endl;
}
of << std::endl;
of << "CELL_TYPES " << tetra_num + tri_num + edge_num << std::endl;
for (int i = 0; i < tetra_num; i++) {
of << "10 ";
}
for (int i = 0; i < tri_num; i++) {
of << "5 ";
}
for (int i = 0; i < edge_num; i++) {
of << "3 ";
}
of << std::endl;
of.close();
}