1 #include <iostream>
2 #include <limits>
3 #include "vnl/vnl_math.h"
4 #include "boxm2_export_oriented_point_cloud_function.h"
5 //:
6 // \file
7 
8 #include <cassert>
9 #ifdef _MSC_VER
10 #  include "vcl_msvc_warnings.h"
11 #endif
12 #include <vnl/algo/vnl_symmetric_eigensystem.h>
13 
14 #include <rply.h>   //.ply parser
15 
16 #include <boxm2/cpp/algo/boxm2_mog3_grey_processor.h>
17 
18 
exportPointCloudXYZ(const boxm2_scene_sptr & scene,const boxm2_block_metadata &,boxm2_block * blk,boxm2_data_base * alpha,boxm2_data_base * vis,boxm2_data_base * vis_sum,boxm2_data_base * exp,boxm2_data_base * nobs,boxm2_data_base * points,boxm2_data_base * normals,boxm2_data_base * ray_dir_sum,std::ofstream & file,bool output_aux,float vis_t,float nmag_t,float prob_t,float exp_t,vgl_box_3d<double> bb)19 void boxm2_export_oriented_point_cloud_function::exportPointCloudXYZ(const boxm2_scene_sptr& scene, const boxm2_block_metadata&  /*data*/, boxm2_block* blk,
20                                                                      boxm2_data_base* alpha, boxm2_data_base* vis, boxm2_data_base* vis_sum, boxm2_data_base* exp,boxm2_data_base* nobs,
21                                                                      boxm2_data_base* points, boxm2_data_base* normals,
22                                                                      boxm2_data_base* ray_dir_sum, std::ofstream& file,
23                                                                      bool output_aux, float vis_t, float nmag_t, float prob_t, float exp_t, vgl_box_3d<double> bb)
24 {
25   auto *     alpha_data = (boxm2_data_traits<BOXM2_ALPHA>::datatype*) alpha->data_buffer();
26   auto *     points_data = (boxm2_data_traits<BOXM2_POINT>::datatype*) points->data_buffer();
27   auto *    normals_data = (boxm2_data_traits<BOXM2_NORMAL>::datatype*) normals->data_buffer();
28   auto * vis_data = (boxm2_data_traits<BOXM2_VIS_SCORE>::datatype*) vis->data_buffer();
29   auto * vis_sum_data = (boxm2_data_traits<BOXM2_VIS_SCORE>::datatype*) vis_sum->data_buffer();
30   auto *  exp_data = (boxm2_data_traits<BOXM2_EXPECTATION>::datatype*) exp->data_buffer();
31   auto *  nobs_data = (boxm2_data_traits<BOXM2_NUM_OBS_SINGLE_INT>::datatype*) nobs->data_buffer();
32   auto *  ray_dir_sum_data = (boxm2_data_traits<BOXM2_RAY_DIR>::datatype*) ray_dir_sum->data_buffer();
33 
34   file << std::fixed;
35   int pointTypeSize = (int)boxm2_data_info::datasize(boxm2_data_traits<BOXM2_POINT>::prefix());
36   //check for invalid parameters
37   if( pointTypeSize == 0 ) //This should never happen, it will result in division by zero later
38   {
39     std::cerr << "ERROR: Division by zero in " << __FILE__ << __LINE__ << std::endl;
40     throw 0;
41   }
42 
43   for (unsigned currIdx=0; currIdx < (points->buffer_length()/pointTypeSize) ; currIdx++) {
44     //check normal magnitude and vis score and that point data is valid
45     if (normals_data[currIdx][3] >= nmag_t && vis_data[currIdx] >= vis_t && points_data[currIdx][3] != -1)
46     {
47       float prob;
48       if (!calculateProbOfPoint(scene, blk, points_data[currIdx], alpha_data[currIdx], prob))
49         continue;
50 
51       //check prob
52       if (prob >= prob_t && exp_data[currIdx] >= exp_t)
53       {
54         //check bounding box
55         if (bb.is_empty() || bb.contains(points_data[currIdx][0] ,points_data[currIdx][1] ,points_data[currIdx][2]) )
56         {
57           file <<  points_data[currIdx][0] << ' ' << points_data[currIdx][1] << ' ' << points_data[currIdx][2] << ' '
58                <<  normals_data[currIdx][0] << ' ' << normals_data[currIdx][1] << ' ' << normals_data[currIdx][2] << ' ';
59 
60           if (output_aux) {
61             file  <<  prob  << ' ' <<  normals_data[currIdx][3] << ' ' <<  vis_data[currIdx] << ' ' <<  vis_sum_data[currIdx];
62             file << ' ' << ray_dir_sum_data[currIdx][0] << " " << ray_dir_sum_data[currIdx][1] << " " << ray_dir_sum_data[currIdx][2] << " ";
63             file << ' ' <<  exp_data[currIdx] << ' ' <<  nobs_data[currIdx];
64           }
65           file << std::endl;
66         }
67       }
68     }
69   }
70 }
71 
exportPointCloudPLY(const boxm2_scene_sptr & scene,const boxm2_block_metadata &,boxm2_block * blk,boxm2_data_base * alpha,boxm2_data_base * vis,boxm2_data_base * points,boxm2_data_base * normals,std::ofstream & file,bool output_aux,float vis_t,float nmag_t,float prob_t,vgl_box_3d<double> bb,unsigned & num_vertices)72 void boxm2_export_oriented_point_cloud_function::exportPointCloudPLY(const boxm2_scene_sptr& scene, const boxm2_block_metadata&  /*data*/, boxm2_block* blk,
73                                             boxm2_data_base* alpha, boxm2_data_base* vis,
74                                             boxm2_data_base* points, boxm2_data_base* normals,
75                                             std::ofstream& file,
76                                             bool output_aux, float vis_t, float nmag_t, float prob_t, vgl_box_3d<double> bb, unsigned& num_vertices)
77 {
78   auto *   alpha_data = (boxm2_data_traits<BOXM2_ALPHA>::datatype*) alpha->data_buffer();
79   auto *   points_data = (boxm2_data_traits<BOXM2_POINT>::datatype*) points->data_buffer();
80   auto *  normals_data = (boxm2_data_traits<BOXM2_NORMAL>::datatype*) normals->data_buffer();
81   auto * vis_data = (boxm2_data_traits<BOXM2_VIS_SCORE>::datatype*) vis->data_buffer();
82 
83   file << std::fixed;
84   int pointTypeSize = (int)boxm2_data_info::datasize(boxm2_data_traits<BOXM2_POINT>::prefix());
85   //check for invalid parameters
86   if( pointTypeSize == 0 ) //This should never happen, it will result in division by zero later
87   {
88     std::cerr << "ERROR: Division by 0 in " << __FILE__ << __LINE__ << std::endl;
89     throw 0;
90   }
91 
92   for (unsigned currIdx=0; currIdx < (points->buffer_length()/pointTypeSize) ; currIdx++) {
93     //check normal magnitude and vis score and that point data is valid
94     if (normals_data[currIdx][3] >= nmag_t && vis_data[currIdx] >= vis_t && points_data[currIdx][3] != -1)
95     {
96       float prob;
97       if (!calculateProbOfPoint(scene, blk, points_data[currIdx], alpha_data[currIdx], prob))
98         continue;
99 
100       //check prob
101       if (prob >= prob_t )
102       {
103         //check bounding box
104         if (bb.is_empty() || bb.contains(points_data[currIdx][0] ,points_data[currIdx][1] ,points_data[currIdx][2]) )
105         {
106           file <<  points_data[currIdx][0] << ' ' << points_data[currIdx][1] << ' ' << points_data[currIdx][2];
107           num_vertices++;
108 
109           if (output_aux){
110             file << ' ' << normals_data[currIdx][0] << ' ' << normals_data[currIdx][1] << ' ' << normals_data[currIdx][2] << ' ' << prob  << ' ' << vis_data[currIdx] << ' ' << normals_data[currIdx][3];
111           }
112           file << std::endl;
113         }
114       }
115     }
116   }
117 }
118 
exportPointCloudPLY(const boxm2_scene_sptr &,const boxm2_block_metadata &,boxm2_block *,boxm2_data_base * mog,boxm2_data_base * alpha,boxm2_data_base * points,boxm2_data_base * covariances,std::ofstream & file,float prob_t,float LE_t,float CE_t,vgl_box_3d<double> bb,unsigned & num_vertices,const std::string & datatype)119 void boxm2_export_oriented_point_cloud_function::exportPointCloudPLY(const boxm2_scene_sptr&  /*scene*/, const boxm2_block_metadata&  /*data*/, boxm2_block*  /*blk*/,
120                                                                      boxm2_data_base* mog, boxm2_data_base* alpha,
121                                                                      boxm2_data_base* points, boxm2_data_base* covariances, std::ofstream& file,
122                                                                      float prob_t, float LE_t, float CE_t, vgl_box_3d<double> bb, unsigned& num_vertices, const std::string& datatype)
123 {
124   auto *   points_data = (boxm2_data_traits<BOXM2_POINT>::datatype*) points->data_buffer();
125   auto *  covs_data = (boxm2_data_traits<BOXM2_COVARIANCE>::datatype*) covariances->data_buffer();
126   auto     *   alpha_data   = (boxm2_data_traits<BOXM2_ALPHA>::datatype*) alpha->data_buffer();
127 
128   file << std::fixed;
129   int pointTypeSize = (int)boxm2_data_info::datasize(boxm2_data_traits<BOXM2_POINT>::prefix());
130   //check for invalid parameters
131   if( pointTypeSize == 0 ) //This should never happen, it will result in division by zero later
132   {
133     std::cerr << "ERROR: Division by 0 in " << __FILE__ << __LINE__ << std::endl;
134     throw 0;
135   }
136 
137   for (unsigned currIdx=0; currIdx < (points->buffer_length()/pointTypeSize) ; currIdx++) {
138     //check if the point data is valid
139     //if (covs_data[currIdx][0] >= 0.0)
140     //{
141 
142       //check bounding box
143       if (bb.is_empty() || bb.contains(points_data[currIdx][0] ,points_data[currIdx][1] ,points_data[currIdx][2]) )
144       {
145 
146         //float prob = 0.0f;
147         float prob = points_data[currIdx][3]; // during extractPointCloud process the prob of this point should be saved in this field
148                                               // that process actually also checks whether the prob is below threshold and marks this field as -1.0
149 
150         if (prob >= prob_t)
151         {
152 
153           vnl_vector_fixed<double, 3> axes;
154           //vnl_vector_fixed<double, 3> eval;
155           double LE, CE, exp_color;
156           //if (!calculateProbOfPoint(scene, blk, points_data[currIdx], covs_data[currIdx], alpha_data[currIdx], prob, exp_color, axes, LE, CE))
157           //  continue;
158           if (!calculateLECEofPoint(covs_data[currIdx], axes, LE, CE))
159             continue;
160 
161           if (LE >= LE_t || CE >= CE_t)
162             continue;
163 
164           file <<  points_data[currIdx][0] << ' ' << points_data[currIdx][1] << ' ' << points_data[currIdx][2] << ' ';
165 
166           if(datatype == boxm2_data_traits<BOXM2_MOG3_GREY>::prefix() )
167           {
168             auto * mog_data = (boxm2_data_traits<BOXM2_MOG3_GREY>::datatype*) mog->data_buffer();
169             exp_color = boxm2_processor_type<BOXM2_MOG3_GREY>::type::expected_color(mog_data[currIdx]);
170             int col = (int)(exp_color*255);
171             col = col > 255 ? 255 : col;
172             file << col << ' ' << col << ' ' << col << ' ';
173           }
174           else if ( datatype == boxm2_data_traits<BOXM2_GAUSS_RGB>::prefix() )
175           {
176             auto *color_data = (boxm2_data_traits<BOXM2_GAUSS_RGB>::datatype*) mog->data_buffer();
177             vnl_vector_fixed<float,3> exp_color = boxm2_processor_type<BOXM2_GAUSS_RGB>::type::expected_color(color_data[currIdx]);
178 
179             int col0 = (int)(exp_color[0]*255);
180             col0 = col0 > 255 ? 255 : col0;
181             int col1 = (int)(exp_color[1]*255);
182             col1 = col1 > 255 ? 255 : col1;
183             int col2 = (int)(exp_color[2]*255);
184             col2 = col2 > 255 ? 255 : col2;
185             file << col0 << ' ' << col1 << ' ' << col2 << ' ';
186           }
187           else
188           {
189             file << 0 << ' ' << 0 << ' ' << 0 << ' ';
190           }
191           file << axes[0] << ' ' << axes[1] << ' ' << axes[2] << ' '
192                << LE << ' ' << CE << ' ' <<  prob << std::endl;
193           num_vertices++;
194         }
195       }
196     //}
197   }
198 }
exportColorPointCloudPLY(const boxm2_scene_sptr &,const boxm2_block_metadata &,boxm2_block *,boxm2_data_base * mog,boxm2_data_base * alpha,const std::string & datatype,boxm2_data_base * points,std::ofstream & file,float prob_t,vgl_box_3d<double> bb,unsigned & num_vertices)199 void boxm2_export_oriented_point_cloud_function::exportColorPointCloudPLY(const boxm2_scene_sptr&  /*scene*/, const boxm2_block_metadata&  /*data*/, boxm2_block*  /*blk*/,
200                                                                         boxm2_data_base* mog, boxm2_data_base* alpha,const std::string& datatype ,
201                                                                         boxm2_data_base* points,std::ofstream& file,float prob_t,vgl_box_3d<double> bb, unsigned& num_vertices)
202 {
203     auto     *   points_data  = (boxm2_data_traits<BOXM2_POINT>::datatype*) points->data_buffer();
204 
205     auto     *   alpha_data   = (boxm2_data_traits<BOXM2_ALPHA>::datatype*) alpha->data_buffer();
206     file << std::fixed;
207     int pointTypeSize = (int)boxm2_data_info::datasize(boxm2_data_traits<BOXM2_POINT>::prefix());
208     //check for invalid parameters
209     if( pointTypeSize == 0 ) //This should never happen, it will result in division by zero later
210     {
211         std::cerr << "ERROR: Division by 0 in " << __FILE__ << __LINE__ << std::endl;
212         throw 0;
213     }
214     for (unsigned currIdx=0; currIdx < (points->buffer_length()/pointTypeSize) ; currIdx++) {
215         float prob = points_data[currIdx][3]; // during extractPointCloud process the prob of this point should be saved in this field
216                                               // that process also checks whether the prob is below threshold and marks this field as -1.0
217         // the probability check should have already been done by ExtractPointCloud processes
218         //if (!calculateProbOfPoint(scene, blk, points_data[currIdx], alpha_data[currIdx], prob))
219         //    continue;
220         if (prob >= prob_t)
221         {
222             //check bounding box
223             if (bb.is_empty() || bb.contains(points_data[currIdx][0] ,points_data[currIdx][1] ,points_data[currIdx][2]) )
224             {
225                 file <<  points_data[currIdx][0] << ' ' << points_data[currIdx][1] << ' ' << points_data[currIdx][2] << ' ';
226 
227                 if(datatype == boxm2_data_traits<BOXM2_MOG3_GREY>::prefix() )
228                 {
229                     auto *   mog_data     = (boxm2_data_traits<BOXM2_MOG3_GREY>::datatype*) mog->data_buffer();
230                     float exp_color  = boxm2_processor_type<BOXM2_MOG3_GREY>::type::expected_color(mog_data[currIdx]);
231                     int col = (int)(exp_color*255);
232                     col = col > 255 ? 255 : col;
233                     file << col << ' ' << col << ' ' << col;
234                 }
235                 else if ( datatype == boxm2_data_traits<BOXM2_GAUSS_RGB>::prefix() )
236                 {
237                     auto *   color_data     = (boxm2_data_traits<BOXM2_GAUSS_RGB>::datatype*) mog->data_buffer();
238                     vnl_vector_fixed<float,3> exp_color = boxm2_processor_type<BOXM2_GAUSS_RGB>::type::expected_color(color_data[currIdx]);
239 
240                     int col0 = (int)(exp_color[0]*255);
241                     col0 = col0 > 255 ? 255 : col0;
242                     int col1 = (int)(exp_color[1]*255);
243                     col1 = col1 > 255 ? 255 : col1;
244                     int col2 = (int)(exp_color[2]*255);
245                     col2 = col2 > 255 ? 255 : col2;
246                     file << col0 << ' ' << col1 << ' ' << col2;
247 
248                 }
249                 else{
250                     file << 0 << ' ' << 0 << ' ' << 0;
251                 }
252 
253                 file << ' ' << prob << ' ' <<"\n";
254 
255                 num_vertices++;
256             }
257         }
258     }
259 }
calculateProbOfPoint(const boxm2_scene_sptr & scene,boxm2_block * blk,const vnl_vector_fixed<float,4> & point,const vnl_vector_fixed<float,9> & cov,const float & alpha,float & prob,double &,vnl_vector_fixed<double,3> & axes,double & LE,double & CE)260 bool boxm2_export_oriented_point_cloud_function::calculateProbOfPoint(const boxm2_scene_sptr& scene, boxm2_block * blk,
261                                                                       const vnl_vector_fixed<float, 4>& point,
262                                                                       const vnl_vector_fixed<float, 9>& cov,
263                                                                       const float& alpha,
264                                                                       float& prob, double&  /*color*/, vnl_vector_fixed<double, 3>& axes, double& LE, double& CE)
265 {
266   boxm2_block_id id;
267 
268   vgl_point_3d<double> vgl_point(point[0],point[1],point[2]);
269 
270   //the following method checks whether the scene contains the point or not
271   if (!calculateProbOfPoint(scene, blk, point, alpha, prob))
272     return false;
273 
274   if (!calculateLECEofPoint(cov, axes, LE, CE))
275     return false;
276 
277   return true;
278 }
279 
calculateLECEofPoint(const vnl_vector_fixed<float,9> & cov,vnl_vector_fixed<double,3> & axes,double & LE,double & CE)280 bool boxm2_export_oriented_point_cloud_function::calculateLECEofPoint(const vnl_vector_fixed<float, 9>& cov,
281                                                                       vnl_vector_fixed<double, 3>&axes, double& LE, double& CE)
282 {
283   if (vnl_math::isinf(cov[0]) || vnl_math::isnan(cov[0]))  // the covariance matrices with such values cause problems for eigen value calculation, skip these points!
284                                                            // if the covariance has invalid values, then over or under flows might have happened during cov computation using float point precision
285                                                            // ideally we should switch to double point precision during covariance calculations
286     return false;
287 
288   // compute the eigenvalues
289   vnl_matrix<double> pt_cov(3,3,0.0);
290   pt_cov[0][0] = cov[0];
291   pt_cov[0][1] = cov[1];
292   pt_cov[0][2] = cov[2];
293 
294   pt_cov[1][0] = cov[3];
295   pt_cov[1][1] = cov[4];
296   pt_cov[1][2] = cov[5];
297 
298   pt_cov[2][0] = cov[6];
299   pt_cov[2][1] = cov[7];
300   pt_cov[2][2] = cov[8];
301 
302   vnl_matrix<double> V(3,3,0.0); vnl_vector<double> eigs(3);
303   if (!vnl_symmetric_eigensystem_compute(pt_cov, V, eigs))
304     return false;
305 
306   // place from the longest axis to the shortest, largest eigen value is in eigs[2]
307   axes[0] =2*std::sqrt(eigs[2])*2.5;  // to find 90% confidence ellipsoid, scale the eigenvalues, see pg. 416 on Intro To Modern Photogrammetry, Mikhail, et. al.
308   axes[1] =2*std::sqrt(eigs[1])*2.5;
309   axes[2] =2*std::sqrt(eigs[0])*2.5;
310   // check if values are valid (AND is the only way to detect invalid value, do not change into ORs
311   if (!(axes[0] < std::numeric_limits<double>::max() && axes[0] > std::numeric_limits<double>::min() &&
312         axes[1] < std::numeric_limits<double>::max() && axes[1] > std::numeric_limits<double>::min() &&
313         axes[2] < std::numeric_limits<double>::max() && axes[2] > std::numeric_limits<double>::min()))
314     return false;
315 
316   // now find LE (vertical error) using the eigenvector that corresponds to major axis
317   vnl_vector<double> major = V.get_column(2);
318 
319   // create the vector that corresponds to error ellipsoid
320   vnl_vector<double> major_ellipsoid = axes[0]*major;
321 
322 
323   LE = std::abs(major_ellipsoid.get(2));
324   double CEx = std::abs(major_ellipsoid.get(0));
325   double CEy = std::abs(major_ellipsoid.get(1));
326 
327   CE = CEx > CEy ? CEx : CEy;
328 
329   return true;
330 }
331 
calculateProbOfPoint(const boxm2_scene_sptr & scene,boxm2_block * blk,const vnl_vector_fixed<float,4> & point,const float & alpha,float & prob)332 bool boxm2_export_oriented_point_cloud_function::calculateProbOfPoint(const boxm2_scene_sptr& scene, boxm2_block * blk,
333                                                                       const vnl_vector_fixed<float, 4>& point,
334                                                                       const float& alpha, float& prob)
335 {
336   vgl_point_3d<double> local;
337   boxm2_block_id id;
338   vgl_point_3d<double> vgl_point(point[0],point[1],point[2]);
339   //boxm2_block_id id = blk->block_id();
340   //if (!scene->block_contains(vgl_point, id, local))
341   //  return false;
342 
343   //if the scene doesn't contain point,
344   if (!scene->contains(vgl_point, id, local)) {
345     return false;
346   }
347   //if the block passed isn't the block that contains the point, there is something wrong...
348   //this happens when the point data is empty (0,0,0,0) for instance, or simply wrong.
349   /*if (blk->block_id() != id)
350     return false;
351   */
352   int index_x=(int)std::floor(local.x());
353   int index_y=(int)std::floor(local.y());
354   int index_z=(int)std::floor(local.z());
355 
356   boxm2_block_metadata mdata = scene->get_block_metadata_const(id);
357   vnl_vector_fixed<unsigned char,16> treebits=blk->trees()(index_x,index_y,index_z);
358   boct_bit_tree tree(treebits.data_block(),mdata.max_level_);
359   int bit_index=tree.traverse(local);
360   int depth=tree.depth_at(bit_index);
361   auto side_len=static_cast<float>(mdata.sub_block_dim_.x()/((float)(1<<depth)));
362 
363   prob=1.0f-std::exp(-alpha*side_len);
364   return true;
365 }
366 
writePLYHeader(std::ofstream & file,unsigned num_vertices,std::stringstream & ss,bool output_aux)367 void boxm2_export_oriented_point_cloud_function::writePLYHeader(std::ofstream& file, unsigned num_vertices,std::stringstream& ss, bool output_aux)
368 {
369    file << "ply\nformat ascii 1.0\nelement vertex " << num_vertices;
370    file << "\nproperty float32 x\nproperty float32 y\nproperty float32 z";
371    if (output_aux) {
372      //file << "\nproperty float x\nproperty float y\nproperty float z\nproperty float nx\nproperty float ny\nproperty float nz\n" << "property float prob\nproperty float vis_score\n";
373      file << "\nproperty float nx\nproperty float ny\nproperty float nz\n" << "property float prob\nproperty float vis_score\nproperty float nmag";
374    }
375    file << "\nend_header\n"
376         << ss.str();
377 }
378 
writePLYHeaderOnlyPoints(std::ofstream & file,unsigned num_vertices,std::stringstream & ss)379 void boxm2_export_oriented_point_cloud_function::writePLYHeaderOnlyPoints(std::ofstream& file, unsigned num_vertices, std::stringstream& ss)
380 {
381   file << "ply\nformat ascii 1.0\nelement vertex " << num_vertices
382        << "\nproperty float x\nproperty float y\nproperty float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\n"
383        << "property float axes_a\nproperty float axes_b\nproperty float axes_c\n"
384     // << "property float eval_x\nproperty float eval_y\nproperty eval_z\n"
385        << "property float le\nproperty float ce\n"
386        << "property float prob\n"
387        << "end_header\n"
388        << ss.str();
389 }
390 
391 //helper class to read in bb from file
392 class ply_bb_reader
393 {
394  public:
395   vgl_box_3d<double> bbox;
396   double p[3];
397   std::vector<int > vertex_indices;
398 };
399 
400 
401 //: Call-back function for a "vertex" element
boxm2_plyio_vertex_cb(p_ply_argument argument)402 int boxm2_plyio_vertex_cb(p_ply_argument argument)
403 {
404   long index;
405   void* temp;
406   ply_get_argument_user_data(argument, &temp, &index);
407 
408   auto* parsed_ply =  (ply_bb_reader*) temp;
409 
410   switch (index)
411   {
412     case 0: // "x" coordinate
413       parsed_ply->p[0] = ply_get_argument_value(argument);
414       break;
415     case 1: // "y" coordinate
416       parsed_ply->p[1] = ply_get_argument_value(argument);
417       break;
418     case 2: // "z" coordinate
419       parsed_ply->p[2] = ply_get_argument_value(argument);
420       // INSERT VERTEX INTO THE MESH
421       parsed_ply->bbox.add(vgl_point_3d<double>(parsed_ply->p));
422       break;
423     default:
424       assert(!"This should not happen: index out of range");
425   }
426   return 1;
427 }
428 
429 
readBBFromPLY(const std::string & filename,vgl_box_3d<double> & box)430 void boxm2_export_oriented_point_cloud_function::readBBFromPLY(const std::string& filename, vgl_box_3d<double>& box)
431 {
432   ply_bb_reader parsed_ply;
433   parsed_ply.bbox = box;
434 
435   p_ply ply = ply_open(filename.c_str(), nullptr, 0, nullptr);
436   if (!ply) {
437     std::cout << "File " << filename << " doesn't exist.";
438   }
439   if (!ply_read_header(ply))
440     std::cout << "File " << filename << " doesn't have header.";
441 
442   // vertex
443   ply_set_read_cb(ply, "vertex", "x", boxm2_plyio_vertex_cb, (void*) (&parsed_ply), 0);
444   ply_set_read_cb(ply, "vertex", "y", boxm2_plyio_vertex_cb, (void*) (&parsed_ply), 1);
445   ply_set_read_cb(ply, "vertex", "z", boxm2_plyio_vertex_cb, (void*) (&parsed_ply), 2);
446 
447   // Read DATA
448   ply_read(ply);
449 
450   // CLOSE file
451   ply_close(ply);
452 
453   box=parsed_ply.bbox;
454 }
455