Skip to content

Commit ce3c1e1

Browse files
authored
Merge pull request #1803 from alicevision/dev/sfmLidar
Use lidar as constraints in sfm
2 parents 5a90d46 + 0d4bbda commit ce3c1e1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1355
-286
lines changed

src/aliceVision/keyframe/KeyframeSelector.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,8 @@ bool KeyframeSelector::writeSfMData(const std::string& mediaPath,
12521252

12531253
bool KeyframeSelector::writeSfMDataFromSfMData(const std::string& mediaPath)
12541254
{
1255+
auto& keyframesPoses = _outputSfmKeyframes.getPoses();
1256+
12551257
auto& keyframesViews = _outputSfmKeyframes.getViews();
12561258
auto& framesViews = _outputSfmFrames.getViews();
12571259

@@ -1292,6 +1294,18 @@ bool KeyframeSelector::writeSfMDataFromSfMData(const std::string& mediaPath)
12921294
{
12931295
viewId = views[i]->getViewId();
12941296
intrinsicId = views[i]->getIntrinsicId();
1297+
IndexT poseId = views[i]->getPoseId();
1298+
1299+
bool hasPose = inputSfm.isPoseAndIntrinsicDefined(viewId);
1300+
if (hasPose)
1301+
{
1302+
ALICEVISION_LOG_INFO("A fully calibrated view (Id:" << viewId << ") is moved to keyframes");
1303+
_selectedFrames[i] = '1';
1304+
1305+
//Make sure to keep the pose
1306+
keyframesPoses.emplace(poseId, inputSfm.getPoses().at(poseId));
1307+
}
1308+
12951309
if (_selectedFrames[i] == '1')
12961310
{
12971311
keyframesViews.emplace(viewId, views[i]);

src/aliceVision/mesh/MeshIntersection.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ bool MeshIntersection::initialize(const std::string & pathToModel)
2828
return true;
2929
}
3030

31-
bool MeshIntersection::peekPoint(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords)
31+
bool MeshIntersection::pickPoint(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords)
3232
{
3333
const Vec3 posCamera = _pose.center();
3434
const Vec3 wdir = intrinsic.backprojectTransform(imageCoords, true, _pose, 1.0);
@@ -58,7 +58,7 @@ bool MeshIntersection::peekPoint(Vec3 & output, const camera::IntrinsicBase & in
5858
return true;
5959
}
6060

61-
bool MeshIntersection::peekNormal(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords)
61+
bool MeshIntersection::pickNormal(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords)
6262
{
6363
const Vec3 posCamera = _pose.center();
6464
const Vec3 wdir = intrinsic.backprojectTransform(imageCoords, true, _pose, 1.0);
@@ -88,5 +88,41 @@ bool MeshIntersection::peekNormal(Vec3 & output, const camera::IntrinsicBase & i
8888
return true;
8989
}
9090

91+
bool MeshIntersection::pickPointAndNormal(Vec3 & point, Vec3 & normal, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords)
92+
{
93+
const Vec3 posCamera = _pose.center();
94+
const Vec3 wdir = intrinsic.backprojectTransform(imageCoords, true, _pose, 1.0);
95+
const Vec3 dir = (wdir - posCamera).normalized();
96+
97+
//Create geogram ray from alicevision ray
98+
GEO::Ray ray;
99+
ray.origin.x = posCamera.x();
100+
ray.origin.y = -posCamera.y();
101+
ray.origin.z = -posCamera.z();
102+
ray.direction.x = dir.x();
103+
ray.direction.y = -dir.y();
104+
ray.direction.z = -dir.z();
105+
106+
GEO::MeshFacetsAABB::Intersection intersection;
107+
if (!_aabb.ray_nearest_intersection(ray, intersection))
108+
{
109+
return false;
110+
}
111+
112+
const GEO::vec3 p = ray.origin + intersection.t * ray.direction;
113+
114+
point.x() = p.x;
115+
point.y() = -p.y;
116+
point.z() = -p.z;
117+
118+
const GEO::vec3 n = GEO::normalize(intersection.N);
119+
120+
normal.x() = n.x;
121+
normal.y() = -n.y;
122+
normal.z() = -n.z;
123+
124+
return true;
125+
}
126+
91127
}
92128
}

src/aliceVision/mesh/MeshIntersection.hpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class MeshIntersection
2222
bool initialize(const std::string & pathToModel);
2323

2424
/**
25-
* @brief Update pose to use for peeking
25+
* @brief Update pose to use for picking
2626
* @param pose transformation to use (in aliceVision standard form)
2727
*/
2828
void setPose(const geometry::Pose3 & pose)
@@ -31,22 +31,32 @@ class MeshIntersection
3131
}
3232

3333
/**
34-
* @brief peek a point on the mesh given a input camera observation
34+
* @brief pick a point on the mesh given a input camera observation
3535
* @param output the output measured point
3636
* @param intrinsic the camera intrinsics to use for ray computation
3737
* @param imageCoords the camera observation we want to use to estimate its 'depth'
38-
* @return true if the ray intersect the mesh.
38+
* @return true if the ray intersects the mesh.
3939
*/
40-
bool peekPoint(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords);
40+
bool pickPoint(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords);
4141

4242
/**
43-
* @brief peek a point and get its normal on the mesh given a input camera observation
43+
* @brief pick a point and get its normal on the mesh given a input camera observation
4444
* @param output the output measured normal
4545
* @param intrinsic the camera intrinsics to use for ray computation
4646
* @param imageCoords the camera observation we want to use to estimate its 'depth'
47-
* @return true if the ray intersect the mesh.
47+
* @return true if the ray intersects the mesh.
4848
*/
49-
bool peekNormal(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords);
49+
bool pickNormal(Vec3 & output, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords);
50+
51+
/**
52+
* @brief pick a point and get its normal on the mesh given a input camera observation
53+
* @param point the output measured point
54+
* @param normal the output measured normal
55+
* @param intrinsic the camera intrinsics to use for ray computation
56+
* @param imageCoords the camera observation we want to use to estimate its 'depth'
57+
* @return true if the ray intersects the mesh.
58+
*/
59+
bool pickPointAndNormal(Vec3 & point, Vec3 & normal, const camera::IntrinsicBase & intrinsic, const Vec2 & imageCoords);
5060

5161
private:
5262
GEO::Mesh _mesh;

src/aliceVision/sfm/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ set(sfm_files_headers
2424
pipeline/RelativePoseInfo.hpp
2525
pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.hpp
2626
pipeline/panorama/ReconstructionEngine_panorama.hpp
27+
pipeline/bootstrapping/EstimateAngle.hpp
28+
pipeline/bootstrapping/PairsScoring.hpp
29+
pipeline/bootstrapping/Bootstrap.hpp
2730
pipeline/expanding/SfmTriangulation.hpp
2831
pipeline/expanding/SfmResection.hpp
2932
pipeline/expanding/SfmBundle.hpp
@@ -65,6 +68,9 @@ set(sfm_files_sources
6568
pipeline/RelativePoseInfo.cpp
6669
pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp
6770
pipeline/panorama/ReconstructionEngine_panorama.cpp
71+
pipeline/bootstrapping/EstimateAngle.cpp
72+
pipeline/bootstrapping/PairsScoring.cpp
73+
pipeline/bootstrapping/Bootstrap.cpp
6874
pipeline/expanding/SfmTriangulation.cpp
6975
pipeline/expanding/SfmResection.cpp
7076
pipeline/expanding/SfmBundle.cpp

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <aliceVision/sfm/bundle/BundleAdjustmentCeres.hpp>
99
#include <aliceVision/sfm/bundle/costfunctions/constraint2d.hpp>
10+
#include <aliceVision/sfm/bundle/costfunctions/constraintPoint.hpp>
1011
#include <aliceVision/sfm/bundle/costfunctions/projection.hpp>
1112
#include <aliceVision/sfm/bundle/costfunctions/rotationPrior.hpp>
1213
#include <aliceVision/sfm/bundle/manifolds/intrinsics.hpp>
@@ -86,6 +87,17 @@ ceres::CostFunction* createConstraintsCostFunctionFromIntrinsics(std::shared_ptr
8687
return costFunction;
8788
}
8889

90+
ceres::CostFunction* createCostFunctionFromContraintPoint(const sfmData::Landmark & landmark, const Vec3 & normal)
91+
{
92+
const double weight = 100.0;
93+
auto costFunction = new ceres::DynamicAutoDiffCostFunction<ConstraintPointErrorFunctor>(new ConstraintPointErrorFunctor(weight, normal, landmark.X));
94+
95+
costFunction->AddParameterBlock(3);
96+
costFunction->SetNumResiduals(1);
97+
98+
return costFunction;
99+
}
100+
89101
void BundleAdjustmentCeres::CeresOptions::setDenseBA()
90102
{
91103
// default configuration use a DENSE representation
@@ -671,6 +683,33 @@ void BundleAdjustmentCeres::addConstraints2DToProblem(const sfmData::SfMData& sf
671683
}
672684
}
673685

686+
void BundleAdjustmentCeres::addConstraintsPointToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem)
687+
{
688+
// set a LossFunction to be less penalized by false measurements.
689+
// note: set it to NULL if you don't want use a lossFunction.
690+
ceres::LossFunction* lossFunction = _ceresOptions.lossFunction.get();
691+
692+
for (const auto& [landmarkId, constraint] : sfmData.getConstraintsPoint())
693+
{
694+
if (sfmData.getLandmarks().find(landmarkId) == sfmData.getLandmarks().end())
695+
{
696+
continue;
697+
}
698+
699+
if (_landmarksBlocks.find(landmarkId) == _landmarksBlocks.end())
700+
{
701+
continue;
702+
}
703+
704+
const sfmData::Landmark & l = sfmData.getLandmarks().at(landmarkId);
705+
double * ldata = _landmarksBlocks.at(landmarkId).data();
706+
707+
ceres::CostFunction* costFunction = createCostFunctionFromContraintPoint(l, constraint.normal);
708+
709+
problem.AddResidualBlock(costFunction, lossFunction, ldata);
710+
}
711+
}
712+
674713
void BundleAdjustmentCeres::addRotationPriorsToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem)
675714
{
676715
// set a LossFunction to be less penalized by false measurements.
@@ -718,6 +757,9 @@ void BundleAdjustmentCeres::createProblem(const sfmData::SfMData& sfmData, ERefi
718757
// add 2D constraints to the Ceres problem
719758
addConstraints2DToProblem(sfmData, refineOptions, problem);
720759

760+
// add 2D constraints to the Ceres problem
761+
addConstraintsPointToProblem(sfmData, refineOptions, problem);
762+
721763
// add rotation priors to the Ceres problem
722764
addRotationPriorsToProblem(sfmData, refineOptions, problem);
723765
}

src/aliceVision/sfm/bundle/BundleAdjustmentCeres.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ class BundleAdjustmentCeres : public BundleAdjustment, ceres::EvaluationCallback
189189
*/
190190
void addConstraints2DToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem);
191191

192+
/**
193+
* @brief Create a residual block for each point constraints
194+
* @param[in] sfmData The input SfMData contains all the information about the reconstruction, notably the intrinsics
195+
* @param[in] refineOptions The chosen refine flag
196+
* @param[out] problem The Ceres bundle adjustement problem
197+
*/
198+
void addConstraintsPointToProblem(const sfmData::SfMData& sfmData, ERefineOptions refineOptions, ceres::Problem& problem);
199+
192200
/**
193201
* @brief Create a residual block for each rotation priors
194202
* @param[in] sfmData The input SfMData contains all the information about the reconstruction, notably the intrinsics
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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/camera/camera.hpp>
10+
11+
namespace aliceVision {
12+
namespace sfm {
13+
14+
struct ConstraintPointErrorFunctor
15+
{
16+
explicit ConstraintPointErrorFunctor(const double weight, const Vec3 & normal, const Vec3 & point)
17+
: _weight(weight), _normal(normal)
18+
{
19+
_constraintDistance = _normal.dot(point);
20+
}
21+
22+
template<typename T>
23+
bool operator()(T const* const* parameters, T* residuals) const
24+
{
25+
const T* parameter_point = parameters[0];
26+
27+
T distance = parameter_point[0] * _normal[0] + parameter_point[1] * _normal[1] + parameter_point[2] * _normal[2];
28+
residuals[0] = _weight * (distance - _constraintDistance);
29+
30+
return true;
31+
}
32+
33+
double _weight;
34+
Vec3 _normal;
35+
double _constraintDistance;
36+
};
37+
38+
} // namespace sfm
39+
} // namespace aliceVision

0 commit comments

Comments
 (0)