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