Skip to content

Commit e6c666f

Browse files
authored
Merge pull request #1780 from alicevision/dev/sfmPoseInjecting
[utils] Pose injection from JSON file
2 parents f59dd85 + b45c719 commit e6c666f

File tree

2 files changed

+239
-0
lines changed

2 files changed

+239
-0
lines changed

src/software/utils/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@ if(ALICEVISION_BUILD_SFM)
218218
Boost::program_options
219219
)
220220

221+
# SfM Pose Injecting
222+
alicevision_add_software(aliceVision_sfmPoseInjecting
223+
SOURCE main_sfmPoseInjecting.cpp
224+
FOLDER ${FOLDER_SOFTWARE_UTILS}
225+
LINKS aliceVision_system
226+
aliceVision_cmdline
227+
aliceVision_sfmData
228+
aliceVision_sfmDataIO
229+
aliceVision_sfm
230+
Boost::program_options
231+
Boost::json
232+
)
233+
221234
# SfM Regression
222235
alicevision_add_software(aliceVision_sfmRegression
223236
SOURCE main_sfmRegression.cpp
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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

Comments
 (0)