Skip to content

Commit b855aa2

Browse files
author
Fabien Servant
committed
Upgrade survey injection for new format
1 parent 4605586 commit b855aa2

File tree

5 files changed

+143
-84
lines changed

5 files changed

+143
-84
lines changed

src/aliceVision/sfmData/SurveyPoint.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ struct SurveyPoint
1818
{
1919
SurveyPoint() = default;
2020

21-
SurveyPoint(const Vec3 & paramPoint3d, const Vec2 & paramSurvey)
21+
SurveyPoint(const Vec3 & paramPoint3d, const Vec2 & paramSurvey, const Vec2 & paramResidual)
2222
: point3d(paramPoint3d),
23-
survey(paramSurvey)
23+
survey(paramSurvey),
24+
residual(paramResidual)
2425
{}
2526

2627
Vec3 point3d;
2728
Vec2 survey;
29+
Vec2 residual;
2830

2931
bool operator==(const SurveyPoint& other) const
3032
{
31-
return (point3d == other.point3d) && (survey == other.survey);
33+
return (point3d == other.point3d) && (survey == other.survey) && (residual == other.residual);
3234
}
3335

3436
inline bool operator!=(const SurveyPoint& other) const { return !(*this == other); }

src/aliceVision/sfmDataIO/AlembicExporter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,8 @@ void AlembicExporter::addSurveys(const sfmData::SurveyPoints & points)
676676
data.push_back(sp.point3d.z());
677677
data.push_back(sp.survey.x());
678678
data.push_back(sp.survey.y());
679+
data.push_back(sp.residual.x());
680+
data.push_back(sp.residual.y());
679681
}
680682
}
681683

src/aliceVision/sfmDataIO/AlembicImporter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ bool readSurveys(IObject iObj, sfmData::SfMData& sfmdata)
468468
IDoubleArrayProperty propData(userProps, "mvg_surveyData");
469469
propData.get(sampleData);
470470

471-
const int sampleDataSize = 5;
471+
const int sampleDataSize = 7;
472472

473473
if (sampleId.size() * sampleDataSize != sampleData->size())
474474
{
@@ -486,6 +486,8 @@ bool readSurveys(IObject iObj, sfmData::SfMData& sfmdata)
486486
pt.point3d.z() = (*sampleData)[pos * sampleDataSize + 2];
487487
pt.survey.x() = (*sampleData)[pos * sampleDataSize + 3];
488488
pt.survey.y() = (*sampleData)[pos * sampleDataSize + 4];
489+
pt.residual.x() = (*sampleData)[pos * sampleDataSize + 5];
490+
pt.residual.y() = (*sampleData)[pos * sampleDataSize + 6];
489491

490492
output[viewId].push_back(pt);
491493
}

src/aliceVision/sfmDataIO/jsonIO.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,8 @@ void saveSurveyPoints(const sfmData::SurveyPoints& spoints, bpt::ptree& parentTr
598598
pointTree.put("Z", point.point3d.z());
599599
pointTree.put("u", point.survey.x());
600600
pointTree.put("v", point.survey.y());
601+
pointTree.put("ru", point.residual.x());
602+
pointTree.put("rv", point.residual.y());
601603

602604
pointsTree.push_back(std::make_pair("", pointTree));
603605
}
@@ -623,6 +625,8 @@ void loadSurveyPoints(sfmData::SurveyPoints& spoints, bpt::ptree& parentTree)
623625
p.point3d.z() = pointTree.second.get<double>("Z");
624626
p.survey.x() = pointTree.second.get<double>("u");
625627
p.survey.y() = pointTree.second.get<double>("v");
628+
p.residual.x() = pointTree.second.get<double>("ru");
629+
p.residual.y() = pointTree.second.get<double>("rv");
626630

627631
spoints[viewId].push_back(p);
628632
}

src/software/utils/main_sfmSurveyInjecting.cpp

Lines changed: 129 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -27,62 +27,104 @@
2727
using namespace aliceVision;
2828
namespace po = boost::program_options;
2929

30-
struct SurveyObservation
30+
// Internal structures for json reading
31+
32+
struct Position3D
3133
{
32-
Vec3 point3d;
33-
Vec2 point2d;
34+
double x;
35+
double y;
36+
double z;
3437
};
3538

36-
struct SurveyInput
39+
struct ImagePoint
3740
{
38-
int frameId;
39-
std::vector<SurveyObservation> observations;
41+
int frame;
42+
double x;
43+
double y;
4044
};
4145

42-
/**
43-
* @brief get a survey from a boost json object (assume the file format is ok)
44-
* @param obj the input json object
45-
* @param readSurvey the output survey information
46-
* @return false if the process failed
47-
*/
48-
bool getSurveyFromJson(const boost::json::object& obj, SurveyInput& readSurvey)
46+
struct Point
4947
{
50-
readSurvey.observations.clear();
51-
readSurvey.frameId = boost::json::value_to<IndexT>(obj.at("frame_no"));
52-
53-
boost::json::value jv = obj.at("points");
54-
if (!jv.is_array())
55-
{
56-
return false;
57-
}
48+
std::string point_id;
49+
std::string point_name;
50+
std::string calc_mode;
51+
std::string survey_mode;
52+
Position3D calc_3d;
53+
Position3D survey_3d;
54+
std::vector<ImagePoint> image_points;
55+
};
5856

