-
-
Notifications
You must be signed in to change notification settings - Fork 828
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bootstrapping cleanup and mesh support
- Loading branch information
1 parent
2ed1cf5
commit ebb74d6
Showing
10 changed files
with
565 additions
and
211 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 169 additions & 0 deletions
169
src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// This file is part of the AliceVision project. | ||
// Copyright (c) 2025 AliceVision contributors. | ||
// This Source Code Form is subject to the terms of the Mozilla Public License, | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#include <aliceVision/sfmData/SfMData.hpp> | ||
#include <aliceVision/track/tracksUtils.hpp> | ||
#include <aliceVision/multiview/triangulation/triangulationDLT.hpp> | ||
#include <aliceVision/sfm/pipeline/expanding/SfmResection.hpp> | ||
#include <vector> | ||
#include <random> | ||
|
||
namespace aliceVision { | ||
namespace sfm { | ||
|
||
bool bootstrapBase(sfmData::SfMData & sfmData, | ||
const IndexT referenceViewId, | ||
const IndexT otherViewId, | ||
const geometry::Pose3 & otherTreference, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView) | ||
{ | ||
const sfmData::View& refView = sfmData.getView(referenceViewId); | ||
const sfmData::View& nextView = sfmData.getView(otherViewId); | ||
|
||
std::shared_ptr<camera::IntrinsicBase> refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); | ||
std::shared_ptr<camera::IntrinsicBase> nextIntrinsics = sfmData.getIntrinsicSharedPtr(nextView.getIntrinsicId()); | ||
|
||
sfmData::CameraPose cposeNext(otherTreference, false); | ||
sfmData.getPoses()[refView.getPoseId()] = sfmData::CameraPose(); | ||
sfmData.getPoses()[nextView.getPoseId()] = cposeNext; | ||
|
||
const Mat4 T1 = Eigen::Matrix4d::Identity(); | ||
Mat4 T2 = otherTreference.getHomogeneous(); | ||
|
||
aliceVision::track::TracksMap mapTracksCommon; | ||
track::getCommonTracksInImagesFast({referenceViewId, otherViewId}, tracksMap, tracksPerView, mapTracksCommon); | ||
|
||
size_t count = 0; | ||
std::vector<double> angles; | ||
for(const auto& [trackId, track] : mapTracksCommon) | ||
{ | ||
const track::TrackItem & refItem = track.featPerView.at(referenceViewId); | ||
const track::TrackItem & nextItem = track.featPerView.at(otherViewId); | ||
|
||
const Vec2 refpt = refItem.coords; | ||
const Vec2 nextpt = nextItem.coords; | ||
|
||
const Vec3 pt3d1 = refIntrinsics->toUnitSphere(refIntrinsics->removeDistortion(refIntrinsics->ima2cam(refpt))); | ||
const Vec3 pt3d2 = nextIntrinsics->toUnitSphere(nextIntrinsics->removeDistortion(nextIntrinsics->ima2cam(nextpt))); | ||
|
||
|
||
Vec3 X; | ||
multiview::TriangulateSphericalDLT(T1, pt3d1, T2, pt3d2, X); | ||
|
||
Eigen::Vector3d dirX1 = (T1 * X.homogeneous()).head(3).normalized(); | ||
Eigen::Vector3d dirX2 = (T2 * X.homogeneous()).head(3).normalized(); | ||
if (!(dirX1.dot(pt3d1) > 0.0 && dirX2.dot(pt3d2) > 0.0)) | ||
{ | ||
continue; | ||
} | ||
|
||
sfmData::Landmark landmark; | ||
landmark.descType = track.descType; | ||
landmark.X = X; | ||
|
||
sfmData::Observation refObs; | ||
refObs.setFeatureId(refItem.featureId); | ||
refObs.setScale(refItem.scale); | ||
refObs.setCoordinates(refItem.coords); | ||
|
||
sfmData::Observation nextObs; | ||
nextObs.setFeatureId(nextItem.featureId); | ||
nextObs.setScale(nextItem.scale); | ||
nextObs.setCoordinates(nextItem.coords); | ||
|
||
landmark.getObservations()[referenceViewId] = refObs; | ||
landmark.getObservations()[otherViewId] = nextObs; | ||
|
||
sfmData.getLandmarks()[trackId] = landmark; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool bootstrapMesh(sfmData::SfMData & sfmData, | ||
const sfmData::Landmarks & landmarks, | ||
const IndexT referenceViewId, | ||
const IndexT nextViewId, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView) | ||
{ | ||
//Select which view to resect | ||
IndexT viewId = referenceViewId; | ||
if (sfmData.isPoseAndIntrinsicDefined(viewId)) | ||
{ | ||
viewId = nextViewId; | ||
} | ||
|
||
//Assign new landmarks to sfmData to allow resection to work | ||
sfmData.getLandmarks() = landmarks; | ||
|
||
std::mt19937 randomNumberGenerator; | ||
Eigen::Matrix4d pose; | ||
double threshold; | ||
|
||
//Compute resection for selected view | ||
SfmResection resection(50000, std::numeric_limits<double>::infinity()); | ||
if (!resection.processView(sfmData, tracksMap, tracksPerView, randomNumberGenerator, viewId, pose, threshold)) | ||
{ | ||
return false; | ||
} | ||
|
||
ALICEVISION_LOG_INFO("Resection succeeded with a threshold of " << threshold); | ||
|
||
|
||
//Prepare objects for second view | ||
const sfmData::View& view = sfmData.getView(viewId); | ||
std::shared_ptr<camera::IntrinsicBase> intrinsics = sfmData.getIntrinsicSharedPtr(view.getIntrinsicId()); | ||
|
||
geometry::Pose3 pose3(pose); | ||
sfmData::CameraPose cpose(pose3, false); | ||
sfmData.getPoses()[viewId] = cpose; | ||
|
||
// Cleanup output | ||
sfmData::Landmarks & outLandmarks = sfmData.getLandmarks(); | ||
outLandmarks.clear(); | ||
|
||
for (const auto & [landmarkId, landmark] : landmarks) | ||
{ | ||
//Retrieve track object | ||
const auto & track = tracksMap.at(landmarkId); | ||
|
||
//Maybe this track is not observed in the next view | ||
if (track.featPerView.find(viewId) == track.featPerView.end()) | ||
{ | ||
continue; | ||
} | ||
|
||
//Compute error | ||
const track::TrackItem & item = track.featPerView.at(viewId); | ||
const Vec2 pt = item.coords; | ||
const Vec2 estpt = intrinsics->transformProject(pose3, landmark.X.homogeneous(), true); | ||
double err = (pt - estpt).norm(); | ||
|
||
//If error is ok, then we add it to the sfmData | ||
if (err <= threshold) | ||
{ | ||
sfmData::Observation obs; | ||
obs.setFeatureId(item.featureId); | ||
obs.setScale(item.scale); | ||
obs.setCoordinates(item.coords); | ||
|
||
|
||
//Add landmark to sfmData | ||
outLandmarks[landmarkId] = landmark; | ||
|
||
//Add observation to landmark | ||
sfmData::Observations & observations = outLandmarks[landmarkId].getObservations(); | ||
observations[nextViewId] = obs; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// This file is part of the AliceVision project. | ||
// Copyright (c) 2025 AliceVision contributors. | ||
// This Source Code Form is subject to the terms of the Mozilla Public License, | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#include <aliceVision/sfmData/SfMData.hpp> | ||
#include <aliceVision/track/tracksUtils.hpp> | ||
|
||
#include <vector> | ||
|
||
namespace aliceVision { | ||
namespace sfm { | ||
|
||
/** | ||
* @brief Create a minimal SfmData with poses and landmarks for two views | ||
* @param sfmData the input sfmData which contains camera information | ||
* @param referenceViewId the reference view id | ||
* @param otherViewId the other view id | ||
* @param otherTreference the relative pose | ||
* @param tracksMap the input map of tracks | ||
* @param tracksPerView tracks grouped by views | ||
* @return true | ||
*/ | ||
bool bootstrapBase(sfmData::SfMData & sfmData, | ||
const IndexT referenceViewId, | ||
const IndexT otherViewId, | ||
const geometry::Pose3 & otherTreference, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView); | ||
|
||
/** | ||
* @brief Create a minimal SfmData with poses and landmarks for two views (given a mesh) | ||
* @param sfmData the input sfmData which contains camera information | ||
* @param landmarks the input Landmarks file which contains mesh geometry | ||
* @param referenceViewId the reference view id | ||
* @param nextViewId the reference view id | ||
* @param tracksMap the input map of tracks | ||
* @param tracksPerView tracks grouped by views | ||
* @return true | ||
*/ | ||
bool bootstrapMesh(sfmData::SfMData & sfmData, | ||
const sfmData::Landmarks & landmarks, | ||
const IndexT referenceViewId, | ||
const IndexT nextViewId, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView); | ||
|
||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// This file is part of the AliceVision project. | ||
// Copyright (c) 2025 AliceVision contributors. | ||
// This Source Code Form is subject to the terms of the Mozilla Public License, | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#include <aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp> | ||
#include <aliceVision/multiview/triangulation/triangulationDLT.hpp> | ||
|
||
#include <algorithm> | ||
|
||
namespace aliceVision { | ||
namespace sfm { | ||
|
||
bool estimatePairAngle(const sfmData::SfMData & sfmData, | ||
const IndexT referenceViewId, | ||
const IndexT otherViewId, | ||
const geometry::Pose3 & otherTreference, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView, | ||
double & resultAngle, | ||
std::vector<size_t> & usedTracks) | ||
{ | ||
usedTracks.clear(); | ||
|
||
const sfmData::View& refView = sfmData.getView(referenceViewId); | ||
const sfmData::View& nextView = sfmData.getView(otherViewId); | ||
|
||
std::shared_ptr<camera::IntrinsicBase> refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); | ||
std::shared_ptr<camera::IntrinsicBase> nextIntrinsics = sfmData.getIntrinsicSharedPtr(nextView.getIntrinsicId()); | ||
|
||
aliceVision::track::TracksMap mapTracksCommon; | ||
track::getCommonTracksInImagesFast({referenceViewId, otherViewId}, tracksMap, tracksPerView, mapTracksCommon); | ||
|
||
const Mat4 T1 = Eigen::Matrix4d::Identity(); | ||
const Mat4 T2 = otherTreference.getHomogeneous(); | ||
|
||
const Eigen::Vector3d c = otherTreference.center(); | ||
|
||
size_t count = 0; | ||
std::vector<double> angles; | ||
for(const auto& commonItem : mapTracksCommon) | ||
{ | ||
const track::Track& track = commonItem.second; | ||
|
||
const IndexT refFeatureId = track.featPerView.at(referenceViewId).featureId; | ||
const IndexT nextfeatureId = track.featPerView.at(otherViewId).featureId; | ||
|
||
const Vec2 refpt = track.featPerView.at(referenceViewId).coords; | ||
const Vec2 nextpt = track.featPerView.at(otherViewId).coords; | ||
|
||
const Vec3 pt3d1 = refIntrinsics->toUnitSphere(refIntrinsics->removeDistortion(refIntrinsics->ima2cam(refpt))); | ||
const Vec3 pt3d2 = nextIntrinsics->toUnitSphere(nextIntrinsics->removeDistortion(nextIntrinsics->ima2cam(nextpt))); | ||
|
||
|
||
Vec3 X; | ||
multiview::TriangulateSphericalDLT(T1, pt3d1, T2, pt3d2, X); | ||
|
||
//Make sure | ||
Eigen::Vector3d dirX1 = (T1 * X.homogeneous()).head(3).normalized(); | ||
Eigen::Vector3d dirX2 = (T2 * X.homogeneous()).head(3).normalized(); | ||
if (!(dirX1.dot(pt3d1) > 0.0 && dirX2.dot(pt3d2) > 0.0)) | ||
{ | ||
continue; | ||
} | ||
|
||
const Vec3 ray1 = - X; | ||
const Vec3 ray2 = c - X; | ||
const double cangle = clamp(ray1.normalized().dot(ray2.normalized()), -1.0, 1.0); | ||
const double angle = std::acos(cangle); | ||
angles.push_back(angle); | ||
|
||
usedTracks.push_back(commonItem.first); | ||
} | ||
|
||
if (angles.size() == 0) | ||
{ | ||
resultAngle = 0.0; | ||
return false; | ||
} | ||
|
||
const unsigned medianIndex = angles.size() / 2; | ||
std::nth_element(angles.begin(), angles.begin() + medianIndex, angles.end()); | ||
resultAngle = angles[medianIndex]; | ||
return true; | ||
} | ||
|
||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// This file is part of the AliceVision project. | ||
// Copyright (c) 2025 AliceVision contributors. | ||
// This Source Code Form is subject to the terms of the Mozilla Public License, | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, | ||
// You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
#include <aliceVision/sfmData/SfMData.hpp> | ||
#include <aliceVision/track/tracksUtils.hpp> | ||
|
||
#include <vector> | ||
|
||
namespace aliceVision { | ||
namespace sfm { | ||
|
||
/** | ||
* @brief estimate a median angle (parallax) between a reference view and another view | ||
* @param sfmData the input sfmData which contains camera information | ||
* @param referenceViewId the reference view id | ||
* @param otherViewId the other view id | ||
* @param otherTreference the relative pose | ||
* @param tracksMap the input map of tracks | ||
* @param tracksPerView tracks grouped by views | ||
* @param resultAngle the output median angle | ||
* @param usedTracks the list of tracks which were succesfully reconstructed | ||
* @return true | ||
*/ | ||
bool estimatePairAngle(const sfmData::SfMData & sfmData, | ||
const IndexT referenceViewId, | ||
const IndexT otherViewId, | ||
const geometry::Pose3 & otherTreference, | ||
const track::TracksMap& tracksMap, | ||
const track::TracksPerView & tracksPerView, | ||
double & resultAngle, | ||
std::vector<size_t> & usedTracks); | ||
|
||
} | ||
} |
Oops, something went wrong.