Skip to content

Commit 07d3359

Browse files
author
Fabien Servant
committed
Survey cost function update
1 parent 9c12693 commit 07d3359

File tree

4 files changed

+180
-103
lines changed

4 files changed

+180
-103
lines changed

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.cpp

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <aliceVision/sfm/bundle/costfunctions/rotationPrior.hpp>
1717
#include <aliceVision/sfm/bundle/costfunctions/temporalConstraint.hpp>
1818
#include <aliceVision/sfm/bundle/costfunctions/depth.hpp>
19+
#include <aliceVision/sfm/bundle/costfunctions/survey.hpp>
1920
#include <aliceVision/sfm/bundle/manifolds/intrinsics.hpp>
2021
#include <aliceVision/sfm/utils/poseFilter.hpp>
2122
#include <aliceVision/sfm/utils/statistics.hpp>
@@ -707,14 +708,33 @@ void BundleAdjustmentCeres::addSurveyPointsToProblem(const sfmData::SfMData& sfm
707708
double* fakeDistortionBlockPtr = &_fakeDistortionBlock;
708709

709710
const sfmData::View& view = sfmData.getView(idView);
711+
if (!sfmData.isPoseAndIntrinsicDefined(view))
712+
{
713+
continue;
714+
}
715+
710716
const IndexT intrinsicId = view.getIntrinsicId();
717+
const IndexT poseId = view.getPoseId();
718+
719+
// Do not add surveys if the pose is not to be estimated
720+
if (_posesBlocks.find(poseId) == _posesBlocks.end())
721+
{
722+
continue;
723+
}
724+
725+
// Do not add surveys if the intrinsic is not to be estimated
726+
if (_intrinsicsBlocks.find(intrinsicId) == _intrinsicsBlocks.end())
727+
{
728+
continue;
729+
}
711730

712731
// each residual block takes a point and a camera as input and outputs a 2
713732
// dimensional residual. Internally, the cost function stores the observed
714733
// image location and compares the reprojection against the observation.
715734
const auto& pose = sfmData.getPose(view);
716735

717736
// needed parameters to create a residual block (K, pose)
737+
718738
double* poseBlockPtr = _posesBlocks.at(view.getPoseId()).data();
719739
double* intrinsicBlockPtr = _intrinsicsBlocks.at(intrinsicId).data();
720740
const std::shared_ptr<IntrinsicBase> intrinsic = _intrinsicObjects[intrinsicId];
@@ -738,7 +758,7 @@ void BundleAdjustmentCeres::addSurveyPointsToProblem(const sfmData::SfMData& sfm
738758
{
739759
sfmData::Observation observation(spoint.survey, 0, 1.0);
740760
ceres::CostFunction* costFunction =
741-
ProjectionSurveyErrorFunctor::createCostFunction(intrinsic, spoint.point3d, observation);
761+
SurveyErrorFunctor::createCostFunction(intrinsic, spoint.point3d, spoint.residual, observation, 1.0);
742762

743763
std::vector<double*> params;
744764
params.push_back(intrinsicBlockPtr);
@@ -1146,6 +1166,39 @@ void BundleAdjustmentCeres::createJacobian(const sfmData::SfMData& sfmData, ERef
11461166
problem.Evaluate(evalOpt, &cost, NULL, NULL, &jacobian);
11471167
}
11481168

1169+
void BundleAdjustmentCeres::surveyInfos(const sfmData::SfMData & sfmData) const
1170+
{
1171+
if (sfmData.getSurveyPoints().size() == 0)
1172+
{
1173+
return;
1174+
}
1175+
1176+
ALICEVISION_LOG_INFO("Surveys after minimzization:");
1177+
for (const auto & [idView, surveys]: sfmData.getSurveyPoints())
1178+
{
1179+
const sfmData::View & view = sfmData.getView(idView);
1180+
if (!sfmData.isPoseAndIntrinsicDefined(view))
1181+
{
1182+
continue;
1183+
}
1184+
1185+
const auto & intrinsic = sfmData.getIntrinsic(view.getIntrinsicId());
1186+
const auto pose = sfmData.getPose(view);
1187+
1188+
ALICEVISION_LOG_INFO("View " << idView << ":");
1189+
for (const auto & survey : surveys)
1190+
{
1191+
Vec2 obs = intrinsic.transformProject(pose.getTransform(), survey.point3d.homogeneous(), true);
1192+
1193+
double ex = std::abs(obs.x() - survey.survey.x());
1194+
double ey = std::abs(obs.y() - survey.survey.y());
1195+
double rx = std::abs(survey.residual.x());
1196+
double ry = std::abs(survey.residual.x());
1197+
ALICEVISION_LOG_INFO("Surveyed : [" << ex << ", " << ey << "], Current ["<< rx <<", "<< ry <<"]");
1198+
}
1199+
}
1200+
}
1201+
11491202
bool BundleAdjustmentCeres::adjust(sfmData::SfMData& sfmData, ERefineOptions refineOptions)
11501203
{
11511204
ALICEVISION_LOG_INFO("BundleAdjustmentCeres::adjust start");
@@ -1211,6 +1264,9 @@ bool BundleAdjustmentCeres::adjust(sfmData::SfMData& sfmData, ERefineOptions ref
12111264
// update input sfmData with the solution
12121265
updateFromSolution(sfmData, refineOptions);
12131266

1267+
// Info from surveys
1268+
surveyInfos(sfmData);
1269+
12141270
// store some statistics from the summary
12151271
_statistics.time = summary.total_time_in_seconds;
12161272
_statistics.nbSuccessfullIterations = summary.num_successful_steps;

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ class BundleAdjustmentCeres : public BundleAdjustment, ceres::EvaluationCallback
238238
*/
239239
void updateFromSolution(sfmData::SfMData& sfmData, ERefineOptions refineOptions) const;
240240

241+
/**
242+
* @brief Print current state of surveys
243+
* @param[in] sfmData the current sfmData to get infos from
244+
*/
245+
void surveyInfos(const sfmData::SfMData & sfmData) const;
246+
241247
// private members
242248

243249
/// user Ceres options to use in the solver

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

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -93,108 +93,6 @@ struct ProjectionSimpleErrorFunctor
9393
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
9494
};
9595

96-
97-
struct ProjectionSurveyErrorFunctor
98-
{
99-
explicit ProjectionSurveyErrorFunctor(const Vec3 & point,
100-
const sfmData::Observation& obs,
101-
const std::shared_ptr<camera::IntrinsicBase>& intrinsics)
102-
: _intrinsicFunctor(new CostIntrinsicsProject(obs, intrinsics)), _point(point)
103-
{
104-
}
105-
106-
template<typename T>
107-
T func(const T & input) const
108-
{
109-
const T alpha = T(5.0);
110-
const T coeff = T(0.05);
111-
112-
const T p1 = abs(alpha - T(2));
113-
const T p2 = coeff * input * input;
114-
115-
return (p1/alpha) * (pow(T(1) + p2/p1, alpha / T(2)) - T(1));
116-
}
117-
118-
template<typename T>
119-
bool operator()(T const* const* parameters, T* residuals) const
120-
{
121-
const T* parameter_intrinsics = parameters[0];
122-
const T* parameter_distortion = parameters[1];
123-
const T* parameter_pose = parameters[2];
124-
T parameter_point[3];
125-
126-
parameter_point[0] = T(_point.x());
127-
parameter_point[1] = T(_point.y());
128-
parameter_point[2] = T(_point.z());
129-
130-
//--
131-
// Apply external parameters (Pose)
132-
//--
133-
const T* cam_R = parameter_pose;
134-
const T* cam_t = &parameter_pose[3];
135-
136-
T transformedPoint[3];
137-
// Rotate the point according the camera rotation
138-
ceres::AngleAxisRotatePoint(cam_R, parameter_point, transformedPoint);
139-
140-
// Apply the camera translation
141-
transformedPoint[0] += cam_t[0];
142-
transformedPoint[1] += cam_t[1];
143-
transformedPoint[2] += cam_t[2];
144-
145-
const T * innerParameters[3];
146-
innerParameters[0] = parameter_intrinsics;
147-
innerParameters[1] = parameter_distortion;
148-
innerParameters[2] = transformedPoint;
149-
150-
if (!_intrinsicFunctor(innerParameters, residuals))
151-
{
152-
return false;
153-
}
154-
155-
residuals[0] = func(residuals[0]);
156-
residuals[1] = func(residuals[1]);
157-
158-
return true;
159-
}
160-
161-
/**
162-
* @brief Create the appropriate cost functor according the provided input camera intrinsic model
163-
* @param[in] intrinsicPtr The intrinsic pointer
164-
* @param[in] point the reference point to compare to
165-
* @param[in] observation The corresponding observation
166-
* @return cost functor
167-
*/
168-
inline static ceres::CostFunction* createCostFunction(
169-
const std::shared_ptr<camera::IntrinsicBase> intrinsic,
170-
const Vec3 & point,
171-
const sfmData::Observation& observation)
172-
{
173-
auto costFunction = new ceres::DynamicAutoDiffCostFunction<ProjectionSurveyErrorFunctor>(new ProjectionSurveyErrorFunctor(point, observation, intrinsic));
174-
175-
int distortionSize = 1;
176-
auto isod = camera::IntrinsicScaleOffsetDisto::cast(intrinsic);
177-
if (isod)
178-
{
179-
auto distortion = isod->getDistortion();
180-
if (distortion)
181-
{
182-
distortionSize = distortion->getParameters().size();
183-
}
184-
}
185-
186-
costFunction->AddParameterBlock(intrinsic->getParameters().size());
187-
costFunction->AddParameterBlock(distortionSize);
188-
costFunction->AddParameterBlock(6);
189-
costFunction->SetNumResiduals(2);
190-
191-
return costFunction;
192-
}
193-
194-
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
195-
Vec3 _point;
196-
};
197-
19896
struct ProjectionErrorFunctor
19997
{
20098
explicit ProjectionErrorFunctor(const sfmData::Observation& obs, const std::shared_ptr<camera::IntrinsicBase>& intrinsics)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2026 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+
10+
namespace aliceVision {
11+
namespace sfm {
12+
13+
14+
struct SurveyErrorFunctor
15+
{
16+
explicit SurveyErrorFunctor(const Vec3 & point,
17+
const sfmData::Observation& obs,
18+
const Vec2 & residual,
19+
const std::shared_ptr<camera::IntrinsicBase>& intrinsics,
20+
double weight)
21+
: _intrinsicFunctor(new CostIntrinsicsProject(obs, intrinsics)), _point(point), _originalResidual(residual), _weight(weight)
22+
{
23+
}
24+
25+
template<typename T>
26+
bool operator()(T const* const* parameters, T* residuals) const
27+
{
28+
const T* parameter_intrinsics = parameters[0];
29+
const T* parameter_distortion = parameters[1];
30+
const T* parameter_pose = parameters[2];
31+
32+
//Create input point
33+
T parameter_point[3];
34+
parameter_point[0] = T(_point.x());
35+
parameter_point[1] = T(_point.y());
36+
parameter_point[2] = T(_point.z());
37+
38+
//--
39+
// Apply external parameters (Pose)
40+
//--
41+
const T* cam_R = parameter_pose;
42+
const T* cam_t = &parameter_pose[3];
43+
44+
T transformedPoint[3];
45+
// Rotate the point according the camera rotation
46+
ceres::AngleAxisRotatePoint(cam_R, parameter_point, transformedPoint);
47+
48+
// Apply the camera translation
49+
transformedPoint[0] += cam_t[0];
50+
transformedPoint[1] += cam_t[1];
51+
transformedPoint[2] += cam_t[2];
52+
53+
const T * innerParameters[3];
54+
innerParameters[0] = parameter_intrinsics;
55+
innerParameters[1] = parameter_distortion;
56+
innerParameters[2] = transformedPoint;
57+
58+
if (!_intrinsicFunctor(innerParameters, residuals))
59+
{
60+
return false;
61+
}
62+
63+
T epsilon(1e-10);
64+
T rx(std::abs(_originalResidual[0]) * 1.1);
65+
T ry(std::abs(_originalResidual[1]) * 1.1);
66+
67+
residuals[0] = sqrt(_weight) * fmax(T(0), sqrt(residuals[0]*residuals[0] + epsilon) - rx);
68+
residuals[1] = sqrt(_weight) * fmax(T(0), sqrt(residuals[1]*residuals[1] + epsilon) - ry);
69+
70+
return true;
71+
}
72+
73+
/**
74+
* @brief Create the appropriate cost functor according the provided input camera intrinsic model
75+
* @param[in] intrinsicPtr The intrinsic pointer
76+
* @param[in] point the reference point to compare to
77+
* @param[in] residual 2d vector with the original residual for the observation
78+
* @param[in] observation The corresponding observation
79+
* @param[in] weight the scale for the loss
80+
* @return cost functor
81+
*/
82+
inline static ceres::CostFunction* createCostFunction(
83+
const std::shared_ptr<camera::IntrinsicBase> intrinsic,
84+
const Vec3 & point,
85+
const Vec2 & residual,
86+
const sfmData::Observation& observation,
87+
double weight)
88+
{
89+
auto costFunction = new ceres::DynamicAutoDiffCostFunction<SurveyErrorFunctor>(new SurveyErrorFunctor(point, observation, residual, intrinsic, weight));
90+
91+
int distortionSize = 1;
92+
auto isod = camera::IntrinsicScaleOffsetDisto::cast(intrinsic);
93+
if (isod)
94+
{
95+
auto distortion = isod->getDistortion();
96+
if (distortion)
97+
{
98+
distortionSize = distortion->getParameters().size();
99+
}
100+
}
101+
102+
costFunction->AddParameterBlock(intrinsic->getParameters().size());
103+
costFunction->AddParameterBlock(distortionSize);
104+
costFunction->AddParameterBlock(6);
105+
costFunction->SetNumResiduals(2);
106+
107+
return costFunction;
108+
}
109+
110+
ceres::DynamicCostFunctionToFunctorTmp _intrinsicFunctor;
111+
Vec3 _point;
112+
Vec2 _originalResidual;
113+
double _weight;
114+
};
115+
116+
} // namespace sfm
117+
} // namespace aliceVision

0 commit comments

Comments
 (0)