Skip to content

Commit

Permalink
Bootstrapping cleanup and mesh support
Browse files Browse the repository at this point in the history
  • Loading branch information
servantftechnicolor committed Jan 17, 2025
1 parent 2ed1cf5 commit ebb74d6
Show file tree
Hide file tree
Showing 10 changed files with 565 additions and 211 deletions.
6 changes: 6 additions & 0 deletions src/aliceVision/sfm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ set(sfm_files_headers
pipeline/RelativePoseInfo.hpp
pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.hpp
pipeline/panorama/ReconstructionEngine_panorama.hpp
pipeline/bootstrapping/EstimateAngle.hpp
pipeline/bootstrapping/PairsScoring.hpp
pipeline/bootstrapping/Bootstrap.hpp
pipeline/expanding/SfmTriangulation.hpp
pipeline/expanding/SfmResection.hpp
pipeline/expanding/SfmBundle.hpp
Expand Down Expand Up @@ -65,6 +68,9 @@ set(sfm_files_sources
pipeline/RelativePoseInfo.cpp
pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp
pipeline/panorama/ReconstructionEngine_panorama.cpp
pipeline/bootstrapping/EstimateAngle.cpp
pipeline/bootstrapping/PairsScoring.cpp
pipeline/bootstrapping/Bootstrap.cpp
pipeline/expanding/SfmTriangulation.cpp
pipeline/expanding/SfmResection.cpp
pipeline/expanding/SfmBundle.cpp
Expand Down
169 changes: 169 additions & 0 deletions src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp
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;
}

}
}
50 changes: 50 additions & 0 deletions src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.hpp
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 src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.cpp
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 src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp
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);

}
}
Loading

0 comments on commit ebb74d6

Please sign in to comment.