Skip to content

Commit ed30719

Browse files
author
Fabien Servant
committed
Survey cost function
1 parent ed3e162 commit ed30719

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
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)

0 commit comments

Comments
 (0)