59-
boost::json::array vobj = jv.as_array();
60-
for (auto item : vobj)
61-
{
62-
const boost::json::object& obj = item.as_object();
57+
struct Survey
58+
{
59+
std::string pgroup_name;
60+
std::string camera_name;
61+
std::vector<Point> points;
62+
};
6363

64-
SurveyObservation obs;
65-
obs.point3d.x() = boost::json::value_to<double>(obj.at("X"));
66-
obs.point3d.y() = boost::json::value_to<double>(obj.at("Y"));
67-
obs.point3d.z() = boost::json::value_to<double>(obj.at("Z"));
64+
Position3D tag_invoke(boost::json::value_to_tag<Position3D>, boost::json::value const& jv)
65+
{
66+
boost::json::object const& obj = jv.as_object();
67+
return Position3D{
68+
boost::json::value_to<double>(obj.at("x")),
69+
boost::json::value_to<double>(obj.at("y")),
70+
boost::json::value_to<double>(obj.at("z"))
71+
};
72+
}
6873

69-
obs.point2d.x() = boost::json::value_to<double>(obj.at("u"));
70-
obs.point2d.y() = boost::json::value_to<double>(obj.at("v"));
74+
ImagePoint tag_invoke(boost::json::value_to_tag<ImagePoint>, boost::json::value const& jv)
75+
{
76+
boost::json::object const& obj = jv.as_object();
77+
return ImagePoint{
78+
boost::json::value_to<int>(obj.at("frame")),
79+
boost::json::value_to<double>(obj.at("x")),
80+
boost::json::value_to<double>(obj.at("y"))
81+
};
82+
}
7183

72-
readSurvey.observations.push_back(obs);
84+
Point tag_invoke(boost::json::value_to_tag<Point>, boost::json::value const& jv)
85+
{
86+
boost::json::object const& obj = jv.as_object();
87+
Point pt;
88+
pt.point_id = boost::json::value_to<std::string>(obj.at("point_id"));
89+
pt.point_name = boost::json::value_to<std::string>(obj.at("point_name"));
90+
pt.calc_mode = boost::json::value_to<std::string>(obj.at("calc_mode"));
91+
pt.survey_mode = boost::json::value_to<std::string>(obj.at("survey_mode"));
92+
pt.calc_3d = boost::json::value_to<Position3D>(obj.at("calc_3d"));
93+
pt.survey_3d = boost::json::value_to<Position3D>(obj.at("survey_3d"));
94+
95+
boost::json::array const& img_points = obj.at("image_points").as_array();
96+
for (auto const& ip : img_points)
97+
{
98+
pt.image_points.push_back(boost::json::value_to<ImagePoint>(ip));
7399
}
100+
101+
return pt;
102+
}
74103

75-
return true;
104+
Survey tag_invoke(boost::json::value_to_tag<Survey>, boost::json::value const& jv)
105+
{
106+
boost::json::object const& obj = jv.as_object();
107+
Survey survey;
108+
survey.pgroup_name = boost::json::value_to<std::string>(obj.at("pgroup_name"));
109+
survey.camera_name = boost::json::value_to<std::string>(obj.at("camera_name"));
110+
111+
boost::json::array const& points = obj.at("points").as_array();
112+
for (auto const& pt : points)
113+
{
114+
survey.points.push_back(boost::json::value_to<Point>(pt));
115+
}
116+
117+
return survey;
76118
}
77119

78120
/**
79121
* @brief Get a set of surveys from a JSON file (assumes the file format is ok).
80122
* The JSON file contains an array of objects. Each object describes a frameId, and a list of points.
81123
* @param surveyFilename the input JSON filename
82-
* @param output the read vector of surveyinput
124+
* @param output a Survey object filled
83125
* @return false if the process failed, true otherwise
84126
*/
85-
bool getSurveysFromJson(const std::string& surveyFilename, std::vector<SurveyInput> & output)
127+
bool getSurveysFromJson(const std::string& surveyFilename, Survey & output)
86128
{
87129
std::ifstream inputfile(surveyFilename);
88130
if (!inputfile.is_open())
@@ -94,23 +136,7 @@ bool getSurveysFromJson(const std::string& surveyFilename, std::vector<SurveyInp
94136
buffer << inputfile.rdbuf();
95137
boost::json::value jv = boost::json::parse(buffer.str());
96138

97-
if (!jv.is_array())
98-
{
99-
return false;
100-
}
101-
102-
boost::json::array vobj = jv.as_array();
103-
104-
for (auto item : vobj)
105-
{
106-
const boost::json::object& obj = item.as_object();
107-
108-
SurveyInput input;
109-
if (getSurveyFromJson(obj, input))
110-
{
111-
output.push_back(input);
112-
}
113-
}
139+
output = boost::json::value_to<Survey>(jv);
114140

115141
return true;
116142
}
@@ -164,56 +190,79 @@ int aliceVision_main(int argc, char** argv)
164190
return EXIT_FAILURE;
165191
}
166192

167-
auto & landmarks = sfmData.getLandmarks();
193+
// Get First Frame ID as 3DE gives a [0, nbFrames - 1] frame ID
194+
IndexT minFrameId = std::numeric_limits<IndexT>::max();
195+
for (const auto& [viewId, pView] : sfmData.getViews())
196+
{
197+
minFrameId = std::min(minFrameId, pView->getFrameId());
198+
}
199+
ALICEVISION_LOG_INFO("Minimal frame id in sfmData is " << minFrameId << ".");
168200

169-
std::vector<SurveyInput> surveys;
170-
if (!getSurveysFromJson(surveyFilename, surveys))
201+
// Read survey points in an intermediate structure
202+
Survey survey;
203+
if (!getSurveysFromJson(surveyFilename, survey))
171204
{
172205
ALICEVISION_LOG_ERROR("The survey file '" + surveyFilename + "' cannot be read.");
173206
return EXIT_FAILURE;
174207
}
175208

176209
sfmData::SurveyPoints & spoints = sfmData.getSurveyPoints();
177210

178-
// Set the pose for all the views with frame IDs found in the JSON file
179-
size_t idFeature = 0;
180-
for (const auto& [viewId, pView] : sfmData.getViews())
211+
// Fill up sfmData with the intermediate structure
212+
for (const auto& [viewId, view] : sfmData.getViews().valueRange())
181213
{
182-
IndexT frameId = pView->getFrameId();
183-
184-
if (!sfmData.isIntrinsicDefined(viewId))
214+
if (!sfmData.isPoseAndIntrinsicDefined(viewId))
185215
{
186216
continue;
187217
}
188218

189-
const auto & intrinsic = sfmData.getIntrinsic(pView->getIntrinsicId());
219+
const auto & intrinsic = sfmData.getIntrinsic(view.getIntrinsicId());
190220

191-
for (const auto & survey: surveys)
192-
{
193-
if (frameId != survey.frameId)
194-
{
195-
continue;
196-
}
221+
std::vector<sfmData::SurveyPoint> spts;
197222

198-
for (const auto & obs : survey.observations)
199-
{
200-
sfmData::SurveyPoint p;
223+
IndexT frameId = view.getFrameId();
201224

202-
p.survey.x() = obs.point2d.x() * intrinsic.w();
203-
p.survey.y() = intrinsic.h() - 1.0 - (obs.point2d.y() * intrinsic.h());
225+
//We assume this pose is also coming from 3DE and has not been modified !
226+
geometry::Pose3 pose = sfmData.getPose(view).getTransform();
204227

205-
p.point3d.x() = obs.point3d.x();
206-
p.point3d.y() = - obs.point3d.y();
207-
p.point3d.z() = - obs.point3d.z();
228+
for (const auto & point: survey.points)
229+
{
230+
sfmData::SurveyPoint p;
208231

209-
spoints[viewId].push_back(p);
232+
p.point3d.x() = point.survey_3d.x;
233+
p.point3d.y() = -point.survey_3d.y;
234+
p.point3d.z() = -point.survey_3d.z;
210235

211-
idFeature++;
236+
for (const auto & image_point: point.image_points)
237+
{
238+
// Json frame is the sequence offset starting by 1.
239+
if (frameId != (image_point.frame - 1 + minFrameId))
240+
{
241+
continue;
242+
}
243+
244+
// From 3DE representation to Alicevision
245+
p.survey.x() = image_point.x * intrinsic.w();
246+
p.survey.y() = (1.0 - image_point.y) * intrinsic.h();
247+
248+
// Compute residual
249+
Vec2 obsEstimated = intrinsic.transformProject(pose, p.point3d.homogeneous(), true);
250+
p.residual.x() = p.survey.x() - obsEstimated.x();
251+
p.residual.y() = p.survey.y() - obsEstimated.y();
252+
253+
spts.push_back(p);
254+
255+
ALICEVISION_LOG_INFO("Found observation for frame " << frameId << ".");
256+
ALICEVISION_LOG_INFO("X residual : " << std::abs(p.survey.x() - obsEstimated.x()));
257+
ALICEVISION_LOG_INFO("Y residual : " << std::abs(p.survey.y() - obsEstimated.y()));
212258
}
213259
}
214-
}
215260

216-
std::cout << sfmData.getSurveyPoints().size() << std::endl;
261+
if (spts.size() > 0)
262+
{
263+
spoints[viewId] = spts;
264+
}
265+
}
217266

218267
sfmDataIO::save(sfmData, sfmDataOutputFilename, sfmDataIO::ESfMData::ALL);
219268

0 commit comments

Comments
 (0)