Skip to content

Commit ed3e162

Browse files
author
Fabien Servant
committed
Injecting survey points
1 parent b06eba9 commit ed3e162

File tree

14 files changed

+499
-5
lines changed

14 files changed

+499
-5
lines changed

src/aliceVision/sfmData/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(sfmData_files_headers
1212
ExposureSetting.hpp
1313
Observation.hpp
1414
ConstraintPoint.hpp
15+
SurveyPoint.hpp
1516
Constraint2D.hpp
1617
)
1718

src/aliceVision/sfmData/SfMData.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <aliceVision/sfmData/Constraint2D.hpp>
1313
#include <aliceVision/sfmData/ConstraintPoint.hpp>
1414
#include <aliceVision/sfmData/RotationPrior.hpp>
15+
#include <aliceVision/sfmData/SurveyPoint.hpp>
1516
#include <aliceVision/sfmData/View.hpp>
1617
#include <aliceVision/sfmData/Rig.hpp>
1718
#include <aliceVision/camera/camera.hpp>
@@ -54,9 +55,14 @@ using Constraints2D = std::vector<Constraint2D>;
5455
/// Define a collection of constraints
5556
using ConstraintsPoint = std::map<IndexT, ConstraintPoint>;
5657

58+
/// Define a collection of surveyed points
59+
using SurveyPoints = std::map<IndexT, std::vector<SurveyPoint>>;
60+
5761
/// Define a collection of rotation priors
5862
using RotationPriors = std::vector<RotationPrior>;
5963

64+
65+
6066
/**
6167
* @brief SfMData container
6268
* Store structure and camera properties
@@ -148,6 +154,13 @@ class SfMData
148154
const ConstraintsPoint& getConstraintsPoint() const { return _constraintsPoint; }
149155
ConstraintsPoint& getConstraintsPoint() { return _constraintsPoint; }
150156

157+
/**
158+
* @brief Get SurveyPoints
159+
* @return SurveyPoints
160+
*/
161+
const SurveyPoints & getSurveyPoints() const { return _surveyPoints; }
162+
SurveyPoints& getSurveyPoints() { return _surveyPoints; }
163+
151164
/**
152165
* @brief Get RotationPriors
153166
* @return RotationPriors
@@ -661,6 +674,8 @@ class SfMData
661674
ConstraintsPoint _constraintsPoint;
662675
/// Rotation priors
663676
RotationPriors _rotationpriors;
677+
/// Survey points
678+
SurveyPoints _surveyPoints;
664679

665680
/**
666681
* @brief Get Rig pose of a given camera view

src/aliceVision/sfmData/SfMData.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
%include <aliceVision/sfmData/CameraPose.i>
1515
%include <aliceVision/sfmData/Constraint2D.i>
1616
%include <aliceVision/sfmData/ConstraintPoint.i>
17+
%include <aliceVision/sfmData/SurveyPoint.i>
1718
%include <aliceVision/sfmData/Landmark.i>
1819
%include <aliceVision/sfmData/Observation.i>
1920
%include <aliceVision/sfmData/Rig.i>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 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+
#pragma once
8+
9+
#include <aliceVision/numeric/numeric.hpp>
10+
11+
namespace aliceVision {
12+
namespace sfmData {
13+
14+
/**
15+
* @brief SurveyPoint is a surveyed 3D point on an image
16+
*/
17+
struct SurveyPoint
18+
{
19+
SurveyPoint() = default;
20+
21+
SurveyPoint(const Vec3 & paramPoint3d, const Vec2 & paramSurvey)
22+
: point3d(paramPoint3d),
23+
survey(paramSurvey)
24+
{}
25+
26+
Vec3 point3d;
27+
Vec2 survey;
28+
29+
bool operator==(const SurveyPoint& other) const
30+
{
31+
return (point3d == other.point3d) && (survey == other.survey);
32+
}
33+
34+
inline bool operator!=(const SurveyPoint& other) const { return !(*this == other); }
35+
};
36+
37+
} // namespace sfmData
38+
} // namespace aliceVision
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 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/sfmData/SurveyPoint.hpp>
8+
9+
%{
10+
#include <aliceVision/sfmData/SurveyPoint.hpp>
11+
%}

