Browse Source

Fixed writing PLY file, when coordinates are stored as floats (#1581)

* Fixed writing PLY file, when coordinates are stored as floats

* Fix compilation.

Co-authored-by: Jérémie Dumas <[email protected]>
Vladimir S. FONOV 5 years ago
parent
commit
abd9f0f365
3 changed files with 111 additions and 34 deletions
  1. 18 17
      include/igl/readPLY.cpp
  2. 18 17
      include/igl/writePLY.cpp
  3. 75 0
      tests/include/igl/writePLY.cpp

+ 18 - 17
include/igl/readPLY.cpp

@@ -8,17 +8,17 @@
 #include "tinyply.h"
 
 
-namespace igl 
+namespace igl
 {
 
 template <typename T, typename Derived>
 IGL_INLINE bool _tinyply_buffer_to_matrix(
-  tinyply::PlyData & D, 
+  tinyply::PlyData & D,
   Eigen::PlainObjectBase<Derived> & M,
   size_t rows,
   size_t cols )
 {
-  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > 
+  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> >
     _map( reinterpret_cast<T *>( D.buffer.get()), rows, cols );
 
   M = _map.template cast<typename Derived::Scalar>();
@@ -27,9 +27,9 @@ IGL_INLINE bool _tinyply_buffer_to_matrix(
 
 
 
-template <typename Derived> 
+template <typename Derived>
 IGL_INLINE bool tinyply_buffer_to_matrix(
-  tinyply::PlyData & D, 
+  tinyply::PlyData & D,
   Eigen::PlainObjectBase<Derived> & M,
   size_t rows,
   size_t cols )
@@ -60,13 +60,13 @@ IGL_INLINE bool tinyply_buffer_to_matrix(
 
 template <typename T, typename Derived>
 IGL_INLINE bool _tinyply_tristrips_to_trifaces(
-  tinyply::PlyData & D, 
+  tinyply::PlyData & D,
   Eigen::PlainObjectBase<Derived> & M,
   size_t el,
   size_t el_len )
 {
 
-  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > 
+  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> >
      _map( reinterpret_cast<T *>( D.buffer.get()), el, el_len );
 
   // to make it more interesting, triangles in triangle strip can be separated by negative index elements
@@ -77,7 +77,7 @@ IGL_INLINE bool _tinyply_tristrips_to_trifaces(
   for(size_t i=0; i<el; i++)
     for(size_t j=0; j<(el_len-2); j++)
     {
-      if(_map(i,j)>=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0) 
+      if(_map(i,j)>=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0)
         triangles++;
     }
 
@@ -107,9 +107,9 @@ IGL_INLINE bool _tinyply_tristrips_to_trifaces(
   return true;
 }
 
-template <typename Derived> 
+template <typename Derived>
 IGL_INLINE bool tinyply_tristrips_to_faces(
-  tinyply::PlyData & D, 
+  tinyply::PlyData & D,
   Eigen::PlainObjectBase<Derived> & M,
   size_t el,
   size_t el_len )
@@ -212,7 +212,7 @@ IGL_INLINE bool readPLY(
   std::vector<std::string> & comments
   )
 {
-  // buffer the whole file in memory 
+  // buffer the whole file in memory
   // then read from memory buffer
   try
   {
@@ -299,7 +299,7 @@ IGL_INLINE bool readPLY(
   std::set<std::string> edge_std  { "vertex1", "vertex2"}; //non-standard edge indexes
 
   // Tinyply treats parsed data as untyped byte buffers.
-  std::shared_ptr<tinyply::PlyData> vertices, normals, faces, texcoords, edges; 
+  std::shared_ptr<tinyply::PlyData> vertices, normals, faces, texcoords, edges;
 
   // Some ply files contain tristrips instead of faces
   std::shared_ptr<tinyply::PlyData> tristrips;
@@ -315,7 +315,7 @@ IGL_INLINE bool readPLY(
 
   for (auto c : file.get_comments())
       comments.push_back(c);
-  
+
   for (auto e : file.get_elements())
   {
       if(e.name == "vertex" ) // found a vertex
@@ -353,7 +353,7 @@ IGL_INLINE bool readPLY(
 
   // The header information can be used to programmatically extract properties on elements
   // known to exist in the header prior to reading the data. For brevity of this sample, properties
-  // like vertex position are hard-coded:  
+  // like vertex position are hard-coded:
   try {
     vertices = file.request_properties_from_element("vertex", { "x", "y", "z" });
   }
@@ -423,12 +423,12 @@ IGL_INLINE bool readPLY(
       }
   }
 
-  
+
   try {
     edges = file.request_properties_from_element("edge", { "vertex1", "vertex2" });
   }
   catch (const std::exception & ) { }
-  
+
   if(! _vertex_header.empty())
     _vertex_data = file.request_properties_from_element( "vertex", _vertex_header);
   if(! _face_header.empty())
@@ -484,7 +484,7 @@ IGL_INLINE bool readPLY(
     VD.resize(vertices->count,_vertex_header.size());
     tinyply_buffer_to_matrix(*_vertex_data, VD, vertices->count, _vertex_header.size());
   }
-  
+
   /// convert face data:
   Fheader=_face_header;
   if(_face_header.empty())
@@ -682,5 +682,6 @@ template bool igl::readPLY<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<
 template bool igl::readPLY<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
 
 template bool igl::readPLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&);
+template bool igl::readPLY<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&);
 #endif
 

+ 18 - 17
include/igl/writePLY.cpp

@@ -57,9 +57,9 @@ bool writePLY(
     typedef typename DerivedVD::Scalar VDScalar;
     typedef typename DerivedFD::Scalar FDScalar;
     typedef typename DerivedED::Scalar EDScalar;
-    
+
     // temporary storage for data to be passed to tinyply internals
-    std::vector<NScalar> _v;
+    std::vector<VScalar> _v;
     std::vector<NScalar> _n;
     std::vector<UVScalar> _uv;
     std::vector<VDScalar> _vd;
@@ -78,10 +78,10 @@ bool writePLY(
     _v.resize(V.size());
     Eigen::Map< Eigen::Matrix<VScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_v[0], V.rows(), V.cols() ) = V;
 
-    file.add_properties_to_element("vertex", { "x", "y", "z" }, 
+    file.add_properties_to_element("vertex", { "x", "y", "z" },
         tynyply_type<VScalar>(), V.rows(), reinterpret_cast<uint8_t*>( &_v[0] ), tinyply::Type::INVALID, 0);
 
-    if(N.rows()>0) 
+    if(N.rows()>0)
     {
         _n.resize(N.size());
         Eigen::Map<Eigen::Matrix<NScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_n[0], N.rows(), N.cols() ) = N;
@@ -89,7 +89,7 @@ bool writePLY(
             tynyply_type<NScalar>(), N.rows(), reinterpret_cast<uint8_t*>( &_n[0] ),tinyply::Type::INVALID, 0);
     }
 
-    if(UV.rows()>0) 
+    if(UV.rows()>0)
     {
         _uv.resize(UV.size());
         Eigen::Map<Eigen::Matrix<UVScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_uv[0], UV.rows(), UV.cols() ) = UV;
@@ -129,7 +129,7 @@ bool writePLY(
             tynyply_type<FDScalar>(), FD.rows(), reinterpret_cast<uint8_t*>( &_fd[0] ), tinyply::Type::INVALID, 0);
     }
 
-    if(E.rows()>0) 
+    if(E.rows()>0)
     {
         assert(E.cols()==2);
         _ev.resize(E.size());
@@ -233,7 +233,7 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy;
   std::vector<std::string> _dummy_header;
-  
+
   return writePLY(filename,V,F,_dummy, _dummy, _dummy, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 
@@ -251,7 +251,7 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy;
   std::vector<std::string> _dummy_header;
-  
+
   return writePLY(filename,V,F,E, _dummy, _dummy, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 
@@ -272,7 +272,7 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy;
   std::vector<std::string> _dummy_header;
-  
+
   return writePLY(filename,V,F,_dummy, N,UV, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 
@@ -294,7 +294,7 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy;
   std::vector<std::string> _dummy_header;
-  
+
   return writePLY(filename,V,F,E, N,UV, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 
@@ -311,8 +311,8 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy(0,0);
   std::vector<std::string> _dummy_header;
-  
-  return writePLY(filename,V,F,_dummy, _dummy,_dummy, _dummy, _dummy_header, 
+
+  return writePLY(filename,V,F,_dummy, _dummy,_dummy, _dummy, _dummy_header,
                          _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, force_ascii);
 }
 
@@ -331,7 +331,7 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy(0,0);
   std::vector<std::string> _dummy_header;
-  
+
   return writePLY(filename,V,F,E, _dummy,_dummy, _dummy, _dummy_header,
                          _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, force_ascii);
 }
@@ -359,8 +359,8 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy(0,0);
   std::vector<std::string> _dummy_header;
-  
-  return writePLY(filename,V,F,_dummy, N, UV, VD, VDheader, 
+
+  return writePLY(filename,V,F,_dummy, N, UV, VD, VDheader,
                          _dummy, _dummy_header, _dummy, _dummy_header, comments, true);
 }
 
@@ -389,8 +389,8 @@ bool writePLY(
 {
   Eigen::MatrixXd _dummy(0,0);
   std::vector<std::string> _dummy_header;
-  
-  return writePLY(filename,V,F,E, N, UV, VD, VDheader, 
+
+  return writePLY(filename,V,F,E, N, UV, VD, VDheader,
                          _dummy, _dummy_header, _dummy, _dummy_header, comments, true);
 
 }
@@ -405,4 +405,5 @@ bool writePLY(
 // Explicit template instantiation
 template bool igl::writePLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
 template bool igl::writePLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, bool);
+template bool igl::writePLY<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, bool);
 #endif

+ 75 - 0
tests/include/igl/writePLY.cpp

@@ -77,3 +77,78 @@ TEST_CASE("writePLY: bunny.ply", "[igl]")
     // there are comments
     REQUIRE (comments.size() == 2);
 }
+
+
+
+TEST_CASE("writePLY: bunny.ply float", "[igl]")
+{
+    std::ifstream f(test_common::data_path("bunny.ply"));
+    REQUIRE (f.good());
+    f.close();
+    
+    Eigen::MatrixXf V1,N1,UV1,VD1,FD1,ED1;
+    std::vector<std::string> Vheader1,Fheader1,Eheader1,comments1;
+
+    Eigen::MatrixXi F1,E1;
+
+    // load test data first
+    REQUIRE (igl::readPLY(test_common::data_path("bunny.ply"), V1, F1, E1, N1, UV1, VD1,Vheader1, FD1,Fheader1, ED1,Eheader1,comments1));
+
+    // add more data 
+    Vheader1.push_back("dummy_data");
+    Eigen::VectorXf dummy_data(V1.rows());
+    for(size_t i=0;i<V1.rows();++i)
+        dummy_data(i)=(double)i;
+    Eigen::MatrixXf VD2(V1.rows(),VD1.cols()+1);
+    VD2<<VD1,dummy_data;
+
+
+    // test that saving preserves all the data, including new data column
+    REQUIRE (igl::writePLY("test_bunny.ply", V1, F1, E1, N1, UV1, VD2, Vheader1, FD1,Fheader1, ED1, Eheader1, comments1, true));
+
+    Eigen::MatrixXf V,N,UV,VD,FD,ED;
+    Eigen::MatrixXi F,E;
+    std::vector<std::string> Vheader,Fheader,Eheader,comments;
+
+    // test that saving preserves all the data
+    REQUIRE (igl::readPLY("test_bunny.ply", V, F, E, N, UV, VD,Vheader, FD,Fheader, ED,Eheader, comments));
+
+    REQUIRE (V.rows() == 35947);
+    REQUIRE (V.cols() == 3);
+    REQUIRE (F.rows() == 69451);
+    REQUIRE (F.cols() == 3);
+    // no edge data
+    REQUIRE (E.rows() == 0);
+    REQUIRE (E.cols() == 0);
+
+    // no normals or texture coordinates
+    REQUIRE (N.rows() == 0);
+    REQUIRE (N.cols() == 0);
+    REQUIRE (UV.rows() == 0);
+    REQUIRE (UV.cols() == 0);
+
+    // this bunny have additonal data
+    REQUIRE (VD.rows() == 35947);
+    REQUIRE (VD.cols() == 3);
+
+    // the dummy column contents check
+    for(size_t i=0;i<V.rows();++i)
+        REQUIRE (VD(i,2) == (double)i);
+
+    REQUIRE (Vheader.size() == 3);
+    REQUIRE (Vheader[0] == "confidence" );
+    REQUIRE (Vheader[1] == "intensity" );
+    REQUIRE (Vheader[2] == "dummy_data" );
+
+    // no Face data or edge data
+    REQUIRE (FD.rows() == 0);
+    REQUIRE (FD.cols() == 0);
+    REQUIRE (Fheader.size() == 0);
+
+    REQUIRE (ED.rows() == 0);
+    REQUIRE (ED.cols() == 0);
+    REQUIRE (Eheader.size() == 0);
+
+    // there are comments
+    REQUIRE (comments.size() == 2);
+}