Skip to content

Commit 577fe6c

Browse files
authored
Merge pull request #1839 from alicevision/dev/surveyInjecting
Survey Injection
2 parents 94f67d9 + ed30719 commit 577fe6c

18 files changed

+669
-7
lines changed

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@ ceres::CostFunction* createCostFunctionFromIntrinsics(const std::shared_ptr<Intr
6060
return costFunction;
6161
}
6262

63+
ceres::CostFunction* createSurveyPointCostFunction(const std::shared_ptr<IntrinsicBase> intrinsic,
64+
const Vec3 & point,
65+
const sfmData::Observation& observation)
66+
{
67+
auto costFunction = new ceres::DynamicAutoDiffCostFunction<ProjectionSurveyErrorFunctor>(new ProjectionSurveyErrorFunctor(point, observation, intrinsic));
68+
69+
int distortionSize = 1;
70+
auto isod = camera::IntrinsicScaleOffsetDisto::cast(intrinsic);
71+
if (isod)
72+
{
73+
auto distortion = isod->getDistortion();
74+
if (distortion)
75+
{
76+
distortionSize = distortion->getParameters().size();
77+
}
78+
}
79+
80+
costFunction->AddParameterBlock(intrinsic->getParameters().size());
81+
costFunction->AddParameterBlock(distortionSize);
82+
costFunction->AddParameterBlock(6);
83+
costFunction->SetNumResiduals(2);
84+
85+
return costFunction;
86+
}
87+
6388
/**
6489
* @brief Create the appropriate cost functor according the provided input rig camera intrinsic model
6590
* @param[in] intrinsicPtr The intrinsic pointer
@@ -723,6 +748,58 @@ void BundleAdjustmentCeres::addLandmarksToProblem(const sfmData::SfMData& sfmDat
723748
}
724749
}
725750

751+
void BundleAdjustmentCeres::addSurveyPointsToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem)
752+
{
753+
754+
// build the residual blocks corresponding to the track observations
755+
for (const auto& [idView, vspoints] : sfmData.getSurveyPoints())
756+
{
757+
double* fakeDistortionBlockPtr = _fakeDistortionBlock.data();
758+
759+
const sfmData::View& view = sfmData.getView(idView);
760+
const IndexT intrinsicId = view.getIntrinsicId();
761+
762+
// each residual block takes a point and a camera as input and outputs a 2
763+
// dimensional residual. Internally, the cost function stores the observed
764+
// image location and compares the reprojection against the observation.
765+
const auto& pose = sfmData.getPose(view);
766+
767+
// needed parameters to create a residual block (K, pose)
768+
double* poseBlockPtr = _posesBlocks.at(view.getPoseId()).data();
769+
double* intrinsicBlockPtr = _intrinsicsBlocks.at(intrinsicId).data();
770+
const std::shared_ptr<IntrinsicBase> intrinsic = _intrinsicObjects[intrinsicId];
771+
772+
double * distortionBlockPtr = fakeDistortionBlockPtr;
773+
if (_distortionsBlocks.find(intrinsicId) != _distortionsBlocks.end())
774+
{
775+
distortionBlockPtr = _distortionsBlocks.at(intrinsicId).data();
776+
}
777+
778+
// apply a specific parameter ordering:
779+
if (_ceresOptions.useParametersOrdering)
780+
{
781+
_linearSolverOrdering.AddElementToGroup(poseBlockPtr, 1);
782+
_linearSolverOrdering.AddElementToGroup(intrinsicBlockPtr, 2);
783+
_linearSolverOrdering.AddElementToGroup(distortionBlockPtr, 2);
784+
}
785+
786+
787+
for (const auto & spoint: vspoints)
788+
{
789+
sfmData::Observation observation(spoint.survey, 0, 1.0);
790+
ceres::CostFunction* costFunction = createSurveyPointCostFunction(intrinsic, spoint.point3d, observation);
791+
792+
793+
std::vector<double*> params;
794+
params.push_back(intrinsicBlockPtr);
795+
params.push_back(distortionBlockPtr);
796+
params.push_back(poseBlockPtr);
797+
798+
problem.AddResidualBlock(costFunction, nullptr, params);
799+
}
800+
}
801+
}
802+
726803
void BundleAdjustmentCeres::addConstraints2DToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem)
727804
{
728805
// set a LossFunction to be less penalized by false measurements.
@@ -845,6 +922,9 @@ void BundleAdjustmentCeres::createProblem(const sfmData::SfMData& sfmData, ERefi
845922
// add SfM landmarks to the Ceres problem
846923
addLandmarksToProblem(sfmData, refineOptions, problem);
847924

925+
// add SfM landmarks to the Ceres problem
926+
addSurveyPointsToProblem(sfmData, refineOptions, problem);
927+
848928
// add 2D constraints to the Ceres problem
849929
addConstraints2DToProblem(sfmData, refineOptions, problem);
850930

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ class BundleAdjustmentCeres : public BundleAdjustment, ceres::EvaluationCallback
181181
*/
182182
void addLandmarksToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem);
183183

