diff --git a/src/software/convert/main_importKnownPoses.cpp b/src/software/convert/main_importKnownPoses.cpp index 1544f0128a..6f335941bc 100644 --- a/src/software/convert/main_importKnownPoses.cpp +++ b/src/software/convert/main_importKnownPoses.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/src/software/pipeline/main_globalRotationEstimating.cpp b/src/software/pipeline/main_globalRotationEstimating.cpp index 51d71045bf..e563ed14b8 100644 --- a/src/software/pipeline/main_globalRotationEstimating.cpp +++ b/src/software/pipeline/main_globalRotationEstimating.cpp @@ -24,6 +24,7 @@ #include #include +#include // These constants define the current software version. // They must be updated when the command line is changed. diff --git a/src/software/pipeline/main_nodalSfM.cpp b/src/software/pipeline/main_nodalSfM.cpp index 80c4102039..da020df6f0 100644 --- a/src/software/pipeline/main_nodalSfM.cpp +++ b/src/software/pipeline/main_nodalSfM.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include diff --git a/src/software/pipeline/main_sfmBootstraping.cpp b/src/software/pipeline/main_sfmBootstraping.cpp index a5b18a3517..daf75ef9d3 100644 --- a/src/software/pipeline/main_sfmBootstraping.cpp +++ b/src/software/pipeline/main_sfmBootstraping.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // These constants define the current software version. // They must be updated when the command line is changed. diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index 9c961c6e84..3e5217797d 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -53,6 +53,15 @@ if(ALICEVISION_BUILD_SFM) ${Boost_LIBRARIES} ) + alicevision_add_software(aliceVision_maskProcessing + SOURCE main_maskProcessing.cpp + FOLDER ${FOLDER_SOFTWARE_UTILS} + LINKS aliceVision_system + aliceVision_cmdline + aliceVision_image + ${Boost_LIBRARIES} + ) + if(ALICEVISION_HAVE_OPENCV) target_link_libraries(aliceVision_imageProcessing_exe PRIVATE ${OpenCV_LIBS}) endif() diff --git a/src/software/utils/main_maskProcessing.cpp b/src/software/utils/main_maskProcessing.cpp new file mode 100644 index 0000000000..958c0de8e3 --- /dev/null +++ b/src/software/utils/main_maskProcessing.cpp @@ -0,0 +1,204 @@ +// 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 +#include +#include + +// 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_MINOR 0 + +using namespace aliceVision; + +namespace po = boost::program_options; + + +namespace { + +/** + * @brief operator method enum + */ +enum class EMaskOperator : unsigned char +{ + OR = 0, + AND, + NOT +}; + +/** + * @brief Convert an EMaskOperator enum to its corresponding string + * @param[in] maskOperator The given EMaskOperator enum + * @return string + */ +std::string EMaskOperator_enumToString(EMaskOperator maskOperator) +{ + switch (maskOperator) + { + case EMaskOperator::OR: + return "or"; + case EMaskOperator::AND: + return "and"; + case EMaskOperator::NOT: + return "not"; + } + throw std::out_of_range("Invalid EMaskOperator enum"); +} + +/** + * @brief Convert a string to its corresponding EMaskOperator enum + * @param[in] MaskOperator The given string + * @return EMaskOperator enum + */ +EMaskOperator EMaskOperator_stringToEnum(const std::string& maskOperator) +{ + std::string op = maskOperator; + std::transform(op.begin(), op.end(), op.begin(), ::tolower); // tolower + + if (op == "or") + return EMaskOperator::OR; + if (op == "and") + return EMaskOperator::AND; + if (op == "not") + return EMaskOperator::NOT; + + + throw std::out_of_range("Invalid mask operator : " + maskOperator); +} + +inline std::istream& operator>>(std::istream& in, EMaskOperator& maskOperator) +{ + std::string token(std::istreambuf_iterator(in), {}); + maskOperator = EMaskOperator_stringToEnum(token); + return in; +} + +inline std::ostream& operator<<(std::ostream& os, EMaskOperator e) { return os << EMaskOperator_enumToString(e); } + +} // namespace + + +int aliceVision_main(int argc, char** argv) +{ + // command-line parameters + std::vector directoryNames; + std::string outDirectory; + EMaskOperator maskOperator = EMaskOperator::OR; + + // clang-format off + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("inputs,i", po::value>(&directoryNames)->multitoken(), + "Path to directories to process.") + ("output,o", po::value(&outDirectory)->required(), + "Output directory."); + + po::options_description optionalParams("Optional parameters"); + optionalParams.add_options() + ("operator", po::value(&maskOperator)->default_value(maskOperator), ""); + // clang-format on + + CmdLine cmdline("AliceVision sfmMerge"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (directoryNames.empty()) + { + ALICEVISION_LOG_ERROR("At least one directory should be given."); + return EXIT_FAILURE; + } + + std::string path = directoryNames[0]; + for (auto &p : std::filesystem::recursive_directory_iterator(path)) + { + const std::filesystem::path refpath = p.path(); + if (p.path().extension() != ".exr") + { + continue; + } + + ALICEVISION_LOG_INFO("Processing " << refpath.string()); + + std::filesystem::path outputDirectoryPath(outDirectory); + std::filesystem::path outputPath = outputDirectoryPath / refpath.filename(); + + + image::Image img; + aliceVision::image::readImage(refpath.string(), img, image::EImageColorSpace::NO_CONVERSION); + + if (maskOperator == EMaskOperator::NOT) + { + for (int i = 0; i < img.height(); i++) + { + for (int j = 0; j < img.width(); j++) + { + img(i, j) = (img(i, j) > 0)?0:255; + } + } + } + else + { + for (int otherDirIndex = 1; otherDirIndex < directoryNames.size(); otherDirIndex++) + { + std::filesystem::path otherPath(directoryNames[otherDirIndex]); + + std::filesystem::path otherMaskPath = otherPath / refpath.filename(); + if (!std::filesystem::exists(otherMaskPath)) + { + continue; + } + + image::Image otherImg; + aliceVision::image::readImage(otherMaskPath.string(), otherImg, image::EImageColorSpace::NO_CONVERSION); + + if (otherImg.width() != img.width()) + { + continue; + } + + if (otherImg.height() != img.height()) + { + continue; + } + + + for (int i = 0; i < img.height(); i++) + { + for (int j = 0; j < img.width(); j++) + { + if (maskOperator == EMaskOperator::AND) + { + img(i, j) = img(i, j) & otherImg(i, j); + } + else if (maskOperator == EMaskOperator::OR) + { + img(i, j) = img(i, j) | otherImg(i, j); + } + } + } + } + } + + aliceVision::image::ImageWriteOptions wopt; + aliceVision::image::writeImage(outputPath.string(), img, wopt); + } + + + + return EXIT_SUCCESS; +} diff --git a/src/software/utils/main_rigTransform.cpp b/src/software/utils/main_rigTransform.cpp index 49ae04860c..d74e86d7f5 100644 --- a/src/software/utils/main_rigTransform.cpp +++ b/src/software/utils/main_rigTransform.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include diff --git a/src/software/utils/main_sfmMerge.cpp b/src/software/utils/main_sfmMerge.cpp index 1303efd8c6..f195230762 100644 --- a/src/software/utils/main_sfmMerge.cpp +++ b/src/software/utils/main_sfmMerge.cpp @@ -21,7 +21,7 @@ // These constants define the current software version. // They must be updated when the command line is changed. -#define ALICEVISION_SOFTWARE_VERSION_MAJOR 2 +#define ALICEVISION_SOFTWARE_VERSION_MAJOR 3 #define ALICEVISION_SOFTWARE_VERSION_MINOR 0 using namespace aliceVision; @@ -113,11 +113,21 @@ bool simpleMerge(sfmData::SfMData & sfmData1, const sfmData::SfMData & sfmData2) auto& intrinsics2 = sfmData2.getIntrinsics(); const size_t totalSize = intrinsics1.size() + intrinsics2.size(); - intrinsics1.insert(intrinsics2.begin(), intrinsics2.end()); - if (intrinsics1.size() < totalSize) + //If both sfm share a common intrinsicId + //Make sure there is no ambiguity and the content is the same + for (const auto & [key, intrinsic] : intrinsics1) { - ALICEVISION_LOG_ERROR("Unhandled error: common intrinsics ID between both SfMData"); - return false; + const auto & itIntrinsicOther = intrinsics2.find(key); + if (itIntrinsicOther != intrinsics2.end()) + { + const auto & obj1 = *intrinsic; + const auto & obj2 = *(itIntrinsicOther->second); + + if (!(obj1 == obj2)) + { + ALICEVISION_LOG_ERROR("Unhandled error: common intrinsic ID with different parameters between both SfMData"); + } + } } } @@ -355,7 +365,7 @@ bool fromLandmarksMerge(sfmData::SfMData & sfmData1, const sfmData::SfMData & sf int aliceVision_main(int argc, char** argv) { // command-line parameters - std::string sfmDataFilename1, sfmDataFilename2; + std::vector sfmDataFilenames; std::string outSfMDataFilename; EMergeMethod mergeMethod = EMergeMethod::SIMPLE_COPY; std::vector matchesFolders; @@ -364,10 +374,8 @@ int aliceVision_main(int argc, char** argv) // clang-format off po::options_description requiredParams("Required parameters"); requiredParams.add_options() - ("firstinput,i1", po::value(&sfmDataFilename1)->required(), - "First SfMData file to merge.") - ("secondinput,i2", po::value(&sfmDataFilename2)->required(), - "Second SfMData file to merge.") + ("inputs,i", po::value>(&sfmDataFilenames)->multitoken(), + "Path to sfmDatas to merge.") ("output,o", po::value(&outSfMDataFilename)->required(), "Output SfMData scene."); @@ -391,55 +399,69 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - // Load input scene - sfmData::SfMData sfmData1; - if (!sfmDataIO::load(sfmData1, sfmDataFilename1, sfmDataIO::ESfMData::ALL)) + if (sfmDataFilenames.empty()) { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename1 << "' cannot be read"); + ALICEVISION_LOG_ERROR("At least one sfmData input should be given."); return EXIT_FAILURE; } - sfmData::SfMData sfmData2; - if (!sfmDataIO::load(sfmData2, sfmDataFilename2, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename2 << "' cannot be read"); - return EXIT_FAILURE; - } + sfmData::SfMData outputSfmData; - if (mergeMethod == EMergeMethod::SIMPLE_COPY) + for (int id = 0; id < sfmDataFilenames.size(); id++) { - if (!simpleMerge(sfmData1, sfmData2)) + const std::string & filename = sfmDataFilenames[id]; + ALICEVISION_LOG_INFO("Processing " << filename); + + // Load input scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, filename, sfmDataIO::ESfMData::ALL)) { + ALICEVISION_LOG_ERROR("The input SfMData file '" << filename << "' cannot be read"); return EXIT_FAILURE; } - } - else - { - // get imageDescriber type - const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - // matches reading - matching::PairwiseMatches pairwiseMatches; - if (!matching::Load(pairwiseMatches, std::set(), matchesFolders, describerTypes, 0, 0)) + if (id == 0) { - std::stringstream ss("Unable to read the matches file(s) from:\n"); - for (const std::string& folder : matchesFolders) + outputSfmData = sfmData; + continue; + } + + if (mergeMethod == EMergeMethod::SIMPLE_COPY) + { + if (!simpleMerge(outputSfmData, sfmData)) { - ss << "\t- " << folder << "\n"; + return EXIT_FAILURE; } - - ALICEVISION_LOG_WARNING(ss.str()); - - return EXIT_FAILURE; } - - if (!fromLandmarksMerge(sfmData1, sfmData2, pairwiseMatches)) + else { - return EXIT_FAILURE; + // get imageDescriber type + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + + // matches reading + matching::PairwiseMatches pairwiseMatches; + if (!matching::Load(pairwiseMatches, std::set(), matchesFolders, describerTypes, 0, 0)) + { + std::stringstream ss("Unable to read the matches file(s) from:\n"); + for (const std::string& folder : matchesFolders) + { + ss << "\t- " << folder << "\n"; + } + + ALICEVISION_LOG_WARNING(ss.str()); + + return EXIT_FAILURE; + } + + if (!fromLandmarksMerge(outputSfmData, sfmData, pairwiseMatches)) + { + return EXIT_FAILURE; + } } } + - if (!sfmDataIO::save(sfmData1, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::save(outputSfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outSfMDataFilename << "'"); return EXIT_FAILURE; diff --git a/src/software/utils/main_sfmPoseInjecting.cpp b/src/software/utils/main_sfmPoseInjecting.cpp index 1f887520af..1c95888586 100644 --- a/src/software/utils/main_sfmPoseInjecting.cpp +++ b/src/software/utils/main_sfmPoseInjecting.cpp @@ -17,6 +17,8 @@ #include #include +#include + // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 1