1+ // This file is part of the AliceVision project.
2+ // Copyright (c) 2024 AliceVision contributors.
3+ // This Source Code Form is subject to the terms of the Mozilla Public License,
4+ // v. 2.0. If a copy of the MPL was not distributed with this file,
5+ // You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+ #include < aliceVision/types.hpp>
8+ #include < aliceVision/config.hpp>
9+
10+ #include < aliceVision/system/Timer.hpp>
11+ #include < aliceVision/system/Logger.hpp>
12+ #include < aliceVision/system/main.hpp>
13+ #include < aliceVision/cmdline/cmdline.hpp>
14+
15+ #include < aliceVision/sfmDataIO/sfmDataIO.hpp>
16+
17+ #include < boost/program_options.hpp>
18+ #include < boost/json.hpp>
19+
20+ // These constants define the current software version.
21+ // They must be updated when the command line is changed.
22+ #define ALICEVISION_SOFTWARE_VERSION_MAJOR 1
23+ #define ALICEVISION_SOFTWARE_VERSION_MINOR 0
24+
25+ using namespace aliceVision ;
26+ namespace po = boost::program_options;
27+
28+ struct poseInput
29+ {
30+ IndexT frameId;
31+ Eigen::Matrix4d T;
32+ };
33+
34+ /* *
35+ * I/O for Rotation format choice
36+ */
37+
38+ enum class ERotationFormat
39+ {
40+ EulerZXY
41+ };
42+
43+ inline std::string ERotationFormat_enumToString (ERotationFormat format)
44+ {
45+ switch (format)
46+ {
47+ case ERotationFormat::EulerZXY:
48+ {
49+ return " EulerZXY" ;
50+ }
51+ }
52+ throw std::out_of_range (" Invalid RotationFormat type Enum: " + std::to_string (int (format)));
53+ }
54+
55+ inline ERotationFormat ERotationFormat_stringToEnum (const std::string& format)
56+ {
57+ if (format == " EulerZXY" )
58+ {
59+ return ERotationFormat::EulerZXY;
60+ }
61+
62+ throw std::out_of_range (" Invalid RotationFormat type Enum: " + format);
63+ }
64+
65+ inline std::ostream& operator <<(std::ostream& os, ERotationFormat s)
66+ {
67+ return os << ERotationFormat_enumToString (s);
68+ }
69+
70+ inline std::istream& operator >>(std::istream& in, ERotationFormat& s)
71+ {
72+ std::string token (std::istreambuf_iterator<char >(in), {});
73+ s = ERotationFormat_stringToEnum (token);
74+ return in;
75+ }
76+
77+
78+ /* *
79+ * @brief get a pose from a boost json object (assume the file format is ok)
80+ * @param obj the input json object
81+ * @param format the required rotation format to transform to rotation matrix
82+ * @param readPose the output pose information
83+ * @return false if the process failed
84+ */
85+ bool getPoseFromJson (const boost::json::object& obj, ERotationFormat format, poseInput & readPose)
86+ {
87+ Eigen::Matrix3d R = Eigen::Matrix3d::Identity ();
88+
89+ if (format == ERotationFormat::EulerZXY)
90+ {
91+ // Reading information from lineup
92+ const double rx = degreeToRadian (boost::json::value_to<double >(obj.at (" rx" )));
93+ const double ry = degreeToRadian (boost::json::value_to<double >(obj.at (" ry" )));
94+ const double rz = degreeToRadian (boost::json::value_to<double >(obj.at (" rz" )));
95+
96+ Eigen::AngleAxisd Rx (rx, Eigen::Vector3d::UnitX ());
97+ Eigen::AngleAxisd Ry (ry, Eigen::Vector3d::UnitY ());
98+ Eigen::AngleAxisd Rz (rz, Eigen::Vector3d::UnitZ ());
99+
100+ R = Ry.toRotationMatrix () * Rx.toRotationMatrix () * Rz.toRotationMatrix ();
101+ }
102+ else
103+ {
104+ return false ;
105+ }
106+
107+ readPose.frameId = boost::json::value_to<IndexT>(obj.at (" frame_no" ));
108+
109+ Eigen::Vector3d t;
110+ t.x () = boost::json::value_to<double >(obj.at (" tx" ));
111+ t.y () = boost::json::value_to<double >(obj.at (" ty" ));
112+ t.z () = boost::json::value_to<double >(obj.at (" tz" ));
113+
114+ readPose.T = Eigen::Matrix4d::Identity ();
115+ readPose.T .block <3 , 3 >(0 , 0 ) = R;
116+ readPose.T .block <3 , 1 >(0 , 3 ) = t;
117+
118+ return true ;
119+ }
120+
121+ /* *
122+ * @brief get a set of poses from a json file (assume the file format is ok)
123+ * Json file contains an array of objects. Each object describes a frameId, a rotation and a translation.
124+ * @param obj the input json filename
125+ * @param format the required rotation format to transform to rotation matrix
126+ * @param readPose the output poses vector
127+ * @return false if the process failed
128+ */
129+ bool getPosesFromJson (const std::string & posesFilename, ERotationFormat format, std::vector<poseInput> & readPoses)
130+ {
131+ std::ifstream inputfile (posesFilename);
132+ if (!inputfile.is_open ())
133+ {
134+ return false ;
135+ }
136+
137+ std::stringstream buffer;
138+ buffer << inputfile.rdbuf ();
139+ boost::json::value jv = boost::json::parse (buffer.str ());
140+
141+ if (!jv.is_array ())
142+ {
143+ return false ;
144+ }
145+
146+ boost::json::array vobj = jv.as_array ();
147+
148+ for (auto item : vobj)
149+ {
150+ const boost::json::object& obj = item.as_object ();
151+
152+ poseInput input;
153+ if (getPoseFromJson (obj, format, input))
154+ {
155+ readPoses.push_back (input);
156+ }
157+ }
158+
159+ return true ;
160+ }
161+
162+ int aliceVision_main (int argc, char ** argv)
163+ {
164+ // command-line parameters
165+ std::string sfmDataFilename;
166+ std::string sfmDataOutputFilename;
167+ std::string posesFilename;
168+ ERotationFormat format;
169+
170+
171+ po::options_description requiredParams (" Required parameters" );
172+ requiredParams.add_options ()
173+ (" input,i" , po::value<std::string>(&sfmDataFilename)->required (), " SfMData file." )
174+ (" output,o" , po::value<std::string>(&sfmDataOutputFilename)->required (), " SfMData output file." )
175+ (" posesFilename,p" , po::value<std::string>(&posesFilename)->required (), " Poses file." )
176+ (" rotationFormat,r" , po::value<ERotationFormat>(&format)->required (), " Poses file." );
177+
178+
179+ CmdLine cmdline (" AliceVision SfM Pose injecting" );
180+
181+ cmdline.add (requiredParams);
182+ if (!cmdline.execute (argc, argv))
183+ {
184+ return EXIT_FAILURE;
185+ }
186+
187+ // set maxThreads
188+ HardwareContext hwc = cmdline.getHardwareContext ();
189+ omp_set_num_threads (hwc.getMaxThreads ());
190+
191+ // load input SfMData scene
192+ sfmData::SfMData sfmData;
193+ if (!sfmDataIO::load (sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL))
194+ {
195+ ALICEVISION_LOG_ERROR (" The input SfMData file '" + sfmDataFilename + " ' cannot be read." );
196+ return EXIT_FAILURE;
197+ }
198+
199+ std::vector<poseInput> readPoses;
200+ if (!getPosesFromJson (posesFilename, format, readPoses))
201+ {
202+ ALICEVISION_LOG_ERROR (" Cannot read the poses" );
203+ return EXIT_FAILURE;
204+ }
205+
206+
207+ // Set the pose for all views with frame Ids found in the json file
208+ for (const auto & [id, pview] : sfmData.getViews ())
209+ {
210+ for (const auto & rpose : readPoses)
211+ {
212+ if (pview->getFrameId () == rpose.frameId )
213+ {
214+ geometry::Pose3 pose (rpose.T );
215+ sfmData::CameraPose cpose (pose, false );
216+ sfmData.setAbsolutePose (id, cpose);
217+ }
218+ }
219+ }
220+
221+ sfmDataIO::save (sfmData, sfmDataOutputFilename, sfmDataIO::ESfMData::ALL);
222+
223+ return EXIT_SUCCESS;
224+ }
0 commit comments