Skip to content

Commit 72e01e4

Browse files
authored
Merge pull request #2011 from alicevision/dev/depthConstraint
Add observed depth constraint in bundle adjustment
2 parents 33aa4e7 + 4e012a6 commit 72e01e4

File tree

9 files changed

+166
-2
lines changed

9 files changed

+166
-2
lines changed

meshroom/aliceVision/CameraInit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ def createViewpointsFile(self, node, additionalViews=()):
665665
view['metadata'] = json.loads(view['metadata'])
666666

667667
sfmData = {
668-
"version": [1, 2, 12],
668+
"version": [1, 2, 13],
669669
"views": views + newViews,
670670
"intrinsics": intrinsics,
671671
"featureFolder": "",

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <aliceVision/sfm/bundle/costfunctions/constraintPoint.hpp>
1414
#include <aliceVision/sfm/bundle/costfunctions/projection.hpp>
1515
#include <aliceVision/sfm/bundle/costfunctions/rotationPrior.hpp>
16+
#include <aliceVision/sfm/bundle/costfunctions/depth.hpp>
1617
#include <aliceVision/sfm/bundle/manifolds/intrinsics.hpp>
1718
#include <aliceVision/sfmData/SfMData.hpp>
1819
#include <aliceVision/camera/camera.hpp>
@@ -617,6 +618,18 @@ void BundleAdjustmentCeres::addLandmarksToProblem(const sfmData::SfMData& sfmDat
617618
problem.AddResidualBlock(costFunction, lossFunction, params);
618619
}
619620

621+
if (observation.getDepth() > 0.0)
622+
{
623+
const double depthVariance = 0.1; //10 cm ?
624+
ceres::CostFunction* costFunction = DepthErrorFunctor::createCostFunction(observation, depthVariance);
625+
626+
std::vector<double*> params;
627+
params.push_back(poseBlockPtr);
628+
params.push_back(landmarkBlockPtr);
629+
630+
problem.AddResidualBlock(costFunction, nullptr, params);
631+
}
632+
620633
if (!refineStructure || landmark.state == EEstimatorParameterState::CONSTANT)
621634
{
622635
// set the whole landmark parameter block as constant.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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/types.hpp>
10+
#include <ceres/rotation.h>
11+
12+
namespace aliceVision {
13+
namespace sfm {
14+
15+
/**
16+
* Compare the "measured" depth with the estimated point Z position in camera's space
17+
*/
18+
struct DepthErrorFunctor
19+
{
20+
explicit DepthErrorFunctor(double depth, double depthVariance)
21+
: _depth(depth), _depthVariance(depthVariance)
22+
{
23+
}
24+
25+
template<typename T>
26+
bool operator()(T const* const* parameters, T* residuals) const
27+
{
28+
const T* parameter_pose = parameters[0];
29+
const T* parameter_point = parameters[1];
30+
31+
//--
32+
// Apply external parameters (Pose)
33+
//--
34+
const T* cam_R = parameter_pose;
35+
const T* cam_t = &parameter_pose[3];
36+
37+
T transformedPoint[3];
38+
// Rotate the point according the camera rotation
39+
ceres::AngleAxisRotatePoint(cam_R, parameter_point, transformedPoint);
40+
41+
// Apply the camera translation
42+
transformedPoint[2] += cam_t[2];
43+
44+
residuals[0] = T(1.0 / _depthVariance) * (transformedPoint[2] - T(_depth));
45+
46+
return true;
47+
}
48+
49+
/**
50+
* @brief Create the appropriate cost function
51+
* @param[in] observation The corresponding observation
52+
* @return cost functor
53+
*/
54+
inline static ceres::CostFunction* createCostFunction(
55+
const sfmData::Observation& observation, double depthVariance)
56+
{
57+
auto costFunction = new ceres::DynamicAutoDiffCostFunction<DepthErrorFunctor>(new DepthErrorFunctor(observation.getDepth(), depthVariance));
58+
59+
costFunction->AddParameterBlock(6);
60+
costFunction->AddParameterBlock(3);
61+
costFunction->SetNumResiduals(1);
62+
63+
return costFunction;
64+
}
65+
66+
const double _depth;
67+
const double _depthVariance;
68+
};
69+
70+
} // namespace sfm
71+
} // namespace aliceVision

src/aliceVision/sfm/pipeline/expanding/SfmTriangulation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ bool SfmTriangulation::processTrackWithPrior(
263263
o.setFeatureId(trackItem.featureId);
264264
o.setScale(trackItem.scale);
265265
o.setCoordinates(trackItem.coords);
266+
o.setDepth(trackItem.depth);
266267
}
267268

268269
//Store the landmark if it's better than the previously estimated one

src/aliceVision/sfmData/Observation.hpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,97 @@ class Observation
3131
_scale(scale_)
3232
{}
3333

34+
/**
35+
* @brief Comparison operator
36+
* @return true if both featureId and coordinates are equal in both observations
37+
*/
3438
bool operator==(const Observation& other) const;
3539

40+
/**
41+
* @brief Return the coordinates of the observation in pixels
42+
* @return The coordinates in pixels
43+
*/
3644
const Vec2& getCoordinates() const { return _coordinates; }
3745

46+
/**
47+
* @brief Return the coordinates of the observation in pixels
48+
* @return The coordinates in pixels
49+
*/
3850
Vec2& getCoordinates() { return _coordinates; }
3951

52+
/**
53+
* @brief Return the X coordinate of the observation in pixels
54+
* @return The column coordinates in pixels
55+
*/
4056
double getX() const { return _coordinates.x(); }
4157

58+
/**
59+
* @brief Return the Y coordinate of the observation in pixels
60+
* @return The row coordinates in pixels
61+
*/
4262
double getY() const { return _coordinates.y(); }
4363

64+
/**
65+
* @brief Set the image coordinates of the observation
66+
* @param coordinates the 2d vector with pixel coordinates
67+
*/
4468
void setCoordinates(const Vec2& coordinates) { _coordinates = coordinates; }
4569

70+
/**
71+
* @brief Set the image coordinates of the observation
72+
* @param x the x coordinates in the image (column)
73+
* @param y the x coordinates in the image (row)
74+
*/
4675
void setCoordinates(double x, double y)
4776
{
4877
_coordinates(0) = x;
4978
_coordinates(1) = y;
5079
}
5180

81+
/**
82+
* @brief An observation should be associated to a feature. This is the id of the feature.
83+
* @return featureId an id of the feature (view specific)
84+
*/
5285
IndexT getFeatureId() const { return _idFeature; }
5386

87+
/**
88+
* @brief An observation should be associated to a feature. This is the id of the feature.
89+
* @param featureId an id of the feature (view specific)
90+
*/
5491
void setFeatureId(IndexT featureId) { _idFeature = featureId; }
5592

93+
/**
94+
* @brief Get the measured scale of the feature (Scale of the source image)
95+
* @return the scale by which the image has been zoomed or 0 if unknown
96+
*/
5697
double getScale() const { return _scale; }
5798

99+
/**
100+
* @brief Set the measured scale of the feature (Scale of the source image)
101+
* @param scale the scale by which the image has been zoomed or 0 if unknown
102+
*/
58103
void setScale(double scale) { _scale = scale; }
59104

105+
/**
106+
* @brief Get the measured depth (Depth meaning is camera type dependent)
107+
* @return The optional measured depth, or less than 0 if non used
108+
*/
109+
double getDepth() const { return _depth; }
110+
111+
/**
112+
* @brief Set the measured point depth (Depth meaning is camera type dependent)
113+
* @param depth the point depth
114+
*/
115+
void setDepth(double depth) { _depth = depth; }
116+
117+
60118
private:
61119
Vec2 _coordinates = { 0.0, 0.0 };
62120
IndexT _idFeature = UndefinedIndexT;
63121
double _scale = 0.0;
122+
123+
//Optional measured 'depth'
124+
double _depth = -1.0;
64125
};
65126

66127
/// Observations are indexed by their View_id

src/aliceVision/sfmDataIO/AlembicExporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,11 +567,13 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
567567

568568
std::vector<float> featPos2d;
569569
std::vector<float> featScale;
570+
std::vector<float> featDepth;
570571
if (withFeatures)
571572
{
572573
featPos2d.reserve(nbObservations * 2);
573574
visibilityFeatId.reserve(nbObservations);
574575
featScale.reserve(nbObservations);
576+
featDepth.reserve(nbObservations);
575577
}
576578

577579
for (const auto& landmark : landmarks)
@@ -594,6 +596,7 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
594596
featPos2d.emplace_back(obs.getY());
595597

596598
featScale.emplace_back(obs.getScale());
599+
featDepth.emplace_back(obs.getDepth());
597600
}
598601
}
599602
}
@@ -606,6 +609,7 @@ void AlembicExporter::addLandmarks(const sfmData::Landmarks& landmarks,
606609
OUInt32ArrayProperty(userProps, "mvg_visibilityFeatId").set(visibilityFeatId);
607610
OFloatArrayProperty(userProps, "mvg_visibilityFeatPos").set(featPos2d); // feature position (x,y)
608611
OFloatArrayProperty(userProps, "mvg_visibilityFeatScale").set(featScale);
612+
OFloatArrayProperty(userProps, "mvg_visibilityFeatDepth").set(featDepth);
609613
}
610614
}
611615
if (!landmarksUncertainty.empty())

