1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2 
3 // Copyright (c) 2015 Pierre MOULON.
4 
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #ifndef OPENMVG_SFM_SFM_DATA_IO_PLY_HPP
10 #define OPENMVG_SFM_SFM_DATA_IO_PLY_HPP
11 
12 #include "openMVG/sfm/sfm_data_io.hpp"
13 
14 #include <fstream>
15 #include <iomanip>
16 #include <limits>
17 #include <string>
18 
19 namespace openMVG {
20 namespace sfm {
21 
22 /// Save the structure and camera positions of a SfM_Data container as 3D points in a PLY ASCII/BIN file.
Save_PLY(const SfM_Data & sfm_data,const std::string & filename,ESfM_Data flags_part,bool b_write_in_ascii=false)23 inline bool Save_PLY
24 (
25   const SfM_Data & sfm_data,
26   const std::string & filename,
27   ESfM_Data flags_part,
28   bool b_write_in_ascii = false
29 )
30 {
31   const bool b_structure = (flags_part & STRUCTURE) == STRUCTURE;
32   const bool b_control_points = (flags_part & CONTROL_POINTS) == CONTROL_POINTS;
33   const bool b_extrinsics = (flags_part & EXTRINSICS) == EXTRINSICS;
34 
35   if (!(b_structure || b_extrinsics || b_control_points))
36     return false; // No 3D points to display, so it would produce an empty PLY file
37 
38   // Create the stream and check its status
39   std::ofstream stream(filename.c_str(), std::ios::out | std::ios::binary);
40   if (!stream)
41     return false;
42 
43   bool bOk = false;
44   {
45     // Count how many views having valid poses:
46     IndexT view_with_pose_count = 0;
47     IndexT view_with_pose_prior_count = 0;
48     if (b_extrinsics)
49     {
50       for (const auto & view : sfm_data.GetViews())
51       {
52         view_with_pose_count += sfm_data.IsPoseAndIntrinsicDefined(view.second.get());
53       }
54 
55       for (const auto & view : sfm_data.GetViews())
56       {
57         if (const sfm::ViewPriors *prior = dynamic_cast<sfm::ViewPriors*>(view.second.get()))
58         {
59             view_with_pose_prior_count += prior->b_use_pose_center_;
60         }
61       }
62     }
63 
64     stream << std::fixed << std::setprecision (std::numeric_limits<double>::digits10 + 1);
65 
66     using Vec3uc = Eigen::Matrix<unsigned char, 3, 1>;
67 
68     stream << "ply"
69       << '\n' << "format "
70               << (b_write_in_ascii ? "ascii 1.0" : "binary_little_endian 1.0")
71       << '\n' << "comment generated by OpenMVG"
72       << '\n' << "element vertex "
73         // Vertex count: (#landmark + #GCP + #view_with_valid_pose)
74         << (  (b_structure ? sfm_data.GetLandmarks().size() : 0)
75             + (b_control_points ? sfm_data.GetControl_Points().size() : 0)
76             + view_with_pose_count
77             + view_with_pose_prior_count)
78       << '\n' << "property double x"
79       << '\n' << "property double y"
80       << '\n' << "property double z"
81       << '\n' << "property uchar red"
82       << '\n' << "property uchar green"
83       << '\n' << "property uchar blue"
84       << '\n' << "end_header" << std::endl;
85 
86       if (b_extrinsics)
87       {
88         for (const auto & view : sfm_data.GetViews())
89         {
90           // Export pose as Green points
91           if (sfm_data.IsPoseAndIntrinsicDefined(view.second.get()))
92           {
93             const geometry::Pose3 pose = sfm_data.GetPoseOrDie(view.second.get());
94             if (b_write_in_ascii)
95             {
96               stream
97                 << pose.center()(0) << ' '
98                 << pose.center()(1) << ' '
99                 << pose.center()(2) << ' '
100                 << "0 255 0\n";
101             }
102             else
103             {
104               stream.write( reinterpret_cast<const char*> ( pose.center().data() ), sizeof( Vec3 ) );
105               stream.write( reinterpret_cast<const char*> ( Vec3uc(0, 255, 0).data() ), sizeof( Vec3uc ) );
106             }
107           }
108 
109           // Export pose priors as Blue points
110           if (const sfm::ViewPriors *prior = dynamic_cast<sfm::ViewPriors*>(view.second.get()))
111           {
112             if (prior->b_use_pose_center_)
113             {
114               if (b_write_in_ascii)
115               {
116                 stream
117                   << prior->pose_center_(0) << ' '
118                   << prior->pose_center_(1) << ' '
119                   << prior->pose_center_(2) << ' '
120                   << "0 0 255\n";
121               }
122               else
123               {
124                 stream.write( reinterpret_cast<const char*> ( prior->pose_center_.data() ), sizeof( Vec3 ) );
125                 stream.write( reinterpret_cast<const char*> ( Vec3uc(0, 0, 255).data() ), sizeof( Vec3uc ) );
126               }
127             }
128           }
129         }
130       }
131 
132       if (b_structure)
133       {
134         // Export structure points as White points
135         const Landmarks & landmarks = sfm_data.GetLandmarks();
136         for ( const auto & iterLandmarks : landmarks )
137         {
138           if (b_write_in_ascii)
139           {
140             stream
141               << iterLandmarks.second.X(0) << ' '
142               << iterLandmarks.second.X(1) << ' '
143               << iterLandmarks.second.X(2) << ' '
144               << "255 255 255\n";
145           }
146           else
147           {
148             stream.write( reinterpret_cast<const char*> ( iterLandmarks.second.X.data() ), sizeof( Vec3 ) );
149             stream.write( reinterpret_cast<const char*> ( Vec3uc(255, 255, 255).data() ), sizeof( Vec3uc ) );
150           }
151         }
152       }
153 
154       if (b_control_points)
155       {
156         // Export GCP as Red points
157         const Landmarks & landmarks = sfm_data.GetControl_Points();
158         for ( const auto & iterGCP : landmarks )
159         {
160           if (b_write_in_ascii)
161           {
162             stream
163               << iterGCP.second.X(0) << ' '
164               << iterGCP.second.X(1) << ' '
165               << iterGCP.second.X(2) << ' '
166               << "255 0 0\n";
167           }
168           else
169           {
170             stream.write( reinterpret_cast<const char*> ( iterGCP.second.X.data() ), sizeof( Vec3 ) );
171             stream.write( reinterpret_cast<const char*> ( Vec3uc(255, 0, 0).data() ), sizeof( Vec3uc ) );
172           }
173         }
174       }
175 
176       stream.flush();
177       bOk = stream.good();
178       stream.close();
179   }
180   return bOk;
181 }
182 
183 } // namespace sfm
184 } // namespace openMVG
185 
186 #endif // OPENMVG_SFM_SFM_DATA_IO_PLY_HPP
187