src/aliceVision/sfmDataIO/AlembicExporter.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct AlembicExporter::DataImpl
3636
_mvgCloud = Alembic::AbcGeom::OXform(_mvgRoot, "mvgCloud");
3737
_mvgPointCloud = Alembic::AbcGeom::OXform(_mvgCloud, "mvgPointCloud");
3838
_mvgAncestors = Alembic::AbcGeom::OXform(_mvgRoot, "mvgAncestors");
39+
_mvgSurveys = Alembic::AbcGeom::OXform(_mvgCloud, "mvgSurveys");
3940

4041
// add version as custom property
4142
const std::vector<::uint32_t> abcVersion = {
@@ -76,6 +77,7 @@ struct AlembicExporter::DataImpl
7677
Alembic::AbcGeom::OXform _mvgAncestors;
7778
Alembic::AbcGeom::OXform _mvgCloud;
7879
Alembic::AbcGeom::OXform _mvgPointCloud;
80+
Alembic::AbcGeom::OXform _mvgSurveys;
7981
Alembic::AbcGeom::OXform _xform;
8082
Alembic::AbcGeom::OCamera _camObj;
8183
Alembic::AbcGeom::OUInt32ArrayProperty _propSensorSize_pix;
@@ -358,6 +360,11 @@ void AlembicExporter::addSfM(const sfmData::SfMData& sfmData, ESfMData flagsPart
358360
(flagsPart & ESfMData::OBSERVATIONS_WITH_FEATURES));
359361
}
360362

363+
if (flagsPart & ESfMData::SURVEYS)
364+
{
365+
addSurveys(sfmData.getSurveyPoints());
366+
}
367+
361368
if (flagsPart & ESfMData::VIEWS || flagsPart & ESfMData::EXTRINSICS)
362369
{
363370
std::map<IndexT, std::map<IndexT, std::vector<IndexT>>> rigsViewIds; // map<rigId,map<poseId,viewId>>
@@ -612,6 +619,32 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
612619
}
613620
}
614621

622+
void AlembicExporter::addSurveys(const sfmData::SurveyPoints & points)
623+
{
624+
OPoints outPoints(_dataImpl->_mvgSurveys, "surveys");
625+
OPointsSchema& pSchema = outPoints.getSchema();
626+
OCompoundProperty userProps = pSchema.getUserProperties();
627+
628+
std::vector<IndexT> indices;
629+
std::vector<double> data;
630+
631+
for (const auto & [viewId, vsp] : points)
632+
{
633+
for (const auto & sp : vsp)
634+
{
635+
indices.push_back(viewId);
636+
data.push_back(sp.point3d.x());
637+
data.push_back(sp.point3d.y());
638+
data.push_back(sp.point3d.z());
639+
data.push_back(sp.survey.x());
640+
data.push_back(sp.survey.y());
641+
}
642+
}
643+
644+
OUInt32ArrayProperty(userProps, "mvg_surveyIds").set(indices);
645+
ODoubleArrayProperty(userProps, "mvg_surveyData").set(data);
646+
}
647+
615648
void AlembicExporter::addCamera(const std::string& name,
616649
const sfmData::View& view,
617650
const sfmData::CameraPose* pose,

src/aliceVision/sfmDataIO/AlembicExporter.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ class AlembicExporter
6363
bool withVisibility = true,
6464
bool withFeatures = true);
6565