src/aliceVision/sfmDataIO/AlembicImporter.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
314314
AV_UInt32ArraySamplePtr sampleVisibilityFeatId;
315315
FloatArraySamplePtr sampleVisibilityFeatPos;
316316
FloatArraySamplePtr sampleVisibilityFeatScale;
317+
FloatArraySamplePtr sampleVisibilityFeatDepth;
317318

318319
if (userProps.getPropertyHeader("mvg_visibilityFeatId") && userProps.getPropertyHeader("mvg_visibilityFeatPos") &&
319320
flags_part & ESfMData::OBSERVATIONS_WITH_FEATURES)
@@ -329,6 +330,12 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
329330
propVisibilityFeatScale.get(sampleVisibilityFeatScale);
330331
}
331332

333+
if (userProps && userProps.getPropertyHeader("mvg_visibilityFeatDepth"))
334+
{
335+
IFloatArrayProperty propVisibilityFeatDepth(userProps, "mvg_visibilityFeatDepth");
336+
propVisibilityFeatDepth.get(sampleVisibilityFeatDepth);
337+
}
338+
332339
if (sampleVisibilityViewId.size() != sampleVisibilityFeatId.size() ||
333340
2 * sampleVisibilityViewId.size() != sampleVisibilityFeatPos->size())
334341
{
@@ -382,6 +389,11 @@ bool readPointCloud(const Version& abcVersion, IObject iObj, M44d mat, sfmData::
382389
{
383390
observation.setScale((*sampleVisibilityFeatScale)[obsGlobalIndex]);
384391
}
392+
393+
if (sampleVisibilityFeatDepth)
394+
{
395+
observation.setDepth((*sampleVisibilityFeatDepth)[obsGlobalIndex]);
396+
}
385397
}
386398
else
387399
{

src/aliceVision/sfmDataIO/jsonIO.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ void saveLandmark(const std::string& name,
527527
obsTree.put("featureId", observation.getFeatureId());
528528
saveMatrix("x", observation.getCoordinates(), obsTree);
529529
obsTree.put("scale", observation.getScale());
530+
obsTree.put("depth", observation.getDepth());
530531
}
531532

532533
observationsTree.push_back(std::make_pair("", obsTree));
@@ -561,6 +562,7 @@ void loadLandmark(IndexT& landmarkId, sfmData::Landmark& landmark, bpt::ptree& l
561562
observation.setFeatureId(obsTree.get<IndexT>("featureId"));
562563
loadMatrix("x", observation.getCoordinates(), obsTree);
563564
observation.setScale(obsTree.get<double>("scale", 0.0));
565+
observation.setDepth(obsTree.get<double>("depth", -1.0));
564566
}
565567

566568
landmark.getObservations().emplace(obsTree.get<IndexT>("observationId"), observation);

src/aliceVision/sfmDataIO/sfmDataIO.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#define ALICEVISION_SFMDATAIO_VERSION_MAJOR 1
1414
#define ALICEVISION_SFMDATAIO_VERSION_MINOR 2
15-
#define ALICEVISION_SFMDATAIO_VERSION_REVISION 12
15+
#define ALICEVISION_SFMDATAIO_VERSION_REVISION 13
1616

1717
// AliceVision version as a string; for example "0.9.0".
1818
#define ALICEVISION_SFMDATAIO_VERSION_STRING \

0 commit comments

Comments
 (0)