184+
/**
185+
* @brief Create a residual block for each survey point according to the Ceres format
186+
* @param[in] sfmData The input SfMData contains all the information about the reconstruction, notably the intrinsics
187+
* @param[in] refineOptions The chosen refine flag
188+
* @param[out] problem The Ceres bundle adjustment problem
189+
*/
190+
void addSurveyPointsToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem);
191+
184192
/**
185193
* @brief Create a residual block for each 2D constraints
186194
* @param[in] sfmData The input SfMData contains all the information about the reconstruction, notably the intrinsics

src/aliceVision/sfm/bundle/costfunctions/projection.hpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,75 @@ struct ProjectionSimpleErrorFunctor
6161
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
6262
};
6363

64+
65+
struct ProjectionSurveyErrorFunctor
66+
{
67+
explicit ProjectionSurveyErrorFunctor(const Vec3 & point,
68+
const sfmData::Observation& obs,
69+
const std::shared_ptr<camera::IntrinsicBase>& intrinsics)
70+
: _intrinsicFunctor(new CostIntrinsicsProject(obs, intrinsics)), _point(point)
71+
{
72+
}
73+
74+
template<typename T>
75+
T func(const T & input) const
76+
{
77+
const T alpha = T(5.0);
78+
const T coeff = T(0.05);
79+
80+
const T p1 = abs(alpha - T(2));
81+
const T p2 = coeff * input * input;
82+
83+
return (p1/alpha) * (pow(T(1) + p2/p1, alpha / T(2)) - T(1));
84+
}
85+
86+
template<typename T>
87+
bool operator()(T const* const* parameters, T* residuals) const
88+
{
89+
const T* parameter_intrinsics = parameters[0];
90+
const T* parameter_distortion = parameters[1];
91+
const T* parameter_pose = parameters[2];
92+
T parameter_point[3];
93+
94+
parameter_point[0] = T(_point.x());
95+
parameter_point[1] = T(_point.y());
96+
parameter_point[2] = T(_point.z());
97+
98+
//--
99+
// Apply external parameters (Pose)
100+
//--
101+
const T* cam_R = parameter_pose;
102+
const T* cam_t = &parameter_pose[3];
103+
104+
T transformedPoint[3];
105+
// Rotate the point according the camera rotation
106+
ceres::AngleAxisRotatePoint(cam_R, parameter_point, transformedPoint);
107+
108+
// Apply the camera translation
109+
transformedPoint[0] += cam_t[0];
110+
transformedPoint[1] += cam_t[1];
111+
transformedPoint[2] += cam_t[2];
112+
113+
const T * innerParameters[3];
114+
innerParameters[0] = parameter_intrinsics;
115+
innerParameters[1] = parameter_distortion;
116+
innerParameters[2] = transformedPoint;
117+
118+
if (!_intrinsicFunctor(innerParameters, residuals))
119+
{
120+
return false;
121+
}
122+
123+
residuals[0] = func(residuals[0]);
124+
residuals[1] = func(residuals[1]);
125+
126+
return true;
127+
}
128+
129+
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
130+
Vec3 _point;
131+
};
132+
64133
struct ProjectionErrorFunctor
65134
{
66135
explicit ProjectionErrorFunctor(const sfmData::Observation& obs, const std::shared_ptr<camera::IntrinsicBase>& intrinsics)

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

0 commit comments

Comments
 (0)