diff --git a/src/aliceVision/sfm/CMakeLists.txt b/src/aliceVision/sfm/CMakeLists.txt index 529b729924..b0b1d659de 100644 --- a/src/aliceVision/sfm/CMakeLists.txt +++ b/src/aliceVision/sfm/CMakeLists.txt @@ -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 @@ -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 diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp b/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp new file mode 100644 index 0000000000..db246895cc --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.cpp @@ -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 +#include +#include +#include +#include +#include + +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 refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); + std::shared_ptr 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 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::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 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; +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.hpp b/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.hpp new file mode 100644 index 0000000000..ceb8c7067b --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/Bootstrap.hpp @@ -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 +#include + +#include + +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); + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.cpp b/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.cpp new file mode 100644 index 0000000000..de9c6eeb94 --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.cpp @@ -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 +#include + +#include + +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 & usedTracks) +{ + usedTracks.clear(); + + const sfmData::View& refView = sfmData.getView(referenceViewId); + const sfmData::View& nextView = sfmData.getView(otherViewId); + + std::shared_ptr refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); + std::shared_ptr 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 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; +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp b/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp new file mode 100644 index 0000000000..19b12ed3a2 --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/EstimateAngle.hpp @@ -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 +#include + +#include + +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 & usedTracks); + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.cpp b/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.cpp new file mode 100644 index 0000000000..6dc89a14b8 --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.cpp @@ -0,0 +1,71 @@ +// 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 +#include +#include +#include + +namespace aliceVision { +namespace sfm { + +IndexT findBestPair(const sfmData::SfMData & sfmData, + const std::vector & pairs, + const track::TracksMap& tracksMap, + const track::TracksPerView & tracksPerView, + double minAngle, + double maxAngle) +{ + IndexT bestPair = UndefinedIndexT; + double maxScore = std::numeric_limits::lowest(); + + for (IndexT pairId = 0; pairId < pairs.size(); pairId++) + { + const sfm::ReconstructedPair & pair = pairs[pairId]; + + double angle = 0.0; + std::vector usedTracks; + if (!sfm::estimatePairAngle(sfmData, pair.reference, pair.next, pair.pose, tracksMap, tracksPerView, angle, usedTracks)) + { + continue; + } + + if (radianToDegree(angle) > maxAngle) + { + continue; + } + + + + const sfmData::View & vref = sfmData.getView(pair.reference); + const sfmData::View & vnext = sfmData.getView(pair.next); + + int maxref = std::max(vref.getImage().getWidth(), vref.getImage().getHeight()); + int maxnext = std::max(vnext.getImage().getWidth(), vnext.getImage().getHeight()); + + + double refScore = sfm::ExpansionPolicyLegacy::computeScore(tracksMap, usedTracks, pair.reference, maxref, 5); + double nextScore = sfm::ExpansionPolicyLegacy::computeScore(tracksMap, usedTracks, pair.next, maxnext, 5); + + double score = std::min(refScore, nextScore) * std::max(1.0, radianToDegree(angle)); + //If the angle is too small, then dramatically reduce its chances + if (radianToDegree(angle) < minAngle) + { + score = -1.0 / score; + } + + if (score > maxScore) + { + maxScore = score; + bestPair = pairId; + } + } + + return bestPair; +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.hpp b/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.hpp new file mode 100644 index 0000000000..6da3e732f0 --- /dev/null +++ b/src/aliceVision/sfm/pipeline/bootstrapping/PairsScoring.hpp @@ -0,0 +1,35 @@ +// 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/. +#pragma once + +#include +#include +#include + +#include + +namespace aliceVision { +namespace sfm { + +/** +* @brief Get best pair with highest score +* @param sfmData the input sfmData which contains camera information +* @param pairs the input list of reconstructed pairs +* @param tracksMap the input map of tracks +* @param tracksPerView tracks grouped by views +* @param minAngle minimal angle allowed +* @param maxAngle maximal angle allowed +* @return index in "pairs" of the best pair or UndefinedIndexT if no pair found +*/ +IndexT findBestPair(const sfmData::SfMData & sfmData, + const std::vector & pairs, + const track::TracksMap& tracksMap, + const track::TracksPerView & tracksPerView, + double minAngle, + double maxAngle); + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfm/pipeline/relativePoses.hpp b/src/aliceVision/sfm/pipeline/relativePoses.hpp index 62fcc6194b..e3a3c7fcc3 100644 --- a/src/aliceVision/sfm/pipeline/relativePoses.hpp +++ b/src/aliceVision/sfm/pipeline/relativePoses.hpp @@ -3,6 +3,7 @@ // 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/. +#pragma once #include #include diff --git a/src/software/pipeline/CMakeLists.txt b/src/software/pipeline/CMakeLists.txt index 7914294b28..48d7092836 100644 --- a/src/software/pipeline/CMakeLists.txt +++ b/src/software/pipeline/CMakeLists.txt @@ -129,6 +129,7 @@ if(ALICEVISION_BUILD_SFM) aliceVision_sfmData aliceVision_track aliceVision_dataio + aliceVision_mesh Boost::program_options Boost::json ) @@ -144,6 +145,7 @@ if(ALICEVISION_BUILD_SFM) aliceVision_sfmData aliceVision_track aliceVision_dataio + aliceVision_mesh Boost::program_options Boost::json ) diff --git a/src/software/pipeline/main_sfmBootstraping.cpp b/src/software/pipeline/main_sfmBootstraping.cpp index 980c299221..871a0cab2f 100644 --- a/src/software/pipeline/main_sfmBootstraping.cpp +++ b/src/software/pipeline/main_sfmBootstraping.cpp @@ -26,11 +26,11 @@ #include #include -#include - -#include -#include +#include +#include +#include +#include #include #include #include @@ -46,165 +46,58 @@ namespace po = boost::program_options; namespace fs = boost::filesystem; /** - * @brief estimate a median angle (parallax) between a reference view and another view - * @param sfmData the input sfmData which contains camera information + * @brief build an initial set of landmarks from a view and a mesh object + * @param sfmData the input/output sfmData + * @param meshFilename the mesh path * @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 & usedTracks) +bool landmarksFromMesh( + sfmData::Landmarks & landmarks, + const sfmData::SfMData & sfmData, + const std::string & meshFilename, + const IndexT referenceViewId, + const track::TracksHandler& tracksHandler) { - usedTracks.clear(); - - const sfmData::View& refView = sfmData.getView(referenceViewId); - const sfmData::View& nextView = sfmData.getView(otherViewId); - - std::shared_ptr refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); - std::shared_ptr 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 angles; - for(const auto& commonItem : mapTracksCommon) + //Load mesh in the mesh intersection object + ALICEVISION_LOG_INFO("Loading mesh"); + mesh::MeshIntersection mi; + if (!mi.initialize(meshFilename)) { - 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); + return EXIT_FAILURE; } - if (angles.size() == 0) - { - resultAngle = 0.0; - return false; - } + const sfmData::View & v = sfmData.getView(referenceViewId); + const sfmData::CameraPose & cpose = sfmData.getAbsolutePose(v.getPoseId()); + const camera::IntrinsicBase & intrinsic = sfmData.getIntrinsic(v.getIntrinsicId()); - const unsigned medianIndex = angles.size() / 2; - std::nth_element(angles.begin(), angles.begin() + medianIndex, angles.end()); - resultAngle = angles[medianIndex]; - return true; -} + mi.setPose(cpose.getTransform()); + const auto & trackIds = tracksHandler.getTracksPerView().at(referenceViewId); + const auto & tracksMap = tracksHandler.getAllTracks(); -/** - * @brief build an initial sfmData from two views - * @param sfmData the input/output sfmData - * @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 usedTracks the input list of valid tracks - * @return true -*/ -bool buildSfmData(sfmData::SfMData & sfmData, - const IndexT referenceViewId, - const IndexT otherViewId, - const geometry::Pose3 & otherTreference, - const track::TracksMap& tracksMap, - const std::vector & usedTracks) -{ - const sfmData::View& refView = sfmData.getView(referenceViewId); - const sfmData::View& nextView = sfmData.getView(otherViewId); - - std::shared_ptr refIntrinsics = sfmData.getIntrinsicSharedPtr(refView.getIntrinsicId()); - std::shared_ptr 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(); - - size_t count = 0; - std::vector angles; - for(const auto& trackId : usedTracks) + for (const auto trackId : trackIds) { - const track::Track& track = tracksMap.at(trackId); - + const track::Track & track = tracksMap.at(trackId); const track::TrackItem & refItem = track.featPerView.at(referenceViewId); - const track::TrackItem & nextItem = track.featPerView.at(otherViewId); - - 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))); - + const Vec2 refpt = track.featPerView.at(referenceViewId).coords; + const std::size_t featureId = track.featPerView.at(referenceViewId).featureId; + const double scale = track.featPerView.at(referenceViewId).scale; - 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)) + Vec3 point; + if (!mi.peekPoint(point, intrinsic, refpt)) { 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; + sfmData::Landmark l; + l.X = point; + l.descType = feature::EImageDescriberType::SIFT; + sfmData::Observations & observations = l.getObservations(); + observations[referenceViewId] = sfmData::Observation(refpt, featureId, scale); + landmarks[trackId] = l; } return true; @@ -216,6 +109,7 @@ int aliceVision_main(int argc, char** argv) std::string sfmDataFilename; std::string sfmDataOutputFilename; std::string tracksFilename; + std::string meshFilename; std::string pairsDirectory; // user optional parameters @@ -238,6 +132,7 @@ int aliceVision_main(int argc, char** argv) optionalParams.add_options() ("minAngleInitialPair", po::value(&minAngle)->default_value(minAngle), "Minimum angle for the initial pair.") ("maxAngleInitialPair", po::value(&maxAngle)->default_value(maxAngle), "Maximum angle for the initial pair.") + ("meshFilename,t", po::value(&meshFilename)->required(), "Mesh object file.") ("initialPairA", po::value(&initialPairString.first)->default_value(initialPairString.first), "UID or filepath or filename of the first image.") ("initialPairB", po::value(&initialPairString.second)->default_value(initialPairString.second), "UID or filepath or filename of the second image."); @@ -269,6 +164,13 @@ int aliceVision_main(int argc, char** argv) return EXIT_SUCCESS; } + if (sfmData.getValidViews().size() == 1) + { + ALICEVISION_LOG_INFO("SfmData has one view with a pose. Assuming we want to use it."); + initialPairString.first = std::to_string(*sfmData.getValidViews().begin()); + } + + if (!initialPairString.first.empty() || !initialPairString.second.empty()) { if (initialPairString.first == initialPairString.second) @@ -318,6 +220,16 @@ int aliceVision_main(int argc, char** argv) } + //Load mesh in the mesh intersection object + bool useMesh = false; + sfmData::Landmarks landmarks; + if (!meshFilename.empty() && initialPair.first != UndefinedIndexT) + { + landmarksFromMesh(landmarks, sfmData, meshFilename, initialPair.first, tracksHandler); + + useMesh = true; + } + //Result of pair estimations are stored in multiple files std::vector reconstructedPairs; const std::regex regex("pairs\\_[0-9]+\\.json"); @@ -335,11 +247,32 @@ int aliceVision_main(int argc, char** argv) for (const boost::json::value & value : values) { std::vector localVector = boost::json::value_to>(value); - reconstructedPairs.insert(reconstructedPairs.end(), localVector.begin(), localVector.end()); + + for (const auto & pair: localVector) + { + // Filter out pairs given user filters + if (initialPair.first != UndefinedIndexT) + { + if (pair.reference != initialPair.first && pair.next != initialPair.first) + { + continue; + } + } + + // Filter out pairs given user filters + if (initialPair.second != UndefinedIndexT) + { + if (pair.reference != initialPair.second && pair.next != initialPair.second) + { + continue; + } + } + + reconstructedPairs.push_back(pair); + } } } - //Check all pairs ALICEVISION_LOG_INFO("Give a score to all pairs"); int count = 0; @@ -349,79 +282,40 @@ int aliceVision_main(int argc, char** argv) bestPair.reference = UndefinedIndexT; std::vector bestUsedTracks; - for (const sfm::ReconstructedPair & pair: reconstructedPairs) - { - std::vector usedTracks; - double angle = 0.0; - - if (initialPair.first != UndefinedIndexT) - { - if (pair.reference != initialPair.first && pair.next != initialPair.first) - { - continue; - } - } - - if (initialPair.second != UndefinedIndexT) - { - if (pair.reference != initialPair.second && pair.next != initialPair.second) - { - continue; - } - } - - ALICEVISION_LOG_INFO("Processing pair " << initialPair.first << " / " << initialPair.second); - - if (!estimatePairAngle(sfmData, pair.reference, pair.next, pair.pose, tracksHandler.getAllTracks(), tracksHandler.getTracksPerView(), angle, usedTracks)) - { - continue; - } - - ALICEVISION_LOG_INFO("angle " << initialPair.first << " / " << initialPair.second << " : " << radianToDegree(angle)); - - if (radianToDegree(angle) > maxAngle) - { - continue; - } - - //If the angle is too small, then dramatically reduce its chances - if (radianToDegree(angle) < minAngle) - { - angle = -1.0 / angle; - } - - const sfmData::View & vref = sfmData.getView(pair.reference); - const sfmData::View & vnext = sfmData.getView(pair.next); - - int maxref = std::max(vref.getImage().getWidth(), vref.getImage().getHeight()); - int maxnext = std::max(vnext.getImage().getWidth(), vnext.getImage().getHeight()); - - double refScore = sfm::ExpansionPolicyLegacy::computeScore(tracksHandler.getAllTracks(), usedTracks, pair.reference, maxref, 5); - double nextScore = sfm::ExpansionPolicyLegacy::computeScore(tracksHandler.getAllTracks(), usedTracks, pair.next, maxnext, 5); + IndexT bestPairId = findBestPair(sfmData, reconstructedPairs, + tracksHandler.getAllTracks(), tracksHandler.getTracksPerView(), + minAngle, maxAngle); - ALICEVISION_LOG_INFO("image score " << initialPair.first << " / " << initialPair.second << " : " << refScore << " / " << nextScore); - - double score = std::min(refScore, nextScore) * std::max(1.0, radianToDegree(angle)); - - - if (score > bestScore) - { - bestPair = pair; - bestScore = score; - bestUsedTracks = usedTracks; - } - } - - if (bestPair.reference == UndefinedIndexT) + if (bestPairId == UndefinedIndexT) { ALICEVISION_LOG_INFO("No valid pair"); return EXIT_FAILURE; } + + bestPair = reconstructedPairs[bestPairId]; - if (!buildSfmData(sfmData, bestPair.reference, bestPair.next, bestPair.pose, tracksHandler.getAllTracks(), bestUsedTracks)) + if (useMesh) { - return EXIT_FAILURE; + if (!sfm::bootstrapMesh(sfmData, + landmarks, + bestPair.reference, bestPair.next, + tracksHandler.getAllTracks(), tracksHandler.getTracksPerView())) + { + return EXIT_FAILURE; + } } + else + { + if (!sfm::bootstrapBase(sfmData, + bestPair.reference, bestPair.next, + bestPair.pose, + tracksHandler.getAllTracks(), tracksHandler.getTracksPerView())) + { + return EXIT_FAILURE; + } + } + + std::cout << sfmData.getLandmarks().size() << std::endl; ALICEVISION_LOG_INFO("Best selected pair is : "); ALICEVISION_LOG_INFO(" - " << sfmData.getView(bestPair.reference).getImage().getImagePath());