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+ * @brief get a pose from a boost json object (assume the file format is ok)
79+ * @param obj the input json object
80+ * @param format the required rotation format to transform to rotation matrix
81+ * @param readPose the output pose information
82+ * @return false if the process failed
83+ */
84+ bool getPoseFromJson (const boost::json::object& obj, ERotationFormat format, PoseInput& readPose)
85+ {
86+ Eigen::Matrix3d R = Eigen::Matrix3d::Identity ();
87+
88+ if (format == ERotationFormat::EulerZXY)
89+ {
90+ // Reading information from lineup
91+ const double rx = degreeToRadian (boost::json::value_to<double >(obj.at (" rx" )));
92+ const double ry = degreeToRadian (boost::json::value_to<double >(obj.at (" ry" )));
93+ const double rz = degreeToRadian (boost::json::value_to<double >(obj.at (" rz" )));
94+
95+ Eigen::AngleAxisd Rx (rx, Eigen::Vector3d::UnitX ());
96+ Eigen::AngleAxisd Ry (ry, Eigen::Vector3d::UnitY ());
97+ Eigen::AngleAxisd Rz (rz, Eigen::Vector3d::UnitZ ());
98+
99+ R = Ry.toRotationMatrix () * Rx.toRotationMatrix () * Rz.toRotationMatrix ();
100+ }
101+ else
102+ {
103+ return false ;
104+ }
105+
106+ readPose.frameId = boost::json::value_to<IndexT>(obj.at (" frame_no" ));
107+
108+ Eigen::Vector3d t;
109+ t.x () = boost::json::value_to<double >(obj.at (" tx" ));
110+ t.y () = boost::json::value_to<double >(obj.at (" ty" ));
111+ t.z () = boost::json::value_to<double >(obj.at (" tz" ));
112+
113+ readPose.T = Eigen::Matrix4d::Identity ();
114+ readPose.T .block <3 , 3 >(0 , 0 ) = R;
115+ readPose.T .block <3 , 1 >(0 , 3 ) = t;
116+
117+ return true ;
118+ }
119+
120+ /* *
121+ * @brief Get a set of poses from a JSON file (assumes the file format is ok).
122+ * The JSON file contains an array of objects. Each object describes a frameId, a rotation and a translation.
123+ * @param posesFilename the input JSON filename
124+ * @param format the required rotation format to transform to rotation matrix
125+ * @param readPose the output poses vector
126+ * @return false if the process failed, true otherwise
127+ */
128+ bool getPosesFromJson (const std::string& posesFilename, ERotationFormat format, std::vector<PoseInput>& readPoses)
129+ {
130+ std::ifstream inputfile (posesFilename);
131+ if (!inputfile.is_open ())
132+ {
133+ return false ;
134+ }
135+
136+ std::stringstream buffer;
137+ buffer << inputfile.rdbuf ();
138+ boost::json::value jv = boost::json::parse (buffer.str ());
139+
140+ if (!jv.is_array ())
141+ {
142+ return false ;
143+ }
144+
145+ boost::json::array vobj = jv.as_array ();
146+
147+ for (auto item : vobj)
148+ {
149+ const boost::json::object& obj = item.as_object ();
150+
151+ PoseInput input;
152+ if (getPoseFromJson (obj, format, input))
153+ {
154+ readPoses.push_back (input);
155+ }
156+ }
157+
158+ return true ;
159+ }
160+
161+ int aliceVision_main (int argc, char ** argv)
162+ {
163+ // command-line parameters
164+ std::string sfmDataFilename;
165+ std::string sfmDataOutputFilename;
166+ std::string posesFilename;
167+ ERotationFormat format;
168+
169+ // clang-format off
170+ po::options_description requiredParams (" Required parameters" );
171+ requiredParams.add_options ()
172+ (" input,i" , po::value<std::string>(&sfmDataFilename)->required (),
173+ " Input SfMData file." )
174+ (" output,o" , po::value<std::string>(&sfmDataOutputFilename)->required (),
175+ " SfMData output file with the injected poses." )
176+ (" posesFilename,p" , po::value<std::string>(&posesFilename)->required (),
177+ " JSON file containing the poses to inject." )
178+ (" rotationFormat,r" , po::value<ERotationFormat>(&format)->required (),
179+ " Rotation format for the input poses: EulerZXY." );
180+ // clang-format on
181+
182+ CmdLine cmdline (" AliceVision SfM Pose injecting" );
183+
184+ cmdline.add (requiredParams);
185+ if (!cmdline.execute (argc, argv))
186+ {
187+ return EXIT_FAILURE;
188+ }
189+
190+ // Set maxThreads
191+ HardwareContext hwc = cmdline.getHardwareContext ();
192+ omp_set_num_threads (hwc.getMaxThreads ());
193+
194+ // Load input SfMData scene
195+ sfmData::SfMData sfmData;
196+ if (!sfmDataIO::load (sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL))
197+ {
198+ ALICEVISION_LOG_ERROR (" The input SfMData file '" + sfmDataFilename + " ' cannot be read." );
199+ return EXIT_FAILURE;
200+ }
201+
202+ std::vector<PoseInput> readPoses;
203+ if (!getPosesFromJson (posesFilename, format, readPoses))
204+ {
205+ ALICEVISION_LOG_ERROR (" Cannot read the poses" );
206+ return EXIT_FAILURE;
207+ }
208+
209+ // Set the pose for all the views with frame IDs found in the JSON file
210+ for (const auto & [id, pview] : sfmData.getViews ())
211+ {
212+ for (const auto & rpose : readPoses)
213+ {
214+ if (pview->getFrameId () == rpose.frameId )
215+ {
216+ geometry::Pose3 pose (rpose.T );
217+ sfmData::CameraPose cpose (pose, false );
218+ sfmData.setAbsolutePose (id, cpose);
219+ }
220+ }
221+ }
222+
223+ sfmDataIO::save (sfmData, sfmDataOutputFilename, sfmDataIO::ESfMData::ALL);
224+
225+ return EXIT_SUCCESS;
226+ }
0 commit comments