66+
/**
67+
* @brief Add a set of point survey
68+
* @param[in] points The survey points to add
69+
*/
70+
void addSurveys(const sfmData::SurveyPoints & points);
71+
6672
/**
6773
* @brief Add a camera
6874
* @param[in] name The camera identifier

src/aliceVision/sfmDataIO/AlembicImporter.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,53 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
373373
return true;
374374
}
375375

376+
bool readSurveys(IObject iObj, sfmData::SfMData& sfmdata)
377+
{
378+
using namespace aliceVision::geometry;
379+
380+
IPoints points(iObj, kWrapExisting);
381+
IPointsSchema& ms = points.getSchema();
382+
ICompoundProperty userProps = getAbcUserProperties(ms);
383+
384+
if (!(userProps.getPropertyHeader("mvg_surveyIds") && userProps.getPropertyHeader("mvg_surveyData")))
385+
{
386+
return false;
387+
}
388+
389+
AV_UInt32ArraySamplePtr sampleId;
390+
DoubleArraySamplePtr sampleData;
391+
392+
sampleId.read(userProps, "mvg_surveyIds");
393+
394+
IDoubleArrayProperty propData(userProps, "mvg_surveyData");
395+
propData.get(sampleData);
396+
397+
const int sampleDataSize = 5;
398+
399+
if (sampleId.size() * sampleDataSize != sampleData->size())
400+
{
401+
return false;
402+
}
403+
404+
sfmData::SurveyPoints & output = sfmdata.getSurveyPoints();
405+
for (int pos = 0; pos < sampleId.size(); pos++)
406+
{
407+
IndexT viewId = sampleId[pos];
408+
409+
sfmData::SurveyPoint pt;
410+
pt.point3d.x() = (*sampleData)[pos * sampleDataSize + 0];
411+
pt.point3d.y() = (*sampleData)[pos * sampleDataSize + 1];
412+
pt.point3d.z() = (*sampleData)[pos * sampleDataSize + 2];
413+
pt.survey.x() = (*sampleData)[pos * sampleDataSize + 3];
414+
pt.survey.y() = (*sampleData)[pos * sampleDataSize + 4];
415+
416+
output[viewId].push_back(pt);
417+
}
418+
419+
return true;
420+
}
421+
422+
376423
bool readCamera(const Version& abcVersion,
377424
const ICamera& camera,
378425
const M44d& mat,
@@ -1005,9 +1052,22 @@ void visitObject(const Version& abcVersion, IObject iObj, M44d mat, sfmData::SfM
10051052
isReconstructed = false;
10061053

10071054
const MetaData& md = iObj.getMetaData();
1008-
if (IPoints::matches(md) && (flagsPart & ESfMData::STRUCTURE))
1055+
if (IPoints::matches(md))
10091056
{
1010-
readPointCloud(abcVersion, iObj, mat, sfmdata, flagsPart);
1057+
if (iObj.getName() == "surveys")
1058+
{
1059+
if (flagsPart & ESfMData::SURVEYS)
1060+
{
1061+
readSurveys(iObj, sfmdata);
1062+
}
1063+
}
1064+
else
1065+
{
1066+
if (flagsPart & ESfMData::STRUCTURE )
1067+
{
1068+
readPointCloud(abcVersion, iObj, mat, sfmdata, flagsPart);
1069+
}
1070+
}
10111071
}
10121072
else if (IXform::matches(md))
10131073
{

src/aliceVision/sfmDataIO/jsonIO.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,55 @@ void loadLandmark(IndexT& landmarkId, sfmData::Landmark& landmark, bpt::ptree& l
563563
}
564564
}
565565

566+
void saveSurveyPoints(const sfmData::SurveyPoints& spoints, bpt::ptree& parentTree)
567+
{
568+
for (const auto & [viewId, vPoints]: spoints)
569+
{
570+
bpt::ptree viewTree;
571+
572+
viewTree.put("viewId", viewId);
573+
574+
bpt::ptree pointsTree;
575+
for (const auto & point : vPoints)
576+
{
577+
bpt::ptree pointTree;
578+
579+
pointTree.put("X", point.point3d.x());
580+
pointTree.put("Y", point.point3d.y());
581+
pointTree.put("Z", point.point3d.z());
582+
pointTree.put("u", point.survey.x());
583+
pointTree.put("v", point.survey.y());
584+
585+
pointsTree.push_back(std::make_pair("", pointTree));
586+
}
587+
588+
viewTree.add_child("points", pointsTree);
589+
590+
parentTree.push_back(std::make_pair("", viewTree));
591+
}
592+
}
593+
594+
void loadSurveyPoints(sfmData::SurveyPoints& spoints, bpt::ptree& parentTree)
595+
{
596+
for (const bpt::ptree::value_type& viewTree : parentTree.get_child(""))
597+
{
598+
IndexT viewId = viewTree.second.get<IndexT>("viewId");
599+
600+
for (const bpt::ptree::value_type& pointTree : viewTree.second.get_child("points"))
601+
{
602+
sfmData::SurveyPoint p;
603+
604+
p.point3d.x() = pointTree.second.get<double>("X");
605+
p.point3d.y() = pointTree.second.get<double>("Y");
606+
p.point3d.z() = pointTree.second.get<double>("Z");
607+
p.survey.x() = pointTree.second.get<double>("u");
608+
p.survey.y() = pointTree.second.get<double>("v");
609+
610+
spoints[viewId].push_back(p);
611+
}
612+
}
613+
}
614+
566615
bool saveJSON(const sfmData::SfMData& sfmData, const std::string& filename, ESfMData partFlag)
567616
{
568617
const Vec3i version = {ALICEVISION_SFMDATAIO_VERSION_MAJOR, ALICEVISION_SFMDATAIO_VERSION_MINOR, ALICEVISION_SFMDATAIO_VERSION_REVISION};
@@ -573,6 +622,7 @@ bool saveJSON(const sfmData::SfMData& sfmData, const std::string& filename, ESfM
573622
const bool saveIntrinsics = (partFlag & INTRINSICS) == INTRINSICS;
574623
const bool saveExtrinsics = (partFlag & EXTRINSICS) == EXTRINSICS;
575624
const bool saveStructure = (partFlag & STRUCTURE) == STRUCTURE;
625+
const bool saveSurveys = (partFlag & SURVEYS) == SURVEYS;
576626
const bool saveFeatures = (partFlag & OBSERVATIONS_WITH_FEATURES) == OBSERVATIONS_WITH_FEATURES;
577627
const bool saveObservations = saveFeatures || ((partFlag & OBSERVATIONS) == OBSERVATIONS);
578628

@@ -687,6 +737,16 @@ bool saveJSON(const sfmData::SfMData& sfmData, const std::string& filename, ESfM
687737
fileTree.add_child("structure", structureTree);
688738
}
689739

740+
// surveys
741+
if (saveSurveys)
742+
{
743+
bpt::ptree surveyTree;
744+
745+
saveSurveyPoints(sfmData.getSurveyPoints(), surveyTree);
746+
747+
fileTree.add_child("surveys", surveyTree);
748+
}
749+
690750
// write the json file with the tree
691751

692752
bpt::write_json(filename, fileTree);
@@ -709,6 +769,7 @@ bool loadJSON(sfmData::SfMData& sfmData,
709769
const bool loadIntrinsics = (partFlag & INTRINSICS) == INTRINSICS;
710770
const bool loadExtrinsics = (partFlag & EXTRINSICS) == EXTRINSICS;
711771
const bool loadStructure = (partFlag & STRUCTURE) == STRUCTURE;
772+
const bool loadSurveys = (partFlag & SURVEYS) == SURVEYS;
712773
const bool loadFeatures = (partFlag & OBSERVATIONS_WITH_FEATURES) == OBSERVATIONS_WITH_FEATURES;
713774
const bool loadObservations = loadFeatures || ((partFlag & OBSERVATIONS) == OBSERVATIONS);
714775

@@ -881,6 +942,15 @@ bool loadJSON(sfmData::SfMData& sfmData,
881942
}
882943
}
883944

945+
// surveyx
946+
if (loadSurveys && fileTree.count("surveys"))
947+
{
948+
sfmData::SurveyPoints& surveyPoints = sfmData.getSurveyPoints();
949+
950+
loadSurveyPoints(surveyPoints, fileTree.get_child("surveys"));
951+
}
952+
953+
884954
return true;
885955
}
886956

0 commit comments

Comments
 (0)