From f6bae6a5ae3cdc2661f546dca6587518f128b19c Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Thu, 25 Apr 2019 09:51:25 +0200 Subject: [PATCH 1/7] [matching] IO: file prefix for ranges + load files by pattern When using range parameters from main_featureMatching, prefix the resulting files with rangeStart/rangeSize (i.e: iteration index when processing all views by chunks). => with matchFilePerImage: avoids overwriting files if a view is present in several iterations => without matchFilePerImage: avoids overwriting the unique resulting file * io: consider all files containing "matches.txt" when loading matches files from a folder --- src/aliceVision/matching/io.cpp | 113 ++++++++++++------ src/aliceVision/matching/io.hpp | 4 +- .../pipeline/main_featureMatching.cpp | 9 +- 3 files changed, 88 insertions(+), 38 deletions(-) diff --git a/src/aliceVision/matching/io.cpp b/src/aliceVision/matching/io.cpp index 887b68f694..6a88d15fcd 100644 --- a/src/aliceVision/matching/io.cpp +++ b/src/aliceVision/matching/io.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -133,7 +134,7 @@ void filterMatchesByDesc( } -bool LoadMatchFilePerImage( +std::size_t LoadMatchFilePerImage( PairwiseMatches& matches, const std::set& viewsKeys, const std::string& folder, @@ -167,21 +168,60 @@ bool LoadMatchFilePerImage( } } } - if(nbLoadedMatchFiles == 0) + return nbLoadedMatchFiles; +} + +std::size_t LoadMatchesFromFolder( + PairwiseMatches& matches, + const std::string& folder, + const std::string& pattern) +{ + std::size_t nbLoadedMatchFiles = 0; + std::vector matchFiles; + // list all matches files in 'folder' matching (i.e containing) 'pattern' + for(auto& entry : boost::make_iterator_range(fs::directory_iterator(folder), {})) { - ALICEVISION_LOG_WARNING("No matches file loaded in: " << folder); - return false; + if(entry.path().string().find(pattern) != std::string::npos) + { + matchFiles.emplace_back(entry.path().string()); + } } - ALICEVISION_LOG_TRACE("Matches per image pair"); - for(const auto& imagePairIt: matches) + + #pragma omp parallel for num_threads(3) + for(int i = 0; i < matchFiles.size(); ++i) { - std::stringstream ss; - ss << " * " << imagePairIt.first.first << "-" << imagePairIt.first.second << ": " << imagePairIt.second.getNbAllMatches() << " "; - for(const auto& matchesPerDeskIt: imagePairIt.second) - ss << " [" << feature::EImageDescriberType_enumToString(matchesPerDeskIt.first) << ": " << matchesPerDeskIt.second.size() << "]"; - ALICEVISION_LOG_TRACE(ss.str()); + const std::string& matchFile = matchFiles[i]; + PairwiseMatches fileMatches; + ALICEVISION_LOG_DEBUG("Loading match file: " << matchFile); + if(!LoadMatchFile(fileMatches, matchFile)) + { + ALICEVISION_LOG_WARNING("Unable to load match file: " << matchFile); + continue; + } + #pragma omp critical + { + for(const auto& matchesPerView: fileMatches) + { + const Pair& pair = matchesPerView.first; + const MatchesPerDescType& pairMatches = matchesPerView.second; + for(const auto& matchesPerDescType : pairMatches) + { + const feature::EImageDescriberType& descType = matchesPerDescType.first; + const auto& pairMatches = matchesPerDescType.second; + // merge in global map + std::copy( + std::make_move_iterator(pairMatches.begin()), + std::make_move_iterator(pairMatches.end()), + std::back_inserter(matches[pair][descType]) + ); + } + } + ++nbLoadedMatchFiles; + } } - return true; + if(!nbLoadedMatchFiles) + ALICEVISION_LOG_WARNING("No matches file loaded in: " << folder); + return nbLoadedMatchFiles; } bool Load( @@ -191,22 +231,32 @@ bool Load( const std::vector& descTypesFilter, const int maxNbMatches) { - bool res = false; - const std::string fileName = "matches.txt"; + std::size_t nbLoadedMatchFiles = 0; + const std::string pattern = "matches.txt"; for(const std::string& folder : folders) { - const fs::path filePath = fs::path(folder) / fileName; - - if(fs::exists(filePath)) - res = LoadMatchFile(matches, filePath.string()); - else - res = LoadMatchFilePerImage(matches, viewsKeysFilter, folder, fileName); + nbLoadedMatchFiles += LoadMatchesFromFolder(matches, folder, pattern); } - if(!res) + if(!nbLoadedMatchFiles) return false; + const auto logMatches = [](const PairwiseMatches& m) + { + for(const auto& imagePairIt: m) + { + std::stringstream ss; + ss << " * " << imagePairIt.first.first << "-" << imagePairIt.first.second << ": " << imagePairIt.second.getNbAllMatches() << " "; + for(const auto& matchesPerDeskIt: imagePairIt.second) + ss << " [" << feature::EImageDescriberType_enumToString(matchesPerDeskIt.first) << ": " << matchesPerDeskIt.second.size() << "]"; + ALICEVISION_LOG_TRACE(ss.str()); + } + }; + + ALICEVISION_LOG_TRACE("Matches per image pair (before filtering):"); + logMatches(matches); + if(!viewsKeysFilter.empty()) filterMatchesByViews(matches, viewsKeysFilter); @@ -216,19 +266,10 @@ bool Load( if(maxNbMatches > 0) filterTopMatches(matches, maxNbMatches); - ALICEVISION_LOG_TRACE("Matches per image pair"); - for(const auto& imagePairIt: matches) - { - std::stringstream ss; - ss << " * " << imagePairIt.first.first << "-" << imagePairIt.first.second << ": " << imagePairIt.second.getNbAllMatches() << " "; - for(const auto& matchesPerDeskIt: imagePairIt.second) - { - ss << " [" << feature::EImageDescriberType_enumToString(matchesPerDeskIt.first) << ": " << matchesPerDeskIt.second.size() << "]"; - } - ALICEVISION_LOG_TRACE(ss.str()); - } + ALICEVISION_LOG_TRACE("Matches per image pair (after filtering):"); + logMatches(matches); - return res; + return true; } @@ -331,9 +372,11 @@ bool Save( const PairwiseMatches & matches, const std::string & folder, const std::string & extension, - bool matchFilePerImage) + bool matchFilePerImage, + const std::string& prefix + ) { - const std::string filename = "matches." + extension; + const std::string filename = prefix + "matches." + extension; MatchExporter exporter(matches, folder, filename); if(matchFilePerImage) diff --git a/src/aliceVision/matching/io.hpp b/src/aliceVision/matching/io.hpp index 7f2b48101c..b56aaa0c5c 100644 --- a/src/aliceVision/matching/io.hpp +++ b/src/aliceVision/matching/io.hpp @@ -75,12 +75,14 @@ void filterTopMatches( * @param[in] extension: txt or bin file format * @param[in] matchFilePerImage: do we store a global match file * or one match file per image + * @param[in] prefix: optional prefix for the output file(s) */ bool Save( const PairwiseMatches& matches, const std::string& folder, const std::string& extension, - bool matchFilePerImage); + bool matchFilePerImage, + const std::string& prefix=""); } // namespace matching } // namespace aliceVision diff --git a/src/software/pipeline/main_featureMatching.cpp b/src/software/pipeline/main_featureMatching.cpp index 7666583f5f..eda1e2f5e9 100644 --- a/src/software/pipeline/main_featureMatching.cpp +++ b/src/software/pipeline/main_featureMatching.cpp @@ -340,6 +340,11 @@ int main(int argc, char **argv) } } + // when a range is specified, generate a file prefix to reflect the current iteration (rangeStart/rangeSize) + // => with matchFilePerImage: avoids overwriting files if a view is present in several iterations + // => without matchFilePerImage: avoids overwriting the unique resulting file + const std::string filePrefix = rangeSize > 0 ? std::to_string(rangeStart/rangeSize) + "." : ""; + ALICEVISION_LOG_INFO(std::to_string(mapPutativesMatches.size()) << " putative image pair matches"); for(const auto& imageMatch: mapPutativesMatches) @@ -347,7 +352,7 @@ int main(int argc, char **argv) // export putative matches if(savePutativeMatches) - Save(mapPutativesMatches, (fs::path(matchesFolder) / "putativeMatches").string(), fileExtension, matchFilePerImage); + Save(mapPutativesMatches, (fs::path(matchesFolder) / "putativeMatches").string(), fileExtension, matchFilePerImage, filePrefix); ALICEVISION_LOG_INFO("Task (Regions Matching) done in (s): " + std::to_string(timer.elapsed())); @@ -522,7 +527,7 @@ int main(int argc, char **argv) // export geometric filtered matches ALICEVISION_LOG_INFO("Save geometric matches."); - Save(finalMatches, matchesFolder, fileExtension, matchFilePerImage); + Save(finalMatches, matchesFolder, fileExtension, matchFilePerImage, filePrefix); ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); // d. Export some statistics From fe03ca190c7fb557e61e406f10a4b031a186568d Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Thu, 18 Apr 2019 15:08:50 +0200 Subject: [PATCH 2/7] [pipeline] featureMatching: output one match file by default `matchFilePerImage` was activated by default to handle parallel runs of `featureMatching` on different ranges of views of the same scene. This is now handled by the prefix added to output files when using range related parameters. --- src/software/pipeline/main_featureMatching.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/pipeline/main_featureMatching.cpp b/src/software/pipeline/main_featureMatching.cpp index eda1e2f5e9..c1eb1d09a9 100644 --- a/src/software/pipeline/main_featureMatching.cpp +++ b/src/software/pipeline/main_featureMatching.cpp @@ -108,7 +108,7 @@ int main(int argc, char **argv) bool savePutativeMatches = false; bool guidedMatching = false; int maxIteration = 2048; - bool matchFilePerImage = true; + bool matchFilePerImage = false; size_t numMatchesToKeep = 0; bool useGridSort = true; bool exportDebugFiles = false; From 3d137e8214c6eecb82001bf2db0518831d2b2256 Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Thu, 25 Apr 2019 12:05:03 +0200 Subject: [PATCH 3/7] [tests] indMatch: update IO test * matches are accumulated when successively loading several files using the same PairwiseMatches variable without clearing it * check for duplicates and test deduplication function --- src/aliceVision/matching/indMatch_test.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/matching/indMatch_test.cpp b/src/aliceVision/matching/indMatch_test.cpp index 6401853f3e..7b7ef7adad 100644 --- a/src/aliceVision/matching/indMatch_test.cpp +++ b/src/aliceVision/matching/indMatch_test.cpp @@ -76,12 +76,32 @@ BOOST_AUTO_TEST_CASE(IndMatch_IO) matches[std::make_pair(1,2)][EImageDescriberType::UNKNOWN] = {{0,0},{1,1}, {2,2}}; BOOST_CHECK(Save(matches, testFolder, "txt", true)); + // Create an additional pair-wise matches entry + matches[std::make_pair(0,2)][EImageDescriberType::UNKNOWN] = {{3,3},{4,4}}; + + // Don't clear matches and reload saved file BOOST_CHECK(Load(matches, viewsKeys, {testFolder}, {EImageDescriberType::UNKNOWN})); - BOOST_CHECK_EQUAL(2, matches.size()); + BOOST_CHECK_EQUAL(3, matches.size()); BOOST_CHECK_EQUAL(1, matches.count(std::make_pair(0,1))); BOOST_CHECK_EQUAL(1, matches.count(std::make_pair(1,2))); + BOOST_CHECK_EQUAL(1, matches.count(std::make_pair(0,2))); + // 'matches' was not cleared, pair-wise matches are accumulated ({0, 1} and {1,2} contains duplicates) + BOOST_CHECK_EQUAL(4, matches.at(std::make_pair(0,1)).at(EImageDescriberType::UNKNOWN).size()); + BOOST_CHECK_EQUAL(6, matches.at(std::make_pair(1,2)).at(EImageDescriberType::UNKNOWN).size()); + BOOST_CHECK_EQUAL(2, matches.at(std::make_pair(0,2)).at(EImageDescriberType::UNKNOWN).size()); + + // Deduplicate matches + // - {0, 1} has duplicates + BOOST_CHECK(IndMatch::getDeduplicated(matches.at(std::make_pair(0,1)).at(EImageDescriberType::UNKNOWN))); + // - {1, 2} has duplicates + BOOST_CHECK(IndMatch::getDeduplicated(matches.at(std::make_pair(1,2)).at(EImageDescriberType::UNKNOWN))); + // - {0, 2} does not have duplicates + BOOST_CHECK(!IndMatch::getDeduplicated(matches.at(std::make_pair(0,2)).at(EImageDescriberType::UNKNOWN))); + + // Check matches count after deduplication BOOST_CHECK_EQUAL(2, matches.at(std::make_pair(0,1)).at(EImageDescriberType::UNKNOWN).size()); BOOST_CHECK_EQUAL(3, matches.at(std::make_pair(1,2)).at(EImageDescriberType::UNKNOWN).size()); + BOOST_CHECK_EQUAL(2, matches.at(std::make_pair(0,2)).at(EImageDescriberType::UNKNOWN).size()); fs::remove_all("./4/"); } boost::filesystem::remove_all(testFolder); From e8300967a3adba406c44222e161ad5ab9af101dc Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Wed, 24 Apr 2019 16:11:26 +0200 Subject: [PATCH 4/7] [io] remove duplicated folders when loading features/matches Ensure a folder is not considered twice when loading features and matches. --- src/aliceVision/matching/io.cpp | 7 ++++++- src/aliceVision/sfm/pipeline/regionsIO.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/aliceVision/matching/io.cpp b/src/aliceVision/matching/io.cpp index 6a88d15fcd..2cc2971406 100644 --- a/src/aliceVision/matching/io.cpp +++ b/src/aliceVision/matching/io.cpp @@ -234,7 +234,12 @@ bool Load( std::size_t nbLoadedMatchFiles = 0; const std::string pattern = "matches.txt"; - for(const std::string& folder : folders) + // build up a set with normalized paths to remove duplicates + std::set foldersSet; + for(const auto& folder : folders) + foldersSet.insert(fs::canonical(folder).string()); + + for(const auto& folder : foldersSet) { nbLoadedMatchFiles += LoadMatchesFromFolder(matches, folder, pattern); } diff --git a/src/aliceVision/sfm/pipeline/regionsIO.cpp b/src/aliceVision/sfm/pipeline/regionsIO.cpp index 1f8569a6a6..ae1d93db6f 100644 --- a/src/aliceVision/sfm/pipeline/regionsIO.cpp +++ b/src/aliceVision/sfm/pipeline/regionsIO.cpp @@ -84,7 +84,12 @@ std::unique_ptr loadFeatures(const std::vector& f std::string featFilename; - for(const std::string& folder : folders) + // build up a set with normalized paths to remove duplicates + std::set foldersSet; + for(const auto& folder : folders) + foldersSet.insert(fs::canonical(folder).string()); + + for(const auto& folder : foldersSet) { const fs::path featPath = fs::path(folder) / std::string(basename + "." + imageDescriberTypeName + ".feat"); if(fs::exists(featPath)) From dcaba7f0ef8b2fe6aac78d0fa3e7f2dd31daa14e Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Mon, 29 Apr 2019 14:35:40 +0200 Subject: [PATCH 5/7] [sfmData] manage features/matches folders internally * handle conversions to relative and absolute paths when _absolutePath is defined * update internal relative folder paths when absolutePath is modified * ensure features/matches folders contains no duplicates * [tests] add 'sfmData_test' module + test internal folders management * [software] update global/incrementalSfm --- src/aliceVision/sfmData/CMakeLists.txt | 6 ++ src/aliceVision/sfmData/SfMData.cpp | 79 ++++++++++++++++--- src/aliceVision/sfmData/SfMData.hpp | 67 +++++++++++----- src/aliceVision/sfmData/sfmData_test.cpp | 52 ++++++++++++ src/software/pipeline/main_globalSfM.cpp | 8 +- src/software/pipeline/main_incrementalSfM.cpp | 10 +-- 6 files changed, 176 insertions(+), 46 deletions(-) create mode 100644 src/aliceVision/sfmData/sfmData_test.cpp diff --git a/src/aliceVision/sfmData/CMakeLists.txt b/src/aliceVision/sfmData/CMakeLists.txt index 1c2f45d8c3..0a433370be 100644 --- a/src/aliceVision/sfmData/CMakeLists.txt +++ b/src/aliceVision/sfmData/CMakeLists.txt @@ -30,3 +30,9 @@ alicevision_add_library(aliceVision_sfmData ) # Unit tests + +alicevision_add_test(sfmData_test.cpp + NAME "sfmData" + LINKS aliceVision_sfmData + aliceVision_system +) diff --git a/src/aliceVision/sfmData/SfMData.cpp b/src/aliceVision/sfmData/SfMData.cpp index ccecd3fb2f..7c5052e475 100644 --- a/src/aliceVision/sfmData/SfMData.cpp +++ b/src/aliceVision/sfmData/SfMData.cpp @@ -89,22 +89,79 @@ bool SfMData::operator==(const SfMData& other) const { } -std::vector SfMData::getFeaturesFolders() const +/** + * @brief Convert paths in \p folders to absolute paths using \p absolutePath parent folder as base. + * @param[in] folders list of paths to convert + * @param[in] absolutePath filepath which parent folder should be used as base for absolute path conversion + * @return the list of converted absolute paths or input folder if absolutePath is empty + */ +std::vector toAbsoluteFolders(const std::vector& folders, const std::string& absolutePath) { - fs::path sfmFolder = fs::path(_absolutePath).parent_path(); - std::vector absolutePaths(_featuresFolders.size()); + // if absolute path is not set, return input folders + if(absolutePath.empty()) + return folders; + // else, convert relative paths to absolute paths + std::vector absolutePaths(folders.size()); for(int i = 0; i < absolutePaths.size(); ++i) - absolutePaths.at(i) = fs::canonical(fs::path(_featuresFolders.at(i)), sfmFolder).string(); + absolutePaths.at(i) = fs::canonical(folders.at(i), fs::path(absolutePath).parent_path()).string(); return absolutePaths; } +/** + * @brief Add paths contained in \p folders to \p dst as relative paths to \p absolutePath. + * Paths already present in \p dst are omitted. + * @param[in] dst list in which paths should be added + * @param[in] folders paths to add to \p dst as relative folders + * @param[in] absolutePath filepath which parent folder should be used as base for relative path conversions + */ +void addAsRelativeFolders(std::vector& dst, const std::vector& folders, const std::string& absolutePath) +{ + for(auto folderPath: folders) + { + // if absolutePath is set, convert to relative path + if(!absolutePath.empty() && fs::path(folderPath).is_absolute()) + { + folderPath = fs::relative(folderPath, fs::path(absolutePath).parent_path()).string(); + } + // add path only if not already in dst + if(std::find(dst.begin(), dst.end(), folderPath) == dst.end()) + { + dst.emplace_back(folderPath); + } + } +} + +std::vector SfMData::getFeaturesFolders() const +{ + return toAbsoluteFolders(_featuresFolders, _absolutePath); +} + std::vector SfMData::getMatchesFolders() const { - fs::path sfmFolder = fs::path(_absolutePath).parent_path(); - std::vector absolutePaths(_matchesFolders.size()); - for(int i = 0; i < absolutePaths.size(); ++i) - absolutePaths.at(i) = fs::canonical(fs::path(_matchesFolders.at(i)), sfmFolder).string(); - return absolutePaths; + return toAbsoluteFolders(_matchesFolders, _absolutePath); +} + +void SfMData::addFeaturesFolders(const std::vector& folders) +{ + addAsRelativeFolders(_featuresFolders, folders, _absolutePath); +} + +void SfMData::addMatchesFolders(const std::vector& folders) +{ + addAsRelativeFolders(_matchesFolders, folders, _absolutePath); +} + +void SfMData::setAbsolutePath(const std::string& path) +{ + // get absolute path to features/matches folders + const std::vector featuresFolders = getFeaturesFolders(); + const std::vector matchesFolders = getMatchesFolders(); + // change internal absolute path + _absolutePath = path; + // re-set features/matches folders + // they will be converted back to relative paths based on updated _absolutePath + setFeaturesFolders(featuresFolders); + setMatchesFolders(matchesFolders); } std::set SfMData::getValidViews() const @@ -169,10 +226,10 @@ void SfMData::combine(const SfMData& sfmData) throw std::runtime_error("Can't combine two SfMData with rigs"); // feature folder - _featuresFolders.insert(_featuresFolders.end(), sfmData._featuresFolders.begin(), sfmData._featuresFolders.end()); + addFeaturesFolders(sfmData.getFeaturesFolders()); // matching folder - _matchesFolders.insert(_matchesFolders.end(), sfmData._matchesFolders.begin(), sfmData._matchesFolders.end()); + addMatchesFolders(sfmData.getMatchesFolders()); // views views.insert(sfmData.views.begin(), sfmData.views.end()); diff --git a/src/aliceVision/sfmData/SfMData.hpp b/src/aliceVision/sfmData/SfMData.hpp index 86f7c9e6c9..f916f36772 100644 --- a/src/aliceVision/sfmData/SfMData.hpp +++ b/src/aliceVision/sfmData/SfMData.hpp @@ -298,49 +298,74 @@ class SfMData } /** - * @brief Add the given features Folder - * @param[in] featuresFolder The given features folder + * @brief Add the given \p folder to features folders. + * @note If SfmData's absolutePath has been set, + * an absolute path will be converted to a relative one. + * @param[in] folder path to a folder containing features */ - void addFeaturesFolder(const std::string& featuresFolder) + inline void addFeaturesFolder(const std::string& folder) { - _featuresFolders.emplace_back(featuresFolder); + addFeaturesFolders({folder}); } /** - * @brief A the given matches Folder - * @param[in] matchesFolder The given mathes folder + * @brief Add the given \p folders to features folders. + * @note If SfmData's absolutePath has been set, + * absolute paths will be converted to relative ones. + * @param[in] folders paths to folders containing features */ - void addMatchesFolder(const std::string& matchesFolder) + void addFeaturesFolders(const std::vector& folders); + + /** + * @brief Add the given \p folder to matches folders. + * @note If SfmData's absolutePath has been set, + * an absolute path will be converted to a relative one. + * @param[in] folder path to a folder containing matches + */ + inline void addMatchesFolder(const std::string& folder) { - _matchesFolders.emplace_back(matchesFolder); + addMatchesFolders({folder}); } /** - * @brief Set the given features folders - * @param[in] featuresFolders The given features folders + * @brief Add the given \p folders to matches folders. + * @note If SfmData's absolutePath has been set, + * absolute paths will be converted to relative ones. + * @param[in] folders paths to folders containing matches + */ + void addMatchesFolders(const std::vector& folders); + + /** + * @brief Replace the current features folders by the given ones. + * @note If SfmData's absolutePath has been set, + * absolute paths will be converted to relative ones. + * @param[in] folders paths to folders containing features */ - void setFeaturesFolders(const std::vector& featuresFolders) + inline void setFeaturesFolders(const std::vector& folders) { - _featuresFolders = featuresFolders; + _featuresFolders.clear(); + addFeaturesFolders(folders); } /** - * @brief Set the given mathes folders - * @param[in] matchesFolders The given mathes folders + * @brief Replace the current matches folders by the given ones. + * @note If SfmData's absolutePath has been set, + * absolute paths will be converted to relative ones. + * @param[in] folders paths to folders containing matches */ - void setMatchesFolders(const std::vector& matchesFolders) + inline void setMatchesFolders(const std::vector& folders) { - _matchesFolders = matchesFolders; + _matchesFolders.clear(); + addMatchesFolders(folders); } /** - * @brief Set the SfMData file folder absolute path + * @brief Set the SfMData file absolute path. + * @note Internal relative features/matches folders will be remapped + * to be relative to the new absolute \p path. * @param[in] path The absolute path to the SfMData file folder */ - void setAbsolutePath(const std::string& path) - { - _absolutePath = path; - } + void setAbsolutePath(const std::string& path); /** * @brief Set the given pose for the given view diff --git a/src/aliceVision/sfmData/sfmData_test.cpp b/src/aliceVision/sfmData/sfmData_test.cpp new file mode 100644 index 0000000000..6fd0e08e02 --- /dev/null +++ b/src/aliceVision/sfmData/sfmData_test.cpp @@ -0,0 +1,52 @@ + +#include +#include + +#define BOOST_TEST_MODULE sfmData +#include + +using namespace aliceVision; +namespace fs = boost::filesystem; + +BOOST_AUTO_TEST_CASE(SfMData_InternalFolders) +{ + const std::string filename = "InternalFolders.sfm"; + sfmData::SfMData sfmData; + + // add relative features/matches folders with duplicates + std::string refFolder(".."); + sfmData.addFeaturesFolders({refFolder, refFolder}); + sfmData.addMatchesFolders({refFolder, refFolder}); + auto featuresFolders = sfmData.getFeaturesFolders(); + auto matchesFolders = sfmData.getMatchesFolders(); + // ensure duplicates were removed + BOOST_CHECK_EQUAL(featuresFolders.size(), 1); + BOOST_CHECK_EQUAL(matchesFolders.size(), 1); + // sfmData has no absolute path set, folders are still in relative form + BOOST_CHECK_EQUAL(featuresFolders[0], refFolder); + BOOST_CHECK_EQUAL(matchesFolders[0], refFolder); + + // set absolutePath to current filename + sfmData.setAbsolutePath(fs::absolute(filename).string()); + featuresFolders = sfmData.getFeaturesFolders(); + matchesFolders = sfmData.getMatchesFolders(); + // internal folders were kept... + BOOST_CHECK_EQUAL(featuresFolders.size(), 1); + BOOST_CHECK_EQUAL(matchesFolders.size(), 1); + // ... and are now absolute paths + BOOST_CHECK(fs::path(featuresFolders[0]).is_absolute()); + BOOST_CHECK(fs::equivalent(featuresFolders[0], refFolder)); + BOOST_CHECK(fs::path(matchesFolders[0]).is_absolute()); + BOOST_CHECK(fs::equivalent(matchesFolders[0], refFolder)); + + // update sfm absolute path to be in parent/parent folder + fs::path otherFolder = fs::path("../.."); + std::string updatedFilename = ( otherFolder / filename).string(); + sfmData.setAbsolutePath(updatedFilename); + // internal folders still reference the same folder as before + BOOST_CHECK(fs::equivalent(featuresFolders[0], refFolder)); + BOOST_CHECK(fs::equivalent(matchesFolders[0], refFolder)); + BOOST_CHECK_EQUAL(sfmData.getRelativeFeaturesFolders()[0], fs::relative(refFolder, otherFolder)); + BOOST_CHECK_EQUAL(sfmData.getRelativeMatchesFolders()[0], fs::relative(refFolder, otherFolder)); +} + diff --git a/src/software/pipeline/main_globalSfM.cpp b/src/software/pipeline/main_globalSfM.cpp index e845dace1c..3fed48e3a2 100644 --- a/src/software/pipeline/main_globalSfM.cpp +++ b/src/software/pipeline/main_globalSfM.cpp @@ -206,12 +206,8 @@ int main(int argc, char **argv) // set featuresFolders and matchesFolders relative paths { - for(const std::string& featuresFolder : featuresFolders) - sfmEngine.getSfMData().addFeaturesFolder(fs::relative(fs::path(featuresFolder), outDirectory).string()); - - for(const std::string& matchesFolder : matchesFolders) - sfmEngine.getSfMData().addMatchesFolder(fs::relative(fs::path(matchesFolder), outDirectory).string()); - + sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); + sfmEngine.getSfMData().addMatchesFolders(matchesFolders); sfmEngine.getSfMData().setAbsolutePath(outDirectory); } diff --git a/src/software/pipeline/main_incrementalSfM.cpp b/src/software/pipeline/main_incrementalSfM.cpp index 48385be77e..ad334dd225 100644 --- a/src/software/pipeline/main_incrementalSfM.cpp +++ b/src/software/pipeline/main_incrementalSfM.cpp @@ -303,14 +303,8 @@ int main(int argc, char **argv) // set featuresFolders and matchesFolders relative paths { - const fs::path sfmFolder = fs::path(outputSfM).remove_filename(); - - for(const std::string& featuresFolder : featuresFolders) - sfmEngine.getSfMData().addFeaturesFolder(fs::relative(fs::path(featuresFolder), sfmFolder).string()); - - for(const std::string& matchesFolder : matchesFolders) - sfmEngine.getSfMData().addMatchesFolder(fs::relative(fs::path(matchesFolder), sfmFolder).string()); - + sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); + sfmEngine.getSfMData().addMatchesFolders(matchesFolders); sfmEngine.getSfMData().setAbsolutePath(outputSfM); } From abc661c9b6ac2a23f19ae881d016f16e12e3bc98 Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Mon, 29 Apr 2019 14:33:11 +0200 Subject: [PATCH 6/7] [matching] IO: formatting + doc --- src/aliceVision/matching/io.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/aliceVision/matching/io.cpp b/src/aliceVision/matching/io.cpp index 2cc2971406..367429c656 100644 --- a/src/aliceVision/matching/io.cpp +++ b/src/aliceVision/matching/io.cpp @@ -171,19 +171,22 @@ std::size_t LoadMatchFilePerImage( return nbLoadedMatchFiles; } -std::size_t LoadMatchesFromFolder( - PairwiseMatches& matches, - const std::string& folder, - const std::string& pattern) +/** + * Load and add pair-wise matches to \p matches from all files in \p folder matching \p pattern. + * @param[out] matches PairwiseMatches to add loaded matches to + * @param[in] folder Folder to load matches files from + * @param[in] pattern Pattern that files must respect to be loaded + */ +std::size_t loadMatchesFromFolder(PairwiseMatches& matches, const std::string& folder, const std::string& pattern) { std::size_t nbLoadedMatchFiles = 0; std::vector matchFiles; // list all matches files in 'folder' matching (i.e containing) 'pattern' - for(auto& entry : boost::make_iterator_range(fs::directory_iterator(folder), {})) + for(const auto& entry : boost::make_iterator_range(fs::directory_iterator(folder), {})) { if(entry.path().string().find(pattern) != std::string::npos) { - matchFiles.emplace_back(entry.path().string()); + matchFiles.push_back(entry.path().string()); } } @@ -241,7 +244,7 @@ bool Load( for(const auto& folder : foldersSet) { - nbLoadedMatchFiles += LoadMatchesFromFolder(matches, folder, pattern); + nbLoadedMatchFiles += loadMatchesFromFolder(matches, folder, pattern); } if(!nbLoadedMatchFiles) From 0ddefa2d10dcf361628132aa924210fa6543cb69 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Thu, 6 Jun 2019 19:26:57 +0200 Subject: [PATCH 7/7] [software] featureMatching: update major version as the IO naming convention has changed This new version is able to load files with the previous naming convention but not the opposite. --- src/software/pipeline/main_featureMatching.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/pipeline/main_featureMatching.cpp b/src/software/pipeline/main_featureMatching.cpp index c1eb1d09a9..feb4e9a888 100644 --- a/src/software/pipeline/main_featureMatching.cpp +++ b/src/software/pipeline/main_featureMatching.cpp @@ -39,7 +39,7 @@ // These constants define the current software version. // They must be updated when the command line is changed. -#define ALICEVISION_SOFTWARE_VERSION_MAJOR 1 +#define ALICEVISION_SOFTWARE_VERSION_MAJOR 2 #define ALICEVISION_SOFTWARE_VERSION_MINOR 0 using namespace aliceVision;