From ea46ddca23332c115790c02105372bda37d943e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Candice=20Bent=C3=A9jac?= Date: Fri, 12 Jan 2024 10:14:25 +0100 Subject: [PATCH 1/3] [software] Reformat src/software with latest clang-format rules --- src/software/convert/main_convertMesh.cpp | 15 +- .../convert/main_convertSfMFormat.cpp | 228 ++- .../convert/main_importKnownPoses.cpp | 649 ++++---- .../export/main_exportAnimatedCamera.cpp | 174 +-- src/software/export/main_exportColmap.cpp | 7 +- .../export/main_exportColoredPointCloud.cpp | 77 +- src/software/export/main_exportDistortion.cpp | 105 +- src/software/export/main_exportKeypoints.cpp | 135 +- src/software/export/main_exportMVE2.cpp | 473 +++--- .../export/main_exportMVSTexturing.cpp | 149 +- src/software/export/main_exportMatches.cpp | 310 ++-- src/software/export/main_exportMatlab.cpp | 223 ++- src/software/export/main_exportMeshlab.cpp | 188 ++- .../export/main_exportMeshroomMaya.cpp | 145 +- src/software/export/main_exportPMVS.cpp | 571 ++++--- src/software/export/main_exportTracks.cpp | 271 ++-- src/software/export/main_exportUSD.cpp | 81 +- .../pipeline/main_LdrToHdrCalibration.cpp | 124 +- src/software/pipeline/main_LdrToHdrMerge.cpp | 94 +- .../pipeline/main_LdrToHdrSampling.cpp | 49 +- .../pipeline/main_cameraCalibration.cpp | 111 +- src/software/pipeline/main_cameraInit.cpp | 1345 +++++++++-------- .../pipeline/main_cameraLocalization.cpp | 618 ++++---- .../pipeline/main_checkerboardCalibration.cpp | 50 +- .../pipeline/main_checkerboardDetection.cpp | 42 +- .../main_computeStructureFromKnownPoses.cpp | 200 +-- .../pipeline/main_depthMapEstimation.cpp | 153 +- .../pipeline/main_depthMapFiltering.cpp | 18 +- .../pipeline/main_distortionCalibration.cpp | 59 +- .../pipeline/main_featureExtraction.cpp | 31 +- .../pipeline/main_featureMatching.cpp | 698 +++++---- src/software/pipeline/main_globalSfM.cpp | 281 ++-- src/software/pipeline/main_imageMasking.cpp | 85 +- src/software/pipeline/main_imageMatching.cpp | 384 ++--- .../pipeline/main_imageSegmentation.cpp | 62 +- src/software/pipeline/main_incrementalSfM.cpp | 370 +++-- .../pipeline/main_lightingCalibration.cpp | 2 +- src/software/pipeline/main_meshDecimate.cpp | 35 +- src/software/pipeline/main_meshDenoising.cpp | 42 +- src/software/pipeline/main_meshFiltering.cpp | 105 +- src/software/pipeline/main_meshMasking.cpp | 133 +- src/software/pipeline/main_meshResampling.cpp | 46 +- src/software/pipeline/main_meshing.cpp | 214 ++- src/software/pipeline/main_nodalSfM.cpp | 208 +-- .../pipeline/main_normalIntegration.cpp | 5 +- .../pipeline/main_panoramaCompositing.cpp | 293 ++-- .../pipeline/main_panoramaEstimation.cpp | 82 +- src/software/pipeline/main_panoramaInit.cpp | 368 +++-- .../pipeline/main_panoramaMerging.cpp | 100 +- .../pipeline/main_panoramaPostProcessing.cpp | 219 +-- .../pipeline/main_panoramaPrepareImages.cpp | 75 +- src/software/pipeline/main_panoramaSeams.cpp | 58 +- .../pipeline/main_panoramaWarping.cpp | 136 +- .../pipeline/main_photometricStereo.cpp | 8 +- .../pipeline/main_prepareDenseScene.cpp | 140 +- .../pipeline/main_relativePoseEstimating.cpp | 184 +-- src/software/pipeline/main_rigCalibration.cpp | 633 ++++---- .../pipeline/main_rigLocalization.cpp | 616 ++++---- .../pipeline/main_sfmBootstraping.cpp | 105 +- .../pipeline/main_sfmTriangulation.cpp | 25 +- .../pipeline/main_sphereDetection.cpp | 2 +- src/software/pipeline/main_texturing.cpp | 39 +- src/software/pipeline/main_tracksBuilding.cpp | 13 +- src/software/utils/main_applyCalibration.cpp | 33 +- .../utils/main_colorCheckerCorrection.cpp | 73 +- .../utils/main_colorCheckerDetection.cpp | 191 ++- .../utils/main_computeUncertainty.cpp | 194 ++- src/software/utils/main_frustumFiltering.cpp | 130 +- .../utils/main_generateSampleScene.cpp | 6 +- src/software/utils/main_hardwareResources.cpp | 69 +- src/software/utils/main_imageProcessing.cpp | 431 +++--- src/software/utils/main_importMiddlebury.cpp | 15 +- src/software/utils/main_keyframeSelection.cpp | 90 +- .../utils/main_lightingEstimation.cpp | 450 +++--- src/software/utils/main_mergeMeshes.cpp | 72 +- src/software/utils/main_qualityEvaluation.cpp | 147 +- src/software/utils/main_rigTransform.cpp | 118 +- src/software/utils/main_sfmAlignment.cpp | 287 ++-- src/software/utils/main_sfmColorHarmonize.cpp | 83 +- src/software/utils/main_sfmDistances.cpp | 202 +-- src/software/utils/main_sfmMerge.cpp | 22 +- src/software/utils/main_sfmRegression.cpp | 109 +- .../utils/main_sfmSplitReconstructed.cpp | 17 +- src/software/utils/main_sfmToRig.cpp | 16 +- src/software/utils/main_sfmTransfer.cpp | 112 +- src/software/utils/main_sfmTransform.cpp | 597 ++++---- src/software/utils/main_split360Images.cpp | 224 ++- src/software/utils/main_voctreeCreation.cpp | 304 ++-- .../utils/main_voctreeQueryUtility.cpp | 168 +- src/software/utils/main_voctreeStatistics.cpp | 201 ++- .../utils/precisionEvaluationToGt.hpp | 565 ++++--- .../colorHarmonizeEngineGlobal.cpp | 907 ++++++----- .../colorHarmonizeEngineGlobal.hpp | 96 +- src/software/utils/sfmHelper/sfmIOHelper.hpp | 350 ++--- .../utils/sfmHelper/sfmIOHelper_test.cpp | 272 ++-- src/software/utils/sfmHelper/sfmPlyHelper.hpp | 128 +- 96 files changed, 9489 insertions(+), 9651 deletions(-) diff --git a/src/software/convert/main_convertMesh.cpp b/src/software/convert/main_convertMesh.cpp index ed726b563a..5cf02cb53c 100644 --- a/src/software/convert/main_convertMesh.cpp +++ b/src/software/convert/main_convertMesh.cpp @@ -60,14 +60,14 @@ int aliceVision_main(int argc, char** argv) } // check first mesh file path - if(!inputMeshPath.empty() && !fs::exists(inputMeshPath) && !fs::is_regular_file(inputMeshPath)) + if (!inputMeshPath.empty() && !fs::exists(inputMeshPath) && !fs::is_regular_file(inputMeshPath)) { ALICEVISION_LOG_ERROR("The input mesh file doesn't exist"); return EXIT_FAILURE; } // check output file path - if(outputFilePath.empty()) + if (outputFilePath.empty()) { ALICEVISION_LOG_ERROR("Invalid output"); return EXIT_FAILURE; @@ -77,9 +77,9 @@ int aliceVision_main(int argc, char** argv) { const std::string outputFolderPart = fs::path(outputFilePath).parent_path().string(); - if(!outputFolderPart.empty() && !fs::exists(outputFolderPart)) + if (!outputFolderPart.empty() && !fs::exists(outputFolderPart)) { - if(!fs::create_directory(outputFolderPart)) + if (!fs::create_directory(outputFolderPart)) { ALICEVISION_LOG_ERROR("Cannot create output folder"); return EXIT_FAILURE; @@ -92,17 +92,16 @@ int aliceVision_main(int argc, char** argv) texturing.loadWithAtlas(inputMeshPath); mesh::Mesh* inputMesh = texturing.mesh; - if(!inputMesh) + if (!inputMesh) { ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath); return EXIT_FAILURE; } - if(inputMesh->pts.empty() || inputMesh->tris.empty()) + if (inputMesh->pts.empty() || inputMesh->tris.empty()) { ALICEVISION_LOG_ERROR("Error: empty mesh from the file " << inputMeshPath); - ALICEVISION_LOG_ERROR("Input mesh: " << inputMesh->pts.size() << " vertices and " << inputMesh->tris.size() - << " facets."); + ALICEVISION_LOG_ERROR("Input mesh: " << inputMesh->pts.size() << " vertices and " << inputMesh->tris.size() << " facets."); return EXIT_FAILURE; } diff --git a/src/software/convert/main_convertSfMFormat.cpp b/src/software/convert/main_convertSfMFormat.cpp index 02aef19de2..b70d3b7c1c 100644 --- a/src/software/convert/main_convertSfMFormat.cpp +++ b/src/software/convert/main_convertSfMFormat.cpp @@ -22,7 +22,6 @@ #include #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 2 @@ -34,21 +33,21 @@ namespace po = boost::program_options; namespace fs = std::filesystem; // convert from a SfMData format to another -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputSfMDataFilename; + // command-line parameters + std::string sfmDataFilename; + std::string outputSfMDataFilename; - // user optional parameters + // user optional parameters - std::string describerTypesName; - std::vector imageWhiteList; - bool flagViews = true; - bool flagIntrinsics = true; - bool flagExtrinsics = true; - bool flagStructure = true; - bool flagObservations = true; + std::string describerTypesName; + std::vector imageWhiteList; + bool flagViews = true; + bool flagIntrinsics = true; + bool flagExtrinsics = true; + bool flagStructure = true; + bool flagObservations = true; // clang-format off po::options_description requiredParams("Required parameters"); @@ -77,123 +76,118 @@ int aliceVision_main(int argc, char **argv) "Export observations."); // clang-format on - CmdLine cmdline("AliceVision convertSfMFormat"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(sfmDataFilename.empty() || outputSfMDataFilename.empty()) - { - ALICEVISION_LOG_ERROR("Invalid input or output filename"); - return EXIT_FAILURE; - } - - int flags = (flagViews ? sfmDataIO::VIEWS : 0) - | (flagIntrinsics ? sfmDataIO::INTRINSICS : 0) - | (flagExtrinsics ? sfmDataIO::EXTRINSICS : 0) - | (flagObservations ? sfmDataIO::OBSERVATIONS_WITH_FEATURES : 0) - | (flagStructure ? sfmDataIO::STRUCTURE : 0); - - flags = (flags) ? flags : sfmDataIO::ALL; - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - // image white list filter - if(!imageWhiteList.empty()) - { - std::vector imageWhiteRegexList; - imageWhiteRegexList.reserve(imageWhiteList.size()); - for (const std::string& exp : imageWhiteList) + CmdLine cmdline("AliceVision convertSfMFormat"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - imageWhiteRegexList.emplace_back(simpleFilterToRegex_noThrow(exp)); + return EXIT_FAILURE; } - - std::vector viewsToRemove; - std::vector posesToRemove; - std::vector landmarksToRemove; - for(const auto& viewPair : sfmData.getViews()) + if (sfmDataFilename.empty() || outputSfMDataFilename.empty()) { - const sfmData::View& view = *(viewPair.second); - bool toRemove = true; - - for(std::size_t i = 0; i < imageWhiteList.size(); ++i) - { - // Compare to filename, stem (filename without extension), view UID or regex on the full path - if (imageWhiteList[i] == fs::path(view.getImage().getImagePath()).filename() || - imageWhiteList[i] == fs::path(view.getImage().getImagePath()).stem() || - imageWhiteList[i] == std::to_string(view.getViewId()) || - std::regex_match(view.getImage().getImagePath(), imageWhiteRegexList[i]) - ) - { - toRemove = false; - } - } - - if(toRemove) - { - viewsToRemove.push_back(view.getViewId()); - // remove pose only if it exists and it is independent - if(view.isPoseIndependant() && sfmData.existsPose(view)) - posesToRemove.push_back(view.getPoseId()); - } + ALICEVISION_LOG_ERROR("Invalid input or output filename"); + return EXIT_FAILURE; } - for(auto& landmarkPair : sfmData.getLandmarks()) + int flags = (flagViews ? sfmDataIO::VIEWS : 0) | (flagIntrinsics ? sfmDataIO::INTRINSICS : 0) | (flagExtrinsics ? sfmDataIO::EXTRINSICS : 0) | + (flagObservations ? sfmDataIO::OBSERVATIONS_WITH_FEATURES : 0) | (flagStructure ? sfmDataIO::STRUCTURE : 0); + + flags = (flags) ? flags : sfmDataIO::ALL; + + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - sfmData::Landmark& landmark = landmarkPair.second; - for(const IndexT viewId : viewsToRemove) - { - if(landmark.getObservations().find(viewId) != landmark.getObservations().end()) - landmark.getObservations().erase(viewId); - } - if(landmark.getObservations().empty()) - landmarksToRemove.push_back(landmarkPair.first); + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; } - for(const IndexT viewId : viewsToRemove) - sfmData.getViews().erase(viewId); + // image white list filter + if (!imageWhiteList.empty()) + { + std::vector imageWhiteRegexList; + imageWhiteRegexList.reserve(imageWhiteList.size()); + for (const std::string& exp : imageWhiteList) + { + imageWhiteRegexList.emplace_back(simpleFilterToRegex_noThrow(exp)); + } + + std::vector viewsToRemove; + std::vector posesToRemove; + std::vector landmarksToRemove; + + for (const auto& viewPair : sfmData.getViews()) + { + const sfmData::View& view = *(viewPair.second); + bool toRemove = true; + + for (std::size_t i = 0; i < imageWhiteList.size(); ++i) + { + // Compare to filename, stem (filename without extension), view UID or regex on the full path + if (imageWhiteList[i] == fs::path(view.getImage().getImagePath()).filename() || + imageWhiteList[i] == fs::path(view.getImage().getImagePath()).stem() || imageWhiteList[i] == std::to_string(view.getViewId()) || + std::regex_match(view.getImage().getImagePath(), imageWhiteRegexList[i])) + { + toRemove = false; + } + } + + if (toRemove) + { + viewsToRemove.push_back(view.getViewId()); + // remove pose only if it exists and it is independent + if (view.isPoseIndependant() && sfmData.existsPose(view)) + posesToRemove.push_back(view.getPoseId()); + } + } + + for (auto& landmarkPair : sfmData.getLandmarks()) + { + sfmData::Landmark& landmark = landmarkPair.second; + for (const IndexT viewId : viewsToRemove) + { + if (landmark.getObservations().find(viewId) != landmark.getObservations().end()) + landmark.getObservations().erase(viewId); + } + if (landmark.getObservations().empty()) + landmarksToRemove.push_back(landmarkPair.first); + } + + for (const IndexT viewId : viewsToRemove) + sfmData.getViews().erase(viewId); - for(const IndexT poseId : posesToRemove) - sfmData.erasePose(poseId); + for (const IndexT poseId : posesToRemove) + sfmData.erasePose(poseId); - for(const IndexT landmarkId : landmarksToRemove) - sfmData.getLandmarks().erase(landmarkId); - } + for (const IndexT landmarkId : landmarksToRemove) + sfmData.getLandmarks().erase(landmarkId); + } - // landmarks describer types filter - if(describerTypesName.empty()) - { - sfmData.getLandmarks().clear(); - } - else - { - std::vector imageDescriberTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + // landmarks describer types filter + if (describerTypesName.empty()) + { + sfmData.getLandmarks().clear(); + } + else + { + std::vector imageDescriberTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - std::vector toRemove; - for(const auto& landmarkPair : sfmData.getLandmarks()) + std::vector toRemove; + for (const auto& landmarkPair : sfmData.getLandmarks()) + { + if (std::find(imageDescriberTypes.begin(), imageDescriberTypes.end(), landmarkPair.second.descType) == imageDescriberTypes.end()) + toRemove.push_back(landmarkPair.first); + } + for (const IndexT landmarkId : toRemove) + sfmData.getLandmarks().erase(landmarkId); + } + // export the SfMData scene in the expected format + if (!sfmDataIO::save(sfmData, outputSfMDataFilename, sfmDataIO::ESfMData(flags))) { - if(std::find(imageDescriberTypes.begin(), imageDescriberTypes.end(), landmarkPair.second.descType) == imageDescriberTypes.end()) - toRemove.push_back(landmarkPair.first); + ALICEVISION_LOG_ERROR("An error occured while trying to save '" << outputSfMDataFilename << "'"); + return EXIT_FAILURE; } - for(const IndexT landmarkId : toRemove) - sfmData.getLandmarks().erase(landmarkId); - } - // export the SfMData scene in the expected format - if(!sfmDataIO::save(sfmData, outputSfMDataFilename, sfmDataIO::ESfMData(flags))) - { - ALICEVISION_LOG_ERROR("An error occured while trying to save '" << outputSfMDataFilename << "'"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + + return EXIT_SUCCESS; } diff --git a/src/software/convert/main_importKnownPoses.cpp b/src/software/convert/main_importKnownPoses.cpp index 5e80b8a2d6..a97763e548 100644 --- a/src/software/convert/main_importKnownPoses.cpp +++ b/src/software/convert/main_importKnownPoses.cpp @@ -26,7 +26,6 @@ #include #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 2 @@ -38,7 +37,6 @@ namespace po = boost::program_options; namespace json = boost::property_tree; namespace fs = std::filesystem; - struct XMPData { std::vector rotation; @@ -57,18 +55,17 @@ struct XMPData int inMeshing = 0; }; - XMPData read_xmp(const std::string& xmpFilepath, const std::string& knownPosesFilePath, const std::string& stem, fs::directory_entry pathIt) { XMPData xmp; const fs::path path = pathIt.path(); - if(!is_regular_file(path)) + if (!is_regular_file(path)) ALICEVISION_THROW_ERROR("Path isn't a regulat file: " << path); std::string extension = path.extension().string(); boost::to_lower(extension); - if(extension != ".xmp") + if (extension != ".xmp") { - ALICEVISION_THROW_ERROR("Unknown extension: " << extension); + ALICEVISION_THROW_ERROR("Unknown extension: " << extension); } json::ptree tree; @@ -99,7 +96,7 @@ XMPData read_xmp(const std::string& xmpFilepath, const std::string& knownPosesFi std::string positionStr = tree.get("x:xmpmeta.rdf:RDF.rdf:Description.xcr:Position", ""); std::vector positionStrings; - if(!positionStr.empty()) + if (!positionStr.empty()) { boost::split(positionStrings, positionStr, boost::is_any_of(" \t"), boost::token_compress_on); ALICEVISION_LOG_TRACE("position: " << positionStrings); @@ -114,8 +111,7 @@ XMPData read_xmp(const std::string& xmpFilepath, const std::string& knownPosesFi { xmp.position.push_back(std::stod(pos_val)); } - std::string distortionStr = - tree.get("x:xmpmeta.rdf:RDF.rdf:Description.xcr:DistortionCoeficients", ""); + std::string distortionStr = tree.get("x:xmpmeta.rdf:RDF.rdf:Description.xcr:DistortionCoeficients", ""); std::vector distortionStrings; boost::split(distortionStrings, distortionStr, boost::is_any_of(" \t"), boost::token_compress_on); ALICEVISION_LOG_TRACE("distortion: " << distortionStrings); @@ -128,14 +124,14 @@ XMPData read_xmp(const std::string& xmpFilepath, const std::string& knownPosesFi } // import from a SfMData format to another -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string knownPosesFilePath; - std::string sfmDataFilePath; - std::string outputFilename; + // command-line parameters + std::string knownPosesFilePath; + std::string sfmDataFilePath; + std::string outputFilename; - sfmData::SfMData sfmData; + sfmData::SfMData sfmData; // clang-format off po::options_description requiredParams("Required parameters"); @@ -148,358 +144,357 @@ int aliceVision_main(int argc, char **argv) "Output sfmData filepath."); // clang-format on - CmdLine cmdline("AliceVision importKnownPoses"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - // Loading the sfmData to modify it - if(!sfmDataIO::load(sfmData, sfmDataFilePath, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilePath << "' cannot be read."); - return EXIT_FAILURE; - } - if(sfmData.getViews().empty()) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilePath << "' is empty."); - return EXIT_FAILURE; - } - - std::map viewIdPerStem; - for(const auto viewIt : sfmData.getViews()) - { - const std::string stem = fs::path(viewIt.second->getImage().getImagePath()).stem().string(); - viewIdPerStem[stem] = viewIt.first; - } - fs::path knownPosesPath(knownPosesFilePath); - if(fs::is_directory(knownPosesPath)) - { - try - { - for (const auto& pathIt : fs::directory_iterator(knownPosesPath)) - { - const std::string stem = pathIt.path().stem().string(); - if (viewIdPerStem.count(stem) == 0) - { - continue; - } - - const XMPData xmp = read_xmp(pathIt.path().string(), knownPosesFilePath, stem, pathIt); - - const IndexT viewId = viewIdPerStem[stem]; - sfmData::View& view = sfmData.getView(viewId); - sfmData::CameraPose& pose = sfmData.getPoses()[view.getPoseId()]; - - std::shared_ptr intrinsicBase = sfmData.getIntrinsicsharedPtr(view.getIntrinsicId()); - - Eigen::Map> rot(xmp.rotation.data()); - - Vec3 pos_vec(xmp.position[0], xmp.position[1], xmp.position[2]); - Vec3 translation = - rot * pos_vec; - - Eigen::Matrix4d T; - T.setIdentity(); - T.block<3, 3>(0, 0) = rot; - T.block<3, 1>(0, 3) = translation; - - Eigen::Matrix4d av_T_cr = Eigen::Matrix4d::Zero(); - av_T_cr(0, 0) = 1.0; - av_T_cr(1, 2) = -1.0; - av_T_cr(2, 1) = 1.0; - av_T_cr(3, 3) = 1.0; - - T = T * av_T_cr; - Eigen::Matrix3d R = T.block<3, 3>(0, 0); - translation = T.block<3, 1>(0, 3); - pos_vec = -R.transpose() * translation; - - geometry::Pose3 pos3(R, pos_vec); - pose.setTransform(pos3); - - std::shared_ptr intrinsic = std::dynamic_pointer_cast(intrinsicBase); - if (intrinsic == nullptr) - { - ALICEVISION_THROW_ERROR("Invalid intrinsic"); - } - - const double imageRatio = static_cast(view.getImage().getWidth()) / static_cast(view.getImage().getHeight()); - const double sensorWidth = intrinsic->sensorWidth(); - const double maxSize = std::max(view.getImage().getWidth(), view.getImage().getHeight()); - const double focalLengthmm = (sensorWidth * xmp.focalLength35mm) / 36.0; - const double focalLengthPix = maxSize * focalLengthmm / sensorWidth; - const double offsetX = (double(view.getImage().getWidth()) * 0.5) + (xmp.principalPointU * maxSize); - const double offsetY = (double(view.getImage().getHeight()) * 0.5) + (xmp.principalPointV * maxSize); - - intrinsic->setScale({focalLengthPix, focalLengthPix}); - intrinsic->setOffset({offsetX, offsetY}); - - std::cout << focalLengthPix << std::endl; - - if(xmp.distortionModel == "brown3t2") - { - std::shared_ptr camera = std::dynamic_pointer_cast(intrinsic); - if (camera == nullptr) + CmdLine cmdline("AliceVision importKnownPoses"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // Loading the sfmData to modify it + if (!sfmDataIO::load(sfmData, sfmDataFilePath, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilePath << "' cannot be read."); + return EXIT_FAILURE; + } + if (sfmData.getViews().empty()) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilePath << "' is empty."); + return EXIT_FAILURE; + } + + std::map viewIdPerStem; + for (const auto viewIt : sfmData.getViews()) + { + const std::string stem = fs::path(viewIt.second->getImage().getImagePath()).stem().string(); + viewIdPerStem[stem] = viewIt.first; + } + fs::path knownPosesPath(knownPosesFilePath); + if (fs::is_directory(knownPosesPath)) + { + try + { + for (const auto& pathIt : fs::directory_iterator(knownPosesPath)) + { + const std::string stem = pathIt.path().stem().string(); + if (viewIdPerStem.count(stem) == 0) { - camera = camera::createPinhole(camera::EINTRINSIC::PINHOLE_CAMERA); - camera->copyFrom(*intrinsic); - sfmData.getIntrinsics().at(view.getIntrinsicId()) = camera; + continue; } - if(xmp.distortionCoefficients.size() == 6) + const XMPData xmp = read_xmp(pathIt.path().string(), knownPosesFilePath, stem, pathIt); + + const IndexT viewId = viewIdPerStem[stem]; + sfmData::View& view = sfmData.getView(viewId); + sfmData::CameraPose& pose = sfmData.getPoses()[view.getPoseId()]; + + std::shared_ptr intrinsicBase = sfmData.getIntrinsicsharedPtr(view.getIntrinsicId()); + + Eigen::Map> rot(xmp.rotation.data()); + + Vec3 pos_vec(xmp.position[0], xmp.position[1], xmp.position[2]); + Vec3 translation = -rot * pos_vec; + + Eigen::Matrix4d T; + T.setIdentity(); + T.block<3, 3>(0, 0) = rot; + T.block<3, 1>(0, 3) = translation; + + Eigen::Matrix4d av_T_cr = Eigen::Matrix4d::Zero(); + av_T_cr(0, 0) = 1.0; + av_T_cr(1, 2) = -1.0; + av_T_cr(2, 1) = 1.0; + av_T_cr(3, 3) = 1.0; + + T = T * av_T_cr; + Eigen::Matrix3d R = T.block<3, 3>(0, 0); + translation = T.block<3, 1>(0, 3); + pos_vec = -R.transpose() * translation; + + geometry::Pose3 pos3(R, pos_vec); + pose.setTransform(pos3); + + std::shared_ptr intrinsic = + std::dynamic_pointer_cast(intrinsicBase); + if (intrinsic == nullptr) { - std::vector distortionCoefficients; - - distortionCoefficients.push_back(xmp.distortionCoefficients[0]); - distortionCoefficients.push_back(xmp.distortionCoefficients[1]); - distortionCoefficients.push_back(xmp.distortionCoefficients[2]); - // Skip element at index 3 as it is empty - distortionCoefficients.push_back(xmp.distortionCoefficients[5]); - distortionCoefficients.push_back(xmp.distortionCoefficients[4]); - camera->setDistortionParams(distortionCoefficients); // vector of 5 elements (r1, r2, r3, t1, t2) + ALICEVISION_THROW_ERROR("Invalid intrinsic"); } - else + + const double imageRatio = static_cast(view.getImage().getWidth()) / static_cast(view.getImage().getHeight()); + const double sensorWidth = intrinsic->sensorWidth(); + const double maxSize = std::max(view.getImage().getWidth(), view.getImage().getHeight()); + const double focalLengthmm = (sensorWidth * xmp.focalLength35mm) / 36.0; + const double focalLengthPix = maxSize * focalLengthmm / sensorWidth; + const double offsetX = (double(view.getImage().getWidth()) * 0.5) + (xmp.principalPointU * maxSize); + const double offsetY = (double(view.getImage().getHeight()) * 0.5) + (xmp.principalPointV * maxSize); + + intrinsic->setScale({focalLengthPix, focalLengthPix}); + intrinsic->setOffset({offsetX, offsetY}); + + std::cout << focalLengthPix << std::endl; + + if (xmp.distortionModel == "brown3t2") { - ALICEVISION_THROW_ERROR("Error in xmp file: " << stem << " the distortion coefficient doesn't have the right size."); + std::shared_ptr camera = std::dynamic_pointer_cast(intrinsic); + if (camera == nullptr) + { + camera = camera::createPinhole(camera::EINTRINSIC::PINHOLE_CAMERA); + camera->copyFrom(*intrinsic); + sfmData.getIntrinsics().at(view.getIntrinsicId()) = camera; + } + + if (xmp.distortionCoefficients.size() == 6) + { + std::vector distortionCoefficients; + + distortionCoefficients.push_back(xmp.distortionCoefficients[0]); + distortionCoefficients.push_back(xmp.distortionCoefficients[1]); + distortionCoefficients.push_back(xmp.distortionCoefficients[2]); + // Skip element at index 3 as it is empty + distortionCoefficients.push_back(xmp.distortionCoefficients[5]); + distortionCoefficients.push_back(xmp.distortionCoefficients[4]); + camera->setDistortionParams(distortionCoefficients); // vector of 5 elements (r1, r2, r3, t1, t2) + } + else + { + ALICEVISION_THROW_ERROR("Error in xmp file: " << stem << " the distortion coefficient doesn't have the right size."); + } } - } - else if(xmp.distortionModel == "brown3") - { - std::shared_ptr camera = std::dynamic_pointer_cast(intrinsic); - if (camera == nullptr) + else if (xmp.distortionModel == "brown3") { - camera = camera::createPinhole(camera::EINTRINSIC::PINHOLE_CAMERA); - camera->copyFrom(*intrinsic); - sfmData.getIntrinsics().at(view.getIntrinsicId()) = camera; - } + std::shared_ptr camera = std::dynamic_pointer_cast(intrinsic); + if (camera == nullptr) + { + camera = camera::createPinhole(camera::EINTRINSIC::PINHOLE_CAMERA); + camera->copyFrom(*intrinsic); + sfmData.getIntrinsics().at(view.getIntrinsicId()) = camera; + } - if(xmp.distortionCoefficients.size() == 3) - { - std::vector distortionCoefficients = xmp.distortionCoefficients; - camera->setDistortionParams(distortionCoefficients); // vector of 5 elements (r1, r2, r3) + if (xmp.distortionCoefficients.size() == 3) + { + std::vector distortionCoefficients = xmp.distortionCoefficients; + camera->setDistortionParams(distortionCoefficients); // vector of 5 elements (r1, r2, r3) + } + else + { + ALICEVISION_THROW_ERROR("Error in xmp file: " << stem << " the distortion coefficient doesn't have the right size."); + } } else { - ALICEVISION_THROW_ERROR("Error in xmp file: " << stem << " the distortion coefficient doesn't have the right size."); + ALICEVISION_THROW_ERROR("Unsupported distortion model: " << xmp.distortionModel); } - } - else - { - ALICEVISION_THROW_ERROR("Unsupported distortion model: " << xmp.distortionModel); - } - } - } - catch(boost::program_options::error& e) - { - ALICEVISION_CERR("ERROR: " << e.what() << std::endl); - return EXIT_FAILURE; - } - - } - else if(is_regular_file(knownPosesPath)) - { - std::string extension = knownPosesPath.extension().string(); - boost::to_lower(extension); - if(extension == ".json") - { - std::ifstream jsonFile(knownPosesFilePath); - if(!jsonFile) - { - ALICEVISION_LOG_ERROR("Error opening file: " << knownPosesFilePath); - return EXIT_FAILURE; - } - - std::string line; - size_t count = 0; - std::vector> records; - std::vector> frameIdToPoseId; - - // Here we are making a vector that associate a frameId to a PoseId so we can access each easier - for(const auto& view : sfmData.getViews()) - { - frameIdToPoseId.emplace_back(view.second->getFrameId(), view.second->getPoseId()); - } - std::sort(frameIdToPoseId.begin(), frameIdToPoseId.end()); - - // ensure there is no duplicated frameId - auto it = std::adjacent_find(frameIdToPoseId.begin(), frameIdToPoseId.end(), - [](const auto& a, const auto& b) { return a.first == b.first; }); - if(it != frameIdToPoseId.end()) - { - ALICEVISION_THROW_ERROR("Duplicated frameId in sfmData: " << sfmDataFilePath << ", frameID: " << it->first); - } - // This is where we start to read our json line by line - while(getline(jsonFile, line)) - { - std::stringstream linestream(line); - std::vector up; - std::vector forward; - std::vector pose; - json::ptree pt; - - // We put each line in a stringstream because that is what boost's parser needs. - // The parser turns the json into a property tree. - json::json_parser::read_json(linestream, pt); - const int sensor = pt.get("sensorwidth", 0); - const double fov = pt.get("xFovDegrees", 0); - const long timestamp = pt.get("tstamp", 0); - const int frmcnt = pt.get("frmcnt", 0); - const double expoff = pt.get("exposureOff", 0); - const double expdur = pt.get("exposureDur", 0); - // These arguments are lists so we need to loop to store them properly - for(json::ptree::value_type& up_val : pt.get_child("up")) - { - std::string value = up_val.second.data(); - up.push_back(std::stof(value)); - } - for(json::ptree::value_type& for_val : pt.get_child("forward")) - { - std::string value = for_val.second.data(); - forward.push_back(std::stof(value)); - } - for(json::ptree::value_type& pose_val : pt.get_child("pose")) - { - std::string value = pose_val.second.data(); - pose.push_back(std::stof(value)); - } - // We use records to indexify our frame count this way we know which frame started the list, this will be our offset - records.emplace_back(count, frmcnt); - - // Without surprise we store our vector pose to get our position - Vec3 pos_vec(pose[0], pose[1], pose[2]); - Mat3 rot; - // And we need those two vectors to calculate the rotation matrix - Vec3 up_vec(up[0], up[1], up[2]); - Vec3 forward_vec(forward[0], forward[1], forward[2]); - - rot.row(0) = up_vec.cross(forward_vec); - rot.row(1) = -up_vec; - rot.row(2) = forward_vec; - // we store this new information into a pose3 - geometry::Pose3 pos3(rot, pos_vec); - - // And we set this pose into the sfmData using our frameId (which corresponds to the count) to set a new pos3 transform - IndexT sfmPoseId = frameIdToPoseId[count].second; - sfmData.getPoses()[sfmPoseId].setTransform(pos3); - count++; - // We repeat this to each line of the file which contains a json - } - } - else if(extension == ".ma") - { - std::ifstream file(knownPosesPath.string()); - - std::string line; - std::string name; - bool hasName = false; - bool hasPosition = false; - bool hasRotation = false; - double pos[3] = {0.0, 0.0, 0.0}; - double rot[3] = {0.0, 0.0, 0.0}; - - while (std::getline(file, line)) - { - std::regex regex("[^\\s\\t;]+"); - std::vector words; - - for (auto it = std::sregex_iterator(line.begin(), line.end(), regex); it != std::sregex_iterator(); it++) + } + } + catch (boost::program_options::error& e) + { + ALICEVISION_CERR("ERROR: " << e.what() << std::endl); + return EXIT_FAILURE; + } + } + else if (is_regular_file(knownPosesPath)) + { + std::string extension = knownPosesPath.extension().string(); + boost::to_lower(extension); + if (extension == ".json") + { + std::ifstream jsonFile(knownPosesFilePath); + if (!jsonFile) { - std::string tok = it->str(); - tok.erase(std::remove(tok.begin(), tok.end(), '\"'), tok.end()); - words.push_back(tok); + ALICEVISION_LOG_ERROR("Error opening file: " << knownPosesFilePath); + return EXIT_FAILURE; } - if (words.empty()) - continue; + std::string line; + size_t count = 0; + std::vector> records; + std::vector> frameIdToPoseId; - if (words[0] == "createNode") + // Here we are making a vector that associate a frameId to a PoseId so we can access each easier + for (const auto& view : sfmData.getViews()) + { + frameIdToPoseId.emplace_back(view.second->getFrameId(), view.second->getPoseId()); + } + std::sort(frameIdToPoseId.begin(), frameIdToPoseId.end()); + + // ensure there is no duplicated frameId + auto it = + std::adjacent_find(frameIdToPoseId.begin(), frameIdToPoseId.end(), [](const auto& a, const auto& b) { return a.first == b.first; }); + if (it != frameIdToPoseId.end()) + { + ALICEVISION_THROW_ERROR("Duplicated frameId in sfmData: " << sfmDataFilePath << ", frameID: " << it->first); + } + // This is where we start to read our json line by line + while (getline(jsonFile, line)) { - if (words.size() == 4) + std::stringstream linestream(line); + std::vector up; + std::vector forward; + std::vector pose; + json::ptree pt; + + // We put each line in a stringstream because that is what boost's parser needs. + // The parser turns the json into a property tree. + json::json_parser::read_json(linestream, pt); + const int sensor = pt.get("sensorwidth", 0); + const double fov = pt.get("xFovDegrees", 0); + const long timestamp = pt.get("tstamp", 0); + const int frmcnt = pt.get("frmcnt", 0); + const double expoff = pt.get("exposureOff", 0); + const double expdur = pt.get("exposureDur", 0); + // These arguments are lists so we need to loop to store them properly + for (json::ptree::value_type& up_val : pt.get_child("up")) { - name = words[3]; - hasName = true; - hasPosition = false; - hasRotation = false; + std::string value = up_val.second.data(); + up.push_back(std::stof(value)); + } + for (json::ptree::value_type& for_val : pt.get_child("forward")) + { + std::string value = for_val.second.data(); + forward.push_back(std::stof(value)); + } + for (json::ptree::value_type& pose_val : pt.get_child("pose")) + { + std::string value = pose_val.second.data(); + pose.push_back(std::stof(value)); } + // We use records to indexify our frame count this way we know which frame started the list, this will be our offset + records.emplace_back(count, frmcnt); + + // Without surprise we store our vector pose to get our position + Vec3 pos_vec(pose[0], pose[1], pose[2]); + Mat3 rot; + // And we need those two vectors to calculate the rotation matrix + Vec3 up_vec(up[0], up[1], up[2]); + Vec3 forward_vec(forward[0], forward[1], forward[2]); + + rot.row(0) = up_vec.cross(forward_vec); + rot.row(1) = -up_vec; + rot.row(2) = forward_vec; + // we store this new information into a pose3 + geometry::Pose3 pos3(rot, pos_vec); + + // And we set this pose into the sfmData using our frameId (which corresponds to the count) to set a new pos3 transform + IndexT sfmPoseId = frameIdToPoseId[count].second; + sfmData.getPoses()[sfmPoseId].setTransform(pos3); + count++; + // We repeat this to each line of the file which contains a json } - - if (words[0] == "setAttr") + } + else if (extension == ".ma") + { + std::ifstream file(knownPosesPath.string()); + + std::string line; + std::string name; + bool hasName = false; + bool hasPosition = false; + bool hasRotation = false; + double pos[3] = {0.0, 0.0, 0.0}; + double rot[3] = {0.0, 0.0, 0.0}; + + while (std::getline(file, line)) { - if (words[1] == ".translate") + std::regex regex("[^\\s\\t;]+"); + std::vector words; + + for (auto it = std::sregex_iterator(line.begin(), line.end(), regex); it != std::sregex_iterator(); it++) { - if (hasName && (!hasPosition)) + std::string tok = it->str(); + tok.erase(std::remove(tok.begin(), tok.end(), '\"'), tok.end()); + words.push_back(tok); + } + + if (words.empty()) + continue; + + if (words[0] == "createNode") + { + if (words.size() == 4) { - hasPosition = true; - pos[0] = std::stod(words[4]); - pos[1] = std::stod(words[5]); - pos[2] = std::stod(words[6]); + name = words[3]; + hasName = true; + hasPosition = false; + hasRotation = false; } } - if (words[1] == ".rotate") + if (words[0] == "setAttr") { - if (hasName && (!hasRotation)) + if (words[1] == ".translate") + { + if (hasName && (!hasPosition)) + { + hasPosition = true; + pos[0] = std::stod(words[4]); + pos[1] = std::stod(words[5]); + pos[2] = std::stod(words[6]); + } + } + + if (words[1] == ".rotate") { - hasRotation = true; - rot[0] = std::stod(words[4]); - rot[1] = std::stod(words[5]); - rot[2] = std::stod(words[6]); + if (hasName && (!hasRotation)) + { + hasRotation = true; + rot[0] = std::stod(words[4]); + rot[1] = std::stod(words[5]); + rot[2] = std::stod(words[6]); + } } } - } - if (hasName && hasRotation && hasPosition) - { - if (viewIdPerStem.count(name) == 0) + if (hasName && hasRotation && hasPosition) { - continue; - } + if (viewIdPerStem.count(name) == 0) + { + continue; + } - const IndexT viewId = viewIdPerStem[name]; - sfmData::View& view = sfmData.getView(viewId); - sfmData::CameraPose& pose = sfmData.getPoses()[view.getPoseId()]; + const IndexT viewId = viewIdPerStem[name]; + sfmData::View& view = sfmData.getView(viewId); + sfmData::CameraPose& pose = sfmData.getPoses()[view.getPoseId()]; + + Eigen::Matrix3d R = Eigen::Matrix3d::Identity(); + const Eigen::AngleAxis MX(degreeToRadian(rot[0]), Eigen::Vector3d::UnitX()); + const Eigen::AngleAxis MY(degreeToRadian(rot[1]), Eigen::Vector3d::UnitY()); + const Eigen::AngleAxis MZ(degreeToRadian(rot[2]), Eigen::Vector3d::UnitZ()); + R = MZ * MY * MX; - Eigen::Matrix3d R = Eigen::Matrix3d::Identity(); - const Eigen::AngleAxis MX(degreeToRadian(rot[0]), Eigen::Vector3d::UnitX()); - const Eigen::AngleAxis MY(degreeToRadian(rot[1]), Eigen::Vector3d::UnitY()); - const Eigen::AngleAxis MZ(degreeToRadian(rot[2]), Eigen::Vector3d::UnitZ()); - R = MZ * MY * MX; - - Eigen::Vector3d position; - position(0) = pos[0]; - position(1) = pos[1]; - position(2) = pos[2]; + Eigen::Vector3d position; + position(0) = pos[0]; + position(1) = pos[1]; + position(2) = pos[2]; - Vec3 translation = - R * position; + Vec3 translation = -R * position; - Eigen::Matrix3d alice_R_maya = Eigen::Matrix3d::Identity(); + Eigen::Matrix3d alice_R_maya = Eigen::Matrix3d::Identity(); - alice_R_maya(0, 0) = 1.0; - alice_R_maya(1, 1) = -1.0; - alice_R_maya(2, 2) = -1.0; - position = position; + alice_R_maya(0, 0) = 1.0; + alice_R_maya(1, 1) = -1.0; + alice_R_maya(2, 2) = -1.0; + position = position; - R = R * alice_R_maya; + R = R * alice_R_maya; - geometry::Pose3 pose3(R.transpose(), position); - pose.setTransform(pose3); - ALICEVISION_LOG_TRACE("Read maya: " << name); + geometry::Pose3 pose3(R.transpose(), position); + pose.setTransform(pose3); + ALICEVISION_LOG_TRACE("Read maya: " << name); - hasName = false; - hasRotation = false; - hasPosition = false; + hasName = false; + hasRotation = false; + hasPosition = false; + } } - } - } - } - - // export the SfMData scene in the expected format - if(!sfmDataIO::save(sfmData, outputFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("An error occured while trying to save '" << outputFilename << "'"); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + } + } + + // export the SfMData scene in the expected format + if (!sfmDataIO::save(sfmData, outputFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("An error occured while trying to save '" << outputFilename << "'"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportAnimatedCamera.cpp b/src/software/export/main_exportAnimatedCamera.cpp index 08d3e9998a..08e02f3f48 100644 --- a/src/software/export/main_exportAnimatedCamera.cpp +++ b/src/software/export/main_exportAnimatedCamera.cpp @@ -42,7 +42,7 @@ oiio::ROI computeRod(const camera::IntrinsicBase* intrinsic, bool correctPrincip pointToBeChecked.push_back(Vec2(intrinsic->w() - 1, intrinsic->h() - 1)); const Vec2 center(intrinsic->w() * 0.5, intrinsic->h() * 0.5); Vec2 ppCorrection(0, 0); - if(camera::EINTRINSIC::VALID_PINHOLE & intrinsic->getType()) + if (camera::EINTRINSIC::VALID_PINHOLE & intrinsic->getType()) { const camera::Pinhole* pinholePtr = dynamic_cast(intrinsic); ppCorrection = pinholePtr->getPrincipalPoint() - center; @@ -54,26 +54,23 @@ oiio::ROI computeRod(const camera::IntrinsicBase* intrinsic, bool correctPrincip pointToBeChecked.push_back(Vec2(intrinsic->w() - 1, opticalCenter[1])); std::vector maxDistortionVector; - for(const Vec2& n: pointToBeChecked) + for (const Vec2& n : pointToBeChecked) { // Undistort pixel without principal point correction const Vec2 n_undist = intrinsic->get_ud_pixel(n); maxDistortionVector.push_back(n_undist); } - std::sort(std::begin(maxDistortionVector), std::end(maxDistortionVector), - [](Vec2 a, Vec2 b) { return a[0] > b[0]; }); + std::sort(std::begin(maxDistortionVector), std::end(maxDistortionVector), [](Vec2 a, Vec2 b) { return a[0] > b[0]; }); const int xRoiMax = std::round(maxDistortionVector.front()[0]); const int xRoiMin = std::round(maxDistortionVector.back()[0]); - std::sort(std::begin(maxDistortionVector), std::end(maxDistortionVector), - [](Vec2 a, Vec2 b) { return a[1] > b[1]; }); + std::sort(std::begin(maxDistortionVector), std::end(maxDistortionVector), [](Vec2 a, Vec2 b) { return a[1] > b[1]; }); const int yRoiMax = std::round(maxDistortionVector.front()[1]); const int yRoiMin = std::round(maxDistortionVector.back()[1]); - oiio::ROI rod(xRoiMin, xRoiMax + 1, - yRoiMin, yRoiMax + 1); + oiio::ROI rod(xRoiMin, xRoiMax + 1, yRoiMin, yRoiMax + 1); - if(correctPrincipalPoint) + if (correctPrincipalPoint) { rod.xbegin -= ppCorrection(0); rod.xend -= ppCorrection(0); @@ -86,9 +83,8 @@ oiio::ROI computeRod(const camera::IntrinsicBase* intrinsic, bool correctPrincip oiio::ROI convertRodToRoi(const camera::IntrinsicBase* intrinsic, const oiio::ROI& rod) { const int xOffset = rod.xbegin; - const int yOffset = rod.ybegin; // (intrinsic->h() - rod.yend); - const oiio::ROI roi(-xOffset, intrinsic->w() - xOffset, - -yOffset, intrinsic->h() - yOffset); + const int yOffset = rod.ybegin; // (intrinsic->h() - rod.yend); + const oiio::ROI roi(-xOffset, intrinsic->w() - xOffset, -yOffset, intrinsic->h() - yOffset); ALICEVISION_LOG_DEBUG("roi:" << roi.xbegin << ";" << roi.xend << ";" << roi.ybegin << ";" << roi.yend); return roi; @@ -141,7 +137,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("AliceVision exportAnimatedCamera"); cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -150,31 +146,30 @@ int aliceVision_main(int argc, char** argv) const image::EImageFileType outputFileType = image::EImageFileType_stringToEnum(outImageFileTypeName); const image::EImageFileType outputMapFileType = image::EImageFileType_stringToEnum(outMapFileTypeName); - if(exportFullROD && outputFileType != image::EImageFileType::EXR) + if (exportFullROD && outputFileType != image::EImageFileType::EXR) { - ALICEVISION_LOG_ERROR("Export full RoD (Region Of Definition) is only possible in EXR file format and not in '" << - outputFileType << "'."); + ALICEVISION_LOG_ERROR("Export full RoD (Region Of Definition) is only possible in EXR file format and not in '" << outputFileType << "'."); return EXIT_FAILURE; } // Load SfMData files sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); return EXIT_FAILURE; } - if(sfmData.getViews().empty()) + if (sfmData.getViews().empty()) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' is empty."); return EXIT_FAILURE; } sfmData::SfMData sfmDataFilter; - if(!sfmDataFilterFilepath.empty()) + if (!sfmDataFilterFilepath.empty()) { - if(!sfmDataIO::load(sfmDataFilter, sfmDataFilterFilepath, sfmDataIO::ESfMData::VIEWS)) + if (!sfmDataIO::load(sfmDataFilter, sfmDataFilterFilepath, sfmDataIO::ESfMData::VIEWS)) { ALICEVISION_LOG_ERROR("The input filter SfMData file '" << sfmDataFilterFilepath << "' cannot be read."); return EXIT_FAILURE; @@ -184,31 +179,31 @@ int aliceVision_main(int argc, char** argv) // Decide the views and instrinsics to export sfmData::SfMData sfmDataExport; - for(auto& viewPair : sfmData.getViews()) + for (auto& viewPair : sfmData.getViews()) { sfmData::View& view = *(viewPair.second); // Regex filter - if(!viewFilter.empty()) + if (!viewFilter.empty()) { // Skip the view if it does not match the expression filter const std::regex regexFilter = utils::filterToRegex(viewFilter); - if(!std::regex_match(view.getImage().getImagePath(), regexFilter)) + if (!std::regex_match(view.getImage().getImagePath(), regexFilter)) continue; } // SfMData filter - if(!sfmDataFilterFilepath.empty()) + if (!sfmDataFilterFilepath.empty()) { // Skip the view if it exists in the sfmDataFilter - if(sfmDataFilter.getViews().find(view.getViewId()) != sfmDataFilter.getViews().end()) + if (sfmDataFilter.getViews().find(view.getViewId()) != sfmDataFilter.getViews().end()) continue; } sfmDataExport.getViews().emplace(view.getViewId(), viewPair.second); // Export intrinsics with at least one view with a valid pose - if(sfmData.isPoseAndIntrinsicDefined(&view)) + if (sfmData.isPoseAndIntrinsicDefined(&view)) { // std::map::emplace does nothing if the key already exist sfmDataExport.getIntrinsics().emplace(view.getIntrinsicId(), sfmData.getIntrinsics().at(view.getIntrinsicId())); @@ -218,16 +213,16 @@ int aliceVision_main(int argc, char** argv) const fs::path undistortedImagesFolderPath = fs::path(outFolder) / "undistort"; const bool writeUndistordedResult = undistortedImages || exportUVMaps; - if(writeUndistordedResult && !fs::exists(undistortedImagesFolderPath)) + if (writeUndistordedResult && !fs::exists(undistortedImagesFolderPath)) fs::create_directory(undistortedImagesFolderPath); std::map> videoViewPerFrame; - std::map> > dslrViewPerKey; + std::map>> dslrViewPerKey; // Export distortion map / one image per intrinsic - if(exportUVMaps) + if (exportUVMaps) { - for(const auto& intrinsicPair : sfmDataExport.getIntrinsics()) + for (const auto& intrinsicPair : sfmDataExport.getIntrinsics()) { const camera::IntrinsicBase& intrinsic = *(intrinsicPair.second); image::Image image_dist; @@ -238,20 +233,20 @@ int aliceVision_main(int argc, char** argv) const Vec2 center(intrinsic.w() * 0.5, intrinsic.h() * 0.5); Vec2 ppCorrection(0.0, 0.0); - if((camera::EINTRINSIC::VALID_PINHOLE & intrinsic.getType()) && correctPrincipalPoint)// correct principal point + if ((camera::EINTRINSIC::VALID_PINHOLE & intrinsic.getType()) && correctPrincipalPoint) // correct principal point { const camera::Pinhole* pinholePtr = dynamic_cast(intrinsicPair.second.get()); ppCorrection = pinholePtr->getPrincipalPoint() - center; } - ALICEVISION_LOG_DEBUG("ppCorrection:" + std::to_string(ppCorrection[0]) + ";" +std::to_string(ppCorrection[1])); + ALICEVISION_LOG_DEBUG("ppCorrection:" + std::to_string(ppCorrection[0]) + ";" + std::to_string(ppCorrection[1])); // UV Map: Undistort { - // Flip and normalize as UVMap - #pragma omp parallel for - for(int y = 0; y < int(intrinsic.h()); ++y) +// Flip and normalize as UVMap +#pragma omp parallel for + for (int y = 0; y < int(intrinsic.h()); ++y) { - for(int x = 0; x < int(intrinsic.w()); ++x) + for (int x = 0; x < int(intrinsic.w()); ++x) { const Vec2 undisto_pix(x, y); // Compute coordinates with distortion @@ -262,19 +257,19 @@ int aliceVision_main(int argc, char** argv) } } - const std::string dstImage = - (undistortedImagesFolderPath / (std::to_string(intrinsicPair.first) + "_UVMap_Undistort." + - image::EImageFileType_enumToString(outputMapFileType))).string(); + const std::string dstImage = (undistortedImagesFolderPath / (std::to_string(intrinsicPair.first) + "_UVMap_Undistort." + + image::EImageFileType_enumToString(outputMapFileType))) + .string(); image::writeImage(dstImage, image_dist, image::ImageWriteOptions()); } // UV Map: Distort { - // Flip and normalize as UVMap - #pragma omp parallel for - for(int y = 0; y < int(intrinsic.h()); ++y) +// Flip and normalize as UVMap +#pragma omp parallel for + for (int y = 0; y < int(intrinsic.h()); ++y) { - for(int x = 0; x < int(intrinsic.w()); ++x) + for (int x = 0; x < int(intrinsic.w()); ++x) { const Vec2 disto_pix(x, y); // Compute coordinates without distortion @@ -285,9 +280,9 @@ int aliceVision_main(int argc, char** argv) } } - const std::string dstImage = - (undistortedImagesFolderPath / (std::to_string(intrinsicPair.first) + "_UVMap_Distort." + - image::EImageFileType_enumToString(outputMapFileType))).string(); + const std::string dstImage = (undistortedImagesFolderPath / (std::to_string(intrinsicPair.first) + "_UVMap_Distort." + + image::EImageFileType_enumToString(outputMapFileType))) + .string(); image::writeImage(dstImage, image_dist, image::ImageWriteOptions()); } } @@ -296,10 +291,9 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Build animated camera(s)..."); image::Image image, image_ud; - auto progressDisplay = system::createConsoleProgressDisplay(sfmDataExport.getViews().size(), - std::cout); + auto progressDisplay = system::createConsoleProgressDisplay(sfmDataExport.getViews().size(), std::cout); - for(const auto& viewPair : sfmDataExport.getViews()) + for (const auto& viewPair : sfmDataExport.getViews()) { const sfmData::View& view = *(viewPair.second); @@ -308,25 +302,27 @@ int aliceVision_main(int argc, char** argv) const std::string imagePathStem = fs::path(viewPair.second->getImage().getImagePath()).stem().string(); // Undistort camera images - if(undistortedImages) + if (undistortedImages) { sfmData::Intrinsics::const_iterator iterIntrinsic = sfmDataExport.getIntrinsics().find(view.getIntrinsicId()); - const std::string dstImage = (undistortedImagesFolderPath / (std::to_string(view.getIntrinsicId()) + "_" + imagePathStem + "." + image::EImageFileType_enumToString(outputFileType))).string(); - const camera::IntrinsicBase * cam = iterIntrinsic->second.get(); + const std::string dstImage = (undistortedImagesFolderPath / (std::to_string(view.getIntrinsicId()) + "_" + imagePathStem + "." + + image::EImageFileType_enumToString(outputFileType))) + .string(); + const camera::IntrinsicBase* cam = iterIntrinsic->second.get(); image::readImage(view.getImage().getImagePath(), image, image::EImageColorSpace::LINEAR); oiio::ParamValueList metadata = image::readImageMetadata(view.getImage().getImagePath()); - if(cam->isValid() && cam->hasDistortion()) + if (cam->isValid() && cam->hasDistortion()) { // Undistort the image and save it - if(exportFullROD) + if (exportFullROD) { // Build a ROI const IndexT key = view.getIntrinsicId(); oiio::ROI rod; - const camera::IntrinsicBase &intrinsic = (*cam); - if(roiForIntrinsic.find(key) == roiForIntrinsic.end()) + const camera::IntrinsicBase& intrinsic = (*cam); + if (roiForIntrinsic.find(key) == roiForIntrinsic.end()) { rod = computeRod(cam, correctPrincipalPoint); roiForIntrinsic[key] = rod; @@ -336,8 +332,8 @@ int aliceVision_main(int argc, char** argv) rod = roiForIntrinsic[key]; } - ALICEVISION_LOG_DEBUG("rod:" + std::to_string(rod.xbegin) + ";" + std::to_string(rod.xend) + ";" + - std::to_string(rod.ybegin) + ";" + std::to_string(rod.yend)); + ALICEVISION_LOG_DEBUG("rod:" + std::to_string(rod.xbegin) + ";" + std::to_string(rod.xend) + ";" + std::to_string(rod.ybegin) + + ";" + std::to_string(rod.yend)); camera::UndistortImage(image, cam, image_ud, image::FBLACK, correctPrincipalPoint, rod); const oiio::ROI roi = convertRodToRoi(cam, rod); writeImage(dstImage, image_ud, image::ImageWriteOptions(), oiio::ParamValueList(), roi); @@ -348,7 +344,7 @@ int aliceVision_main(int argc, char** argv) image::writeImage(dstImage, image_ud, image::ImageWriteOptions(), metadata); } } - else // No distortion + else // No distortion { // Copy the image since there is no distortion image::writeImage(dstImage, image, image::ImageWriteOptions(), metadata); @@ -357,23 +353,23 @@ int aliceVision_main(int argc, char** argv) // Pose and intrinsic defined // Note: we use "sfmData" and not "sfmDataExport" to have access to poses - if(!sfmData.isPoseAndIntrinsicDefined(&view)) + if (!sfmData.isPoseAndIntrinsicDefined(&view)) continue; - std::string cameraName = view.getImage().getMetadataMake() + "_" + view.getImage().getMetadataModel(); + std::string cameraName = view.getImage().getMetadataMake() + "_" + view.getImage().getMetadataModel(); IndexT frameN = 0; bool isSequence = false; - if(view.isPartOfRig()) + if (view.isPartOfRig()) cameraName += std::string("_") + std::to_string(view.getSubPoseId()); { std::string prefix; std::string suffix; - if(sfmDataIO::extractNumberFromFileStem(imagePathStem, frameN, prefix, suffix)) + if (sfmDataIO::extractNumberFromFileStem(imagePathStem, frameN, prefix, suffix)) { - if(prefix.empty() && suffix.empty()) + if (prefix.empty() && suffix.empty()) cameraName = std::string("Undefined") + "_" + cameraName; else cameraName = prefix + "frame" + suffix + "_" + cameraName; @@ -382,21 +378,21 @@ int aliceVision_main(int argc, char** argv) } } - ALICEVISION_LOG_TRACE("imagePathStem: " << imagePathStem << ", frameN: " << frameN << - ", isSequence: " << isSequence << ", cameraName: " << cameraName); + ALICEVISION_LOG_TRACE("imagePathStem: " << imagePathStem << ", frameN: " << frameN << ", isSequence: " << isSequence + << ", cameraName: " << cameraName); - if(isSequence) // Video + if (isSequence) // Video { const std::size_t frame = frameN; videoViewPerFrame[cameraName][frame] = view.getViewId(); } - else if(view.getImage().hasMetadataDateTimeOriginal()) // Picture + else if (view.getImage().hasMetadataDateTimeOriginal()) // Picture { const std::size_t key = view.getImage().getMetadataDateTimestamp(); dslrViewPerKey[cameraName].push_back({key, view.getViewId()}); } - else // No time or sequence information + else // No time or sequence information { dslrViewPerKey[cameraName].push_back({0, view.getViewId()}); } @@ -408,24 +404,21 @@ int aliceVision_main(int argc, char** argv) ss << "Camera(s) found:" << std::endl << "\t- # video camera(s): " << videoViewPerFrame.size() << std::endl; - for(const auto& camera : videoViewPerFrame) + for (const auto& camera : videoViewPerFrame) ss << "\t - " << camera.first << " | " << camera.second.size() << " frame(s)" << std::endl; ss << "\t- # DSLR camera(s): " << dslrViewPerKey.size() << std::endl; - for(const auto& camera : dslrViewPerKey) + for (const auto& camera : dslrViewPerKey) ss << "\t - " << camera.first << " | " << camera.second.size() << " image(s)" << std::endl; ss << "\t- # Used camera intrinsics: " << sfmDataExport.getIntrinsics().size() << std::endl; - for(const auto& intrinsicIt : sfmDataExport.getIntrinsics()) + for (const auto& intrinsicIt : sfmDataExport.getIntrinsics()) { const auto intrinsic = intrinsicIt.second; - ss << "\t - " - << intrinsicIt.first << " | " - << intrinsic->w() << "x" << intrinsic->h() - << " " << intrinsic->serialNumber() - << std::endl; + ss << "\t - " << intrinsicIt.first << " | " << intrinsic->w() << "x" << intrinsic->h() << " " << intrinsic->serialNumber() + << std::endl; } ALICEVISION_LOG_INFO(ss.str()); @@ -435,18 +428,18 @@ int aliceVision_main(int argc, char** argv) sfmDataIO::AlembicExporter exporter((fs::path(outFolder) / "camera.abc").string()); - for(const auto& cameraViews : videoViewPerFrame) + for (const auto& cameraViews : videoViewPerFrame) { const std::map& frameToView = cameraViews.second; const std::size_t firstFrame = cameraViews.second.begin()->first; exporter.initAnimatedCamera(cameraViews.first, firstFrame); - for(std::size_t frame = firstFrame; frame <= frameToView.rbegin()->first; ++frame) + for (std::size_t frame = firstFrame; frame <= frameToView.rbegin()->first; ++frame) { const auto findFrameIt = frameToView.find(frame); - if(findFrameIt != frameToView.end()) + if (findFrameIt != frameToView.end()) { const IndexT viewId = findFrameIt->second; @@ -455,12 +448,15 @@ int aliceVision_main(int argc, char** argv) const auto findViewIt = sfmData.getViews().find(viewId); assert(findViewIt != sfmData.getViews().end()); - ALICEVISION_LOG_DEBUG("[" + cameraViews.first +"][video] Keyframe added"); + ALICEVISION_LOG_DEBUG("[" + cameraViews.first + "][video] Keyframe added"); const IndexT intrinsicId = findViewIt->second->getIntrinsicId(); const camera::Pinhole* cam = dynamic_cast(sfmData.getIntrinsicPtr(intrinsicId)); const sfmData::CameraPose pose = sfmData.getPose(*findViewIt->second); const std::string& imagePath = findViewIt->second->getImage().getImagePath(); - const std::string undistortedImagePath = (undistortedImagesFolderPath / (std::to_string(intrinsicId) + "_" + fs::path(imagePath).stem().string() + "." + image::EImageFileType_enumToString(outputFileType))).string(); + const std::string undistortedImagePath = + (undistortedImagesFolderPath / (std::to_string(intrinsicId) + "_" + fs::path(imagePath).stem().string() + "." + + image::EImageFileType_enumToString(outputFileType))) + .string(); exporter.addCameraKeyframe(pose.getTransform(), cam, (undistortedImages) ? undistortedImagePath : imagePath, viewId, intrinsicId); } @@ -471,21 +467,25 @@ int aliceVision_main(int argc, char** argv) } } - for(auto& cameraViews : dslrViewPerKey) + for (auto& cameraViews : dslrViewPerKey) { exporter.initAnimatedCamera(cameraViews.first); std::sort(cameraViews.second.begin(), cameraViews.second.end()); - for(const auto& cameraView : cameraViews.second) + for (const auto& cameraView : cameraViews.second) { - ALICEVISION_LOG_DEBUG("[" + cameraViews.first +"][dslr] Keyframe added"); + ALICEVISION_LOG_DEBUG("[" + cameraViews.first + "][dslr] Keyframe added"); const sfmData::View& view = *(sfmData.getViews().at(cameraView.second)); const camera::Pinhole* cam = dynamic_cast(sfmData.getIntrinsicPtr(view.getIntrinsicId())); const sfmData::CameraPose pose = sfmData.getPose(view); const std::string& imagePath = view.getImage().getImagePath(); - const std::string undistortedImagePath = (undistortedImagesFolderPath / (std::to_string(view.getIntrinsicId()) + "_" + fs::path(imagePath).stem().string() + "." + image::EImageFileType_enumToString(outputFileType))).string(); + const std::string undistortedImagePath = + (undistortedImagesFolderPath / (std::to_string(view.getIntrinsicId()) + "_" + fs::path(imagePath).stem().string() + "." + + image::EImageFileType_enumToString(outputFileType))) + .string(); - exporter.addCameraKeyframe(pose.getTransform(), cam, (undistortedImages) ? undistortedImagePath : imagePath, view.getViewId(), view.getIntrinsicId()); + exporter.addCameraKeyframe( + pose.getTransform(), cam, (undistortedImages) ? undistortedImagePath : imagePath, view.getViewId(), view.getIntrinsicId()); } } diff --git a/src/software/export/main_exportColmap.cpp b/src/software/export/main_exportColmap.cpp index dd07ebeaa9..96619a5d7b 100644 --- a/src/software/export/main_exportColmap.cpp +++ b/src/software/export/main_exportColmap.cpp @@ -4,7 +4,6 @@ // 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 @@ -16,7 +15,6 @@ #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 1 @@ -27,7 +25,6 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; - int aliceVision_main(int argc, char* argv[]) { std::string sfmDataFilename; @@ -56,14 +53,14 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - if(!fs::exists(outDirectory)) + if (!fs::exists(outDirectory)) { fs::create_directory(outDirectory); } // Read the input SfM scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("Unable to read the sfmdata file " << sfmDataFilename); return EXIT_FAILURE; diff --git a/src/software/export/main_exportColoredPointCloud.cpp b/src/software/export/main_exportColoredPointCloud.cpp index c4a0b83c98..5a03f4b1fd 100644 --- a/src/software/export/main_exportColoredPointCloud.cpp +++ b/src/software/export/main_exportColoredPointCloud.cpp @@ -28,13 +28,13 @@ using namespace aliceVision; namespace po = boost::program_options; // Convert from a SfMData format to another -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters + // command-line parameters - std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); - std::string sfmDataFilename; - std::string outputSfMDataFilename; + std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); + std::string sfmDataFilename; + std::string outputSfMDataFilename; // clang-format off po::options_description requiredParams("Required parameters"); @@ -45,38 +45,37 @@ int aliceVision_main(int argc, char **argv) "Output point cloud with visibilities as SfMData file."); // clang-format on - CmdLine cmdline("AliceVision exportColoredPointCloud"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - if(outputSfMDataFilename.empty()) - { - ALICEVISION_LOG_ERROR("No output filename specified."); - return EXIT_FAILURE; - } - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - // compute the scene structure color - sfmData::colorizeTracks(sfmData); - - // export the SfMData scene in the expected format - ALICEVISION_LOG_INFO("Saving output result to " << outputSfMDataFilename << "..."); - if(!sfmDataIO::save(sfmData, outputSfMDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The output SfMData file '" + sfmDataFilename + "' cannot be save."); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + CmdLine cmdline("AliceVision exportColoredPointCloud"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (outputSfMDataFilename.empty()) + { + ALICEVISION_LOG_ERROR("No output filename specified."); + return EXIT_FAILURE; + } + + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; + } + + // compute the scene structure color + sfmData::colorizeTracks(sfmData); + + // export the SfMData scene in the expected format + ALICEVISION_LOG_INFO("Saving output result to " << outputSfMDataFilename << "..."); + if (!sfmDataIO::save(sfmData, outputSfMDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The output SfMData file '" + sfmDataFilename + "' cannot be save."); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportDistortion.cpp b/src/software/export/main_exportDistortion.cpp index 7e93101d35..a9a4098765 100644 --- a/src/software/export/main_exportDistortion.cpp +++ b/src/software/export/main_exportDistortion.cpp @@ -38,34 +38,43 @@ std::string toNuke(std::shared_ptr undistortion, EINTRINSIC intrin switch (intrinsicType) { - case EINTRINSIC::PINHOLE_CAMERA_3DEANAMORPHIC4: - ss << "LensDistortion2 {" << "\n" - << " distortionModelPreset \"3DEqualizer/3DE4 Anamorphic - Standard, Degree 4\"" << "\n" - << " lens Anamorphic" << "\n" - << " distortionNumeratorX00 " << params[0] << "\n" - << " distortionNumeratorX01 " << params[4] << "\n" - << " distortionNumeratorX10 " << params[2] << "\n" - << " distortionNumeratorX11 " << params[6] << "\n" - << " distortionNumeratorX20 " << params[8] << "\n" - << " distortionNumeratorY00 " << params[1] << "\n" - << " distortionNumeratorY01 " << params[5] << "\n" - << " distortionNumeratorY10 " << params[3] << "\n" - << " distortionNumeratorY11 " << params[7] << "\n" - << " distortionNumeratorY20 " << params[9] << "\n" - << " output Undistort" << "\n" - << " distortionScalingType Format" << "\n" - << " distortionScalingFormat \"" - << size(0) << " " << size(1) << " 0 0 " - << size(0) << " " << size(1) << " 1 AV_undist_fmt \"" << "\n" - << " distortionModelType \"Radial Asymmetric\"" << "\n" - << " distortionOrder {2 0}" << "\n" - << " normalisationType Diagonal" << "\n" - << " distortInFisheyeSpace false" << "\n" - << "}" << "\n"; - break; - default: - ALICEVISION_THROW_ERROR("Unsupported intrinsic type for conversion to Nuke LensDistortion node: " - << EINTRINSIC_enumToString(intrinsicType)); + case EINTRINSIC::PINHOLE_CAMERA_3DEANAMORPHIC4: + ss << "LensDistortion2 {" + << "\n" + << " distortionModelPreset \"3DEqualizer/3DE4 Anamorphic - Standard, Degree 4\"" + << "\n" + << " lens Anamorphic" + << "\n" + << " distortionNumeratorX00 " << params[0] << "\n" + << " distortionNumeratorX01 " << params[4] << "\n" + << " distortionNumeratorX10 " << params[2] << "\n" + << " distortionNumeratorX11 " << params[6] << "\n" + << " distortionNumeratorX20 " << params[8] << "\n" + << " distortionNumeratorY00 " << params[1] << "\n" + << " distortionNumeratorY01 " << params[5] << "\n" + << " distortionNumeratorY10 " << params[3] << "\n" + << " distortionNumeratorY11 " << params[7] << "\n" + << " distortionNumeratorY20 " << params[9] << "\n" + << " output Undistort" + << "\n" + << " distortionScalingType Format" + << "\n" + << " distortionScalingFormat \"" << size(0) << " " << size(1) << " 0 0 " << size(0) << " " << size(1) << " 1 AV_undist_fmt \"" + << "\n" + << " distortionModelType \"Radial Asymmetric\"" + << "\n" + << " distortionOrder {2 0}" + << "\n" + << " normalisationType Diagonal" + << "\n" + << " distortInFisheyeSpace false" + << "\n" + << "}" + << "\n"; + break; + default: + ALICEVISION_THROW_ERROR( + "Unsupported intrinsic type for conversion to Nuke LensDistortion node: " << EINTRINSIC_enumToString(intrinsicType)); } return ss.str(); @@ -89,22 +98,18 @@ void toSTMap(image::Image& stmap, } stmap.resize(widthRoi, heightRoi, true, image::RGBAfColor(0.0f)); - - #pragma omp parallel for + +#pragma omp parallel for for (int i = 0; i < heightRoi; ++i) { for (int j = 0; j < widthRoi; ++j) { const Vec2 pix((j + xOffset), (i + yOffset)); - const Vec2 disto_pix - = distort ? intrinsic->get_ud_pixel(pix) : intrinsic->get_d_pixel(pix); - - stmap(i, j).b() - = disto_pix[0] / (static_cast(intrinsic->w()) - 1.0f); - stmap(i, j).a() - = (static_cast(intrinsic->h()) - 1.0f - disto_pix[1]) - / (static_cast(intrinsic->h()) - 1.0f); + const Vec2 disto_pix = distort ? intrinsic->get_ud_pixel(pix) : intrinsic->get_d_pixel(pix); + + stmap(i, j).b() = disto_pix[0] / (static_cast(intrinsic->w()) - 1.0f); + stmap(i, j).a() = (static_cast(intrinsic->h()) - 1.0f - disto_pix[1]) / (static_cast(intrinsic->h()) - 1.0f); stmap(i, j).r() = stmap(i, j).b(); stmap(i, j).g() = stmap(i, j).a(); } @@ -137,7 +142,7 @@ int aliceVision_main(int argc, char* argv[]) ("exportLensGridsUndistorted,e", po::value(&exportLensGridsUndistorted)->default_value(exportLensGridsUndistorted), "Export lens grids undistorted for validation."); // clang-format on - + CmdLine cmdline("AliceVision exportDistortion"); cmdline.add(requiredParams); cmdline.add(optionalParams); @@ -148,7 +153,7 @@ int aliceVision_main(int argc, char* argv[]) } sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + if (!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmInputDataFilepath << "' cannot be read."); return EXIT_FAILURE; @@ -159,12 +164,14 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Exporting distortion for intrinsic " << intrinsicId); const auto intrinsicDisto = std::dynamic_pointer_cast(intrinsicPtr); - if (!intrinsicDisto) continue; + if (!intrinsicDisto) + continue; const auto undistortion = intrinsicDisto->getUndistortion(); - if (!undistortion) continue; + if (!undistortion) + continue; - if(exportNukeNode) + if (exportNukeNode) { ALICEVISION_LOG_INFO("Computing Nuke LensDistortion node"); @@ -178,7 +185,7 @@ int aliceVision_main(int argc, char* argv[]) of.close(); } - if(exportSTMaps) + if (exportSTMaps) { ALICEVISION_LOG_INFO("Computing STMaps"); @@ -205,21 +212,21 @@ int aliceVision_main(int argc, char* argv[]) if (exportLensGridsUndistorted) { - for (const auto & pv : sfmData.getViews()) + for (const auto& pv : sfmData.getViews()) { if (pv.second->getIntrinsicId() == intrinsicId) { - //Read image + // Read image image::Image image; std::string path = pv.second->getImageInfo()->getImagePath(); image::readImage(path, image, image::EImageColorSpace::LINEAR); oiio::ParamValueList metadata = image::readImageMetadata(path); - - //Undistort + + // Undistort image::Image image_ud; camera::UndistortImage(image, intrinsicPtr.get(), image_ud, image::FBLACK, false); - //Save undistorted + // Save undistorted std::stringstream ss; ss << outputFilePath << "/lensgrid_" << pv.first << "_undistort.exr"; ALICEVISION_LOG_INFO("Export lens grid undistorted (Source image: " << path << ", Undistorted image: " << ss.str() << ")."); diff --git a/src/software/export/main_exportKeypoints.cpp b/src/software/export/main_exportKeypoints.cpp index e351e98ad9..796b3aede9 100644 --- a/src/software/export/main_exportKeypoints.cpp +++ b/src/software/export/main_exportKeypoints.cpp @@ -42,16 +42,16 @@ using namespace svg; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char ** argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; - std::vector featuresFolders; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; + std::vector featuresFolders; - // user optional parameters + // user optional parameters - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); // clang-format off po::options_description requiredParams("Required parameters"); @@ -69,66 +69,63 @@ int aliceVision_main(int argc, char ** argv) feature::EImageDescriberType_informations().c_str()); // clang-format on - CmdLine cmdline("AliceVision exportKeypoints"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(outputFolder.empty()) - { - ALICEVISION_LOG_ERROR("Invalid output folder"); - return EXIT_FAILURE; - } - - // read SfM Scene (image view names) - - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } - - // load SfM Scene regions - // get imageDescriberMethodType - std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - - // read the features - feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid features"); - return EXIT_FAILURE; - } - - // for each image, export visually the keypoints - - fs::create_directory(outputFolder); - ALICEVISION_LOG_INFO("Export extracted keypoints for all images"); - auto myProgressBar = system::createConsoleProgressDisplay(sfmData.getViews().size(), std::cout); - for(const auto &iterViews : sfmData.getViews()) - { - const View * view = iterViews.second.get(); - const std::string viewImagePath = view->getImage().getImagePath(); - - const std::pair - dimImage = std::make_pair(view->getImage().getWidth(), view->getImage().getHeight()); - - const MapFeaturesPerDesc& features = featuresPerView.getData().at(view->getViewId()); - - // output filename - fs::path outputFilename = fs::path(outputFolder) / std::string(std::to_string(view->getViewId()) + "_" + std::to_string(features.size()) + ".svg"); - - matching::saveFeatures2SVG(viewImagePath, - dimImage, - featuresPerView.getData().at(view->getViewId()), - outputFilename.string()); - - ++myProgressBar; - } - - return EXIT_SUCCESS; + CmdLine cmdline("AliceVision exportKeypoints"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (outputFolder.empty()) + { + ALICEVISION_LOG_ERROR("Invalid output folder"); + return EXIT_FAILURE; + } + + // read SfM Scene (image view names) + + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } + + // load SfM Scene regions + // get imageDescriberMethodType + std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); + + // read the features + feature::FeaturesPerView featuresPerView; + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid features"); + return EXIT_FAILURE; + } + + // for each image, export visually the keypoints + + fs::create_directory(outputFolder); + ALICEVISION_LOG_INFO("Export extracted keypoints for all images"); + auto myProgressBar = system::createConsoleProgressDisplay(sfmData.getViews().size(), std::cout); + for (const auto& iterViews : sfmData.getViews()) + { + const View* view = iterViews.second.get(); + const std::string viewImagePath = view->getImage().getImagePath(); + + const std::pair dimImage = std::make_pair(view->getImage().getWidth(), view->getImage().getHeight()); + + const MapFeaturesPerDesc& features = featuresPerView.getData().at(view->getViewId()); + + // output filename + fs::path outputFilename = + fs::path(outputFolder) / std::string(std::to_string(view->getViewId()) + "_" + std::to_string(features.size()) + ".svg"); + + matching::saveFeatures2SVG(viewImagePath, dimImage, featuresPerView.getData().at(view->getViewId()), outputFilename.string()); + + ++myProgressBar; + } + + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportMVE2.cpp b/src/software/export/main_exportMVE2.cpp index b4d82fc2d3..e1aa9d4a93 100644 --- a/src/software/export/main_exportMVE2.cpp +++ b/src/software/export/main_exportMVE2.cpp @@ -37,14 +37,8 @@ namespace po = boost::program_options; namespace fs = std::filesystem; /// Naive image bilinear resampling of an image for thumbnail generation -template -ImageT -create_thumbnail -( - const ImageT & image, - int thumb_width, - int thumb_height -); +template +ImageT create_thumbnail(const ImageT& image, int thumb_width, int thumb_height); /* Notes: * - An MVE2 scene appears to duplicate camera rot matrix and trans vector per-view data in 'meta.ini' @@ -58,200 +52,188 @@ create_thumbnail * https://github.com/simonfuhrmann/mve/wiki/MVE-File-Format */ -bool exportToMVE2Format( - const SfMData & sfm_data, - const std::string & sOutDirectory // Output MVE2 files folder - ) +bool exportToMVE2Format(const SfMData& sfm_data, + const std::string& sOutDirectory // Output MVE2 files folder +) { - bool bOk = true; - // Create basis folder structure - if (!fs::is_directory(sOutDirectory)) - { - std::cout << "\033[1;31mCreating folder: " << sOutDirectory << "\033[0m\n"; - fs::create_directory(sOutDirectory); - bOk = fs::is_directory(sOutDirectory); - } - - if (!bOk) - { - std::cerr << "Cannot access one of the desired output directories" << std::endl; - return false; - } - - // Export the SfMData scene to the MVE2 format - { - // Create 'views' subfolder - const std::string sOutViewsDirectory = (fs::path(sOutDirectory) / "views").string(); - if (!fs::exists(sOutViewsDirectory)) + bool bOk = true; + // Create basis folder structure + if (!fs::is_directory(sOutDirectory)) + { + std::cout << "\033[1;31mCreating folder: " << sOutDirectory << "\033[0m\n"; + fs::create_directory(sOutDirectory); + bOk = fs::is_directory(sOutDirectory); + } + + if (!bOk) { - std::cout << "\033[1;31mCreating folder: " << sOutViewsDirectory << "\033[0m\n"; - fs::create_directory(sOutViewsDirectory); + std::cerr << "Cannot access one of the desired output directories" << std::endl; + return false; } - // Prepare to write bundle file - // Get cameras and features from AliceVision - size_t cameraCount = 0; - for(const auto& view: sfm_data.getViews()) - if(sfm_data.isPoseAndIntrinsicDefined(view.second.get())) - ++cameraCount; - // Tally global set of feature landmarks - const Landmarks & landmarks = sfm_data.getLandmarks(); - const size_t featureCount = std::distance(landmarks.begin(), landmarks.end()); - const std::string filename = "synth_0.out"; - std::cout << "Writing bundle (" << cameraCount << " cameras, " - << featureCount << " features): to " << filename << "...\n"; - std::ofstream out((fs::path(sOutDirectory) / filename).string()); - out << "drews 1.0\n"; // MVE expects this header - out << cameraCount << " " << featureCount << "\n"; - - // Export (calibrated) views as undistorted images - auto progressDisplay = system::createConsoleProgressDisplay(sfm_data.getViews().size(), std::cout); - std::pair w_h_image_size; - Image image, image_ud, thumbnail; - std::string sOutViewIteratorDirectory; - std::size_t view_index = 0; - std::map viewIdToviewIndex; - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) + // Export the SfMData scene to the MVE2 format { - const View * view = iter->second.get(); - - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - viewIdToviewIndex[view->getViewId()] = view_index; - // Create current view subfolder 'view_xxxx.mve' - std::ostringstream padding; - // Warning: We use view_index instead of view->getViewId() because MVE use indexes instead of IDs. - padding << std::setw(4) << std::setfill('0') << view_index; - - sOutViewIteratorDirectory = (fs::path(sOutViewsDirectory) / ("view_" + padding.str() + ".mve")).string(); - if (!fs::exists(sOutViewIteratorDirectory)) - { - fs::create_directory(sOutViewIteratorDirectory); - } - - // We have a valid view with a corresponding camera & pose - const std::string srcImage = view->getImage().getImagePath(); - const std::string dstImage = (fs::path(sOutViewIteratorDirectory) / "undistorted.png").string(); - - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - const IntrinsicBase * cam = iterIntrinsic->second.get(); - if (cam->isValid() && cam->hasDistortion()) - { - // Undistort and save the image - readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); - UndistortImage(image, cam, image_ud, BLACK); - writeImage(dstImage, image_ud, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); - } - else // (no distortion) - { - // If extensions match, copy the PNG image - if (fs::path(srcImage).extension() == ".PNG" || - fs::path(srcImage).extension() == ".png") + // Create 'views' subfolder + const std::string sOutViewsDirectory = (fs::path(sOutDirectory) / "views").string(); + if (!fs::exists(sOutViewsDirectory)) { - fs::copy_file(srcImage, dstImage); + std::cout << "\033[1;31mCreating folder: " << sOutViewsDirectory << "\033[0m\n"; + fs::create_directory(sOutViewsDirectory); } - else + + // Prepare to write bundle file + // Get cameras and features from AliceVision + size_t cameraCount = 0; + for (const auto& view : sfm_data.getViews()) + if (sfm_data.isPoseAndIntrinsicDefined(view.second.get())) + ++cameraCount; + // Tally global set of feature landmarks + const Landmarks& landmarks = sfm_data.getLandmarks(); + const size_t featureCount = std::distance(landmarks.begin(), landmarks.end()); + const std::string filename = "synth_0.out"; + std::cout << "Writing bundle (" << cameraCount << " cameras, " << featureCount << " features): to " << filename << "...\n"; + std::ofstream out((fs::path(sOutDirectory) / filename).string()); + out << "drews 1.0\n"; // MVE expects this header + out << cameraCount << " " << featureCount << "\n"; + + // Export (calibrated) views as undistorted images + auto progressDisplay = system::createConsoleProgressDisplay(sfm_data.getViews().size(), std::cout); + std::pair w_h_image_size; + Image image, image_ud, thumbnail; + std::string sOutViewIteratorDirectory; + std::size_t view_index = 0; + std::map viewIdToviewIndex; + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) { - readImage( srcImage, image, image::EImageColorSpace::NO_CONVERSION); - writeImage(dstImage, image, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + const View* view = iter->second.get(); + + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + viewIdToviewIndex[view->getViewId()] = view_index; + // Create current view subfolder 'view_xxxx.mve' + std::ostringstream padding; + // Warning: We use view_index instead of view->getViewId() because MVE use indexes instead of IDs. + padding << std::setw(4) << std::setfill('0') << view_index; + + sOutViewIteratorDirectory = (fs::path(sOutViewsDirectory) / ("view_" + padding.str() + ".mve")).string(); + if (!fs::exists(sOutViewIteratorDirectory)) + { + fs::create_directory(sOutViewIteratorDirectory); + } + + // We have a valid view with a corresponding camera & pose + const std::string srcImage = view->getImage().getImagePath(); + const std::string dstImage = (fs::path(sOutViewIteratorDirectory) / "undistorted.png").string(); + + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + const IntrinsicBase* cam = iterIntrinsic->second.get(); + if (cam->isValid() && cam->hasDistortion()) + { + // Undistort and save the image + readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); + UndistortImage(image, cam, image_ud, BLACK); + writeImage(dstImage, image_ud, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + } + else // (no distortion) + { + // If extensions match, copy the PNG image + if (fs::path(srcImage).extension() == ".PNG" || fs::path(srcImage).extension() == ".png") + { + fs::copy_file(srcImage, dstImage); + } + else + { + readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); + writeImage(dstImage, image, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + } + } + + // Prepare to write an MVE 'meta.ini' file for the current view + const Pose3 pose = sfm_data.getPose(*view).getTransform(); + const Pinhole* pinhole_cam = static_cast(cam); + + const Mat3& rotation = pose.rotation(); + const Vec3& translation = pose.translation(); + + // Focal length and principal point must be normalized (0..1) + const float flen = pinhole_cam->getFocalLengthPixX() / static_cast(std::max(cam->w(), cam->h())); + const float pixelAspect = pinhole_cam->getFocalLengthPixX() / pinhole_cam->getFocalLengthPixY(); + const float ppX = std::abs(pinhole_cam->getPrincipalPoint()(0) / cam->w()); + const float ppY = std::abs(pinhole_cam->getPrincipalPoint()(1) / cam->h()); + + // For each camera, write to bundle: focal length, radial distortion[0-1], rotation matrix[0-8], translation vector[0-2] + std::ostringstream fileOut; + fileOut << "# MVE view meta data is stored in INI-file syntax." << fileOut.widen('\n') + << "# This file is generated, formatting will get lost." << fileOut.widen('\n') << fileOut.widen('\n') << "[camera]" + << fileOut.widen('\n') << "focal_length = " << flen << fileOut.widen('\n') << "pixel_aspect = " << pixelAspect + << fileOut.widen('\n') << "principal_point = " << ppX << " " << ppY << fileOut.widen('\n') << "rotation = " << rotation(0, 0) + << " " << rotation(0, 1) << " " << rotation(0, 2) << " " << rotation(1, 0) << " " << rotation(1, 1) << " " << rotation(1, 2) + << " " << rotation(2, 0) << " " << rotation(2, 1) << " " << rotation(2, 2) << fileOut.widen('\n') + << "translation = " << translation[0] << " " << translation[1] << " " << translation[2] << " " << fileOut.widen('\n') + << fileOut.widen('\n') << "[view]" << fileOut.widen('\n') << "id = " << view_index << fileOut.widen('\n') + << "name = " << fs::path(srcImage).filename().string() << fileOut.widen('\n'); + + // To do: trim any extra separator(s) from aliceVision name we receive, e.g.: + // '/home/insight/aliceVision_KevinCain/aliceVision_Build/software/SfM/ImageDataset_SceauxCastle/images//100_7100.JPG' + std::ofstream file((fs::path(sOutViewIteratorDirectory) / "meta.ini").string()); + file << fileOut.str(); + file.close(); + + out << flen << " " + << "0" + << " " + << "0" + << "\n" // Write '0' distortion values for pre-corrected images + << rotation(0, 0) << " " << rotation(0, 1) << " " << rotation(0, 2) << "\n" + << rotation(1, 0) << " " << rotation(1, 1) << " " << rotation(1, 2) << "\n" + << rotation(2, 0) << " " << rotation(2, 1) << " " << rotation(2, 2) << "\n" + << translation[0] << " " << translation[1] << " " << translation[2] << "\n"; + + // Save a thumbnail image "thumbnail.png", 50x50 pixels + thumbnail = create_thumbnail(image, 50, 50); + const std::string dstThumbnailImage = (fs::path(sOutViewIteratorDirectory) / "thumbnail.png").string(); + writeImage(dstThumbnailImage, thumbnail, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + + ++view_index; } - } - - // Prepare to write an MVE 'meta.ini' file for the current view - const Pose3 pose = sfm_data.getPose(*view).getTransform(); - const Pinhole * pinhole_cam = static_cast(cam); - - const Mat3& rotation = pose.rotation(); - const Vec3& translation = pose.translation(); - - // Focal length and principal point must be normalized (0..1) - const float flen = pinhole_cam->getFocalLengthPixX() / static_cast(std::max(cam->w(), cam->h())); - const float pixelAspect = pinhole_cam->getFocalLengthPixX() / pinhole_cam->getFocalLengthPixY(); - const float ppX = std::abs(pinhole_cam->getPrincipalPoint()(0)/cam->w()); - const float ppY = std::abs(pinhole_cam->getPrincipalPoint()(1)/cam->h()); - - // For each camera, write to bundle: focal length, radial distortion[0-1], rotation matrix[0-8], translation vector[0-2] - std::ostringstream fileOut; - fileOut - << "# MVE view meta data is stored in INI-file syntax." << fileOut.widen('\n') - << "# This file is generated, formatting will get lost." << fileOut.widen('\n') - << fileOut.widen('\n') - << "[camera]" << fileOut.widen('\n') - << "focal_length = " << flen << fileOut.widen('\n') - << "pixel_aspect = " << pixelAspect << fileOut.widen('\n') - << "principal_point = " << ppX << " " << ppY << fileOut.widen('\n') - << "rotation = " << rotation(0, 0) << " " << rotation(0, 1) << " " << rotation(0, 2) << " " - << rotation(1, 0) << " " << rotation(1, 1) << " " << rotation(1, 2) << " " - << rotation(2, 0) << " " << rotation(2, 1) << " " << rotation(2, 2) << fileOut.widen('\n') - << "translation = " << translation[0] << " " << translation[1] << " " - << translation[2] << " " << fileOut.widen('\n') - << fileOut.widen('\n') - << "[view]" << fileOut.widen('\n') - << "id = " << view_index << fileOut.widen('\n') - << "name = " << fs::path(srcImage).filename().string() << fileOut.widen('\n'); - - // To do: trim any extra separator(s) from aliceVision name we receive, e.g.: - // '/home/insight/aliceVision_KevinCain/aliceVision_Build/software/SfM/ImageDataset_SceauxCastle/images//100_7100.JPG' - std::ofstream file((fs::path(sOutViewIteratorDirectory) / "meta.ini").string()); - file << fileOut.str(); - file.close(); - - out - << flen << " " << "0" << " " << "0" << "\n" // Write '0' distortion values for pre-corrected images - << rotation(0, 0) << " " << rotation(0, 1) << " " << rotation(0, 2) << "\n" - << rotation(1, 0) << " " << rotation(1, 1) << " " << rotation(1, 2) << "\n" - << rotation(2, 0) << " " << rotation(2, 1) << " " << rotation(2, 2) << "\n" - << translation[0] << " " << translation[1] << " " << translation[2] << "\n"; - - // Save a thumbnail image "thumbnail.png", 50x50 pixels - thumbnail = create_thumbnail(image, 50, 50); - const std::string dstThumbnailImage = (fs::path(sOutViewIteratorDirectory) / "thumbnail.png").string(); - writeImage(dstThumbnailImage, thumbnail, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); - - ++view_index; - } - // For each feature, write to bundle: position XYZ[0-3], color RGB[0-2], all ref.view_id & ref.feature_id - // The following method is adapted from Simon Fuhrmann's MVE project: - // https://github.com/simonfuhrmann/mve/blob/e3db7bc60ce93fe51702ba77ef480e151f927c23/libs/mve/bundle_io.cc + // For each feature, write to bundle: position XYZ[0-3], color RGB[0-2], all ref.view_id & ref.feature_id + // The following method is adapted from Simon Fuhrmann's MVE project: + // https://github.com/simonfuhrmann/mve/blob/e3db7bc60ce93fe51702ba77ef480e151f927c23/libs/mve/bundle_io.cc - for (Landmarks::const_iterator iterLandmarks = landmarks.begin(); iterLandmarks != landmarks.end(); ++iterLandmarks) - { - const Vec3 exportPoint = iterLandmarks->second.X; - out << exportPoint.x() << " " << exportPoint.y() << " " << exportPoint.z() << "\n"; - out << 250 << " " << 100 << " " << 150 << "\n"; // Write arbitrary RGB color, see above note - - // Tally set of feature observations - const Observations & observations = iterLandmarks->second.getObservations(); - const size_t featureCount = std::distance(observations.begin(), observations.end()); - out << featureCount; - - for (Observations::const_iterator itObs = observations.begin(); itObs != observations.end(); ++itObs) - { - const IndexT viewId = itObs->first; - const IndexT viewIndex = viewIdToviewIndex[viewId]; - const IndexT featId = itObs->second.getFeatureId(); - out << " " << viewIndex << " " << featId << " 0"; - } - out << "\n"; + for (Landmarks::const_iterator iterLandmarks = landmarks.begin(); iterLandmarks != landmarks.end(); ++iterLandmarks) + { + const Vec3 exportPoint = iterLandmarks->second.X; + out << exportPoint.x() << " " << exportPoint.y() << " " << exportPoint.z() << "\n"; + out << 250 << " " << 100 << " " << 150 << "\n"; // Write arbitrary RGB color, see above note + + // Tally set of feature observations + const Observations& observations = iterLandmarks->second.getObservations(); + const size_t featureCount = std::distance(observations.begin(), observations.end()); + out << featureCount; + + for (Observations::const_iterator itObs = observations.begin(); itObs != observations.end(); ++itObs) + { + const IndexT viewId = itObs->first; + const IndexT viewIndex = viewIdToviewIndex[viewId]; + const IndexT featId = itObs->second.getFeatureId(); + out << " " << viewIndex << " " << featId << " 0"; + } + out << "\n"; + } + out.close(); } - out.close(); - } - return bOk; + return bOk; } -int aliceVision_main(int argc, char *argv[]) +int aliceVision_main(int argc, char* argv[]) { - // command-line parameters - std::string sfmDataFilename; - std::string outDirectory; + // command-line parameters + std::string sfmDataFilename; + std::string outDirectory; - po::options_description allParams("AliceVision exportMVE2"); + po::options_description allParams("AliceVision exportMVE2"); // clang-format off po::options_description requiredParams("Required parameters"); @@ -263,75 +245,68 @@ int aliceVision_main(int argc, char *argv[]) "Note: this program writes output in MVE file format."); // clang-format on - CmdLine cmdline("AliceVision exportMVE2"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // Create output dir - if (!fs::exists(outDirectory)) - fs::create_directory(outDirectory); - - // Read the input SfM scene - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - std::cerr << std::endl - << "The input SfMData file \""<< sfmDataFilename << "\" cannot be read." << std::endl; - return EXIT_FAILURE; - } - - if (exportToMVE2Format(sfmData, (fs::path(outDirectory) / "MVE").string())) - return EXIT_SUCCESS; - else - return EXIT_FAILURE; + CmdLine cmdline("AliceVision exportMVE2"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // Create output dir + if (!fs::exists(outDirectory)) + fs::create_directory(outDirectory); + + // Read the input SfM scene + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } + + if (exportToMVE2Format(sfmData, (fs::path(outDirectory) / "MVE").string())) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; } /// Naive image bilinear resampling of an image for thumbnail generation /// Inspired by create_thumbnail from MVE (cropping is here ignored) -template -ImageT -create_thumbnail -( - const ImageT & image, - int thumb_width, - int thumb_height -) +template +ImageT create_thumbnail(const ImageT& image, int thumb_width, int thumb_height) { - const int width = image.width(); - const int height = image.height(); - const float image_aspect = static_cast(width) / height; - const float thumb_aspect = static_cast(thumb_width) / thumb_height; - - int rescale_width, rescale_height; - if (image_aspect > thumb_aspect) - { - rescale_width = std::ceil(thumb_height * image_aspect); - rescale_height = thumb_height; - } - else - { - rescale_width = thumb_width; - rescale_height = std::ceil(thumb_width / image_aspect); - } - - // Generation of the sampling grid - std::vector< std::pair > sampling_grid; - sampling_grid.reserve(rescale_height * rescale_width); - for ( int i = 0 ; i < rescale_height ; ++i ) - { - for ( int j = 0 ; j < rescale_width ; ++j ) + const int width = image.width(); + const int height = image.height(); + const float image_aspect = static_cast(width) / height; + const float thumb_aspect = static_cast(thumb_width) / thumb_height; + + int rescale_width, rescale_height; + if (image_aspect > thumb_aspect) + { + rescale_width = std::ceil(thumb_height * image_aspect); + rescale_height = thumb_height; + } + else + { + rescale_width = thumb_width; + rescale_height = std::ceil(thumb_width / image_aspect); + } + + // Generation of the sampling grid + std::vector> sampling_grid; + sampling_grid.reserve(rescale_height * rescale_width); + for (int i = 0; i < rescale_height; ++i) { - const float dx = static_cast(j) * width / rescale_width; - const float dy = static_cast(i) * height / rescale_height; - sampling_grid.push_back( std::make_pair( dy , dx ) ) ; + for (int j = 0; j < rescale_width; ++j) + { + const float dx = static_cast(j) * width / rescale_width; + const float dy = static_cast(i) * height / rescale_height; + sampling_grid.push_back(std::make_pair(dy, dx)); + } } - } - const Sampler2d sampler; - ImageT imageOut; - genericResample(image, sampling_grid, rescale_width, rescale_height, sampler, imageOut); - return imageOut; + const Sampler2d sampler; + ImageT imageOut; + genericResample(image, sampling_grid, rescale_width, rescale_height, sampler, imageOut); + return imageOut; } diff --git a/src/software/export/main_exportMVSTexturing.cpp b/src/software/export/main_exportMVSTexturing.cpp index eb867abf8b..803ef3430b 100644 --- a/src/software/export/main_exportMVSTexturing.cpp +++ b/src/software/export/main_exportMVSTexturing.cpp @@ -28,11 +28,11 @@ using namespace aliceVision::sfmData; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outDirectory; + // command-line parameters + std::string sfmDataFilename; + std::string outDirectory; // clang-format off po::options_description requiredParams("Required parameters"); @@ -43,77 +43,72 @@ int aliceVision_main(int argc, char **argv) "Output folder."); // clang-format on - CmdLine cmdline("AliceVision exportMVSTexturing"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - bool bOneHaveDisto = false; - - // Create output dir - if (!fs::exists(outDirectory)) - fs::create_directory(outDirectory); - - // Read the SfM scene - SfMData sfm_data; - if(!sfmDataIO::load(sfm_data, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - std::cerr << std::endl - << "The input SfMData file \""<< sfmDataFilename << "\" cannot be read." << std::endl; - return EXIT_FAILURE; - } - - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter) - { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - // Valid view, we can ask a pose & intrinsic data - const Pose3 pose = sfm_data.getPose(*view).getTransform(); - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - const IntrinsicBase * cam = iterIntrinsic->second.get(); - - if (!camera::isPinhole(cam->getType())) - continue; - - const Pinhole * pinhole_cam = static_cast(cam); - - // Extrinsic - const Vec3& t = pose.translation(); - const Mat3& R = pose.rotation(); - // Intrinsic - const double fx = pinhole_cam->getFocalLengthPixX(); - const double fy = pinhole_cam->getFocalLengthPixY(); - const Vec2 pp = pinhole_cam->getPrincipalPoint(); - - // Image size in px - const int w = pinhole_cam->w(); - const int h = pinhole_cam->h(); - - // We can now create the .cam file for the View in the output dir - std::ofstream outfile((fs::path(outDirectory) / (fs::path(view->getImage().getImagePath()).stem().string() + ".cam")).string()); - // See https://github.com/nmoehrle/mvs-texturing/blob/master/Arguments.cpp - // for full specs - const int largerDim = w > h ? w : h; - outfile << t(0) << " " << t(1) << " " << t(2) << " " - << R(0,0) << " " << R(0,1) << " " << R(0,2) << " " - << R(1,0) << " " << R(1,1) << " " << R(1,2) << " " - << R(2,0) << " " << R(2,1) << " " << R(2,2) << "\n" - << fx / largerDim << " 0 0 " << fx / fy << " " << pp(0) / w << " " << pp(1) / h; - outfile.close(); - - if(cam->hasDistortion()) - bOneHaveDisto = true; - } - - const std::string sUndistMsg = bOneHaveDisto ? "undistorded" : ""; - const std::string sQuitMsg = std::string("Your SfMData file was succesfully converted!\n") + - "Now you can copy your " + sUndistMsg + " images in the \"" + outDirectory + "\" folder and run MVS Texturing"; - std::cout << sQuitMsg << std::endl; - return EXIT_SUCCESS; + CmdLine cmdline("AliceVision exportMVSTexturing"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + bool bOneHaveDisto = false; + + // Create output dir + if (!fs::exists(outDirectory)) + fs::create_directory(outDirectory); + + // Read the SfM scene + SfMData sfm_data; + if (!sfmDataIO::load(sfm_data, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } + + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter) + { + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + // Valid view, we can ask a pose & intrinsic data + const Pose3 pose = sfm_data.getPose(*view).getTransform(); + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + const IntrinsicBase* cam = iterIntrinsic->second.get(); + + if (!camera::isPinhole(cam->getType())) + continue; + + const Pinhole* pinhole_cam = static_cast(cam); + + // Extrinsic + const Vec3& t = pose.translation(); + const Mat3& R = pose.rotation(); + // Intrinsic + const double fx = pinhole_cam->getFocalLengthPixX(); + const double fy = pinhole_cam->getFocalLengthPixY(); + const Vec2 pp = pinhole_cam->getPrincipalPoint(); + + // Image size in px + const int w = pinhole_cam->w(); + const int h = pinhole_cam->h(); + + // We can now create the .cam file for the View in the output dir + std::ofstream outfile((fs::path(outDirectory) / (fs::path(view->getImage().getImagePath()).stem().string() + ".cam")).string()); + // See https://github.com/nmoehrle/mvs-texturing/blob/master/Arguments.cpp + // for full specs + const int largerDim = w > h ? w : h; + outfile << t(0) << " " << t(1) << " " << t(2) << " " << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << " " << R(1, 0) << " " << R(1, 1) << " " + << R(1, 2) << " " << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << "\n" + << fx / largerDim << " 0 0 " << fx / fy << " " << pp(0) / w << " " << pp(1) / h; + outfile.close(); + + if (cam->hasDistortion()) + bOneHaveDisto = true; + } + + const std::string sUndistMsg = bOneHaveDisto ? "undistorded" : ""; + const std::string sQuitMsg = std::string("Your SfMData file was succesfully converted!\n") + "Now you can copy your " + sUndistMsg + + " images in the \"" + outDirectory + "\" folder and run MVS Texturing"; + std::cout << sQuitMsg << std::endl; + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportMatches.cpp b/src/software/export/main_exportMatches.cpp index 6e37de1159..dbc84d4e33 100644 --- a/src/software/export/main_exportMatches.cpp +++ b/src/software/export/main_exportMatches.cpp @@ -45,44 +45,52 @@ namespace po = boost::program_options; namespace fs = std::filesystem; // Convert HUE color to RGB -inline float hue2rgb(float p, float q, float t){ - if(t < 0) t += 1; - if(t > 1) t -= 1; - if(t < 1.f/6.f) return p + (q - p) * 6.f * t; - if(t < 1.f/2.f) return q; - if(t < 2.f/3.f) return p + (q - p) * (2.f/3.f - t) * 6.f; - return p; +inline float hue2rgb(float p, float q, float t) +{ + if (t < 0) + t += 1; + if (t > 1) + t -= 1; + if (t < 1.f / 6.f) + return p + (q - p) * 6.f * t; + if (t < 1.f / 2.f) + return q; + if (t < 2.f / 3.f) + return p + (q - p) * (2.f / 3.f - t) * 6.f; + return p; } // Converts an HSL color value to RGB. Conversion formula // adapted from http://en.wikipedia.org/wiki/HSL_color_space. // Assumes h, s, and l are contained in the set [0, 1] and // returns r, g, and b in the set [0, 255]. -void hslToRgb(float h, float s, float l, - unsigned char & r, unsigned char & g, unsigned char & b) +void hslToRgb(float h, float s, float l, unsigned char& r, unsigned char& g, unsigned char& b) { - if(s == 0){ - r = g = b = static_cast(l * 255.f); // achromatic - }else{ - const float q = l < 0.5f ? l * (1 + s) : l + s - l * s; - const float p = 2.f * l - q; - r = static_cast(hue2rgb(p, q, h + 1.f/3.f) * 255.f); - g = static_cast(hue2rgb(p, q, h) * 255.f); - b = static_cast(hue2rgb(p, q, h - 1.f/3.f) * 255.f); - } + if (s == 0) + { + r = g = b = static_cast(l * 255.f); // achromatic + } + else + { + const float q = l < 0.5f ? l * (1 + s) : l + s - l * s; + const float p = 2.f * l - q; + r = static_cast(hue2rgb(p, q, h + 1.f / 3.f) * 255.f); + g = static_cast(hue2rgb(p, q, h) * 255.f); + b = static_cast(hue2rgb(p, q, h - 1.f / 3.f) * 255.f); + } } -int aliceVision_main(int argc, char ** argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; - std::vector featuresFolders; - std::vector matchesFolders; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; + std::vector featuresFolders; + std::vector matchesFolders; - // user optional parameters + // user optional parameters - std::string describerTypesName = EImageDescriberType_enumToString(EImageDescriberType::SIFT); + std::string describerTypesName = EImageDescriberType_enumToString(EImageDescriberType::SIFT); // clang-format off po::options_description requiredParams("Required parameters"); @@ -102,145 +110,141 @@ int aliceVision_main(int argc, char ** argv) EImageDescriberType_informations().c_str()); // clang-format on - CmdLine cmdline("AliceVision exportMatches"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if (outputFolder.empty()) - { - ALICEVISION_LOG_ERROR("It is an invalid output folder"); - return EXIT_FAILURE; - } - - // read SfM Scene (image view names) - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } - - // load SfM Scene regions - - // get imageDescriberMethodType - std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - - // read the features - feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid features file"); - return EXIT_FAILURE; - } - - // read matches - matching::PairwiseMatches pairwiseMatches; - if(!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid matches file"); - return EXIT_FAILURE; - } - - // for each pair, export the matches - - fs::create_directory(outputFolder); - ALICEVISION_LOG_INFO("Export pairwise matches"); - const PairSet pairs = matching::getImagePairs(pairwiseMatches); - auto myProgressBar = system::createConsoleProgressDisplay(pairs.size(), std::cout); - for (PairSet::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter, ++myProgressBar) - { - const std::size_t I = iter->first; - const std::size_t J = iter->second; - - const View* viewI = sfmData.getViews().at(I).get(); - const View* viewJ = sfmData.getViews().at(J).get(); - - const std::string viewImagePathI= viewI->getImage().getImagePath(); - const std::string viewImagePathJ= viewJ->getImage().getImagePath(); - - std::string destFilename_I; - std::string destFilename_J; + CmdLine cmdline("AliceVision exportMatches"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - fs::path origImgPath(viewImagePathI); - std::string origFilename = origImgPath.stem().string(); - image::Image originalImage; - image::readImage(viewImagePathI, originalImage, image::EImageColorSpace::LINEAR); - destFilename_I = (fs::path(outputFolder) / (origFilename + ".png")).string(); - image::writeImage(destFilename_I, originalImage, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::SRGB)); + return EXIT_FAILURE; } + if (outputFolder.empty()) { - fs::path origImgPath(viewImagePathJ); - std::string origFilename = origImgPath.stem().string(); - image::Image originalImage; - image::readImage(viewImagePathJ, originalImage, image::EImageColorSpace::LINEAR); - destFilename_J = (fs::path(outputFolder) / (origFilename + ".png")).string(); - image::writeImage(destFilename_J, originalImage, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::SRGB)); + ALICEVISION_LOG_ERROR("It is an invalid output folder"); + return EXIT_FAILURE; } - const std::pair dimImageI = std::make_pair(viewI->getImage().getWidth(), viewI->getImage().getHeight()); - const std::pair dimImageJ = std::make_pair(viewJ->getImage().getWidth(), viewJ->getImage().getHeight()); + // read SfM Scene (image view names) + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } - svgDrawer svgStream(dimImageI.first + dimImageJ.first, std::max(dimImageI.second, dimImageJ.second)); + // load SfM Scene regions - svgStream.drawImage(destFilename_I, dimImageI.first, dimImageI.second); - svgStream.drawImage(destFilename_J, dimImageJ.first, dimImageJ.second, dimImageI.first); + // get imageDescriberMethodType + std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - const matching::MatchesPerDescType& filteredMatches = pairwiseMatches.at(*iter); + // read the features + feature::FeaturesPerView featuresPerView; + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid features file"); + return EXIT_FAILURE; + } - ALICEVISION_LOG_INFO("nb describer: " << filteredMatches.size()); + // read matches + matching::PairwiseMatches pairwiseMatches; + if (!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid matches file"); + return EXIT_FAILURE; + } - if(filteredMatches.empty()) - continue; + // for each pair, export the matches - for(const auto& matchesIt: filteredMatches) + fs::create_directory(outputFolder); + ALICEVISION_LOG_INFO("Export pairwise matches"); + const PairSet pairs = matching::getImagePairs(pairwiseMatches); + auto myProgressBar = system::createConsoleProgressDisplay(pairs.size(), std::cout); + for (PairSet::const_iterator iter = pairs.begin(); iter != pairs.end(); ++iter, ++myProgressBar) { - const feature::EImageDescriberType descType = matchesIt.first; - assert(descType != feature::EImageDescriberType::UNINITIALIZED); - const matching::IndMatches& matches = matchesIt.second; - ALICEVISION_LOG_INFO(EImageDescriberType_enumToString(matchesIt.first) << ": " << matches.size() << " matches"); - - const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); - const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); - - // draw link between features : - for(std::size_t i = 0; i < matches.size(); ++i) - { - const PointFeature& imaA = featuresI[matches[i]._i]; - const PointFeature& imaB = featuresJ[matches[i]._j]; - - // compute a flashy colour for the correspondence - unsigned char r,g,b; - hslToRgb( (rand() % 360) / 360., 1.0, .5, r, g, b); - std::ostringstream osCol; - osCol << "rgb(" << (int)r <<',' << (int)g << ',' << (int)b <<")"; - svgStream.drawLine(imaA.x(), imaA.y(), - imaB.x()+dimImageI.first, imaB.y(), svgStyle().stroke(osCol.str(), 2.0)); - } - - const std::string featColor = describerTypeColor(descType); - // draw features (in two loop, in order to have the features upper the link, svg layer order): - for(std::size_t i=0; i< matches.size(); ++i) - { - const PointFeature& imaA = featuresI[matches[i]._i]; - const PointFeature& imaB = featuresJ[matches[i]._j]; - svgStream.drawCircle(imaA.x(), imaA.y(), 5.0, - svgStyle().stroke(featColor, 2.0)); - svgStream.drawCircle(imaB.x() + dimImageI.first, imaB.y(), 5.0, - svgStyle().stroke(featColor, 2.0)); - } + const std::size_t I = iter->first; + const std::size_t J = iter->second; + + const View* viewI = sfmData.getViews().at(I).get(); + const View* viewJ = sfmData.getViews().at(J).get(); + + const std::string viewImagePathI = viewI->getImage().getImagePath(); + const std::string viewImagePathJ = viewJ->getImage().getImagePath(); + + std::string destFilename_I; + std::string destFilename_J; + { + fs::path origImgPath(viewImagePathI); + std::string origFilename = origImgPath.stem().string(); + image::Image originalImage; + image::readImage(viewImagePathI, originalImage, image::EImageColorSpace::LINEAR); + destFilename_I = (fs::path(outputFolder) / (origFilename + ".png")).string(); + image::writeImage(destFilename_I, originalImage, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::SRGB)); + } + + { + fs::path origImgPath(viewImagePathJ); + std::string origFilename = origImgPath.stem().string(); + image::Image originalImage; + image::readImage(viewImagePathJ, originalImage, image::EImageColorSpace::LINEAR); + destFilename_J = (fs::path(outputFolder) / (origFilename + ".png")).string(); + image::writeImage(destFilename_J, originalImage, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::SRGB)); + } + + const std::pair dimImageI = std::make_pair(viewI->getImage().getWidth(), viewI->getImage().getHeight()); + const std::pair dimImageJ = std::make_pair(viewJ->getImage().getWidth(), viewJ->getImage().getHeight()); + + svgDrawer svgStream(dimImageI.first + dimImageJ.first, std::max(dimImageI.second, dimImageJ.second)); + + svgStream.drawImage(destFilename_I, dimImageI.first, dimImageI.second); + svgStream.drawImage(destFilename_J, dimImageJ.first, dimImageJ.second, dimImageI.first); + + const matching::MatchesPerDescType& filteredMatches = pairwiseMatches.at(*iter); + + ALICEVISION_LOG_INFO("nb describer: " << filteredMatches.size()); + + if (filteredMatches.empty()) + continue; + + for (const auto& matchesIt : filteredMatches) + { + const feature::EImageDescriberType descType = matchesIt.first; + assert(descType != feature::EImageDescriberType::UNINITIALIZED); + const matching::IndMatches& matches = matchesIt.second; + ALICEVISION_LOG_INFO(EImageDescriberType_enumToString(matchesIt.first) << ": " << matches.size() << " matches"); + + const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); + const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); + + // draw link between features : + for (std::size_t i = 0; i < matches.size(); ++i) + { + const PointFeature& imaA = featuresI[matches[i]._i]; + const PointFeature& imaB = featuresJ[matches[i]._j]; + + // compute a flashy colour for the correspondence + unsigned char r, g, b; + hslToRgb((rand() % 360) / 360., 1.0, .5, r, g, b); + std::ostringstream osCol; + osCol << "rgb(" << (int)r << ',' << (int)g << ',' << (int)b << ")"; + svgStream.drawLine(imaA.x(), imaA.y(), imaB.x() + dimImageI.first, imaB.y(), svgStyle().stroke(osCol.str(), 2.0)); + } + + const std::string featColor = describerTypeColor(descType); + // draw features (in two loop, in order to have the features upper the link, svg layer order): + for (std::size_t i = 0; i < matches.size(); ++i) + { + const PointFeature& imaA = featuresI[matches[i]._i]; + const PointFeature& imaB = featuresJ[matches[i]._j]; + svgStream.drawCircle(imaA.x(), imaA.y(), 5.0, svgStyle().stroke(featColor, 2.0)); + svgStream.drawCircle(imaB.x() + dimImageI.first, imaB.y(), 5.0, svgStyle().stroke(featColor, 2.0)); + } + } + + fs::path outputFilename = fs::path(outputFolder) / std::string(std::to_string(iter->first) + "_" + std::to_string(iter->second) + "_" + + std::to_string(filteredMatches.getNbAllMatches()) + ".svg"); + std::ofstream svgFile(outputFilename.string()); + svgFile << svgStream.closeSvgFile().str(); + svgFile.close(); } - - fs::path outputFilename = fs::path(outputFolder) / std::string(std::to_string(iter->first) + "_" + std::to_string(iter->second) + "_" + std::to_string(filteredMatches.getNbAllMatches()) + ".svg"); - std::ofstream svgFile(outputFilename.string()); - svgFile << svgStream.closeSvgFile().str(); - svgFile.close(); - } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportMatlab.cpp b/src/software/export/main_exportMatlab.cpp index d8c8da8481..ad66f6c1a8 100644 --- a/src/software/export/main_exportMatlab.cpp +++ b/src/software/export/main_exportMatlab.cpp @@ -35,114 +35,100 @@ using namespace aliceVision::sfmData; namespace po = boost::program_options; namespace fs = std::filesystem; -bool exportToMatlab( - const SfMData & sfm_data, - const std::string & outDirectory - ) +bool exportToMatlab(const SfMData& sfm_data, const std::string& outDirectory) { - const double unknownScale = 0.0; - // WARNING: Observation::id_feat is used to put the ID of the 3D landmark. - std::map > observationsPerView; - - { - const std::string landmarksFilename = (fs::path(outDirectory) / "scene.landmarks").string(); - std::ofstream landmarksFile(landmarksFilename); - landmarksFile << "# landmarkId X Y Z\n"; - for(const auto& s: sfm_data.getLandmarks()) + const double unknownScale = 0.0; + // WARNING: Observation::id_feat is used to put the ID of the 3D landmark. + std::map> observationsPerView; + { - const IndexT landmarkId = s.first; - const Landmark& landmark = s.second; - landmarksFile << landmarkId << " " << landmark.X[0] << " " << landmark.X[1] << " " << landmark.X[2] << "\n"; - for(const auto& obs: landmark.getObservations()) - { - const IndexT obsView = obs.first; // The ID of the view that provides this 2D observation. - observationsPerView[obsView].push_back(Observation(obs.second.getCoordinates(), landmarkId, unknownScale)); - } + const std::string landmarksFilename = (fs::path(outDirectory) / "scene.landmarks").string(); + std::ofstream landmarksFile(landmarksFilename); + landmarksFile << "# landmarkId X Y Z\n"; + for (const auto& s : sfm_data.getLandmarks()) + { + const IndexT landmarkId = s.first; + const Landmark& landmark = s.second; + landmarksFile << landmarkId << " " << landmark.X[0] << " " << landmark.X[1] << " " << landmark.X[2] << "\n"; + for (const auto& obs : landmark.getObservations()) + { + const IndexT obsView = obs.first; // The ID of the view that provides this 2D observation. + observationsPerView[obsView].push_back(Observation(obs.second.getCoordinates(), landmarkId, unknownScale)); + } + } + landmarksFile.close(); } - landmarksFile.close(); - } - - // Export observations per view - for(const auto & obsPerView : observationsPerView) - { - const std::vector& viewObservations = obsPerView.second; - const IndexT viewId = obsPerView.first; - - const std::string viewFeatFilename = (fs::path(outDirectory) / (std::to_string(viewId) + ".reconstructedFeatures")).string(); - std::ofstream viewFeatFile(viewFeatFilename); - viewFeatFile << "# landmarkId x y\n"; - for(const Observation& obs: viewObservations) + + // Export observations per view + for (const auto& obsPerView : observationsPerView) { - viewFeatFile << obs.getFeatureId() << " " << obs.getX() << " " << obs.getY() << "\n"; + const std::vector& viewObservations = obsPerView.second; + const IndexT viewId = obsPerView.first; + + const std::string viewFeatFilename = (fs::path(outDirectory) / (std::to_string(viewId) + ".reconstructedFeatures")).string(); + std::ofstream viewFeatFile(viewFeatFilename); + viewFeatFile << "# landmarkId x y\n"; + for (const Observation& obs : viewObservations) + { + viewFeatFile << obs.getFeatureId() << " " << obs.getX() << " " << obs.getY() << "\n"; + } + viewFeatFile.close(); } - viewFeatFile.close(); - } - - // Expose camera poses - { - const std::string cameraPosesFilename = (fs::path(outDirectory) / "cameras.poses").string(); - std::ofstream cameraPosesFile(cameraPosesFilename); - cameraPosesFile << "# viewId R11 R12 R13 R21 R22 R23 R31 R32 R33 C1 C2 C3\n"; - for(const auto& v: sfm_data.getViews()) + + // Expose camera poses { - const View& view = *v.second.get(); - if(!sfm_data.isPoseAndIntrinsicDefined(&view)) - continue; - - const Pose3 pose = sfm_data.getPose(view).getTransform(); - cameraPosesFile << view.getViewId() - << " " << pose.rotation()(0, 0) - << " " << pose.rotation()(0, 1) - << " " << pose.rotation()(0, 2) - << " " << pose.rotation()(1, 0) - << " " << pose.rotation()(1, 1) - << " " << pose.rotation()(1, 2) - << " " << pose.rotation()(2, 0) - << " " << pose.rotation()(2, 1) - << " " << pose.rotation()(2, 2) - << " " << pose.center()(0) - << " " << pose.center()(1) - << " " << pose.center()(2) - << "\n"; + const std::string cameraPosesFilename = (fs::path(outDirectory) / "cameras.poses").string(); + std::ofstream cameraPosesFile(cameraPosesFilename); + cameraPosesFile << "# viewId R11 R12 R13 R21 R22 R23 R31 R32 R33 C1 C2 C3\n"; + for (const auto& v : sfm_data.getViews()) + { + const View& view = *v.second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(&view)) + continue; + + const Pose3 pose = sfm_data.getPose(view).getTransform(); + cameraPosesFile << view.getViewId() << " " << pose.rotation()(0, 0) << " " << pose.rotation()(0, 1) << " " << pose.rotation()(0, 2) << " " + << pose.rotation()(1, 0) << " " << pose.rotation()(1, 1) << " " << pose.rotation()(1, 2) << " " << pose.rotation()(2, 0) + << " " << pose.rotation()(2, 1) << " " << pose.rotation()(2, 2) << " " << pose.center()(0) << " " << pose.center()(1) + << " " << pose.center()(2) << "\n"; + } + cameraPosesFile.close(); } - cameraPosesFile.close(); - } - - // Expose camera intrinsics - // Note: we export it per view, It is really redundant but easy to parse. - { - const std::string cameraIntrinsicsFilename = (fs::path(outDirectory) / "cameras.intrinsics").string(); - std::ofstream cameraIntrinsicsFile(cameraIntrinsicsFilename); - cameraIntrinsicsFile << - "# viewId pinhole f u0 v0\n" - "# viewId radial1 f u0 v0 k1\n" - "# viewId radial3 f u0 v0 k1 k2 k3\n" - "# viewId brown f u0 v0 k1 k2 k3 t1 t2\n" - "# viewId fisheye4 f u0 v0 k1 k2 k3 k4\n" - "# viewId fisheye1 f u0 v0 k1\n"; - - for(const auto& v: sfm_data.getViews()) + + // Expose camera intrinsics + // Note: we export it per view, It is really redundant but easy to parse. { - const View& view = *v.second.get(); - if(!sfm_data.isPoseAndIntrinsicDefined(&view)) - continue; - const IntrinsicBase& intrinsics = *sfm_data.getIntrinsics().at(view.getIntrinsicId()).get(); - cameraIntrinsicsFile << view.getViewId() << " " << camera::EINTRINSIC_enumToString(intrinsics.getType()); - for(double p: intrinsics.getParams()) - cameraIntrinsicsFile << " " << p; - cameraIntrinsicsFile << "\n"; + const std::string cameraIntrinsicsFilename = (fs::path(outDirectory) / "cameras.intrinsics").string(); + std::ofstream cameraIntrinsicsFile(cameraIntrinsicsFilename); + cameraIntrinsicsFile << "# viewId pinhole f u0 v0\n" + "# viewId radial1 f u0 v0 k1\n" + "# viewId radial3 f u0 v0 k1 k2 k3\n" + "# viewId brown f u0 v0 k1 k2 k3 t1 t2\n" + "# viewId fisheye4 f u0 v0 k1 k2 k3 k4\n" + "# viewId fisheye1 f u0 v0 k1\n"; + + for (const auto& v : sfm_data.getViews()) + { + const View& view = *v.second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(&view)) + continue; + const IntrinsicBase& intrinsics = *sfm_data.getIntrinsics().at(view.getIntrinsicId()).get(); + cameraIntrinsicsFile << view.getViewId() << " " << camera::EINTRINSIC_enumToString(intrinsics.getType()); + for (double p : intrinsics.getParams()) + cameraIntrinsicsFile << " " << p; + cameraIntrinsicsFile << "\n"; + } + cameraIntrinsicsFile.close(); } - cameraIntrinsicsFile.close(); - } - - return true; + + return true; } -int aliceVision_main(int argc, char *argv[]) +int aliceVision_main(int argc, char* argv[]) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; // clang-format off po::options_description requiredParams("Required parameters"); @@ -153,31 +139,30 @@ int aliceVision_main(int argc, char *argv[]) "Output folder."); // clang-format on - CmdLine cmdline("AliceVision exportMatlab"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // export - { - // Create output dir - if(!fs::exists(outputFolder)) - fs::create_directory(outputFolder); - - // Read the input SfM scene - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + CmdLine cmdline("AliceVision exportMatlab"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) { - std::cerr << std::endl - << "The input SfMData file \""<< sfmDataFilename << "\" cannot be read." << std::endl; - return EXIT_FAILURE; + return EXIT_FAILURE; } - if (!exportToMatlab(sfmData, outputFolder)) - return EXIT_FAILURE; - } + // export + { + // Create output dir + if (!fs::exists(outputFolder)) + fs::create_directory(outputFolder); + + // Read the input SfM scene + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } + + if (!exportToMatlab(sfmData, outputFolder)) + return EXIT_FAILURE; + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportMeshlab.cpp b/src/software/export/main_exportMeshlab.cpp index 18d80392f9..ace5f15203 100644 --- a/src/software/export/main_exportMeshlab.cpp +++ b/src/software/export/main_exportMeshlab.cpp @@ -30,12 +30,12 @@ using namespace aliceVision::sfmData; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string plyPath; - std::string outDirectory; + // command-line parameters + std::string sfmDataFilename; + std::string plyPath; + std::string outDirectory; // clang-format off po::options_description requiredParams("Required parameters"); @@ -48,102 +48,86 @@ int aliceVision_main(int argc, char **argv) "Output folder."); // clang-format on - CmdLine cmdline("AliceVision exportMeshlab"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - // Create output dir - if(!fs::exists(outDirectory)) - fs::create_directory(outDirectory); - - // Read the SfM scene - SfMData sfm_data; - if(!sfmDataIO::load(sfm_data, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - std::cerr << std::endl - << "The input SfMData file \""<< sfmDataFilename << "\" cannot be read." << std::endl; - return EXIT_FAILURE; - } - - std::ofstream outfile((fs::path(outDirectory) / "sceneMeshlab.mlp").string()); - - // Init mlp file - outfile << "" << outfile.widen('\n') - << "" << outfile.widen('\n') - << " " << outfile.widen('\n') - << " " << outfile.widen('\n') - << " " << outfile.widen('\n') - << "1 0 0 0 " << outfile.widen('\n') - << "0 1 0 0 " << outfile.widen('\n') - << "0 0 1 0 " << outfile.widen('\n') - << "0 0 0 1 " << outfile.widen('\n') - << "" << outfile.widen('\n') - << " " << outfile.widen('\n') - << " " << outfile.widen('\n'); - - outfile << " " << outfile.widen('\n'); - - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter) - { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - const Pose3 pose = sfm_data.getPose(*view).getTransform(); - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - - // We have a valid view with a corresponding camera & pose - const std::string srcImage = view->getImage().getImagePath(); - std::shared_ptr cam = iterIntrinsic->second; - std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); - if (!camPinHole) { - ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); - continue; + CmdLine cmdline("AliceVision exportMeshlab"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; } - - Mat34 P = camPinHole->getProjectiveEquivalent(pose); - - for ( int i = 1; i < 3 ; ++i) - for ( int j = 0; j < 4; ++j) - P(i, j) *= -1.; - - Mat3 R, K; - Vec3 t; - KRt_from_P( P, K, R, t); - - const Vec3 optical_center = R.transpose() * t; - - outfile - << " getImage().getImagePath()).filename().string() << "\">" << std::endl - << " w() << " " << cam->h() << "\"" - << " PixelSizeMm=\"" << 1 << " " << 1 << "\"" - << " CenterPx=\"" << cam->w() / 2.0 << " " << cam->h() / 2.0 << "\"" - << " FocalMm=\"" << (double)K(0, 0 ) << "\"" - << " RotationMatrix=\"" - << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << " 0 " - << R(1, 0) << " " << R(1, 1) << " " << R(1, 2) << " 0 " - << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << " 0 " - << "0 0 0 1 \"/>" << std::endl; - - // Link the image plane - outfile << " "<< std::endl; - outfile << " " << std::endl; - } - outfile << " " << std::endl - << "" << std::endl; - - outfile.close(); - - return EXIT_SUCCESS; + + // Create output dir + if (!fs::exists(outDirectory)) + fs::create_directory(outDirectory); + + // Read the SfM scene + SfMData sfm_data; + if (!sfmDataIO::load(sfm_data, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } + + std::ofstream outfile((fs::path(outDirectory) / "sceneMeshlab.mlp").string()); + + // Init mlp file + outfile << "" << outfile.widen('\n') << "" << outfile.widen('\n') << " " + << outfile.widen('\n') << " " << outfile.widen('\n') + << " " << outfile.widen('\n') << "1 0 0 0 " << outfile.widen('\n') << "0 1 0 0 " << outfile.widen('\n') << "0 0 1 0 " + << outfile.widen('\n') << "0 0 0 1 " << outfile.widen('\n') << "" << outfile.widen('\n') << " " + << outfile.widen('\n') << " " << outfile.widen('\n'); + + outfile << " " << outfile.widen('\n'); + + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter) + { + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + const Pose3 pose = sfm_data.getPose(*view).getTransform(); + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + + // We have a valid view with a corresponding camera & pose + const std::string srcImage = view->getImage().getImagePath(); + std::shared_ptr cam = iterIntrinsic->second; + std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); + if (!camPinHole) + { + ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); + continue; + } + + Mat34 P = camPinHole->getProjectiveEquivalent(pose); + + for (int i = 1; i < 3; ++i) + for (int j = 0; j < 4; ++j) + P(i, j) *= -1.; + + Mat3 R, K; + Vec3 t; + KRt_from_P(P, K, R, t); + + const Vec3 optical_center = R.transpose() * t; + + outfile << " getImage().getImagePath()).filename().string() << "\">" << std::endl + << " w() << " " << cam->h() << "\"" + << " PixelSizeMm=\"" << 1 << " " << 1 << "\"" + << " CenterPx=\"" << cam->w() / 2.0 << " " << cam->h() / 2.0 << "\"" + << " FocalMm=\"" << (double)K(0, 0) << "\"" + << " RotationMatrix=\"" << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << " 0 " << R(1, 0) << " " << R(1, 1) << " " << R(1, 2) + << " 0 " << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << " 0 " + << "0 0 0 1 \"/>" << std::endl; + + // Link the image plane + outfile << " " << std::endl; + outfile << " " << std::endl; + } + outfile << " " << std::endl << "" << std::endl; + + outfile.close(); + + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportMeshroomMaya.cpp b/src/software/export/main_exportMeshroomMaya.cpp index 1235a6b6ae..5c86a658da 100644 --- a/src/software/export/main_exportMeshroomMaya.cpp +++ b/src/software/export/main_exportMeshroomMaya.cpp @@ -28,11 +28,11 @@ namespace po = boost::program_options; namespace fs = std::filesystem; namespace oiio = OIIO; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; // clang-format off po::options_description requiredParams("Required parameters"); @@ -43,87 +43,88 @@ int aliceVision_main(int argc, char **argv) "Output folder."); // clang-format on - CmdLine cmdline("AliceVision exportMeshroomMaya"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // create output folders - if(!fs::is_directory(outputFolder)) - fs::create_directory(outputFolder); - if(!fs::is_directory(outputFolder + "/undistort/")) - fs::create_directory(outputFolder + "/undistort/"); - if(!fs::is_directory(outputFolder + "/undistort/proxy/")) - fs::create_directory(outputFolder + "/undistort/proxy/"); - if(!fs::is_directory(outputFolder + "/undistort/thumbnail/")) - fs::create_directory(outputFolder + "/undistort/thumbnail/"); - - // read the SfM scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("Error: The input SfMData file '" + sfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - // export the SfM scene to an alembic at the root of the output folder - ALICEVISION_LOG_INFO("Exporting SfM scene for MeshroomMaya ..."); - sfmDataIO::save(sfmData, outputFolder + "/scene.abc", sfmDataIO::ESfMData::ALL); - - // export undistorted images and thumbnail images - auto progressDisplay = system::createConsoleProgressDisplay(sfmData.getViews().size(), std::cout, - "Exporting Images for MeshroomMaya\n"); - for(auto& viewPair : sfmData.getViews()) - { - const sfmData::View& view = *viewPair.second; - const std::shared_ptr intrinsicPtr = sfmData.getIntrinsicsharedPtr(view.getIntrinsicId()); - - if(intrinsicPtr == nullptr) + CmdLine cmdline("AliceVision exportMeshroomMaya"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) { - ALICEVISION_LOG_ERROR("Error: Can't find intrinsic id '" + std::to_string(view.getIntrinsicId()) + "' in SfMData file."); - return EXIT_FAILURE; + return EXIT_FAILURE; } - image::Image image, imageUd; - image::readImage(view.getImage().getImagePath(), image, image::EImageColorSpace::LINEAR); + // create output folders + if (!fs::is_directory(outputFolder)) + fs::create_directory(outputFolder); + if (!fs::is_directory(outputFolder + "/undistort/")) + fs::create_directory(outputFolder + "/undistort/"); + if (!fs::is_directory(outputFolder + "/undistort/proxy/")) + fs::create_directory(outputFolder + "/undistort/proxy/"); + if (!fs::is_directory(outputFolder + "/undistort/thumbnail/")) + fs::create_directory(outputFolder + "/undistort/thumbnail/"); + + // read the SfM scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("Error: The input SfMData file '" + sfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; + } + + // export the SfM scene to an alembic at the root of the output folder + ALICEVISION_LOG_INFO("Exporting SfM scene for MeshroomMaya ..."); + sfmDataIO::save(sfmData, outputFolder + "/scene.abc", sfmDataIO::ESfMData::ALL); + + // export undistorted images and thumbnail images + auto progressDisplay = system::createConsoleProgressDisplay(sfmData.getViews().size(), std::cout, "Exporting Images for MeshroomMaya\n"); + for (auto& viewPair : sfmData.getViews()) + { + const sfmData::View& view = *viewPair.second; + const std::shared_ptr intrinsicPtr = sfmData.getIntrinsicsharedPtr(view.getIntrinsicId()); + + if (intrinsicPtr == nullptr) + { + ALICEVISION_LOG_ERROR("Error: Can't find intrinsic id '" + std::to_string(view.getIntrinsicId()) + "' in SfMData file."); + return EXIT_FAILURE; + } - // compute undistorted image - if(intrinsicPtr->isValid() && intrinsicPtr->hasDistortion()) - camera::UndistortImage(image, intrinsicPtr.get(), imageUd, image::BLACK, true); - else - imageUd = image; + image::Image image, imageUd; + image::readImage(view.getImage().getImagePath(), image, image::EImageColorSpace::LINEAR); - // export images - oiio::ImageBuf imageBuf; - image::getBufferFromImage(imageUd, imageBuf); + // compute undistorted image + if (intrinsicPtr->isValid() && intrinsicPtr->hasDistortion()) + camera::UndistortImage(image, intrinsicPtr.get(), imageUd, image::BLACK, true); + else + imageUd = image; - image::Image imageProxy(image.width()/2, image.height()/2); - image::Image imageThumbnail(256, image.height() / (image.width() / 256.0f)); // width = 256px, keep height ratio + // export images + oiio::ImageBuf imageBuf; + image::getBufferFromImage(imageUd, imageBuf); - oiio::ImageBuf proxyBuf; - oiio::ImageBuf thumbnailBuf; + image::Image imageProxy(image.width() / 2, image.height() / 2); + image::Image imageThumbnail(256, image.height() / (image.width() / 256.0f)); // width = 256px, keep height ratio - image::getBufferFromImage(imageProxy, proxyBuf); - image::getBufferFromImage(imageThumbnail, thumbnailBuf); + oiio::ImageBuf proxyBuf; + oiio::ImageBuf thumbnailBuf; - const oiio::ROI proxyROI(0, imageProxy.width(), 0, imageProxy.height(), 0, 1, 0, 3); - const oiio::ROI thumbnailROI(0, imageThumbnail.width(), 0, imageThumbnail.height(), 0, 1, 0, 3); + image::getBufferFromImage(imageProxy, proxyBuf); + image::getBufferFromImage(imageThumbnail, thumbnailBuf); - oiio::ImageBufAlgo::resample(proxyBuf, imageBuf, false, proxyROI); // no interpolation - oiio::ImageBufAlgo::resample(thumbnailBuf, imageBuf, false, thumbnailROI); // no interpolation + const oiio::ROI proxyROI(0, imageProxy.width(), 0, imageProxy.height(), 0, 1, 0, 3); + const oiio::ROI thumbnailROI(0, imageThumbnail.width(), 0, imageThumbnail.height(), 0, 1, 0, 3); - const std::string basename = fs::path(view.getImage().getImagePath()).stem().string(); + oiio::ImageBufAlgo::resample(proxyBuf, imageBuf, false, proxyROI); // no interpolation + oiio::ImageBufAlgo::resample(thumbnailBuf, imageBuf, false, thumbnailROI); // no interpolation - image::writeImage(outputFolder + "/undistort/proxy/" + basename + "-" + std::to_string(view.getViewId()) + "-UOP.jpg", - imageProxy, image::ImageWriteOptions()); + const std::string basename = fs::path(view.getImage().getImagePath()).stem().string(); - image::writeImage(outputFolder + "/undistort/thumbnail/" + basename + "-" + std::to_string(view.getViewId()) + "-UOT.jpg", - imageThumbnail, image::ImageWriteOptions()); + image::writeImage(outputFolder + "/undistort/proxy/" + basename + "-" + std::to_string(view.getViewId()) + "-UOP.jpg", + imageProxy, + image::ImageWriteOptions()); - ++progressDisplay; - } + image::writeImage(outputFolder + "/undistort/thumbnail/" + basename + "-" + std::to_string(view.getViewId()) + "-UOT.jpg", + imageThumbnail, + image::ImageWriteOptions()); + + ++progressDisplay; + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportPMVS.cpp b/src/software/export/main_exportPMVS.cpp index cf27af3c0c..1dd696ce6d 100644 --- a/src/software/export/main_exportPMVS.cpp +++ b/src/software/export/main_exportPMVS.cpp @@ -35,305 +35,273 @@ using namespace aliceVision::sfmData; namespace po = boost::program_options; namespace fs = std::filesystem; -bool exportToPMVSFormat( - const SfMData & sfm_data, - const std::string & sOutDirectory, //Output PMVS files folder - const int downsampling_factor, - const int CPU_core_count, - const bool b_VisData = true - ) +bool exportToPMVSFormat(const SfMData& sfm_data, + const std::string& sOutDirectory, // Output PMVS files folder + const int downsampling_factor, + const int CPU_core_count, + const bool b_VisData = true) { - bool bOk = true; - if (!fs::exists(sOutDirectory)) - { - fs::create_directory(sOutDirectory); - bOk = fs::is_directory(sOutDirectory); - } - - // Create basis folder structure - fs::create_directory(fs::path(sOutDirectory) / std::string("models")); - fs::create_directory(fs::path(sOutDirectory) / std::string("txt")); - fs::create_directory(fs::path(sOutDirectory) / std::string("visualize")); - - if (bOk && - fs::is_directory(fs::path(sOutDirectory) / std::string("models")) && - fs::is_directory(fs::path(sOutDirectory) / std::string("txt")) && - fs::is_directory(fs::path(sOutDirectory) / std::string("visualize"))) - { - bOk = true; - } - else { - std::cerr << "Cannot access to one of the desired output folder" << std::endl; - } - - if (bOk) - { - auto progressDisplay = system::createConsoleProgressDisplay(sfm_data.getViews().size() * 2, - std::cout); - - // Since PMVS requires contiguous camera index, and that some views can have some missing poses, - // we reindex the poses to ensure a contiguous pose list. - HashMap map_viewIdToContiguous; - - // Export valid views as Projective Cameras: - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) + bool bOk = true; + if (!fs::exists(sOutDirectory)) { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - const Pose3 pose = sfm_data.getPose(*view).getTransform(); - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - - // View Id re-indexing - map_viewIdToContiguous.insert(std::make_pair(view->getViewId(), map_viewIdToContiguous.size())); - - std::shared_ptr cam = iterIntrinsic->second; - std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); - if (!camPinHole) { - ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); - continue; - } - - // We have a valid view with a corresponding camera & pose - const Mat34 P = camPinHole->getProjectiveEquivalent(pose); - std::ostringstream os; - os << std::setw(8) << std::setfill('0') << map_viewIdToContiguous[view->getViewId()]; - std::ofstream file((fs::path(sOutDirectory) / std::string("txt") / (os.str() + ".txt")).string()); - file << "CONTOUR" << os.widen('\n') - << P.row(0) <<"\n"<< P.row(1) <<"\n"<< P.row(2) << os.widen('\n'); - file.close(); + fs::create_directory(sOutDirectory); + bOk = fs::is_directory(sOutDirectory); } - // Export (calibrated) views as undistorted images - Image image, image_ud; - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) + // Create basis folder structure + fs::create_directory(fs::path(sOutDirectory) / std::string("models")); + fs::create_directory(fs::path(sOutDirectory) / std::string("txt")); + fs::create_directory(fs::path(sOutDirectory) / std::string("visualize")); + + if (bOk && fs::is_directory(fs::path(sOutDirectory) / std::string("models")) && fs::is_directory(fs::path(sOutDirectory) / std::string("txt")) && + fs::is_directory(fs::path(sOutDirectory) / std::string("visualize"))) { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - - // We have a valid view with a corresponding camera & pose - const std::string srcImage = view->getImage().getImagePath(); - std::ostringstream os; - os << std::setw(8) << std::setfill('0') << map_viewIdToContiguous[view->getViewId()]; - const std::string dstImage = (fs::path(sOutDirectory) / std::string("visualize") / (os.str() + ".jpg")).string(); - const IntrinsicBase * cam = iterIntrinsic->second.get(); - if (cam->isValid() && cam->hasDistortion()) - { - // undistort the image and save it - readImage( srcImage, image, image::EImageColorSpace::NO_CONVERSION); - UndistortImage(image, cam, image_ud, BLACK); - writeImage(dstImage, image_ud, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); - } - else // (no distortion) - { - // copy the image if extension match - if (fs::path(srcImage).extension() == ".JPG" || - fs::path(srcImage).extension() == ".jpg") - { - fs::copy_file(srcImage, dstImage); - } - else - { - readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); - writeImage(dstImage, image, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); - } - } + bOk = true; + } + else + { + std::cerr << "Cannot access to one of the desired output folder" << std::endl; } - //pmvs_options.txt - std::ostringstream os; - os << "level " << downsampling_factor << os.widen('\n') - << "csize 2" << os.widen('\n') - << "threshold 0.7" << os.widen('\n') - << "wsize 7" << os.widen('\n') - << "minImageNum 3" << os.widen('\n') - << "CPU " << CPU_core_count << os.widen('\n') - << "setEdge 0" << os.widen('\n') - << "useBound 0" << os.widen('\n') - << "useVisData " << (int) b_VisData << os.widen('\n') - << "sequence -1" << os.widen('\n') - << "maxAngle 10" << os.widen('\n') - << "quad 2.0" << os.widen('\n') - << "timages -1 0 " << map_viewIdToContiguous.size() << os.widen('\n') - << "oimages 0" << os.widen('\n'); - - if (b_VisData) + if (bOk) { - std::map< IndexT, std::set > view_shared; - // From the structure observations, list the putatives pairs (symmetric) - for (Landmarks::const_iterator itL = sfm_data.getLandmarks().begin(); - itL != sfm_data.getLandmarks().end(); ++itL) - { - const Landmark & landmark = itL->second; - const Observations & observations = landmark.getObservations(); - for (Observations::const_iterator itOb = observations.begin(); - itOb != observations.end(); ++itOb) + auto progressDisplay = system::createConsoleProgressDisplay(sfm_data.getViews().size() * 2, std::cout); + + // Since PMVS requires contiguous camera index, and that some views can have some missing poses, + // we reindex the poses to ensure a contiguous pose list. + HashMap map_viewIdToContiguous; + + // Export valid views as Projective Cameras: + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) + { + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + const Pose3 pose = sfm_data.getPose(*view).getTransform(); + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + + // View Id re-indexing + map_viewIdToContiguous.insert(std::make_pair(view->getViewId(), map_viewIdToContiguous.size())); + + std::shared_ptr cam = iterIntrinsic->second; + std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); + if (!camPinHole) + { + ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); + continue; + } + + // We have a valid view with a corresponding camera & pose + const Mat34 P = camPinHole->getProjectiveEquivalent(pose); + std::ostringstream os; + os << std::setw(8) << std::setfill('0') << map_viewIdToContiguous[view->getViewId()]; + std::ofstream file((fs::path(sOutDirectory) / std::string("txt") / (os.str() + ".txt")).string()); + file << "CONTOUR" << os.widen('\n') << P.row(0) << "\n" << P.row(1) << "\n" << P.row(2) << os.widen('\n'); + file.close(); + } + + // Export (calibrated) views as undistorted images + Image image, image_ud; + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter, ++progressDisplay) { - const IndexT viewId = itOb->first; - Observations::const_iterator itOb2 = itOb; - ++itOb2; - for (; itOb2 != observations.end(); ++itOb2) - { - const IndexT viewId2 = itOb2->first; - view_shared[map_viewIdToContiguous[viewId]].insert(map_viewIdToContiguous[viewId2]); - view_shared[map_viewIdToContiguous[viewId2]].insert(map_viewIdToContiguous[viewId]); - } + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + + // We have a valid view with a corresponding camera & pose + const std::string srcImage = view->getImage().getImagePath(); + std::ostringstream os; + os << std::setw(8) << std::setfill('0') << map_viewIdToContiguous[view->getViewId()]; + const std::string dstImage = (fs::path(sOutDirectory) / std::string("visualize") / (os.str() + ".jpg")).string(); + const IntrinsicBase* cam = iterIntrinsic->second.get(); + if (cam->isValid() && cam->hasDistortion()) + { + // undistort the image and save it + readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); + UndistortImage(image, cam, image_ud, BLACK); + writeImage(dstImage, image_ud, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + } + else // (no distortion) + { + // copy the image if extension match + if (fs::path(srcImage).extension() == ".JPG" || fs::path(srcImage).extension() == ".jpg") + { + fs::copy_file(srcImage, dstImage); + } + else + { + readImage(srcImage, image, image::EImageColorSpace::NO_CONVERSION); + writeImage(dstImage, image, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + } + } } - } - // Export the vis.dat file - std::ostringstream osVisData; - osVisData - << "VISDATA" << os.widen('\n') - << view_shared.size() << os.widen('\n'); // #images - // Export view shared visibility - for (std::map< IndexT, std::set >::const_iterator it = view_shared.begin(); - it != view_shared.end(); ++it) - { - const std::set & setView = it->second; - osVisData << it->first << ' ' << setView.size(); - for (std::set::const_iterator itV = setView.begin(); - itV != setView.end(); ++itV) + + // pmvs_options.txt + std::ostringstream os; + os << "level " << downsampling_factor << os.widen('\n') << "csize 2" << os.widen('\n') << "threshold 0.7" << os.widen('\n') << "wsize 7" + << os.widen('\n') << "minImageNum 3" << os.widen('\n') << "CPU " << CPU_core_count << os.widen('\n') << "setEdge 0" << os.widen('\n') + << "useBound 0" << os.widen('\n') << "useVisData " << (int)b_VisData << os.widen('\n') << "sequence -1" << os.widen('\n') << "maxAngle 10" + << os.widen('\n') << "quad 2.0" << os.widen('\n') << "timages -1 0 " << map_viewIdToContiguous.size() << os.widen('\n') << "oimages 0" + << os.widen('\n'); + + if (b_VisData) { - osVisData << ' ' << *itV; + std::map> view_shared; + // From the structure observations, list the putatives pairs (symmetric) + for (Landmarks::const_iterator itL = sfm_data.getLandmarks().begin(); itL != sfm_data.getLandmarks().end(); ++itL) + { + const Landmark& landmark = itL->second; + const Observations& observations = landmark.getObservations(); + for (Observations::const_iterator itOb = observations.begin(); itOb != observations.end(); ++itOb) + { + const IndexT viewId = itOb->first; + Observations::const_iterator itOb2 = itOb; + ++itOb2; + for (; itOb2 != observations.end(); ++itOb2) + { + const IndexT viewId2 = itOb2->first; + view_shared[map_viewIdToContiguous[viewId]].insert(map_viewIdToContiguous[viewId2]); + view_shared[map_viewIdToContiguous[viewId2]].insert(map_viewIdToContiguous[viewId]); + } + } + } + // Export the vis.dat file + std::ostringstream osVisData; + osVisData << "VISDATA" << os.widen('\n') << view_shared.size() << os.widen('\n'); // #images + // Export view shared visibility + for (std::map>::const_iterator it = view_shared.begin(); it != view_shared.end(); ++it) + { + const std::set& setView = it->second; + osVisData << it->first << ' ' << setView.size(); + for (std::set::const_iterator itV = setView.begin(); itV != setView.end(); ++itV) + { + osVisData << ' ' << *itV; + } + osVisData << os.widen('\n'); + } + std::ofstream file((fs::path(sOutDirectory) / "vis.dat").string()); + file << osVisData.str(); + file.close(); } - osVisData << os.widen('\n'); - } - std::ofstream file((fs::path(sOutDirectory) / "vis.dat").string()); - file << osVisData.str(); - file.close(); - } - std::ofstream file((fs::path(sOutDirectory) / "pmvs_options.txt").string()); - file << os.str(); - file.close(); - } - return bOk; + std::ofstream file((fs::path(sOutDirectory) / "pmvs_options.txt").string()); + file << os.str(); + file.close(); + } + return bOk; } -bool exportToBundlerFormat( - const SfMData & sfm_data, - const std::string & sOutFile, //Output Bundle.rd.out file - const std::string & sOutListFile) //Output Bundler list.txt file +bool exportToBundlerFormat(const SfMData& sfm_data, + const std::string& sOutFile, // Output Bundle.rd.out file + const std::string& sOutListFile) // Output Bundler list.txt file { - std::ofstream os(sOutFile); - std::ofstream osList(sOutListFile); - if (! os.is_open() || ! osList.is_open()) - { - return false; - } - else - { - // Since PMVS requires contiguous camera index, and that some views can have some missing poses, - // we reindex the poses to ensure a contiguous pose list. - HashMap map_viewIdToContiguous; - - // Count the number of valid cameras and re-index the viewIds - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter) + std::ofstream os(sOutFile); + std::ofstream osList(sOutListFile); + if (!os.is_open() || !osList.is_open()) { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; - - // View Id re-indexing - map_viewIdToContiguous.insert(std::make_pair(view->getViewId(), map_viewIdToContiguous.size())); + return false; } - - // Fill the "Bundle file" - os << "# Bundle file v0.3" << os.widen('\n') - << map_viewIdToContiguous.size() << " " << sfm_data.getLandmarks().size() << os.widen('\n'); - - // Export camera properties & image filenames - for(Views::const_iterator iter = sfm_data.getViews().begin(); - iter != sfm_data.getViews().end(); ++iter) + else { - const View * view = iter->second.get(); - if (!sfm_data.isPoseAndIntrinsicDefined(view)) - continue; + // Since PMVS requires contiguous camera index, and that some views can have some missing poses, + // we reindex the poses to ensure a contiguous pose list. + HashMap map_viewIdToContiguous; - const Pose3 pose = sfm_data.getPose(*view).getTransform(); - Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); - - // Must export focal, k1, k2, R, t + // Count the number of valid cameras and re-index the viewIds + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter) + { + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; - Mat3 D; - D.fill(0.0); - D .diagonal() = Vec3(1., -1., -1.); // mapping between our pinhole and Bundler convention - const double k1 = 0.0, k2 = 0.0; // distortion already removed + // View Id re-indexing + map_viewIdToContiguous.insert(std::make_pair(view->getViewId(), map_viewIdToContiguous.size())); + } - if(isPinhole(iterIntrinsic->second.get()->getType())) - { - const Pinhole * cam = dynamic_cast(iterIntrinsic->second.get()); + // Fill the "Bundle file" + os << "# Bundle file v0.3" << os.widen('\n') << map_viewIdToContiguous.size() << " " << sfm_data.getLandmarks().size() << os.widen('\n'); - if (cam->getFocalLengthPixX() == cam->getFocalLengthPixY()) + // Export camera properties & image filenames + for (Views::const_iterator iter = sfm_data.getViews().begin(); iter != sfm_data.getViews().end(); ++iter) { - const double focal = cam->getFocalLengthPixX(); - const Mat3 R = D * pose.rotation(); - const Vec3 t = D * pose.translation(); - - os << focal << " " << k1 << " " << k2 << os.widen('\n') //f k1 k2 - << R(0,0) << " " << R(0, 1) << " " << R(0, 2) << os.widen('\n') //R.row(0) - << R(1,0) << " " << R(1, 1) << " " << R(1, 2) << os.widen('\n') //R.row(1) - << R(2,0) << " " << R(2, 1) << " " << R(2, 2) << os.widen('\n') //R.row(2) - << t(0) << " " << t(1) << " " << t(2) << os.widen('\n'); //t - - osList << fs::path(view->getImage().getImagePath()).filename() << " 0 " << focal << os.widen('\n'); + const View* view = iter->second.get(); + if (!sfm_data.isPoseAndIntrinsicDefined(view)) + continue; + + const Pose3 pose = sfm_data.getPose(*view).getTransform(); + Intrinsics::const_iterator iterIntrinsic = sfm_data.getIntrinsics().find(view->getIntrinsicId()); + + // Must export focal, k1, k2, R, t + + Mat3 D; + D.fill(0.0); + D.diagonal() = Vec3(1., -1., -1.); // mapping between our pinhole and Bundler convention + const double k1 = 0.0, k2 = 0.0; // distortion already removed + + if (isPinhole(iterIntrinsic->second.get()->getType())) + { + const Pinhole* cam = dynamic_cast(iterIntrinsic->second.get()); + + if (cam->getFocalLengthPixX() == cam->getFocalLengthPixY()) + { + const double focal = cam->getFocalLengthPixX(); + const Mat3 R = D * pose.rotation(); + const Vec3 t = D * pose.translation(); + + os << focal << " " << k1 << " " << k2 << os.widen('\n') // f k1 k2 + << R(0, 0) << " " << R(0, 1) << " " << R(0, 2) << os.widen('\n') // R.row(0) + << R(1, 0) << " " << R(1, 1) << " " << R(1, 2) << os.widen('\n') // R.row(1) + << R(2, 0) << " " << R(2, 1) << " " << R(2, 2) << os.widen('\n') // R.row(2) + << t(0) << " " << t(1) << " " << t(2) << os.widen('\n'); // t + + osList << fs::path(view->getImage().getImagePath()).filename() << " 0 " << focal << os.widen('\n'); + } + else + { + std::cerr << "Unsupported anamorphic camera for Bundler export." << std::endl; + return false; + } + } + else + { + std::cerr << "Unsupported camera model for Bundler export." << std::endl; + return false; + } } - else + // Export structure and visibility + for (Landmarks::const_iterator iter = sfm_data.getLandmarks().begin(); iter != sfm_data.getLandmarks().end(); ++iter) { - std::cerr << "Unsupported anamorphic camera for Bundler export." << std::endl; - return false; + const Landmark& landmark = iter->second; + const Observations& observations = landmark.getObservations(); + const Vec3& X = landmark.X; + // X, color, obsCount + os << X[0] << " " << X[1] << " " << X[2] << os.widen('\n') << "255 255 255" << os.widen('\n') << observations.size() << " "; + for (Observations::const_iterator iterObs = observations.begin(); iterObs != observations.end(); ++iterObs) + { + const Observation& ob = iterObs->second; + // ViewId, FeatId, x, y + os << map_viewIdToContiguous[iterObs->first] << " " << ob.getFeatureId() << " " << ob.getX() << " " << ob.getY() << " "; + } + os << os.widen('\n'); } - } - else - { - std::cerr << "Unsupported camera model for Bundler export." << std::endl; - return false; - } - } - // Export structure and visibility - for (Landmarks::const_iterator iter = sfm_data.getLandmarks().begin(); - iter != sfm_data.getLandmarks().end(); ++iter) - { - const Landmark & landmark = iter->second; - const Observations & observations = landmark.getObservations(); - const Vec3 & X = landmark.X; - // X, color, obsCount - os << X[0] << " " << X[1] << " " << X[2] << os.widen('\n') - << "255 255 255" << os.widen('\n') - << observations.size() << " "; - for(Observations::const_iterator iterObs = observations.begin(); - iterObs != observations.end(); ++iterObs) - { - const Observation & ob = iterObs->second; - // ViewId, FeatId, x, y - os << map_viewIdToContiguous[iterObs->first] << " " << ob.getFeatureId() << " " << ob.getX() << " " << ob.getY() << " "; - } - os << os.widen('\n'); + os.close(); + osList.close(); } - os.close(); - osList.close(); - } - return true; + return true; } -int aliceVision_main(int argc, char *argv[]) +int aliceVision_main(int argc, char* argv[]) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; - int resolution = 1; - int nbCore = 8; - bool useVisData = true; + int resolution = 1; + int nbCore = 8; + bool useVisData = true; // clang-format off po::options_description requiredParams("Required parameters"); @@ -353,44 +321,35 @@ int aliceVision_main(int argc, char *argv[]) "Use visibility information."); // clang-format on - CmdLine cmdline("AliceVision exportPMVS"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // Create output dir - if (!fs::exists(outputFolder)) - fs::create_directory(outputFolder); - - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - std::cerr << std::endl - << "The input SfMData file \""<< sfmDataFilename << "\" cannot be read." << std::endl; + CmdLine cmdline("AliceVision exportPMVS"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // Create output dir + if (!fs::exists(outputFolder)) + fs::create_directory(outputFolder); + + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } + + { + exportToPMVSFormat(sfmData, (fs::path(outputFolder) / std::string("PMVS")).string(), resolution, nbCore, useVisData); + + exportToBundlerFormat(sfmData, + (fs::path(outputFolder) / std::string("PMVS") / std::string("bundle.rd.out")).string(), + (fs::path(outputFolder) / std::string("PMVS") / std::string("list.txt")).string()); + + return EXIT_SUCCESS; + } + + // Exit program return EXIT_FAILURE; - } - - { - exportToPMVSFormat(sfmData, - (fs::path(outputFolder) / std::string("PMVS")).string(), - resolution, - nbCore, - useVisData); - - exportToBundlerFormat(sfmData, - (fs::path(outputFolder) / - std::string("PMVS") / - std::string("bundle.rd.out")).string(), - (fs::path(outputFolder) / - std::string("PMVS") / - std::string("list.txt")).string()); - - return EXIT_SUCCESS; - } - - // Exit program - return EXIT_FAILURE; } diff --git a/src/software/export/main_exportTracks.cpp b/src/software/export/main_exportTracks.cpp index 033a84c4fb..345747ede0 100644 --- a/src/software/export/main_exportTracks.cpp +++ b/src/software/export/main_exportTracks.cpp @@ -44,18 +44,18 @@ using namespace svg; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char ** argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; - std::vector featuresFolders; - std::vector matchesFolders; - bool clearForks = true; - int minTrackLength = 2; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; + std::vector featuresFolders; + std::vector matchesFolders; + bool clearForks = true; + int minTrackLength = 2; - // user optional parameters - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + // user optional parameters + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); // clang-format off po::options_description requiredParams("Required parameters"); @@ -79,151 +79,150 @@ int aliceVision_main(int argc, char ** argv) feature::EImageDescriberType_informations().c_str()); // clang-format on - CmdLine cmdline("AliceVision exportTracks"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(outputFolder.empty()) - { - ALICEVISION_LOG_ERROR("It is an invalid output folder"); - return EXIT_FAILURE; - } - - // read SfM Scene (image view names) - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } - - // get imageDescriberMethodTypes - std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - - // read the features - feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid features"); - return EXIT_FAILURE; - } - - // read the matches - matching::PairwiseMatches pairwiseMatches; - if(!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid matches file"); - return EXIT_FAILURE; - } - - const std::size_t viewCount = sfmData.getViews().size(); - ALICEVISION_LOG_INFO("# views: " << viewCount); - - // compute tracks from matches - track::TracksMap mapTracks; - { - const aliceVision::matching::PairwiseMatches& map_Matches = pairwiseMatches; - track::TracksBuilder tracksBuilder; - tracksBuilder.build(map_Matches); - tracksBuilder.filter(clearForks, minTrackLength); - tracksBuilder.exportToSTL(mapTracks); - - ALICEVISION_LOG_INFO("# tracks: " << tracksBuilder.nbTracks()); - } - - // for each pair, export the matches - fs::create_directory(outputFolder); - auto myProgressBar = system::createConsoleProgressDisplay((viewCount*(viewCount-1)) / 2.0, - std::cout, "Export pairwise tracks\n"); - - for(std::size_t I = 0; I < viewCount; ++I) - { - auto itI = sfmData.getViews().begin(); - std::advance(itI, I); - - const View* viewI = itI->second.get(); - - for(std::size_t J = I+1; J < viewCount; ++J, ++myProgressBar) + CmdLine cmdline("AliceVision exportTracks"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - auto itJ = sfmData.getViews().begin(); - std::advance(itJ, J); + return EXIT_FAILURE; + } - const View* viewJ = itJ->second.get(); + if (outputFolder.empty()) + { + ALICEVISION_LOG_ERROR("It is an invalid output folder"); + return EXIT_FAILURE; + } - const std::string& viewImagePathI = viewI->getImage().getImagePath(); - const std::string& viewImagePathJ = viewJ->getImage().getImagePath(); + // read SfM Scene (image view names) + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } - const std::pair dimImageI = std::make_pair(viewI->getImage().getWidth(), viewI->getImage().getHeight()); - const std::pair dimImageJ = std::make_pair(viewJ->getImage().getWidth(), viewJ->getImage().getHeight()); + // get imageDescriberMethodTypes + std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - // get common tracks between view I and J - track::TracksMap mapTracksCommon; - std::set setImageIndex; + // read the features + feature::FeaturesPerView featuresPerView; + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid features"); + return EXIT_FAILURE; + } - setImageIndex.insert(viewI->getViewId()); - setImageIndex.insert(viewJ->getViewId()); + // read the matches + matching::PairwiseMatches pairwiseMatches; + if (!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid matches file"); + return EXIT_FAILURE; + } - getCommonTracksInImages(setImageIndex, mapTracks, mapTracksCommon); + const std::size_t viewCount = sfmData.getViews().size(); + ALICEVISION_LOG_INFO("# views: " << viewCount); - if(mapTracksCommon.empty()) - { - ALICEVISION_LOG_TRACE("no common tracks for pair (" << viewI->getViewId() << ", " << viewJ->getViewId() << ")"); - } - else - { - svgDrawer svgStream(dimImageI.first + dimImageJ.first, std::max(dimImageI.second, dimImageJ.second)); - svgStream.drawImage(viewImagePathI, dimImageI.first, dimImageI.second); - svgStream.drawImage(viewImagePathJ, dimImageJ.first, dimImageJ.second, dimImageI.first); + // compute tracks from matches + track::TracksMap mapTracks; + { + const aliceVision::matching::PairwiseMatches& map_Matches = pairwiseMatches; + track::TracksBuilder tracksBuilder; + tracksBuilder.build(map_Matches); + tracksBuilder.filter(clearForks, minTrackLength); + tracksBuilder.exportToSTL(mapTracks); - // draw link between features : - for (track::TracksMap::const_iterator tracksIt = mapTracksCommon.begin(); - tracksIt != mapTracksCommon.end(); ++tracksIt) - { - const feature::EImageDescriberType descType = tracksIt->second.descType; - assert(descType != feature::EImageDescriberType::UNINITIALIZED); - track::Track::FeatureIdPerView::const_iterator obsIt = tracksIt->second.featPerView.begin(); + ALICEVISION_LOG_INFO("# tracks: " << tracksBuilder.nbTracks()); + } - const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); - const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); + // for each pair, export the matches + fs::create_directory(outputFolder); + auto myProgressBar = system::createConsoleProgressDisplay((viewCount * (viewCount - 1)) / 2.0, std::cout, "Export pairwise tracks\n"); - const PointFeature& imaA = featuresI[obsIt->second.featureId]; - ++obsIt; - const PointFeature& imaB = featuresJ[obsIt->second.featureId]; + for (std::size_t I = 0; I < viewCount; ++I) + { + auto itI = sfmData.getViews().begin(); + std::advance(itI, I); - svgStream.drawLine(imaA.x(), imaA.y(), imaB.x()+dimImageI.first, imaB.y(), svgStyle().stroke("green", 2.0)); - } + const View* viewI = itI->second.get(); - // draw features (in two loop, in order to have the features upper the link, svg layer order): - for(track::TracksMap::const_iterator tracksIt = mapTracksCommon.begin(); - tracksIt != mapTracksCommon.end(); ++ tracksIt) + for (std::size_t J = I + 1; J < viewCount; ++J, ++myProgressBar) { - const feature::EImageDescriberType descType = tracksIt->second.descType; - assert(descType != feature::EImageDescriberType::UNINITIALIZED); - track::Track::FeatureIdPerView::const_iterator obsIt = tracksIt->second.featPerView.begin(); + auto itJ = sfmData.getViews().begin(); + std::advance(itJ, J); - const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); - const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); + const View* viewJ = itJ->second.get(); - const PointFeature& imaA = featuresI[obsIt->second.featureId]; - ++obsIt; - const PointFeature& imaB = featuresJ[obsIt->second.featureId]; + const std::string& viewImagePathI = viewI->getImage().getImagePath(); + const std::string& viewImagePathJ = viewJ->getImage().getImagePath(); - const std::string featColor = describerTypeColor(descType); + const std::pair dimImageI = std::make_pair(viewI->getImage().getWidth(), viewI->getImage().getHeight()); + const std::pair dimImageJ = std::make_pair(viewJ->getImage().getWidth(), viewJ->getImage().getHeight()); - svgStream.drawCircle(imaA.x(), imaA.y(), 3.0, svgStyle().stroke(featColor, 2.0)); - svgStream.drawCircle(imaB.x() + dimImageI.first,imaB.y(), 3.0, svgStyle().stroke(featColor, 2.0)); - } + // get common tracks between view I and J + track::TracksMap mapTracksCommon; + std::set setImageIndex; + + setImageIndex.insert(viewI->getViewId()); + setImageIndex.insert(viewJ->getViewId()); + + getCommonTracksInImages(setImageIndex, mapTracks, mapTracksCommon); + + if (mapTracksCommon.empty()) + { + ALICEVISION_LOG_TRACE("no common tracks for pair (" << viewI->getViewId() << ", " << viewJ->getViewId() << ")"); + } + else + { + svgDrawer svgStream(dimImageI.first + dimImageJ.first, std::max(dimImageI.second, dimImageJ.second)); + svgStream.drawImage(viewImagePathI, dimImageI.first, dimImageI.second); + svgStream.drawImage(viewImagePathJ, dimImageJ.first, dimImageJ.second, dimImageI.first); + + // draw link between features : + for (track::TracksMap::const_iterator tracksIt = mapTracksCommon.begin(); tracksIt != mapTracksCommon.end(); ++tracksIt) + { + const feature::EImageDescriberType descType = tracksIt->second.descType; + assert(descType != feature::EImageDescriberType::UNINITIALIZED); + track::Track::FeatureIdPerView::const_iterator obsIt = tracksIt->second.featPerView.begin(); + + const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); + const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); + + const PointFeature& imaA = featuresI[obsIt->second.featureId]; + ++obsIt; + const PointFeature& imaB = featuresJ[obsIt->second.featureId]; - fs::path outputFilename = fs::path(outputFolder) / std::string(std::to_string(viewI->getViewId()) + "_" + std::to_string(viewJ->getViewId()) + "_" + std::to_string(mapTracksCommon.size()) + ".svg"); + svgStream.drawLine(imaA.x(), imaA.y(), imaB.x() + dimImageI.first, imaB.y(), svgStyle().stroke("green", 2.0)); + } - std::ofstream svgFile(outputFilename.string()); - svgFile << svgStream.closeSvgFile().str(); - } + // draw features (in two loop, in order to have the features upper the link, svg layer order): + for (track::TracksMap::const_iterator tracksIt = mapTracksCommon.begin(); tracksIt != mapTracksCommon.end(); ++tracksIt) + { + const feature::EImageDescriberType descType = tracksIt->second.descType; + assert(descType != feature::EImageDescriberType::UNINITIALIZED); + track::Track::FeatureIdPerView::const_iterator obsIt = tracksIt->second.featPerView.begin(); + + const PointFeatures& featuresI = featuresPerView.getFeatures(viewI->getViewId(), descType); + const PointFeatures& featuresJ = featuresPerView.getFeatures(viewJ->getViewId(), descType); + + const PointFeature& imaA = featuresI[obsIt->second.featureId]; + ++obsIt; + const PointFeature& imaB = featuresJ[obsIt->second.featureId]; + + const std::string featColor = describerTypeColor(descType); + + svgStream.drawCircle(imaA.x(), imaA.y(), 3.0, svgStyle().stroke(featColor, 2.0)); + svgStream.drawCircle(imaB.x() + dimImageI.first, imaB.y(), 3.0, svgStyle().stroke(featColor, 2.0)); + } + + fs::path outputFilename = + fs::path(outputFolder) / std::string(std::to_string(viewI->getViewId()) + "_" + std::to_string(viewJ->getViewId()) + "_" + + std::to_string(mapTracksCommon.size()) + ".svg"); + + std::ofstream svgFile(outputFilename.string()); + svgFile << svgStream.closeSvgFile().str(); + } + } } - } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/export/main_exportUSD.cpp b/src/software/export/main_exportUSD.cpp index 9601e7fbb4..1cbbc52f69 100644 --- a/src/software/export/main_exportUSD.cpp +++ b/src/software/export/main_exportUSD.cpp @@ -39,23 +39,8 @@ namespace fs = std::filesystem; PXR_NAMESPACE_USING_DIRECTIVE -TF_DEFINE_PRIVATE_TOKENS( - AvUsdTokens, - (bias) \ - (colorSpace) \ - (diffuseColor) \ - (fallback) \ - (file) \ - (normal) \ - (raw) \ - (Raw) \ - (result) \ - (rgb) \ - (scale) \ - (sourceColorSpace) \ - (st) \ - (varname) -); +TF_DEFINE_PRIVATE_TOKENS(AvUsdTokens, + (bias)(colorSpace)(diffuseColor)(fallback)(file)(normal)(raw)(Raw)(result)(rgb)(scale)(sourceColorSpace)(st)(varname)); enum class EUSDFileType { @@ -76,9 +61,12 @@ EUSDFileType EUSDFileType_stringToEnum(const std::string& usdFileType) noexcept { const std::string type = boost::to_lower_copy(usdFileType); - if (type == "usda") return EUSDFileType::USDA; - if (type == "usdc") return EUSDFileType::USDC; - if (type == "usdz") return EUSDFileType::USDZ; + if (type == "usda") + return EUSDFileType::USDA; + if (type == "usdc") + return EUSDFileType::USDC; + if (type == "usdz") + return EUSDFileType::USDZ; return EUSDFileType::USDA; } @@ -97,10 +85,7 @@ std::string EUSDFileType_enumToString(const EUSDFileType usdFileType) noexcept } } -std::ostream& operator<<(std::ostream& os, EUSDFileType usdFileType) -{ - return os << EUSDFileType_enumToString(usdFileType); -} +std::ostream& operator<<(std::ostream& os, EUSDFileType usdFileType) { return os << EUSDFileType_enumToString(usdFileType); } std::istream& operator>>(std::istream& in, EUSDFileType& usdFileType) { @@ -110,7 +95,7 @@ std::istream& operator>>(std::istream& in, EUSDFileType& usdFileType) return in; } -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { system::Timer timer; @@ -191,10 +176,8 @@ int aliceVision_main(int argc, char **argv) const GfVec3d& bboxMin = bounds.GetRange().GetMin(); const GfVec3d& bboxMax = bounds.GetRange().GetMax(); - VtArray extentData { - {static_cast(bboxMin[0]), static_cast(bboxMin[1]), static_cast(bboxMin[2])}, - {static_cast(bboxMax[0]), static_cast(bboxMax[1]), static_cast(bboxMax[2])} - }; + VtArray extentData{{static_cast(bboxMin[0]), static_cast(bboxMin[1]), static_cast(bboxMin[2])}, + {static_cast(bboxMax[0]), static_cast(bboxMax[1]), static_cast(bboxMax[2])}}; extent.Set(extentData); // write topology @@ -223,9 +206,7 @@ int aliceVision_main(int argc, char **argv) for (int i = 0; i < inputMesh->normals.size(); ++i) { const Point3d& normal = inputMesh->normals[i]; - normalsData[i] = {static_cast(normal.x), - static_cast(-normal.y), - static_cast(-normal.z)}; + normalsData[i] = {static_cast(normal.x), static_cast(-normal.y), static_cast(-normal.z)}; } VtIntArray normalIndices; @@ -240,13 +221,10 @@ int aliceVision_main(int argc, char **argv) } UsdGeomPrimvarsAPI primvarsApi = UsdGeomPrimvarsAPI(mesh); - UsdGeomPrimvar uvs = primvarsApi.CreateIndexedPrimvar(TfToken("normals"), - SdfValueTypeNames->Normal3fArray, - normalsData, - normalIndices, - UsdGeomTokens->faceVarying); + UsdGeomPrimvar uvs = primvarsApi.CreateIndexedPrimvar( + TfToken("normals"), SdfValueTypeNames->Normal3fArray, normalsData, normalIndices, UsdGeomTokens->faceVarying); } - else // compute smooth vertex normals + else // compute smooth vertex normals { StaticVector normals; inputMesh->computeNormalsForPts(normals); @@ -257,9 +235,7 @@ int aliceVision_main(int argc, char **argv) for (int i = 0; i < normals.size(); ++i) { const Point3d& normal = normals[i]; - normalsData[i] = {static_cast(normal.x), - static_cast(-normal.y), - static_cast(-normal.z)}; + normalsData[i] = {static_cast(normal.x), static_cast(-normal.y), static_cast(-normal.z)}; } UsdAttribute normalsAttr = mesh.CreateNormalsAttr(); @@ -290,11 +266,8 @@ int aliceVision_main(int argc, char **argv) } UsdGeomPrimvarsAPI primvarsApi = UsdGeomPrimvarsAPI(mesh); - UsdGeomPrimvar uvs = primvarsApi.CreateIndexedPrimvar(TfToken("st"), - SdfValueTypeNames->TexCoord2fArray, - uvsData, - uvsIndices, - UsdGeomTokens->faceVarying); + UsdGeomPrimvar uvs = + primvarsApi.CreateIndexedPrimvar(TfToken("st"), SdfValueTypeNames->TexCoord2fArray, uvsData, uvsIndices, UsdGeomTokens->faceVarying); } // create material and shaders @@ -311,28 +284,24 @@ int aliceVision_main(int argc, char **argv) // add textures (only supporting diffuse and normal maps) if (texturing.material.hasTextures(mesh::Material::TextureType::DIFFUSE)) { - SdfAssetPath diffuseTexturePath {texturing.material.textureName(mesh::Material::TextureType::DIFFUSE, -1)}; + SdfAssetPath diffuseTexturePath{texturing.material.textureName(mesh::Material::TextureType::DIFFUSE, -1)}; UsdShadeShader diffuseTexture = UsdShadeShader::Define(stage, SdfPath("/root/mesh/mat/diffuseTexture")); diffuseTexture.CreateIdAttr(VtValue(UsdImagingTokens->UsdUVTexture)); - diffuseTexture.CreateInput(AvUsdTokens->st, SdfValueTypeNames->Float2) - .ConnectToSource(uvReader.ConnectableAPI(), AvUsdTokens->result); + diffuseTexture.CreateInput(AvUsdTokens->st, SdfValueTypeNames->Float2).ConnectToSource(uvReader.ConnectableAPI(), AvUsdTokens->result); diffuseTexture.CreateInput(AvUsdTokens->file, SdfValueTypeNames->Asset).Set(diffuseTexturePath); - preview.CreateInput(AvUsdTokens->diffuseColor, SdfValueTypeNames->Color3f) - .ConnectToSource(diffuseTexture.ConnectableAPI(), AvUsdTokens->rgb); + preview.CreateInput(AvUsdTokens->diffuseColor, SdfValueTypeNames->Color3f).ConnectToSource(diffuseTexture.ConnectableAPI(), AvUsdTokens->rgb); } if (texturing.material.hasTextures(mesh::Material::TextureType::NORMAL)) { - SdfAssetPath normalTexturePath {texturing.material.textureName(mesh::Material::TextureType::NORMAL, -1)}; + SdfAssetPath normalTexturePath{texturing.material.textureName(mesh::Material::TextureType::NORMAL, -1)}; UsdShadeShader normalTexture = UsdShadeShader::Define(stage, SdfPath("/root/mesh/mat/normalTexture")); normalTexture.CreateIdAttr(VtValue(UsdImagingTokens->UsdUVTexture)); - normalTexture.CreateInput(AvUsdTokens->st, SdfValueTypeNames->Float2) - .ConnectToSource(uvReader.ConnectableAPI(), AvUsdTokens->result); + normalTexture.CreateInput(AvUsdTokens->st, SdfValueTypeNames->Float2).ConnectToSource(uvReader.ConnectableAPI(), AvUsdTokens->result); UsdShadeInput file = normalTexture.CreateInput(AvUsdTokens->file, SdfValueTypeNames->Asset); file.Set(normalTexturePath); file.GetAttr().SetMetadata(AvUsdTokens->colorSpace, AvUsdTokens->Raw); - preview.CreateInput(AvUsdTokens->normal, SdfValueTypeNames->Normal3f) - .ConnectToSource(normalTexture.ConnectableAPI(), AvUsdTokens->rgb); + preview.CreateInput(AvUsdTokens->normal, SdfValueTypeNames->Normal3f).ConnectToSource(normalTexture.ConnectableAPI(), AvUsdTokens->rgb); normalTexture.CreateInput(AvUsdTokens->fallback, SdfValueTypeNames->Float4).Set(GfVec4f{0.5, 0.5, 0.5, 1.0}); normalTexture.CreateInput(AvUsdTokens->scale, SdfValueTypeNames->Float4).Set(GfVec4f{2.0, 2.0, 2.0, 0.0}); diff --git a/src/software/pipeline/main_LdrToHdrCalibration.cpp b/src/software/pipeline/main_LdrToHdrCalibration.cpp index 2973e4fd36..d87d6d3bdc 100644 --- a/src/software/pipeline/main_LdrToHdrCalibration.cpp +++ b/src/software/pipeline/main_LdrToHdrCalibration.cpp @@ -68,7 +68,8 @@ void computeLuminanceStatFromSamples(const std::vector& sample { const IndexT key = samples[i].descriptions[j].srcId; - const double lum = image::Rgb2GrayLinear(samples[i].descriptions[j].mean[0], samples[i].descriptions[j].mean[1], samples[i].descriptions[j].mean[2]); + const double lum = + image::Rgb2GrayLinear(samples[i].descriptions[j].mean[0], samples[i].descriptions[j].mean[1], samples[i].descriptions[j].mean[2]); if (luminanceInfos.find(key) == luminanceInfos.end()) { @@ -185,7 +186,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("This program recovers the Camera Response Function (CRF) from samples extracted from LDR images with multi-bracketing.\n" "AliceVision LdrToHdrCalibration"); - + cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -226,7 +227,7 @@ int aliceVision_main(int argc, char** argv) } std::set sizeOfGroups; - for(auto& group : globalGroupedViews) + for (auto& group : globalGroupedViews) { sizeOfGroups.insert(group.size()); } @@ -239,9 +240,8 @@ int aliceVision_main(int argc, char** argv) // Nothing to calibrate, export a linear CRF. calibrationMethod = ECalibrationMethod::LINEAR; } - ALICEVISION_LOG_INFO("Number of brackets automatically detected: " - << usedNbBrackets << ". It will generate " << globalGroupedViews.size() - << " HDR images."); + ALICEVISION_LOG_INFO("Number of brackets automatically detected: " << usedNbBrackets << ". It will generate " << globalGroupedViews.size() + << " HDR images."); } else { @@ -250,11 +250,11 @@ int aliceVision_main(int argc, char** argv) } std::map>>> groupedViewsPerIntrinsics; - for (const auto & group : globalGroupedViews) + for (const auto& group : globalGroupedViews) { IndexT intrinsicId = UndefinedIndexT; - for (const auto & v : group) + for (const auto& v : group) { IndexT lid = v->getIntrinsicId(); if (intrinsicId == UndefinedIndexT) @@ -278,10 +278,10 @@ int aliceVision_main(int argc, char** argv) groupedViewsPerIntrinsics[intrinsicId].push_back(group); } - for (const auto & intrinsicGroups: groupedViewsPerIntrinsics) - { + for (const auto& intrinsicGroups : groupedViewsPerIntrinsics) + { IndexT intrinsicId = intrinsicGroups.first; - const auto & groupedViews = intrinsicGroups.second; + const auto& groupedViews = intrinsicGroups.second; std::vector> v_luminanceInfos; std::vector> calibrationSamples; @@ -357,7 +357,7 @@ int aliceVision_main(int argc, char** argv) if (luminanceInfos.find(v->getViewId()) == luminanceInfos.end()) { luminanceInfo lumaInfo; - lumaInfo.exposure = -1.0; // Dummy exposure used later indicating a dummy info + lumaInfo.exposure = -1.0; // Dummy exposure used later indicating a dummy info luminanceInfos[v->getViewId()] = lumaInfo; } } @@ -426,15 +426,15 @@ int aliceVision_main(int argc, char** argv) { switch (calibrationMethod) { - case ECalibrationMethod::DEBEVEC: - calibrationWeightFunction = hdr::EFunctionType_enumToString(hdr::EFunctionType::TRIANGLE); - break; - case ECalibrationMethod::LINEAR: - case ECalibrationMethod::GROSSBERG: - case ECalibrationMethod::LAGUERRE: - default: - calibrationWeightFunction = hdr::EFunctionType_enumToString(hdr::EFunctionType::GAUSSIAN); - break; + case ECalibrationMethod::DEBEVEC: + calibrationWeightFunction = hdr::EFunctionType_enumToString(hdr::EFunctionType::TRIANGLE); + break; + case ECalibrationMethod::LINEAR: + case ECalibrationMethod::GROSSBERG: + case ECalibrationMethod::LAGUERRE: + default: + calibrationWeightFunction = hdr::EFunctionType_enumToString(hdr::EFunctionType::GAUSSIAN); + break; } } calibrationWeight.setFunction(hdr::EFunctionType_stringToEnum(calibrationWeightFunction)); @@ -448,47 +448,48 @@ int aliceVision_main(int argc, char** argv) switch (calibrationMethod) { - case ECalibrationMethod::LINEAR: - { - // Set the response function to linear - response.setLinear(); - break; - } - case ECalibrationMethod::DEBEVEC: - { - hdr::DebevecCalibrate debevec; - const float lambda = channelQuantization; - bool res = debevec.process(calibrationSamples, groupedExposures, channelQuantization, calibrationWeight, lambda, response); - if (!res) { - ALICEVISION_LOG_ERROR("Calibration failed."); - return EXIT_FAILURE; + case ECalibrationMethod::LINEAR: + { + // Set the response function to linear + response.setLinear(); + break; } + case ECalibrationMethod::DEBEVEC: + { + hdr::DebevecCalibrate debevec; + const float lambda = channelQuantization; + bool res = debevec.process(calibrationSamples, groupedExposures, channelQuantization, calibrationWeight, lambda, response); + if (!res) + { + ALICEVISION_LOG_ERROR("Calibration failed."); + return EXIT_FAILURE; + } - response.exponential(); - response.scale(); - break; - } - case ECalibrationMethod::GROSSBERG: - { - hdr::GrossbergCalibrate calibration(5); - calibration.process(calibrationSamples, groupedExposures, channelQuantization, response); - break; - } - case ECalibrationMethod::LAGUERRE: - { - hdr::LaguerreBACalibration calibration; - calibration.process(calibrationSamples, groupedExposures, channelQuantization, false, response); - break; - } - case ECalibrationMethod::AUTO: - { - ALICEVISION_LOG_ERROR("The calibration method cannot be automatically selected."); - return EXIT_FAILURE; - } + response.exponential(); + response.scale(); + break; + } + case ECalibrationMethod::GROSSBERG: + { + hdr::GrossbergCalibrate calibration(5); + calibration.process(calibrationSamples, groupedExposures, channelQuantization, response); + break; + } + case ECalibrationMethod::LAGUERRE: + { + hdr::LaguerreBACalibration calibration; + calibration.process(calibrationSamples, groupedExposures, channelQuantization, false, response); + break; + } + case ECalibrationMethod::AUTO: + { + ALICEVISION_LOG_ERROR("The calibration method cannot be automatically selected."); + return EXIT_FAILURE; + } } const std::string methodName = ECalibrationMethod_enumToString(calibrationMethod); - + const std::string baseName = (fs::path(outputResponsePath).parent_path() / std::string("response_")).string(); const std::string intrinsicName = baseName + std::to_string(intrinsicId); const std::string htmlName = intrinsicName + "_" + methodName + std::string(".html"); @@ -498,7 +499,8 @@ int aliceVision_main(int argc, char** argv) } const std::string lumastatBasename = "luminanceStatistics"; - const std::string lumastatFilename = (fs::path(outputResponsePath).parent_path() / (lumastatBasename + "_" + std::to_string(intrinsicId) + ".txt")).string(); + const std::string lumastatFilename = + (fs::path(outputResponsePath).parent_path() / (lumastatBasename + "_" + std::to_string(intrinsicId) + ".txt")).string(); std::ofstream file(lumastatFilename); if (!file) { @@ -529,7 +531,8 @@ int aliceVision_main(int argc, char** argv) } // write in file file << srcIdWithMinimalExposure << " "; - file << v_luminanceInfos[i][srcIdWithMinimalExposure].exposure << " " << v_luminanceInfos[i][srcIdWithMinimalExposure].itemNb << " "; + file << v_luminanceInfos[i][srcIdWithMinimalExposure].exposure << " " << v_luminanceInfos[i][srcIdWithMinimalExposure].itemNb + << " "; if (v_luminanceInfos[i][srcIdWithMinimalExposure].itemNb > 0) { file << v_luminanceInfos[i][srcIdWithMinimalExposure].meanLum / v_luminanceInfos[i][srcIdWithMinimalExposure].itemNb << " "; @@ -538,7 +541,8 @@ int aliceVision_main(int argc, char** argv) { file << "0.0 "; } - file << v_luminanceInfos[i][srcIdWithMinimalExposure].minLum << " " << v_luminanceInfos[i][srcIdWithMinimalExposure].maxLum << std::endl; + file << v_luminanceInfos[i][srcIdWithMinimalExposure].minLum << " " << v_luminanceInfos[i][srcIdWithMinimalExposure].maxLum + << std::endl; // erase from map v_luminanceInfos[i].erase(srcIdWithMinimalExposure); } diff --git a/src/software/pipeline/main_LdrToHdrMerge.cpp b/src/software/pipeline/main_LdrToHdrMerge.cpp index 8b4ec16fcf..745405a78f 100644 --- a/src/software/pipeline/main_LdrToHdrMerge.cpp +++ b/src/software/pipeline/main_LdrToHdrMerge.cpp @@ -54,10 +54,7 @@ std::string getHdrImagePath(const std::string& outputPath, std::size_t g, const return hdrImagePath; } -std::string getHdrMaskPath(const std::string& outputPath, - std::size_t g, - const std::string& maskname, - const std::string& rootname = "") +std::string getHdrMaskPath(const std::string& outputPath, std::size_t g, const std::string& maskname, const std::string& rootname = "") { // Output image file path std::stringstream sstream; @@ -82,7 +79,7 @@ int aliceVision_main(int argc, char** argv) bool byPass = false; bool keepSourceImageName = false; int channelQuantizationPower = 10; - int offsetRefBracketIndex = 1000; // By default, use the automatic selection + int offsetRefBracketIndex = 1000; // By default, use the automatic selection double meanTargetedLumaForMerging = 0.4; double minSignificantValue = 0.05; double maxSignificantValue = 0.995; @@ -151,7 +148,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("This program merges LDR images into HDR images.\n" "AliceVision LdrToHdrMerge"); - + cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -212,9 +209,8 @@ int aliceVision_main(int argc, char** argv) { ALICEVISION_LOG_INFO("No multi-bracketing."); } - ALICEVISION_LOG_INFO("Number of brackets automatically detected: " - << usedNbBrackets << ". It will generate " << groupedViews.size() - << " HDR images."); + ALICEVISION_LOG_INFO("Number of brackets automatically detected: " << usedNbBrackets << ". It will generate " << groupedViews.size() + << " HDR images."); } else { @@ -225,11 +221,11 @@ int aliceVision_main(int argc, char** argv) // Group all groups sharing the same intrinsic std::map>>> groupedViewsPerIntrinsics; - for (const auto & group : groupedViews) + for (const auto& group : groupedViews) { IndexT intrinsicId = UndefinedIndexT; - for (const auto & v : group) + for (const auto& v : group) { IndexT lid = v->getIntrinsicId(); if (intrinsicId == UndefinedIndexT) @@ -264,8 +260,8 @@ int aliceVision_main(int argc, char** argv) // Fusion always produces linear image. sRGB is the only non linear color space that must be changed to linear (sRGB // linear). - image::EImageColorSpace mergedColorSpace = (workingColorSpace == image::EImageColorSpace::SRGB) ? - image::EImageColorSpace::LINEAR : workingColorSpace; + image::EImageColorSpace mergedColorSpace = + (workingColorSpace == image::EImageColorSpace::SRGB) ? image::EImageColorSpace::LINEAR : workingColorSpace; // Estimate target views for each group std::map>> targetViewsPerIntrinsics; @@ -282,13 +278,16 @@ int aliceVision_main(int argc, char** argv) const int targetIndex = middleIndex + offsetRefBracketIndex; const bool isOffsetRefBracketIndexValid = (targetIndex >= 0) && (targetIndex < usedNbBrackets); - const fs::path lumaStatFilepath(fs::path(inputResponsePath).parent_path() / (std::string("luminanceStatistics") + "_" + std::to_string(intrinsicId) + ".txt")); + const fs::path lumaStatFilepath(fs::path(inputResponsePath).parent_path() / + (std::string("luminanceStatistics") + "_" + std::to_string(intrinsicId) + ".txt")); if (!fs::is_regular_file(lumaStatFilepath) && !isOffsetRefBracketIndexValid) { - ALICEVISION_LOG_ERROR("Unable to open the file '" << lumaStatFilepath.string() << "' with luminance " - "statistics. This file is needed to select the optimal exposure for the creation " - "of HDR images."); + ALICEVISION_LOG_ERROR("Unable to open the file '" + << lumaStatFilepath.string() + << "' with luminance " + "statistics. This file is needed to select the optimal exposure for the creation " + "of HDR images."); return EXIT_FAILURE; } @@ -297,16 +296,14 @@ int aliceVision_main(int argc, char** argv) { meanTargetedLumaForMerging = std::pow((meanTargetedLumaForMerging + 0.055) / 1.055, 2.2); } - targetIndexPerIntrinsics[intrinsicId] = hdr::selectTargetViews(targetViews, - groups, - offsetRefBracketIndex, - lumaStatFilepath.string(), - meanTargetedLumaForMerging); + targetIndexPerIntrinsics[intrinsicId] = + hdr::selectTargetViews(targetViews, groups, offsetRefBracketIndex, lumaStatFilepath.string(), meanTargetedLumaForMerging); if ((targetViews.empty() || targetViews.size() != groups.size()) && !isOffsetRefBracketIndexValid) { - ALICEVISION_LOG_ERROR("File '" << lumaStatFilepath.string() << "' is not valid. This file is required " - "to select the optimal exposure for the creation of HDR images."); + ALICEVISION_LOG_ERROR("File '" << lumaStatFilepath.string() + << "' is not valid. This file is required " + "to select the optimal exposure for the creation of HDR images."); return EXIT_FAILURE; } @@ -344,19 +341,19 @@ int aliceVision_main(int argc, char** argv) // If we are on the first chunk, or we are computing all the dataset // Export a new sfmData with HDR images as new Views and LDR images as ancestors // ancestorIds are similar to viewIds because viewIds are computed only from image infos - for (const auto & groupedViews : groupedViewsPerIntrinsics) + for (const auto& groupedViews : groupedViewsPerIntrinsics) { IndexT intrinsicId = groupedViews.first; - - const auto & groups = groupedViews.second; - const auto & targetViews = targetViewsPerIntrinsics[intrinsicId]; + + const auto& groups = groupedViews.second; + const auto& targetViews = targetViewsPerIntrinsics[intrinsicId]; for (int g = 0; g < groups.size(); g++, pos++) { std::shared_ptr hdrView; - const auto & group = groups[g]; - + const auto& group = groups[g]; + if (group.size() == 1) { hdrView.reset(group.at(0)->clone()); @@ -393,7 +390,7 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } } - + if (byPass) { ALICEVISION_LOG_INFO("Bypass enabled, nothing to compute."); @@ -403,12 +400,12 @@ int aliceVision_main(int argc, char** argv) int rangeEnd = rangeStart + rangeSize; int pos = 0; - for (const auto & pGroupedViews : groupedViewsPerIntrinsics) + for (const auto& pGroupedViews : groupedViewsPerIntrinsics) { IndexT intrinsicId = pGroupedViews.first; - const auto & groupedViews = pGroupedViews.second; - const auto & targetViews = targetViewsPerIntrinsics.at(intrinsicId); + const auto& groupedViews = pGroupedViews.second; + const auto& targetViews = targetViewsPerIntrinsics.at(intrinsicId); hdr::rgbCurve fusionWeight(channelQuantization); fusionWeight.setFunction(fusionWeightFunction); @@ -428,14 +425,14 @@ int aliceVision_main(int argc, char** argv) continue; } - const std::vector> & group = groupedViews[g]; + const std::vector>& group = groupedViews[g]; std::vector> images(group.size()); std::shared_ptr targetView = targetViews[g]; std::vector exposuresSetting(group.size()); // Load all images of the group - for(std::size_t i = 0; i < group.size(); ++i) + for (std::size_t i = 0; i < group.size(); ++i) { const std::string filepath = group[i]->getImage().getImagePath(); ALICEVISION_LOG_INFO("Load " << filepath); @@ -455,7 +452,7 @@ int aliceVision_main(int argc, char** argv) { options.doWBAfterDemosaicing = true; } - + image::readImage(filepath, images[i], options); exposuresSetting[i] = group[i]->getImage().getCameraExposureSetting(); @@ -483,13 +480,17 @@ int aliceVision_main(int argc, char** argv) mergingParams.minSignificantValue = minSignificantValue; mergingParams.maxSignificantValue = maxSignificantValue; mergingParams.computeLightMasks = computeLightMasks; - - merge.process(images, exposures, fusionWeight, response, HDRimage, lowLightMask, highLightMask, - noMidLightMask, mergingParams); + + merge.process(images, exposures, fusionWeight, response, HDRimage, lowLightMask, highLightMask, noMidLightMask, mergingParams); if (highlightCorrectionFactor > 0.0f) { - merge.postProcessHighlight(images, exposures, fusionWeight, response, HDRimage, - targetCameraSetting.getExposure(), highlightCorrectionFactor, + merge.postProcessHighlight(images, + exposures, + fusionWeight, + response, + HDRimage, + targetCameraSetting.getExposure(), + highlightCorrectionFactor, highlightTargetLux); } } @@ -529,12 +530,9 @@ int aliceVision_main(int argc, char** argv) if (computeLightMasks) { - const std::string hdrMaskLowLightPath = - getHdrMaskPath(outputPath, pos, "lowLight", keepSourceImageName ? p.stem().string() : ""); - const std::string hdrMaskHighLightPath = - getHdrMaskPath(outputPath, pos, "highLight", keepSourceImageName ? p.stem().string() : ""); - const std::string hdrMaskNoMidLightPath = - getHdrMaskPath(outputPath, pos, "noMidLight", keepSourceImageName ? p.stem().string() : ""); + const std::string hdrMaskLowLightPath = getHdrMaskPath(outputPath, pos, "lowLight", keepSourceImageName ? p.stem().string() : ""); + const std::string hdrMaskHighLightPath = getHdrMaskPath(outputPath, pos, "highLight", keepSourceImageName ? p.stem().string() : ""); + const std::string hdrMaskNoMidLightPath = getHdrMaskPath(outputPath, pos, "noMidLight", keepSourceImageName ? p.stem().string() : ""); image::ImageWriteOptions maskWriteOptions; maskWriteOptions.exrCompressionMethod(image::EImageExrCompression::None); diff --git a/src/software/pipeline/main_LdrToHdrSampling.cpp b/src/software/pipeline/main_LdrToHdrSampling.cpp index 5b0f2ef9ca..98c603dc31 100644 --- a/src/software/pipeline/main_LdrToHdrSampling.cpp +++ b/src/software/pipeline/main_LdrToHdrSampling.cpp @@ -18,7 +18,6 @@ #include - // HDR Related #include #include @@ -37,7 +36,6 @@ #include #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 0 @@ -103,7 +101,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("This program extracts stable samples from multiple LDR images with different bracketing.\n" "AliceVision LdrToHdrSampling"); - + cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -148,8 +146,7 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("No multi-bracketing."); return EXIT_SUCCESS; } - ALICEVISION_LOG_INFO("Number of brackets: " << usedNbBrackets << ". It will generate " - << groupedViews.size() << " HDR images."); + ALICEVISION_LOG_INFO("Number of brackets: " << usedNbBrackets << ". It will generate " << groupedViews.size() << " HDR images."); } else { @@ -182,16 +179,15 @@ int aliceVision_main(int argc, char** argv) rangeSize = groupedViews.size(); } ALICEVISION_LOG_DEBUG("Range to compute: rangeStart = " << rangeStart << ", rangeSize = " << rangeSize); - std::map>>> groupedViewsPerIntrinsics; for (std::size_t groupIdx = rangeStart; groupIdx < rangeStart + rangeSize; ++groupIdx) { - auto & group = groupedViews[groupIdx]; + auto& group = groupedViews[groupIdx]; IndexT intrinsicId = UndefinedIndexT; - for (const auto & v : group) + for (const auto& v : group) { IndexT lid = v->getIntrinsicId(); if (intrinsicId == UndefinedIndexT) @@ -215,15 +211,15 @@ int aliceVision_main(int argc, char** argv) groupedViewsPerIntrinsics[intrinsicId].push_back(group); } - for (const auto & pGroups : groupedViewsPerIntrinsics) + for (const auto& pGroups : groupedViewsPerIntrinsics) { IndexT intrinsicId = pGroups.first; - const auto & intrinsic = sfmData.getIntrinsics().at(intrinsicId); + const auto& intrinsic = sfmData.getIntrinsics().at(intrinsicId); const std::size_t width = intrinsic->w(); const std::size_t height = intrinsic->h(); - for (const auto & group : pGroups.second) + for (const auto& group : pGroups.second) { std::vector paths; std::vector exposuresSetting; @@ -235,7 +231,7 @@ int aliceVision_main(int argc, char** argv) bool first = true; IndexT firstViewId = UndefinedIndexT; - for (auto & v : group) + for (auto& v : group) { // Retrieve first ViewId to get a unique name for files as one view is only in one group if (first) @@ -266,8 +262,7 @@ int aliceVision_main(int argc, char** argv) colorProfileFileName = v->getImage().getColorProfileFileName(); ALICEVISION_LOG_INFO("Image: " << paths.back() << ", exposure: " << exposuresSetting.back() - << ", raw color interpretation: " - << ERawColorInterpretation_enumToString(rawColorInterpretation)); + << ", raw color interpretation: " << ERawColorInterpretation_enumToString(rawColorInterpretation)); } if (!sfmData::hasComparableExposures(exposuresSetting)) { @@ -283,9 +278,8 @@ int aliceVision_main(int argc, char** argv) const bool simplifiedSampling = byPass || (calibrationMethod == ECalibrationMethod::LINEAR); std::vector out_samples; - const bool res = hdr::Sampling::extractSamplesFromImages(out_samples, paths, viewIds, exposures, - width, height, channelQuantization, imgReadOptions, - params, simplifiedSampling); + const bool res = hdr::Sampling::extractSamplesFromImages( + out_samples, paths, viewIds, exposures, width, height, channelQuantization, imgReadOptions, params, simplifiedSampling); if (!res) { ALICEVISION_LOG_ERROR("Error while extracting samples from group."); @@ -295,25 +289,23 @@ int aliceVision_main(int argc, char** argv) using Accumulator = accumulator_set>; Accumulator acc_nbUsedBrackets; { - utils::Histogram histogram(1, usedNbBrackets, usedNbBrackets-1); + utils::Histogram histogram(1, usedNbBrackets, usedNbBrackets - 1); for (const hdr::ImageSample& sample : out_samples) { acc_nbUsedBrackets(sample.descriptions.size()); histogram.Add(sample.descriptions.size()); } ALICEVISION_LOG_INFO("Number of used brackets in selected samples: " - << " min: " << extract::min(acc_nbUsedBrackets) << " max: " - << extract::max(acc_nbUsedBrackets) << " mean: " << extract::mean(acc_nbUsedBrackets) - << " median: " << extract::median(acc_nbUsedBrackets) << "."); + << " min: " << extract::min(acc_nbUsedBrackets) << " max: " << extract::max(acc_nbUsedBrackets) + << " mean: " << extract::mean(acc_nbUsedBrackets) << " median: " << extract::median(acc_nbUsedBrackets) << "."); - ALICEVISION_LOG_INFO("Histogram of the number of brackets per sample: " << histogram.ToString("", 2) - << "."); + ALICEVISION_LOG_INFO("Histogram of the number of brackets per sample: " << histogram.ToString("", 2) << "."); } if (debug) { image::Image selectedPixels(width, height, true); - for (const hdr::ImageSample& sample: out_samples) + for (const hdr::ImageSample& sample : out_samples) { const float score = float(sample.descriptions.size()) / float(usedNbBrackets); const image::RGBfColor color = getColorFromJetColorMap(score); @@ -327,8 +319,9 @@ int aliceVision_main(int argc, char** argv) metadata.push_back(oiio::ParamValue("AliceVision:medianNbUsedBrackets", extract::median(acc_nbUsedBrackets))); image::writeImage((fs::path(outputFolder) / (std::to_string(firstViewId) + "_selectedPixels.png")).string(), - selectedPixels, image::ImageWriteOptions(), metadata); - + selectedPixels, + image::ImageWriteOptions(), + metadata); } // Store to file @@ -341,9 +334,9 @@ int aliceVision_main(int argc, char** argv) } const std::size_t size = out_samples.size(); - fileSamples.write((const char *)&size, sizeof(size)); + fileSamples.write((const char*)&size, sizeof(size)); - for(std::size_t i = 0; i < out_samples.size(); ++i) + for (std::size_t i = 0; i < out_samples.size(); ++i) { fileSamples << out_samples[i]; } diff --git a/src/software/pipeline/main_cameraCalibration.cpp b/src/software/pipeline/main_cameraCalibration.cpp index 5054dcdd2f..7afb93a950 100644 --- a/src/software/pipeline/main_cameraCalibration.cpp +++ b/src/software/pipeline/main_cameraCalibration.cpp @@ -109,41 +109,36 @@ int aliceVision_main(int argc, char** argv) "AliceVision cameraCalibration"); cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } int cvCalibFlags = 0 | cv::CALIB_ZERO_TANGENT_DIST; - if(nbDistortionCoef < 1 || nbDistortionCoef > 6) + if (nbDistortionCoef < 1 || nbDistortionCoef > 6) { ALICEVISION_LOG_ERROR("Only 2 or 3 radial coefficients are supported. " - "Provided number of distortion coefficients: " << std::to_string(nbDistortionCoef)); + "Provided number of distortion coefficients: " + << std::to_string(nbDistortionCoef)); return EXIT_FAILURE; } const std::array fixDistortionCoefs = { - cv::CALIB_FIX_K1, - cv::CALIB_FIX_K2, - cv::CALIB_FIX_K3, - cv::CALIB_FIX_K4, - cv::CALIB_FIX_K5, - cv::CALIB_FIX_K6 - }; - for(int i = nbDistortionCoef; i < 6; i++) + cv::CALIB_FIX_K1, cv::CALIB_FIX_K2, cv::CALIB_FIX_K3, cv::CALIB_FIX_K4, cv::CALIB_FIX_K5, cv::CALIB_FIX_K6}; + for (int i = nbDistortionCoef; i < 6; i++) { cvCalibFlags |= fixDistortionCoefs[i]; } - - if(checkerboardSize.size() != 2) + if (checkerboardSize.size() != 2) { ALICEVISION_THROW(std::logic_error, "The size of the checkerboard is not defined."); } - if((maxNbFrames != 0 && maxCalibFrames > maxNbFrames) || minInputFrames > maxCalibFrames) + if ((maxNbFrames != 0 && maxCalibFrames > maxNbFrames) || minInputFrames > maxCalibFrames) { - ALICEVISION_THROW(std::logic_error, "Check the value for maxFrames, maxCalibFrames and minInputFrames. " + ALICEVISION_THROW(std::logic_error, + "Check the value for maxFrames, maxCalibFrames and minInputFrames. " "They must be decreasing."); } @@ -162,7 +157,7 @@ int aliceVision_main(int argc, char** argv) // Create the feedProvider aliceVision::dataio::FeedProvider feed(inputPath.string()); - if(!feed.isInit()) + if (!feed.isInit()) { ALICEVISION_LOG_ERROR("Error while initializing the FeedProvider!"); return EXIT_FAILURE; @@ -180,7 +175,7 @@ int aliceVision_main(int argc, char** argv) int nbFramesToProcess = nbFrames; // Compute the discretization's step - if(maxNbFrames && feed.nbFrames() > maxNbFrames) + if (maxNbFrames && feed.nbFrames() > maxNbFrames) { step = feed.nbFrames() / (double)maxNbFrames; nbFramesToProcess = maxNbFrames; @@ -191,41 +186,37 @@ int aliceVision_main(int argc, char** argv) aliceVision::system::Timer duration; std::size_t currentFrame = 0; - while(feed.readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics)) + while (feed.readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics)) { cv::Mat viewGray; cv::eigen2cv(imageGrey.getMat(), viewGray); // Check image is correctly loaded - if(viewGray.size() == cv::Size(0, 0)) + if (viewGray.size() == cv::Size(0, 0)) { throw std::runtime_error(std::string("Invalid image: ") + currentImgName); } // Check image size is always the same - if(imageSize == cv::Size(0, 0)) + if (imageSize == cv::Size(0, 0)) { // First image: initialize the image size. imageSize = viewGray.size(); } // Check image resolutions are always the same - else if(imageSize != viewGray.size()) + else if (imageSize != viewGray.size()) { - throw std::runtime_error( - std::string( - "You cannot mix multiple image resolutions during the camera calibration. See image file: ") + - currentImgName); + throw std::runtime_error(std::string("You cannot mix multiple image resolutions during the camera calibration. See image file: ") + + currentImgName); } std::vector pointbuf; std::vector detectedId; - ALICEVISION_CERR("[" << currentFrame << "/" << nbFrames << "] (" << iInputFrame << "/" << nbFramesToProcess - << ")"); + ALICEVISION_CERR("[" << currentFrame << "/" << nbFrames << "] (" << iInputFrame << "/" << nbFramesToProcess << ")"); // Find the chosen pattern in images - const bool found = - aliceVision::calibration::findPattern(patternType, viewGray, boardSize, detectedId, pointbuf); + const bool found = aliceVision::calibration::findPattern(patternType, viewGray, boardSize, detectedId, pointbuf); - if(found) + if (found) { validFrames.push_back(currentFrame); detectedIdPerFrame.push_back(detectedId); @@ -240,7 +231,7 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_CERR("find points duration: " << aliceVision::system::prettyTime(duration.elapsedMs())); ALICEVISION_CERR("Grid detected in " << imagePoints.size() << " images on " << iInputFrame << " input images."); - if(imagePoints.empty()) + if (imagePoints.empty()) throw std::logic_error("No checkerboard detected."); std::vector remainingImagesIndexes; @@ -249,8 +240,8 @@ int aliceVision_main(int argc, char** argv) std::vector> calibImagePoints; // Select best images based on repartition in images of the calibration landmarks - aliceVision::calibration::selectBestImages(imagePoints, imageSize, maxCalibFrames, calibGridSize, calibImageScore, - calibInputFrames, calibImagePoints, remainingImagesIndexes); + aliceVision::calibration::selectBestImages( + imagePoints, imageSize, maxCalibFrames, calibGridSize, calibImageScore, calibInputFrames, calibImagePoints, remainingImagesIndexes); start = std::clock(); // Create an object which stores all the checker points of the images @@ -260,14 +251,14 @@ int aliceVision_main(int argc, char** argv) // Generate the object points coordinates aliceVision::calibration::calcChessboardCorners(templateObjectPoints, boardSize, squareSize, patternType); // Assign the corners to all items - for(std::size_t frame : calibInputFrames) + for (std::size_t frame : calibInputFrames) { // For some chessboard (ie. CCTag), we have an identification per point, // and only a sub-part of the corners may be detected. // So we only keep the visible corners from the templateObjectPoints std::vector& pointsId = detectedIdPerFrame[frame]; std::vector objectPoints(pointsId.size()); - for(size_t i = 0; i < pointsId.size(); ++i) + for (size_t i = 0; i < pointsId.size(); ++i) { objectPoints[i] = templateObjectPoints[pointsId[i]]; } @@ -284,21 +275,47 @@ int aliceVision_main(int argc, char** argv) duration.reset(); // Refinement loop of the calibration - aliceVision::calibration::calibrationIterativeOptimization( - imageSize, aspectRatio, cvCalibFlags, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, totalAvgErr, - maxTotalAvgErr, minInputFrames, calibInputFrames, calibImagePoints, calibObjectPoints, calibImageScore, - rejectInputFrames); + aliceVision::calibration::calibrationIterativeOptimization(imageSize, + aspectRatio, + cvCalibFlags, + cameraMatrix, + distCoeffs, + rvecs, + tvecs, + reprojErrs, + totalAvgErr, + maxTotalAvgErr, + minInputFrames, + calibInputFrames, + calibImagePoints, + calibObjectPoints, + calibImageScore, + rejectInputFrames); ALICEVISION_LOG_INFO("Calibration duration: " << aliceVision::system::prettyTime(duration.elapsedMs())); - aliceVision::calibration::saveCameraParams( - outputFilename, imageSize, boardSize, squareSize, aspectRatio, cvCalibFlags, cameraMatrix, distCoeffs, - writeExtrinsics ? rvecs : std::vector(), writeExtrinsics ? tvecs : std::vector(), - writeExtrinsics ? reprojErrs : std::vector(), - writePoints ? calibImagePoints : std::vector>(), totalAvgErr); - - aliceVision::calibration::exportDebug(debugSelectedImgFolder, debugRejectedImgFolder, feed, calibInputFrames, - rejectInputFrames, remainingImagesIndexes, cameraMatrix, distCoeffs, + aliceVision::calibration::saveCameraParams(outputFilename, + imageSize, + boardSize, + squareSize, + aspectRatio, + cvCalibFlags, + cameraMatrix, + distCoeffs, + writeExtrinsics ? rvecs : std::vector(), + writeExtrinsics ? tvecs : std::vector(), + writeExtrinsics ? reprojErrs : std::vector(), + writePoints ? calibImagePoints : std::vector>(), + totalAvgErr); + + aliceVision::calibration::exportDebug(debugSelectedImgFolder, + debugRejectedImgFolder, + feed, + calibInputFrames, + rejectInputFrames, + remainingImagesIndexes, + cameraMatrix, + distCoeffs, imageSize); ALICEVISION_LOG_INFO("Total duration: " << aliceVision::system::prettyTime(durationAlgo.elapsedMs())); diff --git a/src/software/pipeline/main_cameraInit.cpp b/src/software/pipeline/main_cameraInit.cpp index 15bc6f2b91..fb27fe1b87 100644 --- a/src/software/pipeline/main_cameraInit.cpp +++ b/src/software/pipeline/main_cameraInit.cpp @@ -46,7 +46,6 @@ using namespace aliceVision::sfmDataIO; namespace po = boost::program_options; namespace fs = std::filesystem; - /** * @brief Recursively list all files from a folder with a specific extension * @param[in] folderOrFile A file or folder path @@ -54,45 +53,44 @@ namespace fs = std::filesystem; * @param[out] outFiles A list of output image paths * @return true if folderOrFile have been load successfully */ -bool listFiles(const fs::path& folderOrFile, - const std::vector& extensions, - std::vector& resources) +bool listFiles(const fs::path& folderOrFile, const std::vector& extensions, std::vector& resources) { - if(fs::is_regular_file(folderOrFile)) - { - std::string fileExtension = fs::path(folderOrFile).extension().string(); - std::transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::tolower); - for(const std::string& extension: extensions) - { - if(fileExtension == extension) - { - resources.push_back(folderOrFile); - return true; - } - } - } - else if(fs::is_directory(folderOrFile)) - { - // list all files of the folder - std::vector allFiles; - - fs::directory_iterator endItr; - for(fs::directory_iterator itr(folderOrFile); itr != endItr; ++itr) - allFiles.push_back(itr->path().string()); - - bool hasFile = false; - for(const std::string& filePath: allFiles) - { - if(listFiles(filePath, extensions, resources)) - hasFile = true; - } - return hasFile; - } - ALICEVISION_LOG_ERROR("'" << folderOrFile << "' is not a valid folder or file path."); - return false; + if (fs::is_regular_file(folderOrFile)) + { + std::string fileExtension = fs::path(folderOrFile).extension().string(); + std::transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::tolower); + for (const std::string& extension : extensions) + { + if (fileExtension == extension) + { + resources.push_back(folderOrFile); + return true; + } + } + } + else if (fs::is_directory(folderOrFile)) + { + // list all files of the folder + std::vector allFiles; + + fs::directory_iterator endItr; + for (fs::directory_iterator itr(folderOrFile); itr != endItr; ++itr) + allFiles.push_back(itr->path().string()); + + bool hasFile = false; + for (const std::string& filePath : allFiles) + { + if (listFiles(filePath, extensions, resources)) + hasFile = true; + } + return hasFile; + } + ALICEVISION_LOG_ERROR("'" << folderOrFile << "' is not a valid folder or file path."); + return false; } -enum class EGroupCameraFallback { +enum class EGroupCameraFallback +{ GLOBAL, FOLDER, IMAGE @@ -100,33 +98,30 @@ enum class EGroupCameraFallback { inline std::string EGroupCameraFallback_enumToString(EGroupCameraFallback strategy) { - switch(strategy) - { - case EGroupCameraFallback::GLOBAL: - return "global"; - case EGroupCameraFallback::FOLDER: - return "folder"; - case EGroupCameraFallback::IMAGE: - return "image"; - } - throw std::out_of_range("Invalid GroupCameraFallback type Enum: " + std::to_string(int(strategy))); + switch (strategy) + { + case EGroupCameraFallback::GLOBAL: + return "global"; + case EGroupCameraFallback::FOLDER: + return "folder"; + case EGroupCameraFallback::IMAGE: + return "image"; + } + throw std::out_of_range("Invalid GroupCameraFallback type Enum: " + std::to_string(int(strategy))); } inline EGroupCameraFallback EGroupCameraFallback_stringToEnum(const std::string& strategy) { - if(strategy == "global") - return EGroupCameraFallback::GLOBAL; - if(strategy == "folder") - return EGroupCameraFallback::FOLDER; - if(strategy == "image") - return EGroupCameraFallback::IMAGE; - throw std::out_of_range("Invalid GroupCameraFallback type string " + strategy); + if (strategy == "global") + return EGroupCameraFallback::GLOBAL; + if (strategy == "folder") + return EGroupCameraFallback::FOLDER; + if (strategy == "image") + return EGroupCameraFallback::IMAGE; + throw std::out_of_range("Invalid GroupCameraFallback type string " + strategy); } -inline std::ostream& operator<<(std::ostream& os, EGroupCameraFallback s) -{ - return os << EGroupCameraFallback_enumToString(s); -} +inline std::ostream& operator<<(std::ostream& os, EGroupCameraFallback s) { return os << EGroupCameraFallback_enumToString(s); } inline std::istream& operator>>(std::istream& in, EGroupCameraFallback& s) { @@ -136,14 +131,15 @@ inline std::istream& operator>>(std::istream& in, EGroupCameraFallback& s) return in; } -bool rigHasUniqueFrameIds(const sfmData::SfMData & sfmData) +bool rigHasUniqueFrameIds(const sfmData::SfMData& sfmData) { std::set> unique_ids; for (auto vitem : sfmData.getViews()) { - if (!(vitem.second->isPartOfRig())) continue; + if (!(vitem.second->isPartOfRig())) + continue; - std::tuple tuple = { vitem.second->getRigId(), vitem.second->getSubPoseId(), vitem.second->getFrameId() }; + std::tuple tuple = {vitem.second->getRigId(), vitem.second->getSubPoseId(), vitem.second->getFrameId()}; if (unique_ids.find(tuple) != unique_ids.end()) { @@ -156,39 +152,38 @@ bool rigHasUniqueFrameIds(const sfmData::SfMData & sfmData) return true; } - /** * @brief Create the description of an input image dataset for AliceVision toolsuite * - Export a SfMData file with View & Intrinsic data */ -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmFilePath; - std::string imageFolder; - std::string sensorDatabasePath; - std::string lensCorrectionProfileInfo; - std::string outputFilePath; - - // user optional parameters - std::string defaultCameraModelName; - std::string allowedCameraModelsStr = "pinhole,radial1,radial3,brown,fisheye4,fisheye1"; - std::string colorProfileDatabaseDirPath; - - double defaultFocalLength = -1.0; - double defaultFieldOfView = -1.0; - double defaultFocalRatio = 1.0; - double defaultOffsetX = 0.0; - double defaultOffsetY = 0.0; - - EGroupCameraFallback groupCameraFallback = EGroupCameraFallback::FOLDER; - EViewIdMethod viewIdMethod = EViewIdMethod::METADATA; - std::string viewIdRegex = ".*?(\\d+)"; - - bool allowSingleView = false; - bool errorOnMissingColorProfile = true; - image::ERawColorInterpretation rawColorInterpretation = image::ERawColorInterpretation::LibRawWhiteBalancing; - bool lensCorrectionProfileSearchIgnoreCameraModel = true; + // command-line parameters + std::string sfmFilePath; + std::string imageFolder; + std::string sensorDatabasePath; + std::string lensCorrectionProfileInfo; + std::string outputFilePath; + + // user optional parameters + std::string defaultCameraModelName; + std::string allowedCameraModelsStr = "pinhole,radial1,radial3,brown,fisheye4,fisheye1"; + std::string colorProfileDatabaseDirPath; + + double defaultFocalLength = -1.0; + double defaultFieldOfView = -1.0; + double defaultFocalRatio = 1.0; + double defaultOffsetX = 0.0; + double defaultOffsetY = 0.0; + + EGroupCameraFallback groupCameraFallback = EGroupCameraFallback::FOLDER; + EViewIdMethod viewIdMethod = EViewIdMethod::METADATA; + std::string viewIdRegex = ".*?(\\d+)"; + + bool allowSingleView = false; + bool errorOnMissingColorProfile = true; + image::ERawColorInterpretation rawColorInterpretation = image::ERawColorInterpretation::LibRawWhiteBalancing; + bool lensCorrectionProfileSearchIgnoreCameraModel = true; // clang-format off po::options_description requiredParams("Required parameters"); @@ -245,680 +240,698 @@ int aliceVision_main(int argc, char **argv) "Warning: if a single view is process, the output file can't be use in many other programs."); // clang-format on - CmdLine cmdline("AliceVision cameraInit"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // set user camera model - camera::EINTRINSIC defaultCameraModel = camera::EINTRINSIC::UNKNOWN; - if(!defaultCameraModelName.empty()) - defaultCameraModel = camera::EINTRINSIC_stringToEnum(defaultCameraModelName); - - // check user choose at least one input option - if(imageFolder.empty() && sfmFilePath.empty()) - { - ALICEVISION_LOG_ERROR("Program need -i or --imageFolder option" << std::endl << "No input images."); - return EXIT_FAILURE; - } - - // check user don't choose both input options - if(!imageFolder.empty() && !sfmFilePath.empty()) - { - ALICEVISION_LOG_ERROR("Cannot combine -i and --imageFolder options"); - return EXIT_FAILURE; - } - - // check input folder - if(!imageFolder.empty() && !fs::exists(imageFolder) && !fs::is_directory(imageFolder)) - { - ALICEVISION_LOG_ERROR("The input folder doesn't exist"); - return EXIT_FAILURE; - } - - // check sfm file - if(!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) - { - ALICEVISION_LOG_ERROR("The input sfm file doesn't exist"); - return EXIT_FAILURE; - } - - // check output string - if(outputFilePath.empty()) - { - ALICEVISION_LOG_ERROR("Invalid output"); - return EXIT_FAILURE; - } - - // ensure output folder exists - { - const std::string outputFolderPart = fs::path(outputFilePath).parent_path().string(); - - if(!outputFolderPart.empty() && !fs::exists(outputFolderPart)) - { - if(!fs::create_directory(outputFolderPart)) - { - ALICEVISION_LOG_ERROR("Cannot create output folder"); + CmdLine cmdline("AliceVision cameraInit"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // set user camera model + camera::EINTRINSIC defaultCameraModel = camera::EINTRINSIC::UNKNOWN; + if (!defaultCameraModelName.empty()) + defaultCameraModel = camera::EINTRINSIC_stringToEnum(defaultCameraModelName); + + // check user choose at least one input option + if (imageFolder.empty() && sfmFilePath.empty()) + { + ALICEVISION_LOG_ERROR("Program need -i or --imageFolder option" << std::endl << "No input images."); return EXIT_FAILURE; - } - } - } - - if(defaultFocalLength > 0 && defaultFieldOfView > 0) - { - ALICEVISION_LOG_ERROR("Cannot combine --defaultFocalLength --defaultFieldOfView options"); - return EXIT_FAILURE; - } - - if (defaultFocalRatio <= 0.0) - { - ALICEVISION_LOG_ERROR("Focal Ratio needs to be a positive value: " << defaultFocalRatio); - return EXIT_FAILURE; - } - - // check sensor database - std::vector sensorDatabase; - if (sensorDatabasePath.empty()) - { - const auto root = image::getAliceVisionRoot(); - if (root.empty()) - { - ALICEVISION_LOG_WARNING("ALICEVISION_ROOT is not defined, default sensor database cannot be accessed."); - } - else - { - sensorDatabasePath = root + "/share/aliceVision/cameraSensors.db"; - } - } - - if(!sensorDatabasePath.empty() && !sensorDB::parseDatabase(sensorDatabasePath, sensorDatabase)) - { - ALICEVISION_LOG_ERROR("Invalid input sensor database '" << sensorDatabasePath << "', please specify a valid file."); - return EXIT_FAILURE; - } - - camera::EINTRINSIC allowedCameraModels = camera::EINTRINSIC_parseStringToBitmask(allowedCameraModelsStr); - - // use current time as seed for random generator for intrinsic Id without metadata - std::srand(std::time(0)); - - std::vector noMetadataImagePaths; // imagePaths - std::map> intrinsicsSetFromFocal35mm; // key imagePath value (sensor width, focal length) - std::vector missingDeviceUID; - std::map, std::string> unknownSensors; // key (make,model), value (first imagePath) - std::map, std::pair> unsureSensors; // key (make,model), value (first imagePath,datasheet) - std::map> detectedRigs; // key rigId, value (subPoseId, nbPose) - - sfmData::SfMData sfmData; - - // number of views with an initialized intrinsic - std::size_t completeViewCount = 0; - // number of views with LCP data used to initialize intrinsics - std::size_t lcpGeometryViewCount = 0; - // number of views with LCP data used to add vignetting params in metadata - std::size_t lcpVignettingViewCount = 0; - // number of views with LCP data used to add chromatic aberration params in metadata - std::size_t lcpChromaticViewCount = 0; - - // load known informations - if(imageFolder.empty()) - { - // fill SfMData from the JSON file - loadJSON(sfmData, sfmFilePath, ESfMData(VIEWS|INTRINSICS|EXTRINSICS), true, viewIdMethod, viewIdRegex); - } - else - { - // fill SfMData with the images in the input folder - sfmData::Views& views = sfmData.getViews(); - std::vector imagePaths; - - if(listFiles(imageFolder, image::getSupportedExtensions(), imagePaths)) - { - #pragma omp parallel for - for(int i = 0; i < imagePaths.size(); ++i) - { - auto view = std::make_shared(imagePaths.at(i).string()); - updateIncompleteView(*view, viewIdMethod, viewIdRegex); - - #pragma omp critical - { - views.emplace(view->getViewId(), view); - } - } - } - else { - return EXIT_FAILURE; - } - } - - if(sfmData.getViews().empty()) - { - ALICEVISION_LOG_ERROR("Can't find views in input."); - return EXIT_FAILURE; - } - - // create missing intrinsics - auto viewPairItBegin = sfmData.getViews().begin(); - - boost::regex extractComposedNumberRegex("\\d+(?:[\\-\\:\\_\\.]\\d+)*"); - boost::regex extractNumberRegex("\\d+"); - - std::map> poseGroups; - - ALICEVISION_LOG_DEBUG("List files in the DCP database: " << colorProfileDatabaseDirPath); - char allColorProfilesFound = 1; // char type instead of bool to support usage of atomic - image::DCPDatabase dcpDatabase(colorProfileDatabaseDirPath); - if (!colorProfileDatabaseDirPath.empty()) - { - ALICEVISION_LOG_INFO(dcpDatabase.size() << " profile(s) stored in the DCP database."); - } - int viewsWithDCPMetadata = 0; + } - ALICEVISION_LOG_DEBUG("List files in the LCP database: " << lensCorrectionProfileInfo); - LCPdatabase lcpStore(lensCorrectionProfileInfo, lensCorrectionProfileSearchIgnoreCameraModel); - if (!lensCorrectionProfileInfo.empty()) - { - ALICEVISION_LOG_INFO(lcpStore.size() << " profile(s) stored in the LCP database."); - } + // check user don't choose both input options + if (!imageFolder.empty() && !sfmFilePath.empty()) + { + ALICEVISION_LOG_ERROR("Cannot combine -i and --imageFolder options"); + return EXIT_FAILURE; + } - #pragma omp parallel for - for (int i = 0; i < sfmData.getViews().size(); ++i) - { - sfmData::View& view = *(std::next(viewPairItBegin,i)->second); + // check input folder + if (!imageFolder.empty() && !fs::exists(imageFolder) && !fs::is_directory(imageFolder)) + { + ALICEVISION_LOG_ERROR("The input folder doesn't exist"); + return EXIT_FAILURE; + } - // try to detect rig structure in the input folder - const fs::path parentPath = fs::path(view.getImage().getImagePath()).parent_path(); - if(boost::starts_with(parentPath.parent_path().stem().string(), "rig")) + // check sfm file + if (!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) { - try - { - IndexT subPoseId; - std::string prefix; - std::string suffix; - if(!sfmDataIO::extractNumberFromFileStem(parentPath.stem().string(), subPoseId, prefix, suffix)) + ALICEVISION_LOG_ERROR("The input sfm file doesn't exist"); + return EXIT_FAILURE; + } + + // check output string + if (outputFilePath.empty()) + { + ALICEVISION_LOG_ERROR("Invalid output"); + return EXIT_FAILURE; + } + + // ensure output folder exists + { + const std::string outputFolderPart = fs::path(outputFilePath).parent_path().string(); + + if (!outputFolderPart.empty() && !fs::exists(outputFolderPart)) { - ALICEVISION_THROW_ERROR("Cannot find sub-pose id from image path: " << parentPath); + if (!fs::create_directory(outputFolderPart)) + { + ALICEVISION_LOG_ERROR("Cannot create output folder"); + return EXIT_FAILURE; + } } + } - std::hash hash; // TODO use boost::hash_combine - view.setRigAndSubPoseId(hash(parentPath.parent_path().string()), subPoseId); + if (defaultFocalLength > 0 && defaultFieldOfView > 0) + { + ALICEVISION_LOG_ERROR("Cannot combine --defaultFocalLength --defaultFieldOfView options"); + return EXIT_FAILURE; + } - #pragma omp critical (rig) - detectedRigs[view.getRigId()][view.getSubPoseId()]++; - } - catch(std::exception& e) - { - ALICEVISION_LOG_WARNING("Invalid rig structure for view: " << view.getImage().getImagePath() << std::endl << e.what() << std::endl << "Used as single image."); - } + if (defaultFocalRatio <= 0.0) + { + ALICEVISION_LOG_ERROR("Focal Ratio needs to be a positive value: " << defaultFocalRatio); + return EXIT_FAILURE; } - // try to detect image sequence + // check sensor database + std::vector sensorDatabase; + if (sensorDatabasePath.empty()) { - IndexT frameId; - std::string prefix; - std::string suffix; - if(sfmDataIO::extractNumberFromFileStem(fs::path(view.getImage().getImagePath()).stem().string(), frameId, prefix, suffix)) + const auto root = image::getAliceVisionRoot(); + if (root.empty()) { - view.setFrameId(frameId); + ALICEVISION_LOG_WARNING("ALICEVISION_ROOT is not defined, default sensor database cannot be accessed."); } + else + { + sensorDatabasePath = root + "/share/aliceVision/cameraSensors.db"; + } + } + + if (!sensorDatabasePath.empty() && !sensorDB::parseDatabase(sensorDatabasePath, sensorDatabase)) + { + ALICEVISION_LOG_ERROR("Invalid input sensor database '" << sensorDatabasePath << "', please specify a valid file."); + return EXIT_FAILURE; + } + + camera::EINTRINSIC allowedCameraModels = camera::EINTRINSIC_parseStringToBitmask(allowedCameraModelsStr); + + // use current time as seed for random generator for intrinsic Id without metadata + std::srand(std::time(0)); + + std::vector noMetadataImagePaths; // imagePaths + std::map> intrinsicsSetFromFocal35mm; // key imagePath value (sensor width, focal length) + std::vector missingDeviceUID; + std::map, std::string> unknownSensors; // key (make,model), value (first imagePath) + std::map, std::pair> + unsureSensors; // key (make,model), value (first imagePath,datasheet) + std::map> detectedRigs; // key rigId, value (subPoseId, nbPose) + + sfmData::SfMData sfmData; + + // number of views with an initialized intrinsic + std::size_t completeViewCount = 0; + // number of views with LCP data used to initialize intrinsics + std::size_t lcpGeometryViewCount = 0; + // number of views with LCP data used to add vignetting params in metadata + std::size_t lcpVignettingViewCount = 0; + // number of views with LCP data used to add chromatic aberration params in metadata + std::size_t lcpChromaticViewCount = 0; + + // load known informations + if (imageFolder.empty()) + { + // fill SfMData from the JSON file + loadJSON(sfmData, sfmFilePath, ESfMData(VIEWS | INTRINSICS | EXTRINSICS), true, viewIdMethod, viewIdRegex); } - - if(boost::algorithm::starts_with(parentPath.stem().string(), "ps_") || - boost::algorithm::starts_with(parentPath.stem().string(), "hdr_")) + else { - std::hash hash; - IndexT tmpPoseID = hash(parentPath.string()); // use a temporary pose Id to group the images + // fill SfMData with the images in the input folder + sfmData::Views& views = sfmData.getViews(); + std::vector imagePaths; + + if (listFiles(imageFolder, image::getSupportedExtensions(), imagePaths)) + { +#pragma omp parallel for + for (int i = 0; i < imagePaths.size(); ++i) + { + auto view = std::make_shared(imagePaths.at(i).string()); + updateIncompleteView(*view, viewIdMethod, viewIdRegex); - #pragma omp critical (poseGroups) +#pragma omp critical + { + views.emplace(view->getViewId(), view); + } + } + } + else { - poseGroups[tmpPoseID].push_back(view.getViewId()); + return EXIT_FAILURE; } } - IndexT intrinsicId = view.getIntrinsicId(); - double sensorWidth = -1; - double sensorHeight = -1; - enum class ESensorWidthSource { - FROM_DB, - FROM_METADATA_ESTIMATION, - UNKNOWN - } sensorWidthSource = ESensorWidthSource::UNKNOWN; + if (sfmData.getViews().empty()) + { + ALICEVISION_LOG_ERROR("Can't find views in input."); + return EXIT_FAILURE; + } + + // create missing intrinsics + auto viewPairItBegin = sfmData.getViews().begin(); + + boost::regex extractComposedNumberRegex("\\d+(?:[\\-\\:\\_\\.]\\d+)*"); + boost::regex extractNumberRegex("\\d+"); - double focalLengthmm = view.getImage().getMetadataFocalLength(); - const std::string& make = view.getImage().getMetadataMake(); - const std::string& model = view.getImage().getMetadataModel(); - const bool hasCameraMetadata = (!make.empty() || !model.empty()); + std::map> poseGroups; - const bool isRaw = image::isRawFormat(view.getImage().getImagePath()); + ALICEVISION_LOG_DEBUG("List files in the DCP database: " << colorProfileDatabaseDirPath); + char allColorProfilesFound = 1; // char type instead of bool to support usage of atomic + image::DCPDatabase dcpDatabase(colorProfileDatabaseDirPath); + if (!colorProfileDatabaseDirPath.empty()) + { + ALICEVISION_LOG_INFO(dcpDatabase.size() << " profile(s) stored in the DCP database."); + } + int viewsWithDCPMetadata = 0; - bool dcpError = true; + ALICEVISION_LOG_DEBUG("List files in the LCP database: " << lensCorrectionProfileInfo); + LCPdatabase lcpStore(lensCorrectionProfileInfo, lensCorrectionProfileSearchIgnoreCameraModel); + if (!lensCorrectionProfileInfo.empty()) + { + ALICEVISION_LOG_INFO(lcpStore.size() << " profile(s) stored in the LCP database."); + } - // if a color profile is required check if a dcp database exists and if one is available inside - // if yes and if metadata exist and image format is raw then update metadata with DCP info - if((rawColorInterpretation == image::ERawColorInterpretation::DcpLinearProcessing || - rawColorInterpretation == image::ERawColorInterpretation::DcpMetadata) && - hasCameraMetadata && isRaw) +#pragma omp parallel for + for (int i = 0; i < sfmData.getViews().size(); ++i) { + sfmData::View& view = *(std::next(viewPairItBegin, i)->second); - if (dcpDatabase.empty() && errorOnMissingColorProfile) + // try to detect rig structure in the input folder + const fs::path parentPath = fs::path(view.getImage().getImagePath()).parent_path(); + if (boost::starts_with(parentPath.parent_path().stem().string(), "rig")) { - ALICEVISION_LOG_ERROR("The specified DCP database for color profiles does not exist or is empty."); - // No color profile available - boost::atomic_ref{allColorProfilesFound} = 0; + try + { + IndexT subPoseId; + std::string prefix; + std::string suffix; + if (!sfmDataIO::extractNumberFromFileStem(parentPath.stem().string(), subPoseId, prefix, suffix)) + { + ALICEVISION_THROW_ERROR("Cannot find sub-pose id from image path: " << parentPath); + } + + std::hash hash; // TODO use boost::hash_combine + view.setRigAndSubPoseId(hash(parentPath.parent_path().string()), subPoseId); + +#pragma omp critical(rig) + detectedRigs[view.getRigId()][view.getSubPoseId()]++; + } + catch (std::exception& e) + { + ALICEVISION_LOG_WARNING("Invalid rig structure for view: " << view.getImage().getImagePath() << std::endl + << e.what() << std::endl + << "Used as single image."); + } } - else + + // try to detect image sequence + { + IndexT frameId; + std::string prefix; + std::string suffix; + if (sfmDataIO::extractNumberFromFileStem(fs::path(view.getImage().getImagePath()).stem().string(), frameId, prefix, suffix)) + { + view.setFrameId(frameId); + } + } + + if (boost::algorithm::starts_with(parentPath.stem().string(), "ps_") || boost::algorithm::starts_with(parentPath.stem().string(), "hdr_")) { - image::DCPProfile dcpProf; + std::hash hash; + IndexT tmpPoseID = hash(parentPath.string()); // use a temporary pose Id to group the images - #pragma omp critical (dcp) +#pragma omp critical(poseGroups) { - if (!dcpDatabase.empty() && dcpDatabase.retrieveDcpForCamera(make, model, dcpProf)) + poseGroups[tmpPoseID].push_back(view.getViewId()); + } + } + + IndexT intrinsicId = view.getIntrinsicId(); + double sensorWidth = -1; + double sensorHeight = -1; + enum class ESensorWidthSource + { + FROM_DB, + FROM_METADATA_ESTIMATION, + UNKNOWN + } sensorWidthSource = ESensorWidthSource::UNKNOWN; + + double focalLengthmm = view.getImage().getMetadataFocalLength(); + const std::string& make = view.getImage().getMetadataMake(); + const std::string& model = view.getImage().getMetadataModel(); + const bool hasCameraMetadata = (!make.empty() || !model.empty()); + + const bool isRaw = image::isRawFormat(view.getImage().getImagePath()); + + bool dcpError = true; + + // if a color profile is required check if a dcp database exists and if one is available inside + // if yes and if metadata exist and image format is raw then update metadata with DCP info + if ((rawColorInterpretation == image::ERawColorInterpretation::DcpLinearProcessing || + rawColorInterpretation == image::ERawColorInterpretation::DcpMetadata) && + hasCameraMetadata && isRaw) + { + if (dcpDatabase.empty() && errorOnMissingColorProfile) + { + ALICEVISION_LOG_ERROR("The specified DCP database for color profiles does not exist or is empty."); + // No color profile available + boost::atomic_ref{allColorProfilesFound} = 0; + } + else + { + image::DCPProfile dcpProf; + +#pragma omp critical(dcp) { - view.getImage().addDCPMetadata(dcpProf); + if (!dcpDatabase.empty() && dcpDatabase.retrieveDcpForCamera(make, model, dcpProf)) + { + view.getImage().addDCPMetadata(dcpProf); - viewsWithDCPMetadata++; + viewsWithDCPMetadata++; - dcpError = false; + dcpError = false; + } + else if (allColorProfilesFound) + { + // there is a missing color profile for at least one image + boost::atomic_ref{allColorProfilesFound} = 0; + } } - else if (allColorProfilesFound) + } + } + + if (isRaw) + { + // Store the color interpretation mode chosen for raw images in metadata, + // so all future loads of this image will be interpreted in the same way. + if (!dcpError) + { + view.getImage().addMetadata("AliceVision:rawColorInterpretation", + image::ERawColorInterpretation_enumToString(rawColorInterpretation)); + } + else + { + view.getImage().addMetadata("AliceVision:rawColorInterpretation", + image::ERawColorInterpretation_enumToString(image::ERawColorInterpretation::LibRawWhiteBalancing)); + if (!dcpDatabase.empty()) + ALICEVISION_LOG_WARNING("DCP Profile not found for image: " << view.getImage().getImagePath() + << ". Use LibRawWhiteBalancing option for raw color processing."); + } + } + + // check if the view intrinsic is already defined + if (intrinsicId != UndefinedIndexT) + { + camera::IntrinsicBase* intrinsicBase = sfmData.getIntrinsicPtr(view.getIntrinsicId()); + camera::Pinhole* intrinsic = dynamic_cast(intrinsicBase); + if (intrinsic != nullptr) + { + if (intrinsic->getFocalLengthPixX() > 0) { - // there is a missing color profile for at least one image - boost::atomic_ref{allColorProfilesFound} = 0; + // the view intrinsic is initialized + boost::atomic_ref(completeViewCount)++; + + // don't need to build a new intrinsic + continue; } } - } - } - if (isRaw) - { - // Store the color interpretation mode chosen for raw images in metadata, - // so all future loads of this image will be interpreted in the same way. - if (!dcpError) + camera::EInitMode intrinsicInitMode = camera::EInitMode::UNKNOWN; + + int errCode = view.getImage().getSensorSize(sensorDatabase, sensorWidth, sensorHeight, focalLengthmm, intrinsicInitMode, false); + + // Throw a warning at the end + if (errCode == 1) { - view.getImage().addMetadata("AliceVision:rawColorInterpretation", image::ERawColorInterpretation_enumToString(rawColorInterpretation)); +#pragma omp critical(unknownSensors) + unknownSensors.emplace(std::make_pair(make, model), view.getImage().getImagePath()); } - else + else if (errCode == 2) { - view.getImage().addMetadata("AliceVision:rawColorInterpretation", image::ERawColorInterpretation_enumToString(image::ERawColorInterpretation::LibRawWhiteBalancing)); - if (!dcpDatabase.empty()) - ALICEVISION_LOG_WARNING("DCP Profile not found for image: " << view.getImage().getImagePath() << ". Use LibRawWhiteBalancing option for raw color processing."); +#pragma omp critical(noMetadataImagePaths) + noMetadataImagePaths.emplace_back(view.getImage().getImagePath()); + } + else if (errCode == 3) + { + sensorDB::Datasheet datasheet; + sensorDB::getInfo(make, model, sensorDatabase, datasheet); +#pragma omp critical(unsureSensors) + unsureSensors.emplace(std::make_pair(make, model), std::make_pair(view.getImage().getImagePath(), datasheet)); + } + else if (errCode == 4) + { +#pragma omp critical(intrinsicsSetFromFocal35mm) + intrinsicsSetFromFocal35mm.emplace(view.getImage().getImagePath(), std::make_pair(sensorWidth, focalLengthmm)); } - } - // check if the view intrinsic is already defined - if(intrinsicId != UndefinedIndexT) - { - camera::IntrinsicBase* intrinsicBase = sfmData.getIntrinsicPtr(view.getIntrinsicId()); - camera::Pinhole* intrinsic = dynamic_cast(intrinsicBase); - if(intrinsic != nullptr) - { - if(intrinsic->getFocalLengthPixX() > 0) + // try to find an appropriate Lens Correction Profile + LCPinfo* lcpData = nullptr; + if (lcpStore.size() == 1) { - // the view intrinsic is initialized - boost::atomic_ref(completeViewCount)++; + lcpData = lcpStore.retrieveLCP(); + } + else if (!lcpStore.empty()) + { + // Find an LCP file that matches the camera model and the lens model. + const std::string& lensModel = view.getImage().getMetadataLensModel(); + const int lensID = view.getImage().getMetadataLensID(); - // don't need to build a new intrinsic - continue; + if (!make.empty() && !lensModel.empty()) + { +#pragma omp critical(lcp) + lcpData = lcpStore.findLCP(make, model, lensModel, lensID, 1); + } } - } - } - camera::EInitMode intrinsicInitMode = camera::EInitMode::UNKNOWN; + const float apertureValue = 2.f * std::log(view.getImage().getMetadataFNumber()) / std::log(2.0); + const float focusDistance = 0.f; - int errCode = view.getImage().getSensorSize(sensorDatabase, sensorWidth, sensorHeight, focalLengthmm, intrinsicInitMode, false); + LensParam lensParam; + if ((lcpData != nullptr) && !(lcpData->isEmpty())) + { + lcpData->getDistortionParams(focalLengthmm, focusDistance, lensParam); + lcpData->getVignettingParams(focalLengthmm, apertureValue, lensParam); + lcpData->getChromaticParams(focalLengthmm, focusDistance, lensParam); + } - // Throw a warning at the end - if (errCode == 1) - { - #pragma omp critical(unknownSensors) - unknownSensors.emplace(std::make_pair(make, model), view.getImage().getImagePath()); - } - else if (errCode == 2) - { - #pragma omp critical(noMetadataImagePaths) - noMetadataImagePaths.emplace_back(view.getImage().getImagePath()); - } - else if (errCode == 3) - { - sensorDB::Datasheet datasheet; - sensorDB::getInfo(make, model, sensorDatabase, datasheet); - #pragma omp critical(unsureSensors) - unsureSensors.emplace(std::make_pair(make, model), std::make_pair(view.getImage().getImagePath(), datasheet)); - } - else if (errCode == 4) - { - #pragma omp critical(intrinsicsSetFromFocal35mm) - intrinsicsSetFromFocal35mm.emplace(view.getImage().getImagePath(), std::make_pair(sensorWidth, focalLengthmm)); - } + if (lensParam.hasVignetteParams() && !lensParam.vignParams.isEmpty) + { + view.getImage().addVignettingMetadata(lensParam); + ++lcpVignettingViewCount; + } - // try to find an appropriate Lens Correction Profile - LCPinfo* lcpData = nullptr; - if (lcpStore.size() == 1) - { - lcpData = lcpStore.retrieveLCP(); - } - else if (!lcpStore.empty()) - { - // Find an LCP file that matches the camera model and the lens model. - const std::string& lensModel = view.getImage().getMetadataLensModel(); - const int lensID = view.getImage().getMetadataLensID(); + if (lensParam.hasChromaticParams() && !lensParam.ChromaticGreenParams.isEmpty) + { + view.getImage().addChromaticMetadata(lensParam); + ++lcpChromaticViewCount; + } - if (!make.empty() && !lensModel.empty()) - { - #pragma omp critical(lcp) - lcpData = lcpStore.findLCP(make, model, lensModel, lensID, 1); - } - } + // build intrinsic + std::shared_ptr intrinsicBase = getViewIntrinsic(view, + focalLengthmm, + sensorWidth, + defaultFocalLength, + defaultFieldOfView, + defaultFocalRatio, + defaultOffsetX, + defaultOffsetY, + &lensParam, + defaultCameraModel, + allowedCameraModels); - const float apertureValue = 2.f * std::log(view.getImage().getMetadataFNumber()) / std::log(2.0); - const float focusDistance = 0.f; + if (!lensParam.isEmpty()) + ++lcpGeometryViewCount; - LensParam lensParam; - if ((lcpData != nullptr) && !(lcpData->isEmpty())) - { - lcpData->getDistortionParams(focalLengthmm, focusDistance, lensParam); - lcpData->getVignettingParams(focalLengthmm, apertureValue, lensParam); - lcpData->getChromaticParams(focalLengthmm, focusDistance, lensParam); - } + std::shared_ptr intrinsic = std::dynamic_pointer_cast(intrinsicBase); - if (lensParam.hasVignetteParams() && !lensParam.vignParams.isEmpty) - { - view.getImage().addVignettingMetadata(lensParam); - ++lcpVignettingViewCount; - } + // set initialization mode + intrinsic->setInitializationMode(intrinsicInitMode); - if(lensParam.hasChromaticParams() && !lensParam.ChromaticGreenParams.isEmpty) - { - view.getImage().addChromaticMetadata(lensParam); - ++lcpChromaticViewCount; - } + // Set sensor size + intrinsicBase->setSensorWidth(sensorWidth); + intrinsicBase->setSensorHeight(sensorHeight); - // build intrinsic - std::shared_ptr intrinsicBase = getViewIntrinsic(view, focalLengthmm, sensorWidth, defaultFocalLength, defaultFieldOfView, defaultFocalRatio, - defaultOffsetX, defaultOffsetY, &lensParam, defaultCameraModel, allowedCameraModels); + if (intrinsic && intrinsic->isValid()) + { + // the view intrinsic is initialized + boost::atomic_ref(completeViewCount)++; + } - if (!lensParam.isEmpty()) - ++lcpGeometryViewCount; + // Create serial number if not already filled + if (intrinsic->serialNumber().empty()) + { + // Create custom serial number + const std::string& bodySerialNumber = view.getImage().getMetadataBodySerialNumber(); + const std::string& lensSerialNumber = view.getImage().getMetadataLensSerialNumber(); - std::shared_ptr intrinsic = std::dynamic_pointer_cast(intrinsicBase); + if (!bodySerialNumber.empty() || !lensSerialNumber.empty()) + { + // We can identify the device based on a unique ID. + intrinsic->setSerialNumber(bodySerialNumber + "_" + lensSerialNumber); + } + else + { + // We have no way to identify a camera device correctly. +#pragma omp critical(missingDeviceUID) + { + missingDeviceUID.emplace_back(view.getImage().getImagePath()); // will throw a warning message at the end + } - // set initialization mode - intrinsic->setInitializationMode(intrinsicInitMode); + // To avoid stopping the process, we fallback to a solution selected by the user: + if (groupCameraFallback == EGroupCameraFallback::FOLDER) + { + // when we don't have a serial number, the folder will become part of the device ID. + // This means that 2 images in different folder will NOT share intrinsics. + intrinsic->setSerialNumber(fs::path(view.getImage().getImagePath()).parent_path().string()); + } + else if (groupCameraFallback == EGroupCameraFallback::IMAGE) + { + // if no serial number, each view will get its own camera intrinsic parameters. + intrinsic->setSerialNumber(view.getImage().getImagePath()); + } + else if (groupCameraFallback == EGroupCameraFallback::GLOBAL) + { + // if no serial number, images with the same make/model/focal or no make/model/focal + // will be considered as a single group of camera intrinsics. + } - // Set sensor size - intrinsicBase->setSensorWidth(sensorWidth); - intrinsicBase->setSensorHeight(sensorHeight); + if (!make.empty() || !model.empty()) + { + // We have no correct way to identify a camera device, we fallback on the camera make/model. + // If you use multiple identical devices, they will be fused together incorrectly. + intrinsic->setSerialNumber(intrinsic->serialNumber() + "_" + make + "_" + model); + } - if(intrinsic && intrinsic->isValid()) - { - // the view intrinsic is initialized - boost::atomic_ref(completeViewCount)++; - } + if (view.isPartOfRig()) + { + // when we have no unique camera identifier, so for rig images, we ensure that each camera of the rig have different serial + // numbers. + intrinsic->setSerialNumber(intrinsic->serialNumber() + "_rig_" + std::to_string(view.getRigId()) + "_" + + std::to_string(view.getSubPoseId())); + } + } - // Create serial number if not already filled - if(intrinsic->serialNumber().empty()) - { - // Create custom serial number - const std::string& bodySerialNumber = view.getImage().getMetadataBodySerialNumber(); - const std::string& lensSerialNumber = view.getImage().getMetadataLensSerialNumber(); + // If we have not managed to initialize the focal length, we need to use the focalLength in mm + if (intrinsic->getScale()(0) <= 0 && focalLengthmm > 0) + { + intrinsic->setSerialNumber(intrinsic->serialNumber() + "_FocalLengthMM_" + std::to_string(focalLengthmm)); + } + } - if(!bodySerialNumber.empty() || !lensSerialNumber.empty()) - { - // We can identify the device based on a unique ID. - intrinsic->setSerialNumber(bodySerialNumber + "_" + lensSerialNumber); - } - else - { - // We have no way to identify a camera device correctly. -#pragma omp critical (missingDeviceUID) + // create intrinsic id + // group camera that share common properties (leads to more faster & stable BA). + if (intrinsicId == UndefinedIndexT) { - missingDeviceUID.emplace_back(view.getImage().getImagePath()); // will throw a warning message at the end + intrinsicId = intrinsic->hashValue(); } - // To avoid stopping the process, we fallback to a solution selected by the user: - if(groupCameraFallback == EGroupCameraFallback::FOLDER) +#pragma omp critical(intrinsics) { - // when we don't have a serial number, the folder will become part of the device ID. - // This means that 2 images in different folder will NOT share intrinsics. - intrinsic->setSerialNumber(fs::path(view.getImage().getImagePath()).parent_path().string()); + view.setIntrinsicId(intrinsicId); + sfmData.getIntrinsics().emplace(intrinsicId, intrinsicBase); } - else if(groupCameraFallback == EGroupCameraFallback::IMAGE) + } + + // create detected rigs structures + if (!detectedRigs.empty()) + { + for (const auto& subPosesPerRigId : detectedRigs) { - // if no serial number, each view will get its own camera intrinsic parameters. - intrinsic->setSerialNumber(view.getImage().getImagePath()); + const IndexT rigId = subPosesPerRigId.first; + const std::size_t nbSubPose = subPosesPerRigId.second.size(); + const std::size_t nbPoses = subPosesPerRigId.second.begin()->second; + + for (const auto& nbPosePerSubPoseId : subPosesPerRigId.second) + { + // check subPoseId + if (nbPosePerSubPoseId.first >= nbSubPose) + { + ALICEVISION_LOG_ERROR("Wrong subPoseId in rig structure: it should be an index not a random number (subPoseId: " + << nbPosePerSubPoseId.first << ", number of subposes: " << nbSubPose << ")."); + return EXIT_FAILURE; + } + if (nbPosePerSubPoseId.first < 0) + { + ALICEVISION_LOG_ERROR("Wrong subPoseId in rig structure: cannot contain negative value: " << nbPosePerSubPoseId.first); + return EXIT_FAILURE; + } + + // check nbPoses + if (nbPosePerSubPoseId.second != nbPoses) + { + ALICEVISION_LOG_WARNING("Wrong number of poses per subPose in detected rig structure (" << nbPosePerSubPoseId.second + << " != " << nbPoses << ")."); + } + } + + sfmData.getRigs().emplace(rigId, sfmData::Rig(nbSubPose)); } - else if(groupCameraFallback == EGroupCameraFallback::GLOBAL) + } + + if (!allColorProfilesFound) + { + if (errorOnMissingColorProfile) { - // if no serial number, images with the same make/model/focal or no make/model/focal - // will be considered as a single group of camera intrinsics. + ALICEVISION_LOG_ERROR("Can't find color profile for at least one input image."); + return EXIT_FAILURE; } - - if(!make.empty() || !model.empty()) + else { - // We have no correct way to identify a camera device, we fallback on the camera make/model. - // If you use multiple identical devices, they will be fused together incorrectly. - intrinsic->setSerialNumber(intrinsic->serialNumber() + "_" + make + "_" + model); + ALICEVISION_LOG_WARNING("Can't find color profile for at least one input image."); + ALICEVISION_LOG_WARNING("Images without color profiles have been decoded with the LibRawWhiteBalancing option."); } + } - if(view.isPartOfRig()) + // Update poseId for detected multi-exposure or multi-lighting images (multiple shots with the same camera pose) + if (!poseGroups.empty()) + { + for (const auto& poseGroup : poseGroups) { - // when we have no unique camera identifier, so for rig images, we ensure that each camera of the rig have different serial numbers. - intrinsic->setSerialNumber(intrinsic->serialNumber() + "_rig_" + std::to_string(view.getRigId()) + "_" + std::to_string(view.getSubPoseId())); - } - } + bool hasAmbiant = false; + + // Photometric stereo : ambiant viewId used for all pictures + for (const IndexT vId : poseGroup.second) + { + const fs::path imagePath = fs::path(sfmData.getView(vId).getImage().getImagePath()); + if (boost::algorithm::icontains(imagePath.stem().string(), "ambiant")) + { + hasAmbiant = true; + for (const auto it : poseGroup.second) + { + // Update poseId with ambiant view id + sfmData.getView(it).setPoseId(vId); + } + break; + } + } + if (!hasAmbiant) + { + // Sort views of the poseGroup per timestamps + std::vector> sortedViews; + for (const IndexT vId : poseGroup.second) + { + int64_t t = sfmData.getView(vId).getImage().getMetadataDateTimestamp(); + sortedViews.push_back(std::make_pair(t, vId)); + } + std::sort(sortedViews.begin(), sortedViews.end()); + + // Get the view which was taken at the middle of the sequence + int median = sortedViews.size() / 2; + IndexT middleViewId = sortedViews[median].second; - // If we have not managed to initialize the focal length, we need to use the focalLength in mm - if(intrinsic->getScale()(0) <= 0 && focalLengthmm > 0) - { - intrinsic->setSerialNumber(intrinsic->serialNumber() + "_FocalLengthMM_" + std::to_string(focalLengthmm)); - } + for (const auto it : sortedViews) + { + const IndexT vId = it.second; + // Update poseId with middle view id + sfmData.getView(vId).setPoseId(middleViewId); + } + } + } } - - // create intrinsic id - // group camera that share common properties (leads to more faster & stable BA). - if(intrinsicId == UndefinedIndexT) + + if (!noMetadataImagePaths.empty()) { - intrinsicId = intrinsic->hashValue(); + std::stringstream ss; + ss << "No metadata in image(s):\n"; + for (const auto& imagePath : noMetadataImagePaths) + ss << "\t- '" << imagePath << "'\n"; + ALICEVISION_LOG_DEBUG(ss.str()); } - #pragma omp critical (intrinsics) + if (!missingDeviceUID.empty()) { - view.setIntrinsicId(intrinsicId); - sfmData.getIntrinsics().emplace(intrinsicId, intrinsicBase); + ALICEVISION_LOG_WARNING( + "Some image(s) have no serial number to identify the camera/lens device.\n" + "This makes it impossible to correctly group the images by device if you have used multiple identical (same model) camera devices.\n" + "The reconstruction will assume that only one device has been used, " + "so if 2 images share the same focal length approximation they will share the same internal camera parameters.\n" + << missingDeviceUID.size() << " image(s) are concerned."); + ALICEVISION_LOG_DEBUG("The following images are concerned:\n"); + ALICEVISION_LOG_DEBUG(boost::algorithm::join(missingDeviceUID, "\n")); } - } - // create detected rigs structures - if(!detectedRigs.empty()) - { - for(const auto& subPosesPerRigId : detectedRigs) + if (!unsureSensors.empty()) { - const IndexT rigId = subPosesPerRigId.first; - const std::size_t nbSubPose = subPosesPerRigId.second.size(); - const std::size_t nbPoses = subPosesPerRigId.second.begin()->second; + ALICEVISION_LOG_WARNING("The camera found in the sensor database is slightly different for image(s):"); + for (const auto& unsureSensor : unsureSensors) + ALICEVISION_LOG_WARNING("image: '" << fs::path(unsureSensor.second.first).filename().string() << "'" << std::endl + << "\t- image camera brand: " << unsureSensor.first.first << std::endl + << "\t- image camera model: " << unsureSensor.first.second << std::endl + << "\t- sensor database camera brand: " << unsureSensor.second.second._brand << std::endl + << "\t- sensor database camera model: " << unsureSensor.second.second._model << std::endl + << "\t- sensor database camera sensor width: " << unsureSensor.second.second._sensorWidth << " mm"); + ALICEVISION_LOG_WARNING("Please check and correct camera model(s) name in the sensor database." << std::endl); + } - for(const auto& nbPosePerSubPoseId : subPosesPerRigId.second) - { - // check subPoseId - if(nbPosePerSubPoseId.first >= nbSubPose) - { - ALICEVISION_LOG_ERROR("Wrong subPoseId in rig structure: it should be an index not a random number (subPoseId: " << nbPosePerSubPoseId.first << ", number of subposes: " << nbSubPose << ")."); - return EXIT_FAILURE; - } - if(nbPosePerSubPoseId.first < 0) + if (!unknownSensors.empty()) + { + std::stringstream ss; + ss << "Sensor width doesn't exist in the sensor database for image(s):\n"; + for (const auto& unknownSensor : unknownSensors) { - ALICEVISION_LOG_ERROR("Wrong subPoseId in rig structure: cannot contain negative value: " << nbPosePerSubPoseId.first); - return EXIT_FAILURE; + ss << "\t- camera brand: " << unknownSensor.first.first << "\n" + << "\t- camera model: " << unknownSensor.first.second << "\n" + << "\t - image: " << fs::path(unknownSensor.second).filename().string() << "\n"; } + ss << "Please add camera model(s) and sensor width(s) in the database."; + + ALICEVISION_LOG_WARNING(ss.str()); + } - // check nbPoses - if(nbPosePerSubPoseId.second != nbPoses) + if (!intrinsicsSetFromFocal35mm.empty()) + { + std::stringstream ss; + ss << "Intrinsic(s) initialized from 'FocalLengthIn35mmFilm' exif metadata in image(s):\n"; + for (const auto& intrinsicSetFromFocal35mm : intrinsicsSetFromFocal35mm) { - ALICEVISION_LOG_WARNING("Wrong number of poses per subPose in detected rig structure (" << nbPosePerSubPoseId.second << " != " << nbPoses << ")."); + ss << "\t- image: " << fs::path(intrinsicSetFromFocal35mm.first).filename().string() << "\n" + << "\t - sensor width: " << intrinsicSetFromFocal35mm.second.first << "\n" + << "\t - focal length: " << intrinsicSetFromFocal35mm.second.second << "\n"; } - } + ALICEVISION_LOG_DEBUG(ss.str()); + } - sfmData.getRigs().emplace(rigId, sfmData::Rig(nbSubPose)); + if (completeViewCount < 1 || (completeViewCount < 2 && !allowSingleView)) + { + ALICEVISION_LOG_ERROR( + "At least " << std::string(allowSingleView ? "one image" : "two images") << " should have an initialized intrinsic." << std::endl + << "Check your input images metadata (brand, model, focal length, ...), more should be set and correct." << std::endl); + return EXIT_FAILURE; } - } - if (!allColorProfilesFound) - { - if (errorOnMissingColorProfile) - { - ALICEVISION_LOG_ERROR("Can't find color profile for at least one input image."); - return EXIT_FAILURE; - } - else - { - ALICEVISION_LOG_WARNING("Can't find color profile for at least one input image."); - ALICEVISION_LOG_WARNING("Images without color profiles have been decoded with the LibRawWhiteBalancing option."); - } - } + // Check unique frame id per rig element + if (!rigHasUniqueFrameIds(sfmData)) + { + ALICEVISION_LOG_ERROR("Multiple frames have the same frame id."); + return EXIT_FAILURE; + } - // Update poseId for detected multi-exposure or multi-lighting images (multiple shots with the same camera pose) - if(!poseGroups.empty()) - { - for(const auto& poseGroup : poseGroups) - { - bool hasAmbiant = false; + // store SfMData views & intrinsic data + if (!save(sfmData, outputFilePath, ESfMData(VIEWS | INTRINSICS | EXTRINSICS))) + { + return EXIT_FAILURE; + } - // Photometric stereo : ambiant viewId used for all pictures - for(const IndexT vId : poseGroup.second) - { - const fs::path imagePath = fs::path(sfmData.getView(vId).getImage().getImagePath()); - if(boost::algorithm::icontains(imagePath.stem().string(), "ambiant")) - { - hasAmbiant = true; - for(const auto it : poseGroup.second) - { - // Update poseId with ambiant view id - sfmData.getView(it).setPoseId(vId); - } - break; - } - } - if(!hasAmbiant) - { - // Sort views of the poseGroup per timestamps - std::vector> sortedViews; - for(const IndexT vId : poseGroup.second) - { - int64_t t = sfmData.getView(vId).getImage().getMetadataDateTimestamp(); - sortedViews.push_back(std::make_pair(t, vId)); - } - std::sort(sortedViews.begin(), sortedViews.end()); - - // Get the view which was taken at the middle of the sequence - int median = sortedViews.size() / 2; - IndexT middleViewId = sortedViews[median].second; - - for(const auto it : sortedViews) - { - const IndexT vId = it.second; - // Update poseId with middle view id - sfmData.getView(vId).setPoseId(middleViewId); - } - } - } - } - - if(!noMetadataImagePaths.empty()) - { - std::stringstream ss; - ss << "No metadata in image(s):\n"; - for(const auto& imagePath : noMetadataImagePaths) - ss << "\t- '" << imagePath << "'\n"; - ALICEVISION_LOG_DEBUG(ss.str()); - } - - if(!missingDeviceUID.empty()) - { - ALICEVISION_LOG_WARNING( - "Some image(s) have no serial number to identify the camera/lens device.\n" - "This makes it impossible to correctly group the images by device if you have used multiple identical (same model) camera devices.\n" - "The reconstruction will assume that only one device has been used, " - "so if 2 images share the same focal length approximation they will share the same internal camera parameters.\n" - << missingDeviceUID.size() << " image(s) are concerned."); - ALICEVISION_LOG_DEBUG("The following images are concerned:\n"); - ALICEVISION_LOG_DEBUG(boost::algorithm::join(missingDeviceUID, "\n")); - } - - if(!unsureSensors.empty()) - { - ALICEVISION_LOG_WARNING("The camera found in the sensor database is slightly different for image(s):"); - for(const auto& unsureSensor : unsureSensors) - ALICEVISION_LOG_WARNING("image: '" << fs::path(unsureSensor.second.first).filename().string() << "'" << std::endl - << "\t- image camera brand: " << unsureSensor.first.first << std::endl - << "\t- image camera model: " << unsureSensor.first.second << std::endl - << "\t- sensor database camera brand: " << unsureSensor.second.second._brand << std::endl - << "\t- sensor database camera model: " << unsureSensor.second.second._model << std::endl - << "\t- sensor database camera sensor width: " << unsureSensor.second.second._sensorWidth << " mm"); - ALICEVISION_LOG_WARNING("Please check and correct camera model(s) name in the sensor database." << std::endl); - } - - if(!unknownSensors.empty()) - { - std::stringstream ss; - ss << "Sensor width doesn't exist in the sensor database for image(s):\n"; - for(const auto& unknownSensor : unknownSensors) - { - ss << "\t- camera brand: " << unknownSensor.first.first << "\n" - << "\t- camera model: " << unknownSensor.first.second << "\n" - << "\t - image: " << fs::path(unknownSensor.second).filename().string() << "\n"; - } - ss << "Please add camera model(s) and sensor width(s) in the database."; - - ALICEVISION_LOG_WARNING(ss.str()); - } - - if(!intrinsicsSetFromFocal35mm.empty()) - { - std::stringstream ss; - ss << "Intrinsic(s) initialized from 'FocalLengthIn35mmFilm' exif metadata in image(s):\n"; - for(const auto& intrinsicSetFromFocal35mm : intrinsicsSetFromFocal35mm) - { - ss << "\t- image: " << fs::path(intrinsicSetFromFocal35mm.first).filename().string() << "\n" - << "\t - sensor width: " << intrinsicSetFromFocal35mm.second.first << "\n" - << "\t - focal length: " << intrinsicSetFromFocal35mm.second.second << "\n"; - } - ALICEVISION_LOG_DEBUG(ss.str()); - } - - if(completeViewCount < 1 || (completeViewCount < 2 && !allowSingleView)) - { - ALICEVISION_LOG_ERROR("At least " << std::string(allowSingleView ? "one image" : "two images") << " should have an initialized intrinsic." << std::endl - << "Check your input images metadata (brand, model, focal length, ...), more should be set and correct." << std::endl); - return EXIT_FAILURE; - } - - //Check unique frame id per rig element - if (!rigHasUniqueFrameIds(sfmData)) - { - ALICEVISION_LOG_ERROR("Multiple frames have the same frame id."); - return EXIT_FAILURE; - } - - // store SfMData views & intrinsic data - if(!save(sfmData, outputFilePath, ESfMData(VIEWS|INTRINSICS|EXTRINSICS))) - { - return EXIT_FAILURE; - } - - // print report - ALICEVISION_LOG_INFO("CameraInit report:" - << "\n\t- # Views: " << sfmData.getViews().size() - << "\n\t - # with focal length initialization (from metadata): " << completeViewCount - << "\n\t - # without metadata: " << noMetadataImagePaths.size() - << "\n\t - # with DCP color calibration (raw images only): " << viewsWithDCPMetadata - << "\n\t - # with LCP lens distortion initialization: " << lcpGeometryViewCount - << "\n\t - # with LCP vignetting calibration: " << lcpVignettingViewCount - << "\n\t - # with LCP chromatic aberration correction models: " << lcpChromaticViewCount - << "\n\t- # Cameras Intrinsics: " << sfmData.getIntrinsics().size()); - - return EXIT_SUCCESS; + // print report + ALICEVISION_LOG_INFO("CameraInit report:" + << "\n\t- # Views: " << sfmData.getViews().size() << "\n\t - # with focal length initialization (from metadata): " + << completeViewCount << "\n\t - # without metadata: " << noMetadataImagePaths.size() + << "\n\t - # with DCP color calibration (raw images only): " << viewsWithDCPMetadata + << "\n\t - # with LCP lens distortion initialization: " << lcpGeometryViewCount + << "\n\t - # with LCP vignetting calibration: " << lcpVignettingViewCount + << "\n\t - # with LCP chromatic aberration correction models: " << lcpChromaticViewCount + << "\n\t- # Cameras Intrinsics: " << sfmData.getIntrinsics().size()); + + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_cameraLocalization.cpp b/src/software/pipeline/main_cameraLocalization.cpp index d333a59872..05df112fde 100644 --- a/src/software/pipeline/main_cameraLocalization.cpp +++ b/src/software/pipeline/main_cameraLocalization.cpp @@ -8,7 +8,7 @@ #include #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) -#include + #include #endif #include #include @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include @@ -40,8 +40,8 @@ #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) -#include -#endif // ALICEVISION_HAVE_ALEMBIC + #include +#endif // ALICEVISION_HAVE_ALEMBIC // These constants define the current software version. // They must be updated when the command line is changed. @@ -56,76 +56,76 @@ namespace po = boost::program_options; int aliceVision_main(int argc, char** argv) { - /// the calibration file - std::string calibFile; - /// the AliceVision .json data file - std::string sfmFilePath; - /// the folder containing the descriptors - std::string descriptorsFolder; - /// the media file to localize - std::string mediaFilepath; - - /// the describer types name to use for the matching - std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - /// the preset for the feature extractor - feature::ConfigurationPreset featDescPreset; - /// the describer types to use for the matching - std::vector matchDescTypes; - /// the estimator to use for resection - robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the estimator to use for matching - robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the possible choices for the estimators as strings - const std::string str_estimatorChoices = robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) - +", "+robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); - bool refineIntrinsics = false; - /// the maximum reprojection error allowed for resection - double resectionErrorMax = 4.0; - /// the maximum reprojection error allowed for image matching with geometric validation - double matchingErrorMax = 4.0; - /// whether to use the voctreeLocalizer or cctagLocalizer - bool useVoctreeLocalizer = true; - - // voctree parameters - std::string algostring = "AllResults"; - /// number of similar images to search when querying the voctree - std::size_t numResults = 4; - /// maximum number of successfully matched similar images - std::size_t maxResults = 10; - std::size_t numCommonViews = 3; - /// the vocabulary tree file - std::string vocTreeFilepath; - /// the vocabulary tree weights file - std::string weightsFilepath; - /// Number of previous frame of the sequence to use for matching - std::size_t nbFrameBufferMatching = 10; - /// enable/disable the robust matching (geometric validation) when matching query image - /// and databases images - bool robustMatching = true; - - /// the Alembic export file - std::string exportAlembicFile = "trackedcameras.abc"; - /// the JSON export file - std::string exportJsonFile = ""; + /// the calibration file + std::string calibFile; + /// the AliceVision .json data file + std::string sfmFilePath; + /// the folder containing the descriptors + std::string descriptorsFolder; + /// the media file to localize + std::string mediaFilepath; + + /// the describer types name to use for the matching + std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + /// the preset for the feature extractor + feature::ConfigurationPreset featDescPreset; + /// the describer types to use for the matching + std::vector matchDescTypes; + /// the estimator to use for resection + robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the estimator to use for matching + robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the possible choices for the estimators as strings + const std::string str_estimatorChoices = robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) + ", " + + robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); + bool refineIntrinsics = false; + /// the maximum reprojection error allowed for resection + double resectionErrorMax = 4.0; + /// the maximum reprojection error allowed for image matching with geometric validation + double matchingErrorMax = 4.0; + /// whether to use the voctreeLocalizer or cctagLocalizer + bool useVoctreeLocalizer = true; + + // voctree parameters + std::string algostring = "AllResults"; + /// number of similar images to search when querying the voctree + std::size_t numResults = 4; + /// maximum number of successfully matched similar images + std::size_t maxResults = 10; + std::size_t numCommonViews = 3; + /// the vocabulary tree file + std::string vocTreeFilepath; + /// the vocabulary tree weights file + std::string weightsFilepath; + /// Number of previous frame of the sequence to use for matching + std::size_t nbFrameBufferMatching = 10; + /// enable/disable the robust matching (geometric validation) when matching query image + /// and databases images + bool robustMatching = true; + + /// the Alembic export file + std::string exportAlembicFile = "trackedcameras.abc"; + /// the JSON export file + std::string exportJsonFile = ""; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - // parameters for cctag localizer - std::size_t nNearestKeyFrames = 5; + // parameters for cctag localizer + std::size_t nNearestKeyFrames = 5; #endif - // parameters for the final bundle adjustment - /// If !refineIntrinsics it can run a final global bundle to refine the scene - bool globalBundle = false; - /// It does not count the distortion - bool noDistortion = false; - /// It does not refine intrinsics during BA - bool noBArefineIntrinsics = false; - /// remove the points that does not have a minimum visibility over the sequence - /// ie that are seen at least by minPointVisibility frames of the sequence - std::size_t minPointVisibility = 0; - - /// whether to save visual debug info - std::string visualDebug = ""; - int randomSeed = std::mt19937::default_seed; + // parameters for the final bundle adjustment + /// If !refineIntrinsics it can run a final global bundle to refine the scene + bool globalBundle = false; + /// It does not count the distortion + bool noDistortion = false; + /// It does not refine intrinsics during BA + bool noBArefineIntrinsics = false; + /// remove the points that does not have a minimum visibility over the sequence + /// ie that are seen at least by minPointVisibility frames of the sequence + std::size_t minPointVisibility = 0; + + /// whether to save visual debug info + std::string visualDebug = ""; + int randomSeed = std::mt19937::default_seed; // clang-format off po::options_description inputParams("Required input parameters"); @@ -221,277 +221,265 @@ int aliceVision_main(int argc, char** argv) ("outputJSON", po::value(&exportJsonFile)->default_value(exportJsonFile), "Filename for the localization results (raw data) as .json."); // clang-format on - - CmdLine cmdline("This program takes as input a media (image, image sequence, video) and a database (vocabulary tree, 3D scene data) \n" - "and returns for each frame a pose estimation for the camera.\n" - "AliceVision cameraLocalization"); - cmdline.add(inputParams); - cmdline.add(outputParams); - cmdline.add(commonParams); - cmdline.add(voctreeParams); - cmdline.add(bundleParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - std::mt19937 generator(randomSeed == -1 ? std::random_device()() : randomSeed); + CmdLine cmdline("This program takes as input a media (image, image sequence, video) and a database (vocabulary tree, 3D scene data) \n" + "and returns for each frame a pose estimation for the camera.\n" + "AliceVision cameraLocalization"); + cmdline.add(inputParams); + cmdline.add(outputParams); + cmdline.add(commonParams); + cmdline.add(voctreeParams); + cmdline.add(bundleParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } - const double defaultLoRansacMatchingError = 4.0; - const double defaultLoRansacResectionError = 4.0; - if(!robustEstimation::adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || - !robustEstimation::adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) - { - return EXIT_FAILURE; - } + std::mt19937 generator(randomSeed == -1 ? std::random_device()() : randomSeed); - // Init descTypes from command-line string - matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); + const double defaultLoRansacMatchingError = 4.0; + const double defaultLoRansacResectionError = 4.0; + if (!robustEstimation::adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || + !robustEstimation::adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) + { + return EXIT_FAILURE; + } + + // Init descTypes from command-line string + matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); - // decide the localizer to use based on the type of feature + // decide the localizer to use based on the type of feature #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - useVoctreeLocalizer = !(matchDescTypes.size() == 1 && - ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || - (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); + useVoctreeLocalizer = !(matchDescTypes.size() == 1 && ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || + (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); #endif - - // the bundle adjustment can be run for now only if the refine intrinsics option is not set - globalBundle = (globalBundle && !refineIntrinsics); - - // if the provided folder for visual debugging does not exist create it - // recursively - if((!visualDebug.empty()) && (!fs::exists(visualDebug))) - { - fs::create_directories(visualDebug); - } - - // this contains the full path and the root name of the file without the extension - const bool wantsJsonOutput = exportJsonFile.empty(); + + // the bundle adjustment can be run for now only if the refine intrinsics option is not set + globalBundle = (globalBundle && !refineIntrinsics); + + // if the provided folder for visual debugging does not exist create it + // recursively + if ((!visualDebug.empty()) && (!fs::exists(visualDebug))) + { + fs::create_directories(visualDebug); + } + + // this contains the full path and the root name of the file without the extension + const bool wantsJsonOutput = exportJsonFile.empty(); #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - std::string basenameAlembic = (fs::path(exportJsonFile).parent_path() / fs::path(exportJsonFile).stem()).string(); + std::string basenameAlembic = (fs::path(exportJsonFile).parent_path() / fs::path(exportJsonFile).stem()).string(); #endif - std::string basenameJson; - if(wantsJsonOutput) - { - basenameJson = (fs::path(exportJsonFile).parent_path() / fs::path(exportJsonFile).stem()).string(); - } - - // load SfMData - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); - return EXIT_FAILURE; - } - - //*********************************************************************** - // Localizer initialization - //*********************************************************************** - - std::unique_ptr param; - - std::unique_ptr localizer; - - // initialize the localizer according to the chosen type of describer + std::string basenameJson; + if (wantsJsonOutput) + { + basenameJson = (fs::path(exportJsonFile).parent_path() / fs::path(exportJsonFile).stem()).string(); + } + + // load SfMData + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); + return EXIT_FAILURE; + } + + //*********************************************************************** + // Localizer initialization + //*********************************************************************** + + std::unique_ptr param; + + std::unique_ptr localizer; + + // initialize the localizer according to the chosen type of describer #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - if(!useVoctreeLocalizer) - { - localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); - localizer.reset(tmpLoc); - - localization::CCTagLocalizer::Parameters* tmpParam = new localization::CCTagLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_nNearestKeyFrames = nNearestKeyFrames; - } - else + if (!useVoctreeLocalizer) + { + localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); + localizer.reset(tmpLoc); + + localization::CCTagLocalizer::Parameters* tmpParam = new localization::CCTagLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_nNearestKeyFrames = nNearestKeyFrames; + } + else #endif - { - - localization::VoctreeLocalizer* tmpLoc = new localization::VoctreeLocalizer(sfmData, - descriptorsFolder, - vocTreeFilepath, - weightsFilepath, - matchDescTypes); - - localizer.reset(tmpLoc); - - localization::VoctreeLocalizer::Parameters *tmpParam = new localization::VoctreeLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring);; - tmpParam->_numResults = numResults; - tmpParam->_maxResults = maxResults; - tmpParam->_numCommonViews = numCommonViews; - tmpParam->_ccTagUseCuda = false; - tmpParam->_matchingError = matchingErrorMax; - tmpParam->_nbFrameBufferMatching = nbFrameBufferMatching; - tmpParam->_useRobustMatching = robustMatching; - } - - assert(localizer); - assert(param); - - // set other common parameters - param->_featurePreset = featDescPreset; - param->_refineIntrinsics = refineIntrinsics; - param->_visualDebug = visualDebug; - param->_errorMax = resectionErrorMax; - param->_resectionEstimator = resectionEstimator; - param->_matchingEstimator = matchingEstimator; - - - if(!localizer->isInit()) - { - ALICEVISION_CERR("ERROR while initializing the localizer!"); - return EXIT_FAILURE; - } - - // create the feedProvider - dataio::FeedProvider feed(mediaFilepath, calibFile); - if(!feed.isInit()) - { - ALICEVISION_CERR("ERROR while initializing the FeedProvider!"); - return EXIT_FAILURE; - } - + { + localization::VoctreeLocalizer* tmpLoc = + new localization::VoctreeLocalizer(sfmData, descriptorsFolder, vocTreeFilepath, weightsFilepath, matchDescTypes); + + localizer.reset(tmpLoc); + + localization::VoctreeLocalizer::Parameters* tmpParam = new localization::VoctreeLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring); + ; + tmpParam->_numResults = numResults; + tmpParam->_maxResults = maxResults; + tmpParam->_numCommonViews = numCommonViews; + tmpParam->_ccTagUseCuda = false; + tmpParam->_matchingError = matchingErrorMax; + tmpParam->_nbFrameBufferMatching = nbFrameBufferMatching; + tmpParam->_useRobustMatching = robustMatching; + } + + assert(localizer); + assert(param); + + // set other common parameters + param->_featurePreset = featDescPreset; + param->_refineIntrinsics = refineIntrinsics; + param->_visualDebug = visualDebug; + param->_errorMax = resectionErrorMax; + param->_resectionEstimator = resectionEstimator; + param->_matchingEstimator = matchingEstimator; + + if (!localizer->isInit()) + { + ALICEVISION_CERR("ERROR while initializing the localizer!"); + return EXIT_FAILURE; + } + + // create the feedProvider + dataio::FeedProvider feed(mediaFilepath, calibFile); + if (!feed.isInit()) + { + ALICEVISION_CERR("ERROR while initializing the FeedProvider!"); + return EXIT_FAILURE; + } + #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - // init alembic exporter - sfmDataIO::AlembicExporter exporter(exportAlembicFile); - exporter.initAnimatedCamera("camera"); + // init alembic exporter + sfmDataIO::AlembicExporter exporter(exportAlembicFile); + exporter.initAnimatedCamera("camera"); #endif - - image::Image imageGrey; - camera::Pinhole queryIntrinsics; - bool hasIntrinsics = false; - - std::size_t frameCounter = 0; - std::size_t goodFrameCounter = 0; - std::vector goodFrameList; - std::string currentImgName; - - //*********************************************************************** - // Main loop - //*********************************************************************** - - // Define an accumulator set for computing the mean and the - // standard deviation of the time taken for localization - bacc::accumulator_set > stats; - - std::vector vec_localizationResults; - - while(feed.readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics)) - { - ALICEVISION_COUT("******************************"); - ALICEVISION_COUT("FRAME " << utils::toStringZeroPadded(frameCounter, 4)); - ALICEVISION_COUT("******************************"); - localization::LocalizationResult localizationResult; - auto detect_start = std::chrono::steady_clock::now(); - localizer->localize(imageGrey, - param.get(), - generator, - hasIntrinsics /*useInputIntrinsics*/, - queryIntrinsics, - localizationResult, - currentImgName); - auto detect_end = std::chrono::steady_clock::now(); - auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("\nLocalization took " << detect_elapsed.count() << " [ms]"); - stats(detect_elapsed.count()); - - vec_localizationResults.emplace_back(localizationResult); - - // save data - if(localizationResult.isValid()) + + image::Image imageGrey; + camera::Pinhole queryIntrinsics; + bool hasIntrinsics = false; + + std::size_t frameCounter = 0; + std::size_t goodFrameCounter = 0; + std::vector goodFrameList; + std::string currentImgName; + + //*********************************************************************** + // Main loop + //*********************************************************************** + + // Define an accumulator set for computing the mean and the + // standard deviation of the time taken for localization + bacc::accumulator_set> stats; + + std::vector vec_localizationResults; + + while (feed.readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics)) { + ALICEVISION_COUT("******************************"); + ALICEVISION_COUT("FRAME " << utils::toStringZeroPadded(frameCounter, 4)); + ALICEVISION_COUT("******************************"); + localization::LocalizationResult localizationResult; + auto detect_start = std::chrono::steady_clock::now(); + localizer->localize( + imageGrey, param.get(), generator, hasIntrinsics /*useInputIntrinsics*/, queryIntrinsics, localizationResult, currentImgName); + auto detect_end = std::chrono::steady_clock::now(); + auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("\nLocalization took " << detect_elapsed.count() << " [ms]"); + stats(detect_elapsed.count()); + + vec_localizationResults.emplace_back(localizationResult); + + // save data + if (localizationResult.isValid()) + { #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - exporter.addCameraKeyframe(localizationResult.getPose(), &queryIntrinsics, currentImgName, frameCounter, frameCounter); + exporter.addCameraKeyframe(localizationResult.getPose(), &queryIntrinsics, currentImgName, frameCounter, frameCounter); #endif - - goodFrameCounter++; - goodFrameList.push_back(currentImgName + " : " + std::to_string(localizationResult.getIndMatch3D2D().size()) ); - } - else - { - ALICEVISION_CERR("Unable to localize frame " << frameCounter); + + goodFrameCounter++; + goodFrameList.push_back(currentImgName + " : " + std::to_string(localizationResult.getIndMatch3D2D().size())); + } + else + { + ALICEVISION_CERR("Unable to localize frame " << frameCounter); #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - exporter.jumpKeyframe(currentImgName); + exporter.jumpKeyframe(currentImgName); #endif + } + ++frameCounter; + feed.goToNextFrame(); } - ++frameCounter; - feed.goToNextFrame(); - } - - if(wantsJsonOutput) - { - localization::LocalizationResult::save(vec_localizationResults, basenameJson + ".json"); - } - - //*********************************************************************** - // Global bundle - //*********************************************************************** - - if(globalBundle) - { - ALICEVISION_COUT("\n\n\n***********************************************"); - ALICEVISION_COUT("Bundle Adjustment - Refining the whole sequence"); - ALICEVISION_COUT("***********************************************\n\n"); - // run a bundle adjustment - const bool b_allTheSame = true; - const bool b_refineStructure = false; - const bool b_refinePose = true; - const bool BAresult = localization::refineSequence(vec_localizationResults, - b_allTheSame, - !noBArefineIntrinsics, - noDistortion, - b_refinePose, - b_refineStructure, - basenameJson + ".sfmdata.BUNDLE", - minPointVisibility); - if(!BAresult) + + if (wantsJsonOutput) { - ALICEVISION_CERR("Bundle Adjustment failed!"); + localization::LocalizationResult::save(vec_localizationResults, basenameJson + ".json"); } - else + + //*********************************************************************** + // Global bundle + //*********************************************************************** + + if (globalBundle) { -#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - // now copy back in a new abc with the same name file and BUNDLE appended at the end - sfmDataIO::AlembicExporter exporterBA( basenameAlembic +".BUNDLE.abc" ); - exporterBA.initAnimatedCamera("camera"); - std::size_t idx = 0; - for(const localization::LocalizationResult &res : vec_localizationResults) - { - if(res.isValid()) + ALICEVISION_COUT("\n\n\n***********************************************"); + ALICEVISION_COUT("Bundle Adjustment - Refining the whole sequence"); + ALICEVISION_COUT("***********************************************\n\n"); + // run a bundle adjustment + const bool b_allTheSame = true; + const bool b_refineStructure = false; + const bool b_refinePose = true; + const bool BAresult = localization::refineSequence(vec_localizationResults, + b_allTheSame, + !noBArefineIntrinsics, + noDistortion, + b_refinePose, + b_refineStructure, + basenameJson + ".sfmdata.BUNDLE", + minPointVisibility); + if (!BAresult) { - assert(idx < vec_localizationResults.size()); - exporterBA.addCameraKeyframe(res.getPose(), &res.getIntrinsics(), currentImgName, idx, idx); + ALICEVISION_CERR("Bundle Adjustment failed!"); } else { - exporterBA.jumpKeyframe(currentImgName); - } - idx++; - } +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) + // now copy back in a new abc with the same name file and BUNDLE appended at the end + sfmDataIO::AlembicExporter exporterBA(basenameAlembic + ".BUNDLE.abc"); + exporterBA.initAnimatedCamera("camera"); + std::size_t idx = 0; + for (const localization::LocalizationResult& res : vec_localizationResults) + { + if (res.isValid()) + { + assert(idx < vec_localizationResults.size()); + exporterBA.addCameraKeyframe(res.getPose(), &res.getIntrinsics(), currentImgName, idx, idx); + } + else + { + exporterBA.jumpKeyframe(currentImgName); + } + idx++; + } #endif - if(wantsJsonOutput) - { - localization::LocalizationResult::save(vec_localizationResults, basenameJson +".BUNDLE.json"); - } - + if (wantsJsonOutput) + { + localization::LocalizationResult::save(vec_localizationResults, basenameJson + ".BUNDLE.json"); + } + } } - } - - // print out some time stats - ALICEVISION_COUT("\n\n******************************"); - ALICEVISION_COUT("Localized " << goodFrameCounter << "/" << frameCounter << " images"); - ALICEVISION_COUT("Images localized with the number of 2D/3D matches during localization :"); - for(std::size_t i = 0; i < goodFrameList.size(); ++i) - ALICEVISION_COUT(goodFrameList[i]); - ALICEVISION_COUT("Processing took " << bacc::sum(stats)/1000 << " [s] overall"); - ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); - ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); - ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); - - return EXIT_SUCCESS; + + // print out some time stats + ALICEVISION_COUT("\n\n******************************"); + ALICEVISION_COUT("Localized " << goodFrameCounter << "/" << frameCounter << " images"); + ALICEVISION_COUT("Images localized with the number of 2D/3D matches during localization :"); + for (std::size_t i = 0; i < goodFrameList.size(); ++i) + ALICEVISION_COUT(goodFrameList[i]); + ALICEVISION_COUT("Processing took " << bacc::sum(stats) / 1000 << " [s] overall"); + ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); + ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); + ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); + + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_checkerboardCalibration.cpp b/src/software/pipeline/main_checkerboardCalibration.cpp index 8ac30baf64..3682ea678a 100644 --- a/src/software/pipeline/main_checkerboardCalibration.cpp +++ b/src/software/pipeline/main_checkerboardCalibration.cpp @@ -4,7 +4,6 @@ // 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/. - // This application tries to estimate the intrinsics and extrinsics of a set of images. // It is assumed that for each image we have a result of the checkerboard detector. // It is assumed that the distortion is at least approximately known or calibrated. @@ -15,7 +14,6 @@ #include #include - #include #include @@ -33,11 +31,9 @@ #include #include - #include #include - #include #include @@ -66,9 +62,7 @@ Eigen::Matrix computeV(const Eigen::Matrix3d& H, int i, int j) return v; } -bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, - std::map& boardsAllImages, - const double squareSize) +bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, std::map& boardsAllImages, const double squareSize) { if (boardsAllImages.size() < 2) { @@ -100,7 +94,8 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, std::map homographies; for (auto& pv : sfmData.getViews()) { - if (pv.second->getIntrinsicId() != intrinsicId) continue; + if (pv.second->getIntrinsicId() != intrinsicId) + continue; const calibration::CheckerDetector& detector = boardsAllImages[pv.first]; if (detector.getBoards().size() != 1) @@ -135,7 +130,6 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, continue; } - Eigen::Vector2d refpt; refpt(0) = double(j) * squareSize; refpt(1) = double(i) * squareSize; @@ -165,10 +159,11 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, } using KernelType = multiview::RelativePoseKernel; + multiview::relativePose::HomographyAsymmetricError, + multiview::UnnormalizerI, + robustEstimation::Mat3Model>; - KernelType kernel(Mref, 1.0, 1.0, Mcur, cameraPinhole->w(), cameraPinhole->h(), false); // configure as point to point error model. + KernelType kernel(Mref, 1.0, 1.0, Mcur, cameraPinhole->w(), cameraPinhole->h(), false); // configure as point to point error model. robustEstimation::Mat3Model model; std::mt19937 generator; @@ -177,7 +172,8 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, robustEstimation::ACRANSAC(kernel, generator, vec_inliers, 1024, &model, std::numeric_limits::infinity()); Eigen::Matrix3d H = model.getMatrix(); - if (vec_inliers.size() < 10) continue; + if (vec_inliers.size() < 10) + continue; homographies[pv.first] = H; } @@ -347,10 +343,9 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, sfm::BundleAdjustmentCeres::CeresOptions options; options.summary = true; sfm::BundleAdjustmentCeres ba(options); - sfm::BundleAdjustment::ERefineOptions boptions = - sfm::BundleAdjustment::ERefineOptions::REFINE_ROTATION | - sfm::BundleAdjustment::ERefineOptions::REFINE_TRANSLATION | - sfm::BundleAdjustment::ERefineOptions::REFINE_INTRINSICS_ALL; + sfm::BundleAdjustment::ERefineOptions boptions = sfm::BundleAdjustment::ERefineOptions::REFINE_ROTATION | + sfm::BundleAdjustment::ERefineOptions::REFINE_TRANSLATION | + sfm::BundleAdjustment::ERefineOptions::REFINE_INTRINSICS_ALL; if (!ba.adjust(sfmData, boptions)) { ALICEVISION_LOG_ERROR("Failed to calibrate"); @@ -361,7 +356,6 @@ bool estimateIntrinsicsPoses(sfmData::SfMData& sfmData, return true; } - bool estimateRigs(sfmData::SfMData& sfmData) { // Calibrate rigs @@ -431,10 +425,9 @@ bool estimateRigs(sfmData::SfMData& sfmData) sfm::BundleAdjustmentCeres::CeresOptions options; options.summary = true; sfm::BundleAdjustmentCeres ba(options); - sfm::BundleAdjustment::ERefineOptions boptions = - sfm::BundleAdjustment::ERefineOptions::REFINE_ROTATION | - sfm::BundleAdjustment::ERefineOptions::REFINE_TRANSLATION | - sfm::BundleAdjustment::ERefineOptions::REFINE_INTRINSICS_ALL; + sfm::BundleAdjustment::ERefineOptions boptions = sfm::BundleAdjustment::ERefineOptions::REFINE_ROTATION | + sfm::BundleAdjustment::ERefineOptions::REFINE_TRANSLATION | + sfm::BundleAdjustment::ERefineOptions::REFINE_INTRINSICS_ALL; if (!ba.adjust(sfmData, boptions)) { ALICEVISION_LOG_ERROR("Failed to calibrate"); @@ -445,8 +438,7 @@ bool estimateRigs(sfmData::SfMData& sfmData) return true; } - -int aliceVision_main(int argc, char* argv[]) +int aliceVision_main(int argc, char* argv[]) { std::string sfmInputDataFilepath; std::string checkerBoardsPath; @@ -488,16 +480,18 @@ int aliceVision_main(int argc, char* argv[]) } // Load the checkerboards - std::map < IndexT, calibration::CheckerDetector> boardsAllImages; + std::map boardsAllImages; for (auto& pv : sfmData.getViews()) { IndexT viewId = pv.first; // Read the json file std::stringstream ss; - ss << checkerBoardsPath << "/" << "checkers_" << viewId << ".json"; + ss << checkerBoardsPath << "/" + << "checkers_" << viewId << ".json"; std::ifstream inputfile(ss.str()); - if (inputfile.is_open() == false) continue; + if (inputfile.is_open() == false) + continue; std::stringstream buffer; buffer << inputfile.rdbuf(); @@ -544,5 +538,5 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_checkerboardDetection.cpp b/src/software/pipeline/main_checkerboardDetection.cpp index d8f4cc2fd6..c1127e8fab 100644 --- a/src/software/pipeline/main_checkerboardDetection.cpp +++ b/src/software/pipeline/main_checkerboardDetection.cpp @@ -71,7 +71,7 @@ int aliceVision_main(int argc, char* argv[]) } sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmInputDataFilepath << "' cannot be read."); return EXIT_FAILURE; @@ -79,28 +79,28 @@ int aliceVision_main(int argc, char* argv[]) // Order views by their image names std::vector> viewsOrderedByName; - for(auto& viewIt : sfmData.getViews()) + for (auto& viewIt : sfmData.getViews()) { viewsOrderedByName.push_back(viewIt.second); } - std::sort(viewsOrderedByName.begin(), viewsOrderedByName.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if(a == nullptr || b == nullptr) + std::sort(viewsOrderedByName.begin(), + viewsOrderedByName.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool { + if (a == nullptr || b == nullptr) return true; return (a->getImage().getImagePath() < b->getImage().getImagePath()); }); // Define range to compute - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0 || static_cast(rangeStart) > viewsOrderedByName.size()) + if (rangeStart < 0 || rangeSize < 0 || static_cast(rangeStart) > viewsOrderedByName.size()) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; } - if(static_cast(rangeStart + rangeSize) > viewsOrderedByName.size()) + if (static_cast(rangeStart + rangeSize) > viewsOrderedByName.size()) { rangeSize = static_cast(viewsOrderedByName.size()) - rangeStart; } @@ -119,7 +119,7 @@ int aliceVision_main(int argc, char* argv[]) IndexT viewId = view->getViewId(); - //Load image and convert it to sRGB colorspace + // Load image and convert it to sRGB colorspace std::string imagePath = view->getImage().getImagePath(); ALICEVISION_LOG_INFO("Load image with path " << imagePath); image::Image source; @@ -136,7 +136,7 @@ int aliceVision_main(int argc, char* argv[]) // if pixel are not squared, convert the image for easier lines extraction const double w = source.width(); const double h = source.height(); - + const double nw = w * ((doubleSize) ? 2.0 : 1.0); const double nh = h * ((doubleSize) ? 2.0 : 1.0) / pixelRatio; @@ -147,10 +147,10 @@ int aliceVision_main(int argc, char* argv[]) source.swap(resizedInput); } - //Lookup checkerboard + // Lookup checkerboard calibration::CheckerDetector detect; ALICEVISION_LOG_INFO("Launching checkerboard detection"); - if(!detect.process(source, useNestedGrids, exportDebugImages)) + if (!detect.process(source, useNestedGrids, exportDebugImages)) { ALICEVISION_LOG_ERROR("Detection failed"); continue; @@ -158,11 +158,11 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Detected " << detect.getBoards().size() << " boards and " << detect.getCorners().size() << " corners"); - //Restore aspect ratio for corners coordinates + // Restore aspect ratio for corners coordinates if (pixelRatio != 1.0 || doubleSize) { - std::vector & cs = detect.getCorners(); - for (auto &c : cs) + std::vector& cs = detect.getCorners(); + for (auto& c : cs) { c.center(1) *= pixelRatio; @@ -173,17 +173,19 @@ int aliceVision_main(int argc, char* argv[]) } } } - + // write the json file with the tree - ALICEVISION_LOG_INFO("Writing detection output in " << "checkers_" << viewId << ".json"); + ALICEVISION_LOG_INFO("Writing detection output in " + << "checkers_" << viewId << ".json"); std::stringstream ss; - ss << outputFilePath << "/" << "checkers_" << viewId << ".json"; + ss << outputFilePath << "/" + << "checkers_" << viewId << ".json"; boost::json::value jv = boost::json::value_from(detect); std::ofstream of(ss.str()); of << boost::json::serialize(jv); of.close(); - if(exportDebugImages) + if (exportDebugImages) { ALICEVISION_LOG_INFO("Writing debug image"); std::stringstream ss; diff --git a/src/software/pipeline/main_computeStructureFromKnownPoses.cpp b/src/software/pipeline/main_computeStructureFromKnownPoses.cpp index f28fe97a5d..a71d769d9a 100644 --- a/src/software/pipeline/main_computeStructureFromKnownPoses.cpp +++ b/src/software/pipeline/main_computeStructureFromKnownPoses.cpp @@ -31,18 +31,18 @@ namespace po = boost::program_options; namespace fs = std::filesystem; /// Compute the structure of a scene according existing camera poses. -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outSfMDataFilename; - std::vector featuresFolders; - double geometricErrorMax = 5.0; + // command-line parameters + std::string sfmDataFilename; + std::string outSfMDataFilename; + std::vector featuresFolders; + double geometricErrorMax = 5.0; - // user optional parameters - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - std::vector matchesFolders; - int randomSeed = std::mt19937::default_seed; + // user optional parameters + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + std::vector matchesFolders; + int randomSeed = std::mt19937::default_seed; // clang-format off po::options_description requiredParams("Required parameters"); @@ -67,95 +67,95 @@ int aliceVision_main(int argc, char **argv) "This seed value will generate a sequence using a linear random generator. Set -1 to use a random seed."); // clang-format on - CmdLine cmdline("AliceVision computeStructureFromKnownPoses"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + CmdLine cmdline("AliceVision computeStructureFromKnownPoses"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); + + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } + + // init the regions_type from the image describer file (used for image regions extraction) + using namespace aliceVision::feature; + + // get imageDescriberMethodType + std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); + + // prepare the Regions provider + feature::RegionsPerView regionsPerView; + if (!sfm::loadRegionsPerView(regionsPerView, sfmData, featuresFolders, describerMethodTypes)) + { + ALICEVISION_LOG_ERROR("Invalid regions."); + return EXIT_FAILURE; + } + + // Pair selection method: + // - geometry guided -> camera frustum intersection, + // - putative matches guided (photometric matches) + // (keep pairs that have valid Intrinsic & Pose ids). + PairSet pairs; + if (matchesFolders.empty()) + { + // no image pair provided, so we use cameras frustum intersection. + // build the list of connected images pairs from frustum intersections + pairs = sfm::FrustumFilter(sfmData).getFrustumIntersectionPairs(); + } + else + { + // load pre-computed matches + matching::PairwiseMatches matches; + if (!sfm::loadPairwiseMatches(matches, sfmData, matchesFolders, describerMethodTypes)) + return EXIT_FAILURE; + + pairs = matching::getImagePairs(matches); + // keep only Pairs that belong to valid view indexes. + const std::set valid_viewIdx = sfmData.getValidViews(); + pairs = sfm::filterPairs(pairs, valid_viewIdx); + } + + aliceVision::system::Timer timer; + + // clear previous 3D landmarks + sfmData.getLandmarks().clear(); + + // compute Structure from known camera poses + sfm::StructureEstimationFromKnownPoses structureEstimator; + structureEstimator.match(sfmData, pairs, regionsPerView, geometricErrorMax); + + // unload descriptors before triangulation + regionsPerView.clearDescriptors(); + + // filter matches + structureEstimator.filter(sfmData, pairs, regionsPerView); + + // create 3D landmarks + structureEstimator.triangulate(sfmData, regionsPerView, randomNumberGenerator); + + sfm::removeOutliersWithAngleError(sfmData, 2.0); + + ALICEVISION_LOG_INFO("Structure estimation took (s): " << timer.elapsed() << "." << std::endl + << "\t- # landmarks found: " << sfmData.getLandmarks().size()); + + if (fs::path(outSfMDataFilename).extension() != ".ply") + { + sfmDataIO::save(sfmData, + (fs::path(outSfMDataFilename).parent_path() / (fs::path(outSfMDataFilename).stem().string() + ".ply")).string(), + sfmDataIO::ESfMData::ALL); + } + + if (sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) + return EXIT_SUCCESS; + + ALICEVISION_LOG_ERROR("Can't save the output SfMData."); return EXIT_FAILURE; - } - - // init the regions_type from the image describer file (used for image regions extraction) - using namespace aliceVision::feature; - - // get imageDescriberMethodType - std::vector describerMethodTypes = EImageDescriberType_stringToEnums(describerTypesName); - - // prepare the Regions provider - feature::RegionsPerView regionsPerView; - if(!sfm::loadRegionsPerView(regionsPerView, sfmData, featuresFolders, describerMethodTypes)) - { - ALICEVISION_LOG_ERROR("Invalid regions."); - return EXIT_FAILURE; - } - - // Pair selection method: - // - geometry guided -> camera frustum intersection, - // - putative matches guided (photometric matches) - // (keep pairs that have valid Intrinsic & Pose ids). - PairSet pairs; - if(matchesFolders.empty()) - { - // no image pair provided, so we use cameras frustum intersection. - // build the list of connected images pairs from frustum intersections - pairs = sfm::FrustumFilter(sfmData).getFrustumIntersectionPairs(); - } - else - { - // load pre-computed matches - matching::PairwiseMatches matches; - if(!sfm::loadPairwiseMatches(matches, sfmData, matchesFolders, describerMethodTypes)) - return EXIT_FAILURE; - - pairs = matching::getImagePairs(matches); - // keep only Pairs that belong to valid view indexes. - const std::set valid_viewIdx = sfmData.getValidViews(); - pairs = sfm::filterPairs(pairs, valid_viewIdx); - } - - aliceVision::system::Timer timer; - - // clear previous 3D landmarks - sfmData.getLandmarks().clear(); - - // compute Structure from known camera poses - sfm::StructureEstimationFromKnownPoses structureEstimator; - structureEstimator.match(sfmData, pairs, regionsPerView, geometricErrorMax); - - // unload descriptors before triangulation - regionsPerView.clearDescriptors(); - - // filter matches - structureEstimator.filter(sfmData, pairs, regionsPerView); - - // create 3D landmarks - structureEstimator.triangulate(sfmData, regionsPerView, randomNumberGenerator); - - sfm::removeOutliersWithAngleError(sfmData, 2.0); - - ALICEVISION_LOG_INFO("Structure estimation took (s): " << timer.elapsed() << "." << std::endl - << "\t- # landmarks found: " << sfmData.getLandmarks().size()); - - if(fs::path(outSfMDataFilename).extension() != ".ply") - { - sfmDataIO::save(sfmData, - (fs::path(outSfMDataFilename).parent_path() / (fs::path(outSfMDataFilename).stem().string() + ".ply")).string(), - sfmDataIO::ESfMData::ALL); - } - - if(sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) - return EXIT_SUCCESS; - - ALICEVISION_LOG_ERROR("Can't save the output SfMData."); - return EXIT_FAILURE; } diff --git a/src/software/pipeline/main_depthMapEstimation.cpp b/src/software/pipeline/main_depthMapEstimation.cpp index 14a02afbbf..758ae85ac1 100644 --- a/src/software/pipeline/main_depthMapEstimation.cpp +++ b/src/software/pipeline/main_depthMapEstimation.cpp @@ -38,7 +38,7 @@ int computeDownscale(const mvsUtils::MultiViewParams& mp, int scale, int maxWidt int downscaleWidth = mp.getMaxImageWidth() / scale; int downscaleHeight = mp.getMaxImageHeight() / scale; - while((downscaleWidth > maxWidth) || (downscaleHeight > maxHeight)) + while ((downscaleWidth > maxWidth) || (downscaleHeight > maxHeight)) { downscale++; downscaleWidth = maxImageWidth / downscale; @@ -221,7 +221,8 @@ int aliceVision_main(int argc, char* argv[]) // clang-format on CmdLine cmdline("Dense Reconstruction.\n" - "This program estimate a depth map for each input calibrated camera using Plane Sweeping, a multi-view stereo algorithm notable for its efficiency on modern graphics hardware (GPU).\n" + "This program estimate a depth map for each input calibrated camera using Plane Sweeping, a multi-view stereo algorithm notable " + "for its efficiency on modern graphics hardware (GPU).\n" "AliceVision depthMapEstimation"); cmdline.add(requiredParams); cmdline.add(optionalParams); @@ -248,53 +249,51 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO(gpu::gpuInformationCUDA()); // check if the gpu suppport CUDA compute capability 2.0 - if(!gpu::gpuSupportCUDA(2,0)) + if (!gpu::gpuSupportCUDA(2, 0)) { - ALICEVISION_LOG_ERROR("This program needs a CUDA-Enabled GPU (with at least compute capability 2.0)."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("This program needs a CUDA-Enabled GPU (with at least compute capability 2.0)."); + return EXIT_FAILURE; } // check if the scale is correct - if(downscale < 1) + if (downscale < 1) { - ALICEVISION_LOG_ERROR("Invalid value for downscale parameter. Should be at least 1."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("Invalid value for downscale parameter. Should be at least 1."); + return EXIT_FAILURE; } // check that Sgm scaleStep is greater or equal to the Refine scaleStep - if(depthMapParams.useRefine) + if (depthMapParams.useRefine) { - const int sgmScaleStep = sgmParams.scale * sgmParams.stepXY; - const int refineScaleStep = refineParams.scale * refineParams.stepXY; + const int sgmScaleStep = sgmParams.scale * sgmParams.stepXY; + const int refineScaleStep = refineParams.scale * refineParams.stepXY; - if(sgmScaleStep < refineScaleStep) - { - ALICEVISION_LOG_ERROR("SGM downscale (scale x step) should be greater or equal to the Refine downscale (scale x step)."); - return EXIT_FAILURE; - } + if (sgmScaleStep < refineScaleStep) + { + ALICEVISION_LOG_ERROR("SGM downscale (scale x step) should be greater or equal to the Refine downscale (scale x step)."); + return EXIT_FAILURE; + } - if(sgmScaleStep % refineScaleStep != 0) - { - ALICEVISION_LOG_ERROR("SGM downscale (scale x step) should be a multiple of the Refine downscale (scale x step)."); - return EXIT_FAILURE; - } + if (sgmScaleStep % refineScaleStep != 0) + { + ALICEVISION_LOG_ERROR("SGM downscale (scale x step) should be a multiple of the Refine downscale (scale x step)."); + return EXIT_FAILURE; + } } // check min/max view angle - if(minViewAngle < 0.f || minViewAngle > 360.f || - maxViewAngle < 0.f || maxViewAngle > 360.f || - minViewAngle > maxViewAngle) + if (minViewAngle < 0.f || minViewAngle > 360.f || maxViewAngle < 0.f || maxViewAngle > 360.f || minViewAngle > maxViewAngle) { - ALICEVISION_LOG_ERROR("Invalid value for minViewAngle/maxViewAngle parameter(s). Should be between 0 and 360."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("Invalid value for minViewAngle/maxViewAngle parameter(s). Should be between 0 and 360."); + return EXIT_FAILURE; } // read the input SfM scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; } // MultiViewParams initialization @@ -305,46 +304,45 @@ int aliceVision_main(int argc, char* argv[]) mp.setMaxViewAngle(maxViewAngle); // set undefined tile dimensions - if(tileParams.bufferWidth <= 0 || tileParams.bufferHeight <= 0) + if (tileParams.bufferWidth <= 0 || tileParams.bufferHeight <= 0) { - tileParams.bufferWidth = mp.getMaxImageWidth(); - tileParams.bufferHeight = mp.getMaxImageHeight(); + tileParams.bufferWidth = mp.getMaxImageWidth(); + tileParams.bufferHeight = mp.getMaxImageHeight(); } // check if the tile padding is correct - if(tileParams.padding < 0 && - tileParams.padding * 2 < tileParams.bufferWidth && - tileParams.padding * 2 < tileParams.bufferHeight) + if (tileParams.padding < 0 && tileParams.padding * 2 < tileParams.bufferWidth && tileParams.padding * 2 < tileParams.bufferHeight) { ALICEVISION_LOG_ERROR("Invalid value for tilePadding parameter. Should be at least 0 and not exceed half buffer width and height."); return EXIT_FAILURE; } // check if tile size > max image size - if(tileParams.bufferWidth > mp.getMaxImageWidth() || tileParams.bufferHeight > mp.getMaxImageHeight()) + if (tileParams.bufferWidth > mp.getMaxImageWidth() || tileParams.bufferHeight > mp.getMaxImageHeight()) { - ALICEVISION_LOG_WARNING("Tile buffer size (width: " << tileParams.bufferWidth << ", height: " << tileParams.bufferHeight - << ") is larger than the maximum image size (width: " << mp.getMaxImageWidth() << ", height: " << mp.getMaxImageHeight() << ")."); + ALICEVISION_LOG_WARNING("Tile buffer size (width: " << tileParams.bufferWidth << ", height: " << tileParams.bufferHeight + << ") is larger than the maximum image size (width: " << mp.getMaxImageWidth() + << ", height: " << mp.getMaxImageHeight() << ")."); } // check if SGM scale and step are set to -1 bool autoSgmScaleStep = false; // compute SGM scale and step - if(sgmParams.scale == -1 || sgmParams.stepXY == -1) + if (sgmParams.scale == -1 || sgmParams.stepXY == -1) { - const int fileScale = 1; // input images scale (should be one) - const int maxSideXY = 700 / mp.getProcessDownscale(); // max side in order to fit in device memory + const int fileScale = 1; // input images scale (should be one) + const int maxSideXY = 700 / mp.getProcessDownscale(); // max side in order to fit in device memory const int maxImageW = mp.getMaxImageWidth(); const int maxImageH = mp.getMaxImageHeight(); int maxW = maxSideXY; int maxH = maxSideXY * 0.8; - if(maxImageW < maxImageH) + if (maxImageW < maxImageH) std::swap(maxW, maxH); - if(sgmParams.scale == -1) + if (sgmParams.scale == -1) { // compute the number of scales that will be used in the plane sweeping. // the highest scale should have a resolution close to 700x550 (or less). @@ -352,7 +350,7 @@ int aliceVision_main(int argc, char* argv[]) sgmParams.scale = std::min(2, scaleTmp); } - if(sgmParams.stepXY == -1) + if (sgmParams.stepXY == -1) { sgmParams.stepXY = computeDownscale(mp, fileScale * sgmParams.scale, maxW, maxH); } @@ -361,33 +359,34 @@ int aliceVision_main(int argc, char* argv[]) } // single tile case, update parameters - if(depthMapParams.autoAdjustSmallImage && mvsUtils::hasOnlyOneTile(tileParams, mp.getMaxImageWidth(), mp.getMaxImageHeight())) + if (depthMapParams.autoAdjustSmallImage && mvsUtils::hasOnlyOneTile(tileParams, mp.getMaxImageWidth(), mp.getMaxImageHeight())) { // update SGM maxTCamsPerTile - if(sgmParams.maxTCamsPerTile < depthMapParams.maxTCams) + if (sgmParams.maxTCamsPerTile < depthMapParams.maxTCams) { - ALICEVISION_LOG_WARNING("Single tile computation, override SGM maximum number of T cameras per tile (before: " << sgmParams.maxTCamsPerTile << ", now: " << depthMapParams.maxTCams << ")."); - sgmParams.maxTCamsPerTile = depthMapParams.maxTCams; + ALICEVISION_LOG_WARNING("Single tile computation, override SGM maximum number of T cameras per tile (before: " + << sgmParams.maxTCamsPerTile << ", now: " << depthMapParams.maxTCams << ")."); + sgmParams.maxTCamsPerTile = depthMapParams.maxTCams; } // update Refine maxTCamsPerTile - if(refineParams.maxTCamsPerTile < depthMapParams.maxTCams) + if (refineParams.maxTCamsPerTile < depthMapParams.maxTCams) { - ALICEVISION_LOG_WARNING("Single tile computation, override Refine maximum number of T cameras per tile (before: " << refineParams.maxTCamsPerTile << ", now: " << depthMapParams.maxTCams << ")."); - refineParams.maxTCamsPerTile = depthMapParams.maxTCams; + ALICEVISION_LOG_WARNING("Single tile computation, override Refine maximum number of T cameras per tile (before: " + << refineParams.maxTCamsPerTile << ", now: " << depthMapParams.maxTCams << ")."); + refineParams.maxTCamsPerTile = depthMapParams.maxTCams; } - const int maxSgmBufferWidth = divideRoundUp(mp.getMaxImageWidth() , sgmParams.scale * sgmParams.stepXY); + const int maxSgmBufferWidth = divideRoundUp(mp.getMaxImageWidth(), sgmParams.scale * sgmParams.stepXY); const int maxSgmBufferHeight = divideRoundUp(mp.getMaxImageHeight(), sgmParams.scale * sgmParams.stepXY); // update SGM step XY - if(!autoSgmScaleStep && // user define SGM scale & stepXY - (sgmParams.stepXY == 2) && // default stepXY - (maxSgmBufferWidth < tileParams.bufferWidth * 0.5) && - (maxSgmBufferHeight < tileParams.bufferHeight * 0.5)) + if (!autoSgmScaleStep && // user define SGM scale & stepXY + (sgmParams.stepXY == 2) && // default stepXY + (maxSgmBufferWidth < tileParams.bufferWidth * 0.5) && (maxSgmBufferHeight < tileParams.bufferHeight * 0.5)) { - ALICEVISION_LOG_WARNING("Single tile computation, override SGM step XY (before: " << sgmParams.stepXY << ", now: 1)."); - sgmParams.stepXY = 1; + ALICEVISION_LOG_WARNING("Single tile computation, override SGM step XY (before: " << sgmParams.stepXY << ", now: 1)."); + sgmParams.stepXY = 1; } } @@ -395,36 +394,36 @@ int aliceVision_main(int argc, char* argv[]) const int maxDownscale = std::max(sgmParams.scale * sgmParams.stepXY, refineParams.scale * refineParams.stepXY); // check padding - if(tileParams.padding % maxDownscale != 0) + if (tileParams.padding % maxDownscale != 0) { - const int padding = divideRoundUp(tileParams.padding, maxDownscale) * maxDownscale; - ALICEVISION_LOG_WARNING("Override tiling padding parameter (before: " << tileParams.padding << ", now: " << padding << ")."); - tileParams.padding = padding; + const int padding = divideRoundUp(tileParams.padding, maxDownscale) * maxDownscale; + ALICEVISION_LOG_WARNING("Override tiling padding parameter (before: " << tileParams.padding << ", now: " << padding << ")."); + tileParams.padding = padding; } // camera list std::vector cams; cams.reserve(mp.ncams); - if(rangeSize == -1) + if (rangeSize == -1) { - for(int rc = 0; rc < mp.ncams; ++rc) // process all cameras - cams.push_back(rc); + for (int rc = 0; rc < mp.ncams; ++rc) // process all cameras + cams.push_back(rc); } else { - if(rangeStart < 0) - { - ALICEVISION_LOG_ERROR("invalid subrange of cameras to process."); - return EXIT_FAILURE; - } - for(int rc = rangeStart; rc < std::min(rangeStart + rangeSize, mp.ncams); ++rc) - cams.push_back(rc); - if(cams.empty()) - { - ALICEVISION_LOG_INFO("No camera to process."); - return EXIT_SUCCESS; - } + if (rangeStart < 0) + { + ALICEVISION_LOG_ERROR("invalid subrange of cameras to process."); + return EXIT_FAILURE; + } + for (int rc = rangeStart; rc < std::min(rangeStart + rangeSize, mp.ncams); ++rc) + cams.push_back(rc); + if (cams.empty()) + { + ALICEVISION_LOG_INFO("No camera to process."); + return EXIT_SUCCESS; + } } // initialize depth map estimator diff --git a/src/software/pipeline/main_depthMapFiltering.cpp b/src/software/pipeline/main_depthMapFiltering.cpp index 439ee69c56..7e45f155a3 100644 --- a/src/software/pipeline/main_depthMapFiltering.cpp +++ b/src/software/pipeline/main_depthMapFiltering.cpp @@ -101,10 +101,10 @@ int aliceVision_main(int argc, char* argv[]) // read the input SfM scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; } // initialization @@ -116,21 +116,21 @@ int aliceVision_main(int argc, char* argv[]) std::vector cams; cams.reserve(mp.ncams); - if(rangeSize == -1) + if (rangeSize == -1) { - for(int rc = 0; rc < mp.ncams; rc++) // process all cameras + for (int rc = 0; rc < mp.ncams; rc++) // process all cameras cams.push_back(rc); } else { - if(rangeStart < 0) + if (rangeStart < 0) { ALICEVISION_LOG_ERROR("invalid subrange of cameras to process."); return EXIT_FAILURE; } - for(int rc = rangeStart; rc < std::min(rangeStart + rangeSize, mp.ncams); ++rc) + for (int rc = rangeStart; rc < std::min(rangeStart + rangeSize, mp.ncams); ++rc) cams.push_back(rc); - if(cams.empty()) + if (cams.empty()) { ALICEVISION_LOG_INFO("No camera to process."); return EXIT_SUCCESS; @@ -145,7 +145,7 @@ int aliceVision_main(int argc, char* argv[]) fs.filterDepthMaps(cams, minNumOfConsistentCams, minNumOfConsistentCamsWithLowSimilarity); } - if(computeNormalMaps) + if (computeNormalMaps) { int nbGPUs = 0; diff --git a/src/software/pipeline/main_distortionCalibration.cpp b/src/software/pipeline/main_distortionCalibration.cpp index 5013b3f3f2..cd802b1a15 100644 --- a/src/software/pipeline/main_distortionCalibration.cpp +++ b/src/software/pipeline/main_distortionCalibration.cpp @@ -8,8 +8,8 @@ // It is assumed that for each image we have a result of the checkerboard detector. // The constraint for this calibration is that we may not know : -// - the checkerboard size -// - the squares sizes +// - the checkerboard size +// - the squares sizes // - the checkerboard relative poses // We may only have only one image per distortion to estimate. @@ -17,7 +17,6 @@ // The idea is is to calibrate distortion parameters without estimating the pose or the intrinsics. // This algorithms groups the corners by lines and minimize a distance between corners and lines using distortion. - #include #include #include @@ -45,7 +44,7 @@ namespace po = boost::program_options; using namespace aliceVision; -bool retrieveLines(std::vector& lineWithPoints, const calibration::CheckerDetector & detect) +bool retrieveLines(std::vector& lineWithPoints, const calibration::CheckerDetector& detect) { const std::size_t minPointsPerLine = 10; @@ -55,15 +54,16 @@ bool retrieveLines(std::vector& lineWithPoints, con // Utility lambda to create lines by iterating over a board's cells in a given order auto createLines = [&](const calibration::CheckerDetector::CheckerBoard& board, bool exploreByRow, - bool replaceRowWithSum, bool replaceColWithSum, - bool flipRow, bool flipCol) -> void - { + bool replaceRowWithSum, + bool replaceColWithSum, + bool flipRow, + bool flipCol) -> void { int dim1 = exploreByRow ? board.rows() : board.cols(); int dim2 = exploreByRow ? board.cols() : board.rows(); for (int i = 0; i < dim1; ++i) { - //Random init + // Random init calibration::LineWithPoints line; line.angle = boost::math::constants::pi() * .25; line.dist = 1; @@ -76,17 +76,20 @@ bool retrieveLines(std::vector& lineWithPoints, con int j_cell = replaceColWithSum ? i + j : (exploreByRow ? j : i); j_cell = flipCol ? board.cols() - 1 - j_cell : j_cell; - if (i_cell < 0 || i_cell >= board.rows() || j_cell < 0 || j_cell >= board.cols()) continue; + if (i_cell < 0 || i_cell >= board.rows() || j_cell < 0 || j_cell >= board.cols()) + continue; const IndexT idx = board(i_cell, j_cell); - if (idx == UndefinedIndexT) continue; + if (idx == UndefinedIndexT) + continue; const calibration::CheckerDetector::CheckerBoardCorner& p = corners[idx]; line.points.push_back(p.center); } - //Check that we don't have a too small line which won't be easy to estimate - if (line.points.size() < minPointsPerLine) continue; + // Check that we don't have a too small line which won't be easy to estimate + if (line.points.size() < minPointsPerLine) + continue; lineWithPoints.push_back(line); } @@ -137,7 +140,7 @@ bool estimateDistortionMultiStep(std::shared_ptr undistort return true; } -int aliceVision_main(int argc, char* argv[]) +int aliceVision_main(int argc, char* argv[]) { std::string sfmInputDataFilepath; std::string checkerBoardsPath; @@ -179,16 +182,18 @@ int aliceVision_main(int argc, char* argv[]) } // Load the checkerboards - std::map < IndexT, calibration::CheckerDetector> boardsAllImages; + std::map boardsAllImages; for (auto& pv : sfmData.getViews()) { IndexT viewId = pv.first; // Read the json file std::stringstream ss; - ss << checkerBoardsPath << "/" << "checkers_" << viewId << ".json"; + ss << checkerBoardsPath << "/" + << "checkers_" << viewId << ".json"; std::ifstream inputfile(ss.str()); - if (!inputfile.is_open()) continue; + if (!inputfile.is_open()) + continue; std::stringstream buffer; buffer << inputfile.rdbuf(); @@ -206,13 +211,11 @@ int aliceVision_main(int argc, char* argv[]) for (auto& [intrinsicId, intrinsicPtr] : sfmData.getIntrinsics()) { // Convert to pinhole - std::shared_ptr cameraIn - = std::dynamic_pointer_cast(intrinsicPtr); + std::shared_ptr cameraIn = std::dynamic_pointer_cast(intrinsicPtr); // Create new camera corresponding to given model - std::shared_ptr cameraOut - = std::dynamic_pointer_cast( - camera::createIntrinsic(cameraModel, cameraIn->w(), cameraIn->h())); + std::shared_ptr cameraOut = + std::dynamic_pointer_cast(camera::createIntrinsic(cameraModel, cameraIn->w(), cameraIn->h())); if (!cameraIn || !cameraOut) { @@ -267,14 +270,10 @@ int aliceVision_main(int argc, char* argv[]) if (cameraModel == camera::EINTRINSIC::PINHOLE_CAMERA_3DEANAMORPHIC4) { - initialParams = { - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0 - }; - lockSteps = { - {true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, false, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, false, false, false, false, false, false, false, true, true, true, true} - }; + initialParams = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0}; + lockSteps = {{true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, false, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, true, true, true, true}}; } else { @@ -303,5 +302,5 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_featureExtraction.cpp b/src/software/pipeline/main_featureExtraction.cpp index ecaa328b3e..0086cae28d 100644 --- a/src/software/pipeline/main_featureExtraction.cpp +++ b/src/software/pipeline/main_featureExtraction.cpp @@ -11,10 +11,9 @@ #include #include #include -#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_POPSIFT) \ - || ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) -#define ALICEVISION_HAVE_GPU_FEATURES -#include +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_POPSIFT) || ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) + #define ALICEVISION_HAVE_GPU_FEATURES + #include #endif #include #include @@ -41,7 +40,7 @@ namespace fs = std::filesystem; /// - Compute view image description (feature & descriptor extraction) /// - Export computed data -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { // command-line parameters std::string sfmDataFilename; @@ -111,16 +110,16 @@ int aliceVision_main(int argc, char **argv) return EXIT_FAILURE; } - if(describerTypesName.empty()) + if (describerTypesName.empty()) { ALICEVISION_LOG_ERROR("--describerTypes option is empty."); return EXIT_FAILURE; } // create output folder - if(!fs::exists(outputFolder)) + if (!fs::exists(outputFolder)) { - if(!fs::create_directory(outputFolder)) + if (!fs::create_directory(outputFolder)) { ALICEVISION_LOG_ERROR("Cannot create output folder"); return EXIT_FAILURE; @@ -134,8 +133,8 @@ int aliceVision_main(int argc, char **argv) // load input scene sfmData::SfMData sfmData; - std::cout << sfmData.getViews().size() << std::endl; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) + std::cout << sfmData.getViews().size() << std::endl; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilename + "' cannot be read"); return EXIT_FAILURE; @@ -151,18 +150,18 @@ int aliceVision_main(int argc, char **argv) hwc.setUserCoresLimit(maxThreads); // set extraction range - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0) + if (rangeStart < 0 || rangeSize < 0) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; } - if(rangeStart + rangeSize > sfmData.getViews().size()) + if (rangeStart + rangeSize > sfmData.getViews().size()) rangeSize = sfmData.getViews().size() - rangeStart; - if(rangeSize <= 0) + if (rangeSize <= 0) { ALICEVISION_LOG_WARNING("Nothing to compute."); return EXIT_SUCCESS; @@ -175,11 +174,11 @@ int aliceVision_main(int argc, char **argv) { std::vector imageDescriberTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - for(const auto& imageDescriberType: imageDescriberTypes) + for (const auto& imageDescriberType : imageDescriberTypes) { std::shared_ptr imageDescriber = feature::createImageDescriber(imageDescriberType); imageDescriber->setConfigurationPreset(featDescConfig); - if(forceCpuExtraction) + if (forceCpuExtraction) imageDescriber->setUseCuda(false); extractor.addImageDescriber(imageDescriber); diff --git a/src/software/pipeline/main_featureMatching.cpp b/src/software/pipeline/main_featureMatching.cpp index fc9ac8d8a1..74f7bf2de9 100644 --- a/src/software/pipeline/main_featureMatching.cpp +++ b/src/software/pipeline/main_featureMatching.cpp @@ -60,25 +60,25 @@ namespace fs = std::filesystem; void getStatsMap(const PairwiseMatches& map) { #ifdef ALICEVISION_DEBUG_MATCHING - std::map stats; - for(const auto& imgMatches: map) - { - for(const auto& featMatchesPerDesc: imgMatches.second) + std::map stats; + for (const auto& imgMatches : map) { - for(const matching::IndMatch& featMatches: featMatchesPerDesc.second) - { - int d = std::floor(featMatches._distance / 1000.0); - if( stats.find(d) != stats.end() ) - stats[d] += 1; - else - stats[d] = 1; - } + for (const auto& featMatchesPerDesc : imgMatches.second) + { + for (const matching::IndMatch& featMatches : featMatchesPerDesc.second) + { + int d = std::floor(featMatches._distance / 1000.0); + if (stats.find(d) != stats.end()) + stats[d] += 1; + else + stats[d] = 1; + } + } + } + for (const auto& stat : stats) + { + ALICEVISION_LOG_DEBUG(std::to_string(stat.first) + "\t" + std::to_string(stat.second)); } - } - for(const auto& stat: stats) - { - ALICEVISION_LOG_DEBUG(std::to_string(stat.first) + "\t" + std::to_string(stat.second)); - } #endif } @@ -87,37 +87,38 @@ void getStatsMap(const PairwiseMatches& map) /// - Compute putative local feature matches (descriptors matching) /// - Compute geometric coherent feature matches (robust model estimation from putative matches) /// - Export computed data -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string matchesFolder; - std::vector featuresFolders; - - // user optional parameters - - std::string geometricFilterTypeName = matchingImageCollection::EGeometricFilterType_enumToString(matchingImageCollection::EGeometricFilterType::FUNDAMENTAL_MATRIX); - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - float distRatio = 0.8f; - std::vector predefinedPairList; - int rangeStart = -1; - int rangeSize = 0; - std::string nearestMatchingMethod = "ANN_L2"; - robustEstimation::ERobustEstimator geometricEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - double geometricErrorMax = 0.0; //< the maximum reprojection error allowed for image matching with geometric validation - double knownPosesGeometricErrorMax = 4.0; - bool savePutativeMatches = false; - bool guidedMatching = false; - bool crossMatching = false; - int maxIteration = 2048; - bool matchFilePerImage = false; - size_t numMatchesToKeep = 0; - bool useGridSort = true; - bool exportDebugFiles = false; - bool matchFromKnownCameraPoses = false; - const std::string fileExtension = "txt"; - int randomSeed = std::mt19937::default_seed; - double minRequired2DMotion = -1.0; + // command-line parameters + std::string sfmDataFilename; + std::string matchesFolder; + std::vector featuresFolders; + + // user optional parameters + + std::string geometricFilterTypeName = + matchingImageCollection::EGeometricFilterType_enumToString(matchingImageCollection::EGeometricFilterType::FUNDAMENTAL_MATRIX); + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + float distRatio = 0.8f; + std::vector predefinedPairList; + int rangeStart = -1; + int rangeSize = 0; + std::string nearestMatchingMethod = "ANN_L2"; + robustEstimation::ERobustEstimator geometricEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + double geometricErrorMax = 0.0; //< the maximum reprojection error allowed for image matching with geometric validation + double knownPosesGeometricErrorMax = 4.0; + bool savePutativeMatches = false; + bool guidedMatching = false; + bool crossMatching = false; + int maxIteration = 2048; + bool matchFilePerImage = false; + size_t numMatchesToKeep = 0; + bool useGridSort = true; + bool exportDebugFiles = false; + bool matchFromKnownCameraPoses = false; + const std::string fileExtension = "txt"; + int randomSeed = std::mt19937::default_seed; + double minRequired2DMotion = -1.0; // clang-format off po::options_description requiredParams("Required parameters"); @@ -187,375 +188,372 @@ int aliceVision_main(int argc, char **argv) "This seed value will generate a sequence using a linear random generator. Set -1 to use a random seed."); // clang-format on - CmdLine cmdline("This program computes corresponding features between a series of views:\n" - "- Load view images description (regions: features & descriptors)\n" - "- Compute putative local feature matches (descriptors matching)\n" - "- Compute geometric coherent feature matches (robust model estimation from putative matches)\n" - "- Export computed data\n" - "AliceVision featureMatching"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - const double defaultLoRansacMatchingError = 20.0; - if(!adjustRobustEstimatorThreshold(geometricEstimator, geometricErrorMax, defaultLoRansacMatchingError)) - return EXIT_FAILURE; - - - std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); - - // check and set input options - if(matchesFolder.empty() || !fs::is_directory(matchesFolder)) - { - ALICEVISION_LOG_ERROR("Invalid output matches folder: " + matchesFolder); - return EXIT_FAILURE; - } - - - const matchingImageCollection::EGeometricFilterType geometricFilterType = matchingImageCollection::EGeometricFilterType_stringToEnum(geometricFilterTypeName); - - if(describerTypesName.empty()) - { - ALICEVISION_LOG_ERROR("Empty option: --describerMethods"); - return EXIT_FAILURE; - } - - // Feature matching - // a. Load SfMData Views & intrinsics data - // b. Compute putative descriptor matches - // c. Geometric filtering of putative matches - // d. Export some statistics - - // a. Load SfMData (image view & intrinsics data) - - SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } - - // b. Compute putative descriptor matches - // - Descriptor matching (according user method choice) - // - Keep correspondences only if NearestNeighbor ratio is ok - - // from matching mode compute the pair list that have to be matched - PairSet pairs; - std::set filter; - - - // We assume that there is only one pair for (I,J) and (J,I) - if(predefinedPairList.empty()) - { - pairs = exhaustivePairs(sfmData.getViews(), rangeStart, rangeSize); - } - else - { - for(const std::string& imagePairsFile: predefinedPairList) + CmdLine cmdline("This program computes corresponding features between a series of views:\n" + "- Load view images description (regions: features & descriptors)\n" + "- Compute putative local feature matches (descriptors matching)\n" + "- Compute geometric coherent feature matches (robust model estimation from putative matches)\n" + "- Export computed data\n" + "AliceVision featureMatching"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - ALICEVISION_LOG_INFO("Load pair list from file: " << imagePairsFile); - if (!matchingImageCollection::loadPairsFromFile(imagePairsFile, pairs, rangeStart, rangeSize)) - return EXIT_FAILURE; + return EXIT_FAILURE; } - } - if(pairs.empty()) - { - ALICEVISION_LOG_INFO("No image pair to match."); - // if we only compute a selection of matches, we may have no match. - return rangeSize ? EXIT_SUCCESS : EXIT_FAILURE; - } + const double defaultLoRansacMatchingError = 20.0; + if (!adjustRobustEstimatorThreshold(geometricEstimator, geometricErrorMax, defaultLoRansacMatchingError)) + return EXIT_FAILURE; - ALICEVISION_LOG_INFO("Number of pairs: " << pairs.size()); + std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); - // filter creation - for(const auto& pair: pairs) - { - filter.insert(pair.first); - filter.insert(pair.second); - } + // check and set input options + if (matchesFolder.empty() || !fs::is_directory(matchesFolder)) + { + ALICEVISION_LOG_ERROR("Invalid output matches folder: " + matchesFolder); + return EXIT_FAILURE; + } - PairwiseMatches mapPutativesMatches; + const matchingImageCollection::EGeometricFilterType geometricFilterType = + matchingImageCollection::EGeometricFilterType_stringToEnum(geometricFilterTypeName); - // allocate the right Matcher according the Matching requested method - EMatcherType collectionMatcherType = EMatcherType_stringToEnum(nearestMatchingMethod); - std::unique_ptr imageCollectionMatcher = createImageCollectionMatcher(collectionMatcherType, distRatio, crossMatching); + if (describerTypesName.empty()) + { + ALICEVISION_LOG_ERROR("Empty option: --describerMethods"); + return EXIT_FAILURE; + } - const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + // Feature matching + // a. Load SfMData Views & intrinsics data + // b. Compute putative descriptor matches + // c. Geometric filtering of putative matches + // d. Export some statistics - ALICEVISION_LOG_INFO("There are " << sfmData.getViews().size() << " views and " << pairs.size() << " image pairs."); + // a. Load SfMData (image view & intrinsics data) - ALICEVISION_LOG_INFO("Load features and descriptors"); + SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } - // load the corresponding view regions - RegionsPerView regionPerView; - if(!sfm::loadRegionsPerView(regionPerView, sfmData, featuresFolders, describerTypes, filter)) - { - ALICEVISION_LOG_ERROR("Invalid regions in '" + sfmDataFilename + "'"); - return EXIT_FAILURE; - } + // b. Compute putative descriptor matches + // - Descriptor matching (according user method choice) + // - Keep correspondences only if NearestNeighbor ratio is ok - // perform the matching - system::Timer timer; - PairSet pairsPoseKnown; - PairSet pairsPoseUnknown; + // from matching mode compute the pair list that have to be matched + PairSet pairs; + std::set filter; - if(matchFromKnownCameraPoses) - { - for(const auto& p: pairs) - { - if(sfmData.isPoseAndIntrinsicDefined(p.first) && sfmData.isPoseAndIntrinsicDefined(p.second)) - { - pairsPoseKnown.insert(p); - } - else + // We assume that there is only one pair for (I,J) and (J,I) + if (predefinedPairList.empty()) + { + pairs = exhaustivePairs(sfmData.getViews(), rangeStart, rangeSize); + } + else + { + for (const std::string& imagePairsFile : predefinedPairList) { - pairsPoseUnknown.insert(p); + ALICEVISION_LOG_INFO("Load pair list from file: " << imagePairsFile); + if (!matchingImageCollection::loadPairsFromFile(imagePairsFile, pairs, rangeStart, rangeSize)) + return EXIT_FAILURE; } - } - } - else - { - pairsPoseUnknown = pairs; - } - - if(!pairsPoseKnown.empty()) - { - // compute matches from known camera poses when you have an initialization on the camera poses - ALICEVISION_LOG_INFO("Putative matches from known poses: " << pairsPoseKnown.size() << " image pairs."); - - sfm::StructureEstimationFromKnownPoses structureEstimator; - structureEstimator.match(sfmData, pairsPoseKnown, regionPerView, knownPosesGeometricErrorMax); - mapPutativesMatches = structureEstimator.getPutativesMatches(); - } - - if(!pairsPoseUnknown.empty()) - { - ALICEVISION_LOG_INFO("Putative matches (unknown poses): " << pairsPoseUnknown.size() << " image pairs."); - // match feature descriptors between them without geometric notion - - for(const feature::EImageDescriberType descType : describerTypes) - { - assert(descType != feature::EImageDescriberType::UNINITIALIZED); - ALICEVISION_LOG_INFO(EImageDescriberType_enumToString(descType) + " Regions Matching"); + } + + if (pairs.empty()) + { + ALICEVISION_LOG_INFO("No image pair to match."); + // if we only compute a selection of matches, we may have no match. + return rangeSize ? EXIT_SUCCESS : EXIT_FAILURE; + } - // photometric matching of putative pairs - imageCollectionMatcher->Match(randomNumberGenerator, regionPerView, pairsPoseUnknown, descType, mapPutativesMatches); + ALICEVISION_LOG_INFO("Number of pairs: " << pairs.size()); - // TODO: DELI - // if(!guided_matching) regionPerView.clearDescriptors() - } + // filter creation + for (const auto& pair : pairs) + { + filter.insert(pair.first); + filter.insert(pair.second); + } + + PairwiseMatches mapPutativesMatches; - } + // allocate the right Matcher according the Matching requested method + EMatcherType collectionMatcherType = EMatcherType_stringToEnum(nearestMatchingMethod); + std::unique_ptr imageCollectionMatcher = createImageCollectionMatcher(collectionMatcherType, distRatio, crossMatching); - filterMatchesByMin2DMotion(mapPutativesMatches, regionPerView, minRequired2DMotion); + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - if(mapPutativesMatches.empty()) - { - ALICEVISION_LOG_INFO("No putative feature matches."); - // If we only compute a selection of matches, we may have no match. - return rangeSize ? EXIT_SUCCESS : EXIT_FAILURE; - } + ALICEVISION_LOG_INFO("There are " << sfmData.getViews().size() << " views and " << pairs.size() << " image pairs."); - if(geometricFilterType == EGeometricFilterType::HOMOGRAPHY_GROWING) - { - // sort putative matches according to their Lowe ratio - // This is suggested by [F.Srajer, 2016]: the matches used to be the seeds of the homographies growing are chosen according - // to the putative matches order. This modification should improve recall. - for(auto& imgPair: mapPutativesMatches) + ALICEVISION_LOG_INFO("Load features and descriptors"); + + // load the corresponding view regions + RegionsPerView regionPerView; + if (!sfm::loadRegionsPerView(regionPerView, sfmData, featuresFolders, describerTypes, filter)) { - for(auto& descType: imgPair.second) - { - IndMatches & matches = descType.second; - sortMatches_byDistanceRatio(matches); - } + ALICEVISION_LOG_ERROR("Invalid regions in '" + sfmDataFilename + "'"); + return EXIT_FAILURE; } - } - // 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) + "." : ""; + // perform the matching + system::Timer timer; + PairSet pairsPoseKnown; + PairSet pairsPoseUnknown; - ALICEVISION_LOG_INFO(std::to_string(mapPutativesMatches.size()) << " putative image pair matches"); - - for(const auto& imageMatch: mapPutativesMatches) - ALICEVISION_LOG_INFO("\t- image pair (" + std::to_string(imageMatch.first.first) << ", " + std::to_string(imageMatch.first.second) + ") contains " + std::to_string(imageMatch.second.getNbAllMatches()) + " putative matches."); + if (matchFromKnownCameraPoses) + { + for (const auto& p : pairs) + { + if (sfmData.isPoseAndIntrinsicDefined(p.first) && sfmData.isPoseAndIntrinsicDefined(p.second)) + { + pairsPoseKnown.insert(p); + } + else + { + pairsPoseUnknown.insert(p); + } + } + } + else + { + pairsPoseUnknown = pairs; + } - // export putative matches - if(savePutativeMatches) - Save(mapPutativesMatches, (fs::path(matchesFolder) / "putativeMatches").string(), fileExtension, matchFilePerImage, filePrefix); + if (!pairsPoseKnown.empty()) + { + // compute matches from known camera poses when you have an initialization on the camera poses + ALICEVISION_LOG_INFO("Putative matches from known poses: " << pairsPoseKnown.size() << " image pairs."); - ALICEVISION_LOG_INFO("Task (Regions Matching) done in (s): " + std::to_string(timer.elapsed())); + sfm::StructureEstimationFromKnownPoses structureEstimator; + structureEstimator.match(sfmData, pairsPoseKnown, regionPerView, knownPosesGeometricErrorMax); + mapPutativesMatches = structureEstimator.getPutativesMatches(); + } - /* - // TODO: DELI - if(exportDebugFiles) - { - //-- export putative matches Adjacency matrix - PairwiseMatchingToAdjacencyMatrixSVG(sfmData.getViews().size(), - mapPutativesMatches, - (fs::path(matchesFolder) / "PutativeAdjacencyMatrix.svg").string()); - //-- export view pair graph once putative graph matches have been computed + if (!pairsPoseUnknown.empty()) { - std::set set_ViewIds; + ALICEVISION_LOG_INFO("Putative matches (unknown poses): " << pairsPoseUnknown.size() << " image pairs."); + // match feature descriptors between them without geometric notion - std::transform(sfmData.getViews().begin(), sfmData.getViews().end(), - std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey()); + for (const feature::EImageDescriberType descType : describerTypes) + { + assert(descType != feature::EImageDescriberType::UNINITIALIZED); + ALICEVISION_LOG_INFO(EImageDescriberType_enumToString(descType) + " Regions Matching"); - graph::indexedGraph putativeGraph(set_ViewIds, getPairs(mapPutativesMatches)); + // photometric matching of putative pairs + imageCollectionMatcher->Match(randomNumberGenerator, regionPerView, pairsPoseUnknown, descType, mapPutativesMatches); - graph::exportToGraphvizData( - (fs::path(matchesFolder) / "putative_matches.dot").string(), - putativeGraph.g); + // TODO: DELI + // if(!guided_matching) regionPerView.clearDescriptors() + } } - } - */ -#ifdef ALICEVISION_DEBUG_MATCHING + filterMatchesByMin2DMotion(mapPutativesMatches, regionPerView, minRequired2DMotion); + + if (mapPutativesMatches.empty()) { - ALICEVISION_LOG_DEBUG("PUTATIVE"); - getStatsMap(mapPutativesMatches); + ALICEVISION_LOG_INFO("No putative feature matches."); + // If we only compute a selection of matches, we may have no match. + return rangeSize ? EXIT_SUCCESS : EXIT_FAILURE; } -#endif - // c. Geometric filtering of putative matches - // - AContrario Estimation of the desired geometric model - // - Use an upper bound for the a contrario estimated threshold + if (geometricFilterType == EGeometricFilterType::HOMOGRAPHY_GROWING) + { + // sort putative matches according to their Lowe ratio + // This is suggested by [F.Srajer, 2016]: the matches used to be the seeds of the homographies growing are chosen according + // to the putative matches order. This modification should improve recall. + for (auto& imgPair : mapPutativesMatches) + { + for (auto& descType : imgPair.second) + { + IndMatches& matches = descType.second; + sortMatches_byDistanceRatio(matches); + } + } + } - timer.reset(); - + // 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) + "." : ""; - matching::PairwiseMatches geometricMatches; + ALICEVISION_LOG_INFO(std::to_string(mapPutativesMatches.size()) << " putative image pair matches"); - ALICEVISION_LOG_INFO("Geometric filtering: using " << matchingImageCollection::EGeometricFilterType_enumToString(geometricFilterType)); + for (const auto& imageMatch : mapPutativesMatches) + ALICEVISION_LOG_INFO("\t- image pair (" + std::to_string(imageMatch.first.first) + << ", " + std::to_string(imageMatch.first.second) + ") contains " + std::to_string(imageMatch.second.getNbAllMatches()) + + " putative matches."); - switch(geometricFilterType) - { + // export putative matches + if (savePutativeMatches) + Save(mapPutativesMatches, (fs::path(matchesFolder) / "putativeMatches").string(), fileExtension, matchFilePerImage, filePrefix); - case EGeometricFilterType::NO_FILTERING: - geometricMatches = mapPutativesMatches; - break; + ALICEVISION_LOG_INFO("Task (Regions Matching) done in (s): " + std::to_string(timer.elapsed())); - case EGeometricFilterType::FUNDAMENTAL_MATRIX: - { - matchingImageCollection::robustModelEstimation(geometricMatches, - &sfmData, - regionPerView, - GeometricFilterMatrix_F_AC(geometricErrorMax, maxIteration, geometricEstimator), - mapPutativesMatches, - randomNumberGenerator, - guidedMatching); - } - break; - - case EGeometricFilterType::FUNDAMENTAL_WITH_DISTORTION: - { - matchingImageCollection::robustModelEstimation(geometricMatches, - &sfmData, - regionPerView, - GeometricFilterMatrix_F_AC(geometricErrorMax, maxIteration, geometricEstimator, true), - mapPutativesMatches, - randomNumberGenerator, - guidedMatching); - } - break; - - case EGeometricFilterType::ESSENTIAL_MATRIX: + /* + // TODO: DELI + if(exportDebugFiles) { - matchingImageCollection::robustModelEstimation(geometricMatches, - &sfmData, - regionPerView, - GeometricFilterMatrix_E_AC(geometricErrorMax, maxIteration), + //-- export putative matches Adjacency matrix + PairwiseMatchingToAdjacencyMatrixSVG(sfmData.getViews().size(), mapPutativesMatches, - randomNumberGenerator, - guidedMatching); + (fs::path(matchesFolder) / "PutativeAdjacencyMatrix.svg").string()); + //-- export view pair graph once putative graph matches have been computed + { + std::set set_ViewIds; + + std::transform(sfmData.getViews().begin(), sfmData.getViews().end(), + std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey()); - removePoorlyOverlappingImagePairs(geometricMatches, mapPutativesMatches, 0.3f, 50); + graph::indexedGraph putativeGraph(set_ViewIds, getPairs(mapPutativesMatches)); + + graph::exportToGraphvizData( + (fs::path(matchesFolder) / "putative_matches.dot").string(), + putativeGraph.g); + } } - break; + */ - case EGeometricFilterType::HOMOGRAPHY_MATRIX: +#ifdef ALICEVISION_DEBUG_MATCHING { - const bool onlyGuidedMatching = true; - matchingImageCollection::robustModelEstimation(geometricMatches, - &sfmData, - regionPerView, - GeometricFilterMatrix_H_AC(geometricErrorMax, maxIteration), - mapPutativesMatches, randomNumberGenerator, guidedMatching, - onlyGuidedMatching ? -1.0 : 0.6); + ALICEVISION_LOG_DEBUG("PUTATIVE"); + getStatsMap(mapPutativesMatches); } - break; +#endif + + // c. Geometric filtering of putative matches + // - AContrario Estimation of the desired geometric model + // - Use an upper bound for the a contrario estimated threshold - case EGeometricFilterType::HOMOGRAPHY_GROWING: + timer.reset(); + + matching::PairwiseMatches geometricMatches; + + ALICEVISION_LOG_INFO("Geometric filtering: using " << matchingImageCollection::EGeometricFilterType_enumToString(geometricFilterType)); + + switch (geometricFilterType) { - matchingImageCollection::robustModelEstimation(geometricMatches, - &sfmData, - regionPerView, - GeometricFilterMatrix_HGrowing(geometricErrorMax, maxIteration), - mapPutativesMatches, - randomNumberGenerator, - guidedMatching); + case EGeometricFilterType::NO_FILTERING: + geometricMatches = mapPutativesMatches; + break; + + case EGeometricFilterType::FUNDAMENTAL_MATRIX: + { + matchingImageCollection::robustModelEstimation(geometricMatches, + &sfmData, + regionPerView, + GeometricFilterMatrix_F_AC(geometricErrorMax, maxIteration, geometricEstimator), + mapPutativesMatches, + randomNumberGenerator, + guidedMatching); + } + break; + + case EGeometricFilterType::FUNDAMENTAL_WITH_DISTORTION: + { + matchingImageCollection::robustModelEstimation(geometricMatches, + &sfmData, + regionPerView, + GeometricFilterMatrix_F_AC(geometricErrorMax, maxIteration, geometricEstimator, true), + mapPutativesMatches, + randomNumberGenerator, + guidedMatching); + } + break; + + case EGeometricFilterType::ESSENTIAL_MATRIX: + { + matchingImageCollection::robustModelEstimation(geometricMatches, + &sfmData, + regionPerView, + GeometricFilterMatrix_E_AC(geometricErrorMax, maxIteration), + mapPutativesMatches, + randomNumberGenerator, + guidedMatching); + + removePoorlyOverlappingImagePairs(geometricMatches, mapPutativesMatches, 0.3f, 50); + } + break; + + case EGeometricFilterType::HOMOGRAPHY_MATRIX: + { + const bool onlyGuidedMatching = true; + matchingImageCollection::robustModelEstimation(geometricMatches, + &sfmData, + regionPerView, + GeometricFilterMatrix_H_AC(geometricErrorMax, maxIteration), + mapPutativesMatches, + randomNumberGenerator, + guidedMatching, + onlyGuidedMatching ? -1.0 : 0.6); + } + break; + + case EGeometricFilterType::HOMOGRAPHY_GROWING: + { + matchingImageCollection::robustModelEstimation(geometricMatches, + &sfmData, + regionPerView, + GeometricFilterMatrix_HGrowing(geometricErrorMax, maxIteration), + mapPutativesMatches, + randomNumberGenerator, + guidedMatching); + } + break; } - break; - } - ALICEVISION_LOG_INFO(std::to_string(geometricMatches.size()) + " geometric image pair matches:"); - for(const auto& matchGeo: geometricMatches) - ALICEVISION_LOG_INFO("\t- image pair (" + std::to_string(matchGeo.first.first) + ", " + std::to_string(matchGeo.first.second) + ") contains " + std::to_string(matchGeo.second.getNbAllMatches()) + " geometric matches."); + ALICEVISION_LOG_INFO(std::to_string(geometricMatches.size()) + " geometric image pair matches:"); + for (const auto& matchGeo : geometricMatches) + ALICEVISION_LOG_INFO("\t- image pair (" + std::to_string(matchGeo.first.first) + ", " + std::to_string(matchGeo.first.second) + + ") contains " + std::to_string(matchGeo.second.getNbAllMatches()) + " geometric matches."); - // grid filtering - ALICEVISION_LOG_INFO("Grid filtering"); + // grid filtering + ALICEVISION_LOG_INFO("Grid filtering"); - PairwiseMatches finalMatches; - matchesGridFilteringForAllPairs(geometricMatches, sfmData, regionPerView, useGridSort, - numMatchesToKeep, finalMatches); + PairwiseMatches finalMatches; + matchesGridFilteringForAllPairs(geometricMatches, sfmData, regionPerView, useGridSort, numMatchesToKeep, finalMatches); ALICEVISION_LOG_INFO("After grid filtering:"); - for (const auto& matchGridFiltering: finalMatches) + for (const auto& matchGridFiltering : finalMatches) { - ALICEVISION_LOG_INFO("\t- image pair (" << matchGridFiltering.first.first << ", " - << matchGridFiltering.first.second << ") contains " - << matchGridFiltering.second.getNbAllMatches() - << " geometric matches."); + ALICEVISION_LOG_INFO("\t- image pair (" << matchGridFiltering.first.first << ", " << matchGridFiltering.first.second << ") contains " + << matchGridFiltering.second.getNbAllMatches() << " geometric matches."); } - // export geometric filtered matches - ALICEVISION_LOG_INFO("Save geometric matches."); - Save(finalMatches, matchesFolder, fileExtension, matchFilePerImage, filePrefix); - ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); + // export geometric filtered matches + ALICEVISION_LOG_INFO("Save geometric matches."); + Save(finalMatches, matchesFolder, fileExtension, matchFilePerImage, filePrefix); + ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); - // d. Export some statistics - if(exportDebugFiles) - { - // export Adjacency matrix - ALICEVISION_LOG_INFO("Export Adjacency Matrix of the pairwise's geometric matches"); - PairwiseMatchingToAdjacencyMatrixSVG(sfmData.getViews().size(), - finalMatches,(fs::path(matchesFolder) / "GeometricAdjacencyMatrix.svg").string()); - - /* - // export view pair graph once geometric filter have been done + // d. Export some statistics + if (exportDebugFiles) { - std::set set_ViewIds; - std::transform(sfmData.getViews().begin(), sfmData.getViews().end(), - std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey()); - graph::indexedGraph putativeGraph(set_ViewIds, getPairs(finalMatches)); - graph::exportToGraphvizData( - (fs::path(matchesFolder) / "geometric_matches.dot").string(), - putativeGraph.g); + // export Adjacency matrix + ALICEVISION_LOG_INFO("Export Adjacency Matrix of the pairwise's geometric matches"); + PairwiseMatchingToAdjacencyMatrixSVG( + sfmData.getViews().size(), finalMatches, (fs::path(matchesFolder) / "GeometricAdjacencyMatrix.svg").string()); + + /* + // export view pair graph once geometric filter have been done + { + std::set set_ViewIds; + std::transform(sfmData.getViews().begin(), sfmData.getViews().end(), + std::inserter(set_ViewIds, set_ViewIds.begin()), stl::RetrieveKey()); + graph::indexedGraph putativeGraph(set_ViewIds, getPairs(finalMatches)); + graph::exportToGraphvizData( + (fs::path(matchesFolder) / "geometric_matches.dot").string(), + putativeGraph.g); + } + */ } - */ - } #ifdef ALICEVISION_DEBUG_MATCHING - { - ALICEVISION_LOG_DEBUG("GEOMETRIC"); - getStatsMap(geometricMatches); - } + { + ALICEVISION_LOG_DEBUG("GEOMETRIC"); + getStatsMap(geometricMatches); + } #endif - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_globalSfM.cpp b/src/software/pipeline/main_globalSfM.cpp index 7c5e0b0b2c..89db398500 100644 --- a/src/software/pipeline/main_globalSfM.cpp +++ b/src/software/pipeline/main_globalSfM.cpp @@ -30,23 +30,23 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilepath; - std::vector featuresFolders; - std::vector matchesFolders; - std::string extraInfoFolder; - std::string outputSfMViewsAndPoses; - - // user optional parameters - - std::string outSfMDataFilepath = "SfmData.json"; - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - sfm::ERotationAveragingMethod rotationAveragingMethod = sfm::ROTATION_AVERAGING_L2; - sfm::ETranslationAveragingMethod translationAveragingMethod = sfm::TRANSLATION_AVERAGING_SOFTL1; - bool lockAllIntrinsics = false; - int randomSeed = std::mt19937::default_seed; + // command-line parameters + std::string sfmDataFilepath; + std::vector featuresFolders; + std::vector matchesFolders; + std::string extraInfoFolder; + std::string outputSfMViewsAndPoses; + + // user optional parameters + + std::string outSfMDataFilepath = "SfmData.json"; + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + sfm::ERotationAveragingMethod rotationAveragingMethod = sfm::ROTATION_AVERAGING_L2; + sfm::ETranslationAveragingMethod translationAveragingMethod = sfm::TRANSLATION_AVERAGING_SOFTL1; + bool lockAllIntrinsics = false; + int randomSeed = std::mt19937::default_seed; // clang-format off po::options_description requiredParams("Required parameters"); @@ -81,130 +81,127 @@ int aliceVision_main(int argc, char **argv) "This seed value will generate a sequence using a linear random generator. Set -1 to use a random seed."); // clang-format on - CmdLine cmdline("This program is an implementation of the paper\n" - "\"Global Fusion of Relative Motions for " - "Robust, Accurate and Scalable Structure from Motion.\"\n" - "Pierre Moulon, Pascal Monasse and Renaud Marlet ICCV 2013.\n" - "AliceVision globalSfM"); - - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if (rotationAveragingMethod < sfm::ROTATION_AVERAGING_L1 || - rotationAveragingMethod > sfm::ROTATION_AVERAGING_L2 ) - { - ALICEVISION_LOG_ERROR("Rotation averaging method is invalid"); - return EXIT_FAILURE; - } - - if (translationAveragingMethod < sfm::TRANSLATION_AVERAGING_L1 || - translationAveragingMethod > sfm::TRANSLATION_AVERAGING_SOFTL1 ) - { - ALICEVISION_LOG_ERROR("Translation averaging method is invalid"); - return EXIT_FAILURE; - } - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilepath << "' cannot be read."); - return EXIT_FAILURE; - } - - if(!sfmData.getLandmarks().empty()) - { - ALICEVISION_LOG_ERROR("Part computed SfMData are not currently supported in Global SfM." << std::endl << "Please use Incremental SfM. Aborted"); - return EXIT_FAILURE; - } - - if(!sfmData.getRigs().empty()) - { - ALICEVISION_LOG_ERROR("Rigs are not currently supported in Global SfM." << std::endl << "Please use Incremental SfM. Aborted"); - return EXIT_FAILURE; - } - - // get describerTypes - const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - - // features reading - feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) - { - ALICEVISION_LOG_ERROR("Invalid features"); - return EXIT_FAILURE; - } - - // matches reading - // Load the match file (try to read the two matches file formats). - matching::PairwiseMatches pairwiseMatches; - if(!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerTypes)) - { - ALICEVISION_LOG_ERROR("Unable to load matches files from: " << matchesFolders); - return EXIT_FAILURE; - } - - if(extraInfoFolder.empty()) - extraInfoFolder = fs::path(outSfMDataFilepath).parent_path().string(); - - if (!fs::exists(extraInfoFolder)) - fs::create_directory(extraInfoFolder); - - // global SfM reconstruction process - aliceVision::system::Timer timer; - sfm::ReconstructionEngine_globalSfM sfmEngine( - sfmData, - extraInfoFolder, - (fs::path(extraInfoFolder) / "sfm_log.html").string()); - - sfmEngine.initRandomSeed(randomSeed); - - // configure the featuresPerView & the matches_provider - sfmEngine.setFeaturesProvider(&featuresPerView); - sfmEngine.setMatchesProvider(&pairwiseMatches); - - // configure reconstruction parameters - sfmEngine.setLockAllIntrinsics(lockAllIntrinsics); // TODO: rename param - - // configure motion averaging method - sfmEngine.setRotationAveragingMethod(sfm::ERotationAveragingMethod(rotationAveragingMethod)); - sfmEngine.setTranslationAveragingMethod(sfm::ETranslationAveragingMethod(translationAveragingMethod)); - - if(!sfmEngine.process()) - return EXIT_FAILURE; - - // get the color for the 3D points - sfmEngine.colorize(); - - // set featuresFolders and matchesFolders relative paths - { - sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); - sfmEngine.getSfMData().addMatchesFolders(matchesFolders); - sfmEngine.getSfMData().setAbsolutePath(fs::path(outSfMDataFilepath).parent_path().string()); - } - - ALICEVISION_LOG_INFO("Global structure from motion took (s): " << timer.elapsed()); - ALICEVISION_LOG_INFO("Generating HTML report..."); - - sfm::generateSfMReport(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "sfm_report.html").string()); - - // export to disk computed scene (data & visualizable results) - ALICEVISION_LOG_INFO("Export SfMData to disk"); - - sfmDataIO::save(sfmEngine.getSfMData(), outSfMDataFilepath, sfmDataIO::ESfMData::ALL); - sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "cloud_and_poses.ply").string(), sfmDataIO::ESfMData::ALL); - - if(!outputSfMViewsAndPoses.empty()) - sfmDataIO::save(sfmEngine.getSfMData(), outputSfMViewsAndPoses, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::EXTRINSICS|sfmDataIO::INTRINSICS)); - - ALICEVISION_LOG_INFO("Structure from Motion results:" << std::endl - << "\t- # input images: " << sfmEngine.getSfMData().getViews().size() << std::endl - << "\t- # cameras calibrated: " << sfmEngine.getSfMData().getPoses().size() << std::endl - << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); - - return EXIT_SUCCESS; + CmdLine cmdline("This program is an implementation of the paper\n" + "\"Global Fusion of Relative Motions for " + "Robust, Accurate and Scalable Structure from Motion.\"\n" + "Pierre Moulon, Pascal Monasse and Renaud Marlet ICCV 2013.\n" + "AliceVision globalSfM"); + + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (rotationAveragingMethod < sfm::ROTATION_AVERAGING_L1 || rotationAveragingMethod > sfm::ROTATION_AVERAGING_L2) + { + ALICEVISION_LOG_ERROR("Rotation averaging method is invalid"); + return EXIT_FAILURE; + } + + if (translationAveragingMethod < sfm::TRANSLATION_AVERAGING_L1 || translationAveragingMethod > sfm::TRANSLATION_AVERAGING_SOFTL1) + { + ALICEVISION_LOG_ERROR("Translation averaging method is invalid"); + return EXIT_FAILURE; + } + + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilepath << "' cannot be read."); + return EXIT_FAILURE; + } + + if (!sfmData.getLandmarks().empty()) + { + ALICEVISION_LOG_ERROR("Part computed SfMData are not currently supported in Global SfM." << std::endl + << "Please use Incremental SfM. Aborted"); + return EXIT_FAILURE; + } + + if (!sfmData.getRigs().empty()) + { + ALICEVISION_LOG_ERROR("Rigs are not currently supported in Global SfM." << std::endl << "Please use Incremental SfM. Aborted"); + return EXIT_FAILURE; + } + + // get describerTypes + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + + // features reading + feature::FeaturesPerView featuresPerView; + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + { + ALICEVISION_LOG_ERROR("Invalid features"); + return EXIT_FAILURE; + } + + // matches reading + // Load the match file (try to read the two matches file formats). + matching::PairwiseMatches pairwiseMatches; + if (!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerTypes)) + { + ALICEVISION_LOG_ERROR("Unable to load matches files from: " << matchesFolders); + return EXIT_FAILURE; + } + + if (extraInfoFolder.empty()) + extraInfoFolder = fs::path(outSfMDataFilepath).parent_path().string(); + + if (!fs::exists(extraInfoFolder)) + fs::create_directory(extraInfoFolder); + + // global SfM reconstruction process + aliceVision::system::Timer timer; + sfm::ReconstructionEngine_globalSfM sfmEngine(sfmData, extraInfoFolder, (fs::path(extraInfoFolder) / "sfm_log.html").string()); + + sfmEngine.initRandomSeed(randomSeed); + + // configure the featuresPerView & the matches_provider + sfmEngine.setFeaturesProvider(&featuresPerView); + sfmEngine.setMatchesProvider(&pairwiseMatches); + + // configure reconstruction parameters + sfmEngine.setLockAllIntrinsics(lockAllIntrinsics); // TODO: rename param + + // configure motion averaging method + sfmEngine.setRotationAveragingMethod(sfm::ERotationAveragingMethod(rotationAveragingMethod)); + sfmEngine.setTranslationAveragingMethod(sfm::ETranslationAveragingMethod(translationAveragingMethod)); + + if (!sfmEngine.process()) + return EXIT_FAILURE; + + // get the color for the 3D points + sfmEngine.colorize(); + + // set featuresFolders and matchesFolders relative paths + { + sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); + sfmEngine.getSfMData().addMatchesFolders(matchesFolders); + sfmEngine.getSfMData().setAbsolutePath(fs::path(outSfMDataFilepath).parent_path().string()); + } + + ALICEVISION_LOG_INFO("Global structure from motion took (s): " << timer.elapsed()); + ALICEVISION_LOG_INFO("Generating HTML report..."); + + sfm::generateSfMReport(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "sfm_report.html").string()); + + // export to disk computed scene (data & visualizable results) + ALICEVISION_LOG_INFO("Export SfMData to disk"); + + sfmDataIO::save(sfmEngine.getSfMData(), outSfMDataFilepath, sfmDataIO::ESfMData::ALL); + sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "cloud_and_poses.ply").string(), sfmDataIO::ESfMData::ALL); + + if (!outputSfMViewsAndPoses.empty()) + sfmDataIO::save( + sfmEngine.getSfMData(), outputSfMViewsAndPoses, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); + + ALICEVISION_LOG_INFO("Structure from Motion results:" << std::endl + << "\t- # input images: " << sfmEngine.getSfMData().getViews().size() << std::endl + << "\t- # cameras calibrated: " << sfmEngine.getSfMData().getPoses().size() << std::endl + << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); + + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_imageMasking.cpp b/src/software/pipeline/main_imageMasking.cpp index eaddcbfca4..f453481b3b 100644 --- a/src/software/pipeline/main_imageMasking.cpp +++ b/src/software/pipeline/main_imageMasking.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include @@ -31,7 +31,8 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -enum class EAlgorithm { +enum class EAlgorithm +{ HSV, GrabCut, AutoGrayscaleThreshold, @@ -39,7 +40,7 @@ enum class EAlgorithm { inline std::string EAlgorithm_enumToString(EAlgorithm value) { - switch(value) + switch (value) { case EAlgorithm::HSV: return "hsv"; @@ -54,19 +55,16 @@ inline std::string EAlgorithm_enumToString(EAlgorithm value) inline EAlgorithm EAlgorithm_stringToEnum(std::string value) { boost::to_lower(value); - if(value == "hsv") + if (value == "hsv") return EAlgorithm::HSV; - if(value == "grabcut") + if (value == "grabcut") return EAlgorithm::GrabCut; - if(value == "autograyscalethreshold") + if (value == "autograyscalethreshold") return EAlgorithm::AutoGrayscaleThreshold; throw std::out_of_range("Invalid Algorithm type string " + value); } -inline std::ostream& operator<<(std::ostream& os, EAlgorithm s) -{ - return os << EAlgorithm_enumToString(s); -} +inline std::ostream& operator<<(std::ostream& os, EAlgorithm s) { return os << EAlgorithm_enumToString(s); } inline std::istream& operator>>(std::istream& in, EAlgorithm& s) { @@ -79,7 +77,7 @@ inline std::istream& operator>>(std::istream& in, EAlgorithm& s) /** * @brief Write mask images from input images based on chosen algorithm. */ -int main(int argc, char **argv) +int main(int argc, char** argv) { // command-line parameters std::string sfmFilePath; @@ -163,37 +161,37 @@ int main(int argc, char **argv) } // check user choose at least one input option - if(sfmFilePath.empty()) + if (sfmFilePath.empty()) { ALICEVISION_LOG_ERROR("Program need -i option" << std::endl << "No input images."); return EXIT_FAILURE; } // check sfm file - if(!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) + if (!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) { ALICEVISION_LOG_ERROR("The input sfm file doesn't exist"); return EXIT_FAILURE; } sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::VIEWS)) + if (!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::VIEWS)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); return EXIT_FAILURE; } // check output string - if(outputFilePath.empty()) + if (outputFilePath.empty()) { ALICEVISION_LOG_ERROR("Invalid output"); return EXIT_FAILURE; } // ensure output folder exists - if(!outputFilePath.empty() && !fs::exists(outputFilePath)) + if (!outputFilePath.empty() && !fs::exists(outputFilePath)) { - if(!fs::create_directory(outputFilePath)) + if (!fs::create_directory(outputFilePath)) { ALICEVISION_LOG_ERROR("Cannot create output folder"); return EXIT_FAILURE; @@ -201,7 +199,7 @@ int main(int argc, char **argv) } // check program range - if(rangeStart < 0 || rangeStart >= sfmData.getViews().size()) + if (rangeStart < 0 || rangeStart >= sfmData.getViews().size()) { ALICEVISION_LOG_ERROR("invalid subrange of views to process."); return EXIT_FAILURE; @@ -210,24 +208,23 @@ int main(int argc, char **argv) // algorithms using imageMasking::OutImage; std::function process; - switch(algorithm) + switch (algorithm) { case EAlgorithm::HSV: { // check options - if(hsv.minSaturation > hsv.maxSaturation) + if (hsv.minSaturation > hsv.maxSaturation) { ALICEVISION_LOG_ERROR("hsv-minSaturation must be lower than hsv-maxSaturation"); return EXIT_FAILURE; } - if(hsv.minValue > hsv.maxValue) + if (hsv.minValue > hsv.maxValue) { ALICEVISION_LOG_ERROR("hsv-minValue must be lower than hsv-maxValue"); return EXIT_FAILURE; } - process = [&](OutImage& result, const std::string& inputPath) - { + process = [&](OutImage& result, const std::string& inputPath) { imageMasking::hsv(result, inputPath, hsv.hue, hsv.hueRange, hsv.minSaturation, hsv.maxSaturation, hsv.minValue, hsv.maxValue); }; break; @@ -241,7 +238,7 @@ int main(int argc, char **argv) throw std::runtime_error("GrabCut not yet implemented."); } - if(!process) + if (!process) { ALICEVISION_LOG_ERROR("Invalid algorithm"); return EXIT_FAILURE; @@ -261,7 +258,7 @@ int main(int argc, char **argv) bool useDepthMap = !depthMapExp.empty() || !depthMapFolder.empty(); // #pragma omp parallel for - for(int i = 0; i < size; ++i) + for (int i = 0; i < size; ++i) { const auto& item = std::next(viewPairItBegin, rangeStart + i); const IndexT& index = item->first; @@ -269,35 +266,35 @@ int main(int argc, char **argv) std::string imgPath = view.getImage().getImagePath(); std::string depthMapPath; - if(!depthMapExp.empty()) + if (!depthMapExp.empty()) { depthMapPath = depthMapExp; { const auto pos = depthMapPath.find(k_depthMapFolder); - if(pos != std::string::npos) + if (pos != std::string::npos) depthMapPath.replace(pos, k_depthMapFolder.size(), depthMapFolder); } { const auto pos = depthMapPath.find(k_inputFolder); - if(pos != std::string::npos) + if (pos != std::string::npos) depthMapPath.replace(pos, k_inputFolder.size(), fs::path(imgPath).parent_path().string()); } { const auto pos = depthMapPath.find(k_filename); - if(pos != std::string::npos) + if (pos != std::string::npos) depthMapPath.replace(pos, k_filename.size(), fs::path(imgPath).filename().string()); } { const auto pos = depthMapPath.find(k_stem); - if(pos != std::string::npos) + if (pos != std::string::npos) depthMapPath.replace(pos, k_stem.size(), fs::path(imgPath).stem().string()); } { const auto pos = depthMapPath.find(k_ext); - if(pos != std::string::npos) + if (pos != std::string::npos) depthMapPath.replace(pos, k_stem.size(), fs::path(imgPath).extension().string().substr(1)); } - if(!fs::exists(depthMapPath)) + if (!fs::exists(depthMapPath)) { ALICEVISION_LOG_DEBUG("depthMapPath from expression: \"" << depthMapPath << "\" not found."); depthMapPath.clear(); @@ -307,11 +304,11 @@ int main(int argc, char **argv) ALICEVISION_LOG_DEBUG("depthMapPath from expression: \"" << depthMapPath << "\" found."); } } - else if(!depthMapFolder.empty()) + else if (!depthMapFolder.empty()) { // Look for View UID fs::path p = fs::path(depthMapFolder) / (std::to_string(view.getViewId()) + fs::path(imgPath).extension().string()); - if(fs::exists(p)) + if (fs::exists(p)) { depthMapPath = p.string(); ALICEVISION_LOG_DEBUG("depthMapPath found from folder and View UID: \"" << depthMapPath << "\"."); @@ -320,7 +317,7 @@ int main(int argc, char **argv) { // Look for an image with the same filename p = fs::path(depthMapFolder) / fs::path(imgPath).filename(); - if(fs::exists(p)) + if (fs::exists(p)) { depthMapPath = p.string(); ALICEVISION_LOG_DEBUG("depthMapPath found from folder and input filename: \"" << depthMapPath << "\"."); @@ -332,32 +329,33 @@ int main(int argc, char **argv) image::Image result; process(result, p); - if(invert) + if (invert) { imageMasking::postprocess_invert(result); } - if(growRadius > 0) + if (growRadius > 0) { imageMasking::postprocess_dilate(result, growRadius); } - if(shrinkRadius > 0) + if (shrinkRadius > 0) { imageMasking::postprocess_erode(result, shrinkRadius); } - if(useDepthMap) + if (useDepthMap) { bool viewHorizontal = view.getImage().getWidth() > view.getImage().getHeight(); bool depthMapHorizontal = result.width() > result.height(); - if(viewHorizontal != depthMapHorizontal) + if (viewHorizontal != depthMapHorizontal) { ALICEVISION_LOG_ERROR("Image " << imgPath << " : " << view.getImage().getWidth() << "x" << view.getImage().getHeight()); ALICEVISION_LOG_ERROR("Depth Map " << depthMapPath << " : " << result.width() << "x" << result.height()); throw std::runtime_error("Depth map orientation is not aligned with source image."); } - if(view.getImage().getWidth() != result.width()) + if (view.getImage().getWidth() != result.width()) { - ALICEVISION_LOG_DEBUG("Rescale depth map \"" << imgPath << "\" from: " << result.width() << "x" << result.height() << ", to: " << view.getImage().getWidth() << "x" << view.getImage().getHeight()); + ALICEVISION_LOG_DEBUG("Rescale depth map \"" << imgPath << "\" from: " << result.width() << "x" << result.height() + << ", to: " << view.getImage().getWidth() << "x" << view.getImage().getHeight()); image::Image rescaled(view.getImage().getWidth(), view.getImage().getHeight()); @@ -371,8 +369,7 @@ int main(int argc, char **argv) } const auto resultFilename = fs::path(std::to_string(index)).replace_extension("png"); const std::string resultPath = (fs::path(outputFilePath) / resultFilename).string(); - image::writeImage(resultPath, result, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::LINEAR)); + image::writeImage(resultPath, result, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::LINEAR)); } ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); diff --git a/src/software/pipeline/main_imageMatching.cpp b/src/software/pipeline/main_imageMatching.cpp index d51a12ace8..57f60275e8 100644 --- a/src/software/pipeline/main_imageMatching.cpp +++ b/src/software/pipeline/main_imageMatching.cpp @@ -41,40 +41,39 @@ namespace fs = std::filesystem; int aliceVision_main(int argc, char** argv) { - // command-line parameters - /// the file containing a list of features - std::string sfmDataFilenameA; - /// the folder(s) containing the extracted features with their associated descriptors - std::vector featuresFolders; - /// the file in which to save the results - std::string outputFile; - - // user optional parameters - EImageMatchingMethod method = EImageMatchingMethod::VOCABULARYTREE; - /// minimal number of images to use the vocabulary tree - std::size_t minNbImages = 200; - /// the file containing the list of features - std::size_t nbMaxDescriptors = 500; - /// the number of matches to retrieve for each image in Vocabulary Tree Mode - std::size_t numImageQuery = 50; - /// the number of neighbors to retrieve for each image in Sequential Mode - std::size_t numImageQuerySequential = 50; - /// the filename of the voctree - std::string treeFilepath; - /// the filename for the voctree weights - std::string weightsFilepath; - /// flag for the optional weights file - bool withWeights = false; - - - // multiple SfM parameters - - /// a second file containing a list of features - std::string sfmDataFilenameB; - /// the multiple SfM mode - std::string matchingModeName = EImageMatchingMode_enumToString(EImageMatchingMode::A_A); - /// the combine SfM output - std::string outputCombinedSfM; + // command-line parameters + /// the file containing a list of features + std::string sfmDataFilenameA; + /// the folder(s) containing the extracted features with their associated descriptors + std::vector featuresFolders; + /// the file in which to save the results + std::string outputFile; + + // user optional parameters + EImageMatchingMethod method = EImageMatchingMethod::VOCABULARYTREE; + /// minimal number of images to use the vocabulary tree + std::size_t minNbImages = 200; + /// the file containing the list of features + std::size_t nbMaxDescriptors = 500; + /// the number of matches to retrieve for each image in Vocabulary Tree Mode + std::size_t numImageQuery = 50; + /// the number of neighbors to retrieve for each image in Sequential Mode + std::size_t numImageQuerySequential = 50; + /// the filename of the voctree + std::string treeFilepath; + /// the filename for the voctree weights + std::string weightsFilepath; + /// flag for the optional weights file + bool withWeights = false; + + // multiple SfM parameters + + /// a second file containing a list of features + std::string sfmDataFilenameB; + /// the multiple SfM mode + std::string matchingModeName = EImageMatchingMode_enumToString(EImageMatchingMode::A_A); + /// the combine SfM output + std::string outputCombinedSfM; // clang-format off po::options_description requiredParams("Required parameters"); @@ -122,174 +121,199 @@ int aliceVision_main(int argc, char** argv) "Output file path for the combined SfMData file (if empty, don't combine)."); // clang-format on - CmdLine cmdline("The objective of this software is to find images that are looking to the same areas of the scene. " - "For that, we use the image retrieval techniques to find images that share content without " - "the cost of resolving all feature matches in detail. The ambition is to simplify the image in " - "a compact image descriptor which allows to compute the distance between all images descriptors efficiently.\n" - "This program generates a pair list file to be passed to the aliceVision_featureMatching software. " - "This file contains for each image the list of most similar images.\n" - "AliceVision imageMatching"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - cmdline.add(multiSfMParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // multiple SfM - const bool useMultiSfM = !sfmDataFilenameB.empty(); - const EImageMatchingMode matchingMode = EImageMatchingMode_stringToEnum(matchingModeName); - - if(useMultiSfM == (matchingMode == EImageMatchingMode::A_A)) - { - ALICEVISION_LOG_ERROR("The number of SfMData inputs is not compatible with the selected mode."); - return EXIT_FAILURE; - } - - // load SfMData - sfmData::SfMData sfmDataA, sfmDataB; - - using namespace sfmDataIO; - if(!sfmDataIO::load(sfmDataA, sfmDataFilenameA, ESfMData(ESfMData::VIEWS|ESfMData::EXTRINSICS|ESfMData::INTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilenameA + "' cannot be read."); - return EXIT_FAILURE; - } - - if(useMultiSfM) - { - if(!sfmDataIO::load(sfmDataB, sfmDataFilenameB, ESfMData(ESfMData::VIEWS|ESfMData::EXTRINSICS|ESfMData::INTRINSICS))) + CmdLine cmdline("The objective of this software is to find images that are looking to the same areas of the scene. " + "For that, we use the image retrieval techniques to find images that share content without " + "the cost of resolving all feature matches in detail. The ambition is to simplify the image in " + "a compact image descriptor which allows to compute the distance between all images descriptors efficiently.\n" + "This program generates a pair list file to be passed to the aliceVision_featureMatching software. " + "This file contains for each image the list of most similar images.\n" + "AliceVision imageMatching"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + cmdline.add(multiSfMParams); + if (!cmdline.execute(argc, argv)) { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilenameB + "' cannot be read."); - return EXIT_FAILURE; + return EXIT_FAILURE; } - // remove duplicated view - for(const auto& viewPair : sfmDataB.getViews()) + // multiple SfM + const bool useMultiSfM = !sfmDataFilenameB.empty(); + const EImageMatchingMode matchingMode = EImageMatchingMode_stringToEnum(matchingModeName); + + if (useMultiSfM == (matchingMode == EImageMatchingMode::A_A)) { - sfmData::Views::iterator it = sfmDataA.getViews().find(viewPair.first); - if(it != sfmDataA.getViews().end()) - sfmDataA.getViews().erase(it); + ALICEVISION_LOG_ERROR("The number of SfMData inputs is not compatible with the selected mode."); + return EXIT_FAILURE; } - } - - method = selectImageMatchingMethod(method, sfmDataA, sfmDataB, minNbImages); - - std::map descriptorsFilesA, descriptorsFilesB; - - if(method != EImageMatchingMethod::EXHAUSTIVE && method != EImageMatchingMethod::SEQUENTIAL) - { - // load descriptor filenames - aliceVision::voctree::getListOfDescriptorFiles(sfmDataA, featuresFolders, descriptorsFilesA); - - if(useMultiSfM) - aliceVision::voctree::getListOfDescriptorFiles(sfmDataB, featuresFolders, descriptorsFilesB); - } - OrderedPairList selectedPairs; + // load SfMData + sfmData::SfMData sfmDataA, sfmDataB; - switch(method) - { - case EImageMatchingMethod::EXHAUSTIVE: + using namespace sfmDataIO; + if (!sfmDataIO::load(sfmDataA, sfmDataFilenameA, ESfMData(ESfMData::VIEWS | ESfMData::EXTRINSICS | ESfMData::INTRINSICS))) { - ALICEVISION_LOG_INFO("Use EXHAUSTIVE method."); - if((matchingMode == EImageMatchingMode::A_A_AND_A_B) || - (matchingMode == EImageMatchingMode::A_AB) || - (matchingMode == EImageMatchingMode::A_A)) - generateAllMatchesInOneMap(sfmDataA.getViewsKeys(), selectedPairs); - - if((matchingMode == EImageMatchingMode::A_A_AND_A_B) || - (matchingMode == EImageMatchingMode::A_AB) || - (matchingMode == EImageMatchingMode::A_B)) - generateAllMatchesBetweenTwoMap(sfmDataA.getViewsKeys(), sfmDataB.getViewsKeys(), selectedPairs); - break; + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilenameA + "' cannot be read."); + return EXIT_FAILURE; } - case EImageMatchingMethod::VOCABULARYTREE: + + if (useMultiSfM) { - ALICEVISION_LOG_INFO("Use VOCABULARYTREE matching."); - conditionVocTree(treeFilepath, withWeights, weightsFilepath, matchingMode,featuresFolders, sfmDataA, nbMaxDescriptors, sfmDataFilenameA, sfmDataB, - sfmDataFilenameB, useMultiSfM, descriptorsFilesA, numImageQuery, selectedPairs); - break; + if (!sfmDataIO::load(sfmDataB, sfmDataFilenameB, ESfMData(ESfMData::VIEWS | ESfMData::EXTRINSICS | ESfMData::INTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilenameB + "' cannot be read."); + return EXIT_FAILURE; + } + + // remove duplicated view + for (const auto& viewPair : sfmDataB.getViews()) + { + sfmData::Views::iterator it = sfmDataA.getViews().find(viewPair.first); + if (it != sfmDataA.getViews().end()) + sfmDataA.getViews().erase(it); + } } - case EImageMatchingMethod::SEQUENTIAL: + + method = selectImageMatchingMethod(method, sfmDataA, sfmDataB, minNbImages); + + std::map descriptorsFilesA, descriptorsFilesB; + + if (method != EImageMatchingMethod::EXHAUSTIVE && method != EImageMatchingMethod::SEQUENTIAL) { - ALICEVISION_LOG_INFO("Use SEQUENTIAL matching."); - generateSequentialMatches(sfmDataA, numImageQuerySequential, selectedPairs); - break; + // load descriptor filenames + aliceVision::voctree::getListOfDescriptorFiles(sfmDataA, featuresFolders, descriptorsFilesA); + + if (useMultiSfM) + aliceVision::voctree::getListOfDescriptorFiles(sfmDataB, featuresFolders, descriptorsFilesB); } - case EImageMatchingMethod::SEQUENTIAL_AND_VOCABULARYTREE: + + OrderedPairList selectedPairs; + + switch (method) { - ALICEVISION_LOG_INFO("Use SEQUENTIAL and VOCABULARYTREE matching."); - generateSequentialMatches(sfmDataA, numImageQuerySequential, selectedPairs); - conditionVocTree(treeFilepath, withWeights, weightsFilepath, matchingMode,featuresFolders, sfmDataA, nbMaxDescriptors, sfmDataFilenameA, sfmDataB, - sfmDataFilenameB, useMultiSfM, descriptorsFilesA, numImageQuery, selectedPairs); - break; + case EImageMatchingMethod::EXHAUSTIVE: + { + ALICEVISION_LOG_INFO("Use EXHAUSTIVE method."); + if ((matchingMode == EImageMatchingMode::A_A_AND_A_B) || (matchingMode == EImageMatchingMode::A_AB) || + (matchingMode == EImageMatchingMode::A_A)) + generateAllMatchesInOneMap(sfmDataA.getViewsKeys(), selectedPairs); + + if ((matchingMode == EImageMatchingMode::A_A_AND_A_B) || (matchingMode == EImageMatchingMode::A_AB) || + (matchingMode == EImageMatchingMode::A_B)) + generateAllMatchesBetweenTwoMap(sfmDataA.getViewsKeys(), sfmDataB.getViewsKeys(), selectedPairs); + break; + } + case EImageMatchingMethod::VOCABULARYTREE: + { + ALICEVISION_LOG_INFO("Use VOCABULARYTREE matching."); + conditionVocTree(treeFilepath, + withWeights, + weightsFilepath, + matchingMode, + featuresFolders, + sfmDataA, + nbMaxDescriptors, + sfmDataFilenameA, + sfmDataB, + sfmDataFilenameB, + useMultiSfM, + descriptorsFilesA, + numImageQuery, + selectedPairs); + break; + } + case EImageMatchingMethod::SEQUENTIAL: + { + ALICEVISION_LOG_INFO("Use SEQUENTIAL matching."); + generateSequentialMatches(sfmDataA, numImageQuerySequential, selectedPairs); + break; + } + case EImageMatchingMethod::SEQUENTIAL_AND_VOCABULARYTREE: + { + ALICEVISION_LOG_INFO("Use SEQUENTIAL and VOCABULARYTREE matching."); + generateSequentialMatches(sfmDataA, numImageQuerySequential, selectedPairs); + conditionVocTree(treeFilepath, + withWeights, + weightsFilepath, + matchingMode, + featuresFolders, + sfmDataA, + nbMaxDescriptors, + sfmDataFilenameA, + sfmDataB, + sfmDataFilenameB, + useMultiSfM, + descriptorsFilesA, + numImageQuery, + selectedPairs); + break; + } + case EImageMatchingMethod::FRUSTUM: + { + ALICEVISION_LOG_INFO("Use FRUSTUM intersection from known poses."); + if (sfmDataA.getValidViews().empty()) + { + throw std::runtime_error("No camera with valid pose and intrinsic."); + } + // For all cameras with valid extrinsic/intrinsic, we select the camera with common visibilities based on cameras' frustum. + // We use an epsilon near value for the frustum, to ensure that mulitple images with a pure rotation will not intersect at the nodal + // point. + PairSet pairs = sfm::FrustumFilter(sfmDataA, 0.01).getFrustumIntersectionPairs(); + for (const auto& p : pairs) + { + selectedPairs[p.first].insert(p.second); + } + break; + } + case EImageMatchingMethod::FRUSTUM_OR_VOCABULARYTREE: + { + throw std::runtime_error("FRUSTUM_OR_VOCABULARYTREE should have been decided before."); + } } - case EImageMatchingMethod::FRUSTUM: + + // check if the output folder exists + const auto basePath = fs::path(outputFile).parent_path(); + if (!basePath.empty() && !fs::exists(basePath)) { - ALICEVISION_LOG_INFO("Use FRUSTUM intersection from known poses."); - if(sfmDataA.getValidViews().empty()) - { - throw std::runtime_error("No camera with valid pose and intrinsic."); - } - // For all cameras with valid extrinsic/intrinsic, we select the camera with common visibilities based on cameras' frustum. - // We use an epsilon near value for the frustum, to ensure that mulitple images with a pure rotation will not intersect at the nodal point. - PairSet pairs = sfm::FrustumFilter(sfmDataA, 0.01).getFrustumIntersectionPairs(); - for(const auto& p: pairs) - { - selectedPairs[p.first].insert(p.second); - } - break; + // then create the missing folder + if (!fs::create_directories(basePath)) + { + ALICEVISION_LOG_ERROR("Unable to create folders: " << basePath); + return EXIT_FAILURE; + } } - case EImageMatchingMethod::FRUSTUM_OR_VOCABULARYTREE: + { - throw std::runtime_error("FRUSTUM_OR_VOCABULARYTREE should have been decided before."); + std::size_t nbImagePairs = 0; + for (auto& it : selectedPairs) + nbImagePairs += it.second.size(); + ALICEVISION_LOG_INFO("Number of selected image pairs: " << nbImagePairs); } - } - - // check if the output folder exists - const auto basePath = fs::path(outputFile).parent_path(); - if(!basePath.empty() && !fs::exists(basePath)) - { - // then create the missing folder - if(!fs::create_directories(basePath)) + + // write it to file + PairSet selectedPairsSet; + for (const auto& imagePairs : selectedPairs) { - ALICEVISION_LOG_ERROR("Unable to create folders: " << basePath); - return EXIT_FAILURE; + for (const auto& index : imagePairs.second) + { + selectedPairsSet.emplace(imagePairs.first, index); + } } - } - - { - std::size_t nbImagePairs = 0; - for(auto& it : selectedPairs) - nbImagePairs += it.second.size(); - ALICEVISION_LOG_INFO("Number of selected image pairs: " << nbImagePairs); - } - - // write it to file - PairSet selectedPairsSet; - for (const auto& imagePairs : selectedPairs) { - for (const auto& index : imagePairs.second) { - selectedPairsSet.emplace(imagePairs.first, index); - } - } - matchingImageCollection::savePairsToFile(outputFile, selectedPairsSet); - - ALICEVISION_LOG_INFO("pairList exported in: " << outputFile); - - if(useMultiSfM && !outputCombinedSfM.empty()) - { - // combine A to B - // should not loose B data - sfmDataB.combine(sfmDataA); - - if(!sfmDataIO::save(sfmDataB, outputCombinedSfM, sfmDataIO::ESfMData::ALL)) + matchingImageCollection::savePairsToFile(outputFile, selectedPairsSet); + + ALICEVISION_LOG_INFO("pairList exported in: " << outputFile); + + if (useMultiSfM && !outputCombinedSfM.empty()) { - ALICEVISION_LOG_ERROR("Unable to save combined SfM: " << outputCombinedSfM); - return EXIT_FAILURE; + // combine A to B + // should not loose B data + sfmDataB.combine(sfmDataA); + + if (!sfmDataIO::save(sfmDataB, outputCombinedSfM, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("Unable to save combined SfM: " << outputCombinedSfM); + return EXIT_FAILURE; + } } - } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_imageSegmentation.cpp b/src/software/pipeline/main_imageSegmentation.cpp index 86f1224dbd..08594fdf2c 100644 --- a/src/software/pipeline/main_imageSegmentation.cpp +++ b/src/software/pipeline/main_imageSegmentation.cpp @@ -29,7 +29,6 @@ #include #include - #include // These constants define the current software version. @@ -42,22 +41,22 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -void imageToPlanes(std::vector & output, const image::Image::Base & source) +void imageToPlanes(std::vector& output, const image::Image::Base& source) { size_t planeSize = source.rows() * source.cols(); - + output.resize(planeSize * 3); - float * planeR = output.data(); - float * planeG = planeR + planeSize; - float * planeB = planeG + planeSize; + float* planeR = output.data(); + float* planeG = planeR + planeSize; + float* planeB = planeG + planeSize; size_t pos = 0; for (int i = 0; i < source.rows(); i++) { for (int j = 0; j < source.cols(); j++) { - const image::RGBfColor & rgb = source(i, j); + const image::RGBfColor& rgb = source(i, j); planeR[pos] = rgb.r(); planeG[pos] = rgb.g(); planeB[pos] = rgb.b(); @@ -67,8 +66,7 @@ void imageToPlanes(std::vector & output, const image::Image & mask, const image::Image & labels, const std::set & validClasses, - const bool & maskInvert) +void labelsToMask(image::Image& mask, const image::Image& labels, const std::set& validClasses, const bool& maskInvert) { for (int i = 0; i < mask.height(); i++) { @@ -94,7 +92,7 @@ int aliceVision_main(int argc, char** argv) int rangeSize = 1; bool useGpu = true; bool keepFilename = false; - + // Description of mandatory parameters // clang-format off po::options_description requiredParams("Required parameters"); @@ -132,7 +130,7 @@ int aliceVision_main(int argc, char** argv) // load input scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS))) + if (!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS))) { ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilepath + "' cannot be read"); return EXIT_FAILURE; @@ -140,28 +138,28 @@ int aliceVision_main(int argc, char** argv) // Order views by their image names std::vector> viewsOrderedByName; - for(auto& viewIt : sfmData.getViews()) + for (auto& viewIt : sfmData.getViews()) { viewsOrderedByName.push_back(viewIt.second); } - std::sort(viewsOrderedByName.begin(), viewsOrderedByName.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if(a == nullptr || b == nullptr) + std::sort(viewsOrderedByName.begin(), + viewsOrderedByName.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool { + if (a == nullptr || b == nullptr) return true; return (a->getImage().getImagePath() < b->getImage().getImagePath()); }); // Define range to compute - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0 || static_cast(rangeStart) > viewsOrderedByName.size()) + if (rangeStart < 0 || rangeSize < 0 || static_cast(rangeStart) > viewsOrderedByName.size()) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; } - if(static_cast(rangeStart + rangeSize) > viewsOrderedByName.size()) + if (static_cast(rangeStart + rangeSize) > viewsOrderedByName.size()) { rangeSize = static_cast(viewsOrderedByName.size()) - rangeStart; } @@ -175,19 +173,10 @@ int aliceVision_main(int argc, char** argv) aliceVision::segmentation::Segmentation::Parameters parameters; parameters.modelWeights = modelWeightsPath; - parameters.classes = { - "__background__", - "aeroplane", - "bicycle", "bird", "boat", "bottle", "bus", - "car", "cat", "chair", "cow", - "diningtable", "dog", - "horse", - "motorbike", - "person", "pottedplant", - "sheep", "sofa", - "train", "tvmonitor"}; - parameters.center= {0.485, 0.456, 0.406}; - parameters.scale= {1.0 / 0.229, 1.0 / 0.224, 1.0 / 0.225}; + parameters.classes = {"__background__", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", + "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"}; + parameters.center = {0.485, 0.456, 0.406}; + parameters.scale = {1.0 / 0.229, 1.0 / 0.224, 1.0 / 0.225}; parameters.modelWidth = 1280; parameters.modelHeight = 720; parameters.overlapRatio = 0.3; @@ -195,12 +184,11 @@ int aliceVision_main(int argc, char** argv) aliceVision::segmentation::Segmentation seg(parameters); - const auto & classes = seg.getClasses(); + const auto& classes = seg.getClasses(); - - //Compute the set of valid classes given parameters + // Compute the set of valid classes given parameters std::set validClassesIndices; - for (const auto & s : validClasses) + for (const auto& s : validClasses) { std::string classInput = boost::to_lower_copy(s); boost::trim(classInput); @@ -253,7 +241,6 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Failed to segment image " << path); } - image::Image mask(labels.width(), labels.height()); labelsToMask(mask, labels, validClassesIndices, maskInvert); @@ -285,6 +272,5 @@ int aliceVision_main(int argc, char** argv) image::writeImage(ss.str(), mask, image::ImageWriteOptions()); } - return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_incrementalSfM.cpp b/src/software/pipeline/main_incrementalSfM.cpp index 0c90e5f8e8..50daaf590c 100644 --- a/src/software/pipeline/main_incrementalSfM.cpp +++ b/src/software/pipeline/main_incrementalSfM.cpp @@ -37,7 +37,6 @@ namespace fs = std::filesystem; using namespace aliceVision::track; using namespace aliceVision::sfm; - /** * @brief Retrieve the view id in the sfmData from the image filename. * @param[in] sfmData the SfM scene @@ -45,57 +44,53 @@ using namespace aliceVision::sfm; * @param[out] out_viewId the id found * @return if a view is found */ -bool retrieveViewIdFromImageName(const sfmData::SfMData& sfmData, - const std::string& name, - IndexT& out_viewId) +bool retrieveViewIdFromImageName(const sfmData::SfMData& sfmData, const std::string& name, IndexT& out_viewId) { - out_viewId = UndefinedIndexT; - - // list views uid / filenames and find the one that correspond to the user ones - for(const auto& viewPair : sfmData.getViews()) - { - const sfmData::View& v = *(viewPair.second.get()); - - if(name == std::to_string(v.getViewId()) || - name == fs::path(v.getImage().getImagePath()).filename().string() || - name == v.getImage().getImagePath()) + out_viewId = UndefinedIndexT; + + // list views uid / filenames and find the one that correspond to the user ones + for (const auto& viewPair : sfmData.getViews()) { - out_viewId = v.getViewId(); - break; + const sfmData::View& v = *(viewPair.second.get()); + + if (name == std::to_string(v.getViewId()) || name == fs::path(v.getImage().getImagePath()).filename().string() || + name == v.getImage().getImagePath()) + { + out_viewId = v.getViewId(); + break; + } } - } - if(out_viewId == UndefinedIndexT) - ALICEVISION_LOG_ERROR("Can't find the given initial pair view: " << name); + if (out_viewId == UndefinedIndexT) + ALICEVISION_LOG_ERROR("Can't find the given initial pair view: " << name); - return out_viewId != UndefinedIndexT; + return out_viewId != UndefinedIndexT; } - -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::vector featuresFolders; - std::vector matchesFolders; - std::string outputSfM; - - // user optional parameters - std::string outputSfMViewsAndPoses; - std::string extraInfoFolder; - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - std::pair initialPairString("",""); - bool useAutoTransform = true; - - sfm::ReconstructionEngine_sequentialSfM::Params sfmParams; - bool lockScenePreviouslyReconstructed = true; - int maxNbMatches = 0; - int minNbMatches = 0; - bool useOnlyMatchesFromInputFolder = false; - bool computeStructureColor = true; - - int randomSeed = std::mt19937::default_seed; - bool logIntermediateSteps = false; + // command-line parameters + std::string sfmDataFilename; + std::vector featuresFolders; + std::vector matchesFolders; + std::string outputSfM; + + // user optional parameters + std::string outputSfMViewsAndPoses; + std::string extraInfoFolder; + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + std::pair initialPairString("", ""); + bool useAutoTransform = true; + + sfm::ReconstructionEngine_sequentialSfM::Params sfmParams; + bool lockScenePreviouslyReconstructed = true; + int maxNbMatches = 0; + int minNbMatches = 0; + bool useOnlyMatchesFromInputFolder = false; + bool computeStructureColor = true; + + int randomSeed = std::mt19937::default_seed; + bool logIntermediateSteps = false; // clang-format off po::options_description requiredParams("Required parameters"); @@ -198,172 +193,175 @@ int aliceVision_main(int argc, char **argv) "If set to true, the current state of the scene will be dumped as an SfMData file every 3 resections."); // clang-format on - CmdLine cmdline("Sequential/Incremental reconstruction.\n" - "This program performs incremental SfM (Initial Pair Essential + Resection).\n" - "AliceVision incrementalSfM"); - - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // set maxThreads - HardwareContext hwc = cmdline.getHardwareContext(); - omp_set_num_threads(hwc.getMaxThreads()); - - const double defaultLoRansacLocalizationError = 4.0; - if(!robustEstimation::adjustRobustEstimatorThreshold(sfmParams.localizerEstimator, sfmParams.localizerEstimatorError, defaultLoRansacLocalizationError)) - { - return EXIT_FAILURE; - } - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - // lock scene previously reconstructed - if(lockScenePreviouslyReconstructed) - { - // lock all reconstructed camera poses - for(auto& cameraPosePair : sfmData.getPoses()) - cameraPosePair.second.lock(); - - for(const auto& viewPair : sfmData.getViews()) + CmdLine cmdline("Sequential/Incremental reconstruction.\n" + "This program performs incremental SfM (Initial Pair Essential + Resection).\n" + "AliceVision incrementalSfM"); + + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - // lock all reconstructed views intrinsics - const sfmData::View& view = *(viewPair.second); - if(sfmData.isPoseAndIntrinsicDefined(&view)) - sfmData.getIntrinsics().at(view.getIntrinsicId())->lock(); + return EXIT_FAILURE; } - } - - // get imageDescriber type - const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - - // features reading - feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) - { - ALICEVISION_LOG_ERROR("Invalid features."); - return EXIT_FAILURE; - } - - // matches reading - matching::PairwiseMatches pairwiseMatches; - if(!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) - { - ALICEVISION_LOG_ERROR("Unable to load matches."); - return EXIT_FAILURE; - } - - if(extraInfoFolder.empty()) - extraInfoFolder = fs::path(outputSfM).parent_path().string(); - - if (!fs::exists(extraInfoFolder)) - fs::create_directory(extraInfoFolder); - - // sequential reconstruction process - aliceVision::system::Timer timer; - - if(sfmParams.minNbObservationsForTriangulation < 2) - { - // allows to use to the old triangulatation algorithm (using 2 views only) during resection. - sfmParams.minNbObservationsForTriangulation = 0; - // ALICEVISION_LOG_ERROR("The value associated to the argument '--minNbObservationsForTriangulation' must be >= 2 "); - // return EXIT_FAILURE; - } - - // handle initial pair parameter - if(!initialPairString.first.empty() || !initialPairString.second.empty()) - { - if(initialPairString.first == initialPairString.second) + + // set maxThreads + HardwareContext hwc = cmdline.getHardwareContext(); + omp_set_num_threads(hwc.getMaxThreads()); + + const double defaultLoRansacLocalizationError = 4.0; + if (!robustEstimation::adjustRobustEstimatorThreshold( + sfmParams.localizerEstimator, sfmParams.localizerEstimatorError, defaultLoRansacLocalizationError)) { - ALICEVISION_LOG_ERROR("Invalid image names. You cannot use the same image to initialize a pair."); - return EXIT_FAILURE; + return EXIT_FAILURE; } - if(!initialPairString.first.empty() && !retrieveViewIdFromImageName(sfmData, initialPairString.first, sfmParams.userInitialImagePair.first)) + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - ALICEVISION_LOG_ERROR("Could not find corresponding view in the initial pair: " + initialPairString.first); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; } - if(!initialPairString.second.empty() && !retrieveViewIdFromImageName(sfmData, initialPairString.second, sfmParams.userInitialImagePair.second)) + // lock scene previously reconstructed + if (lockScenePreviouslyReconstructed) { - ALICEVISION_LOG_ERROR("Could not find corresponding view in the initial pair: " + initialPairString.second); - return EXIT_FAILURE; + // lock all reconstructed camera poses + for (auto& cameraPosePair : sfmData.getPoses()) + cameraPosePair.second.lock(); + + for (const auto& viewPair : sfmData.getViews()) + { + // lock all reconstructed views intrinsics + const sfmData::View& view = *(viewPair.second); + if (sfmData.isPoseAndIntrinsicDefined(&view)) + sfmData.getIntrinsics().at(view.getIntrinsicId())->lock(); + } } - } - sfm::ReconstructionEngine_sequentialSfM sfmEngine( - sfmData, - sfmParams, - extraInfoFolder, - (fs::path(extraInfoFolder) / "sfm_log.html").string()); + // get imageDescriber type + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + + // features reading + feature::FeaturesPerView featuresPerView; + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + { + ALICEVISION_LOG_ERROR("Invalid features."); + return EXIT_FAILURE; + } - sfmEngine.initRandomSeed(randomSeed); + // matches reading + matching::PairwiseMatches pairwiseMatches; + if (!sfm::loadPairwiseMatches( + pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) + { + ALICEVISION_LOG_ERROR("Unable to load matches."); + return EXIT_FAILURE; + } - // configure the featuresPerView & the matches_provider - sfmEngine.setFeatures(&featuresPerView); - sfmEngine.setMatches(&pairwiseMatches); + if (extraInfoFolder.empty()) + extraInfoFolder = fs::path(outputSfM).parent_path().string(); - if(!sfmEngine.process()) - return EXIT_FAILURE; + if (!fs::exists(extraInfoFolder)) + fs::create_directory(extraInfoFolder); - //Mimic sfmTransform "EAlignmentMethod::AUTO" - if (useAutoTransform) - { - double S = 1.0; - Mat3 R = Mat3::Identity(); - Vec3 t = Vec3::Zero(); + // sequential reconstruction process + aliceVision::system::Timer timer; - ALICEVISION_LOG_DEBUG("Align automatically"); - sfm::computeNewCoordinateSystemAuto(sfmEngine.getSfMData(), S, R, t); - sfm::applyTransform(sfmEngine.getSfMData(), S, R, t); - - ALICEVISION_LOG_DEBUG("Align with ground"); - sfm::computeNewCoordinateSystemGroundAuto(sfmEngine.getSfMData(), t); - sfm::applyTransform(sfmEngine.getSfMData(), 1.0, Eigen::Matrix3d::Identity(), t); - } + if (sfmParams.minNbObservationsForTriangulation < 2) + { + // allows to use to the old triangulatation algorithm (using 2 views only) during resection. + sfmParams.minNbObservationsForTriangulation = 0; + // ALICEVISION_LOG_ERROR("The value associated to the argument '--minNbObservationsForTriangulation' must be >= 2 "); + // return EXIT_FAILURE; + } - // set featuresFolders and matchesFolders relative paths - { - sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); - sfmEngine.getSfMData().addMatchesFolders(matchesFolders); - sfmEngine.getSfMData().setAbsolutePath(outputSfM); - } + // handle initial pair parameter + if (!initialPairString.first.empty() || !initialPairString.second.empty()) + { + if (initialPairString.first == initialPairString.second) + { + ALICEVISION_LOG_ERROR("Invalid image names. You cannot use the same image to initialize a pair."); + return EXIT_FAILURE; + } + + if (!initialPairString.first.empty() && !retrieveViewIdFromImageName(sfmData, initialPairString.first, sfmParams.userInitialImagePair.first)) + { + ALICEVISION_LOG_ERROR("Could not find corresponding view in the initial pair: " + initialPairString.first); + return EXIT_FAILURE; + } + + if (!initialPairString.second.empty() && + !retrieveViewIdFromImageName(sfmData, initialPairString.second, sfmParams.userInitialImagePair.second)) + { + ALICEVISION_LOG_ERROR("Could not find corresponding view in the initial pair: " + initialPairString.second); + return EXIT_FAILURE; + } + } + + sfm::ReconstructionEngine_sequentialSfM sfmEngine(sfmData, sfmParams, extraInfoFolder, (fs::path(extraInfoFolder) / "sfm_log.html").string()); + + sfmEngine.initRandomSeed(randomSeed); + + // configure the featuresPerView & the matches_provider + sfmEngine.setFeatures(&featuresPerView); + sfmEngine.setMatches(&pairwiseMatches); + + if (!sfmEngine.process()) + return EXIT_FAILURE; + + // Mimic sfmTransform "EAlignmentMethod::AUTO" + if (useAutoTransform) + { + double S = 1.0; + Mat3 R = Mat3::Identity(); + Vec3 t = Vec3::Zero(); + + ALICEVISION_LOG_DEBUG("Align automatically"); + sfm::computeNewCoordinateSystemAuto(sfmEngine.getSfMData(), S, R, t); + sfm::applyTransform(sfmEngine.getSfMData(), S, R, t); + + ALICEVISION_LOG_DEBUG("Align with ground"); + sfm::computeNewCoordinateSystemGroundAuto(sfmEngine.getSfMData(), t); + sfm::applyTransform(sfmEngine.getSfMData(), 1.0, Eigen::Matrix3d::Identity(), t); + } + + // set featuresFolders and matchesFolders relative paths + { + sfmEngine.getSfMData().addFeaturesFolders(featuresFolders); + sfmEngine.getSfMData().addMatchesFolders(matchesFolders); + sfmEngine.getSfMData().setAbsolutePath(outputSfM); + } - // get the color for the 3D points - if(computeStructureColor) - sfmEngine.colorize(); + // get the color for the 3D points + if (computeStructureColor) + sfmEngine.colorize(); - sfmEngine.retrieveMarkersId(); + sfmEngine.retrieveMarkersId(); - ALICEVISION_LOG_INFO("Structure from motion took (s): " + std::to_string(timer.elapsed())); - ALICEVISION_LOG_INFO("Generating HTML report..."); + ALICEVISION_LOG_INFO("Structure from motion took (s): " + std::to_string(timer.elapsed())); + ALICEVISION_LOG_INFO("Generating HTML report..."); - sfm::generateSfMReport(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "sfm_report.html").string()); + sfm::generateSfMReport(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / "sfm_report.html").string()); - // export to disk computed scene (data & visualizable results) - ALICEVISION_LOG_INFO("Export SfMData to disk: " + outputSfM); + // export to disk computed scene (data & visualizable results) + ALICEVISION_LOG_INFO("Export SfMData to disk: " + outputSfM); - sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / ("cloud_and_poses" + sfmParams.sfmStepFileExtension)).string(), sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::EXTRINSICS|sfmDataIO::INTRINSICS|sfmDataIO::STRUCTURE)); - sfmDataIO::save(sfmEngine.getSfMData(), outputSfM, sfmDataIO::ESfMData::ALL); + sfmDataIO::save(sfmEngine.getSfMData(), + (fs::path(extraInfoFolder) / ("cloud_and_poses" + sfmParams.sfmStepFileExtension)).string(), + sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS | sfmDataIO::STRUCTURE)); + sfmDataIO::save(sfmEngine.getSfMData(), outputSfM, sfmDataIO::ESfMData::ALL); - if(!outputSfMViewsAndPoses.empty()) - sfmDataIO::save(sfmEngine.getSfMData(), outputSfMViewsAndPoses, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::EXTRINSICS|sfmDataIO::INTRINSICS)); + if (!outputSfMViewsAndPoses.empty()) + sfmDataIO::save( + sfmEngine.getSfMData(), outputSfMViewsAndPoses, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); - ALICEVISION_LOG_INFO("Structure from Motion results:" << std::endl - << "\t- # input images: " << sfmEngine.getSfMData().getViews().size() << std::endl - << "\t- # cameras calibrated: " << sfmEngine.getSfMData().getValidViews().size() << std::endl - << "\t- # poses: " << sfmEngine.getSfMData().getPoses().size() << std::endl - << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); + ALICEVISION_LOG_INFO("Structure from Motion results:" << std::endl + << "\t- # input images: " << sfmEngine.getSfMData().getViews().size() << std::endl + << "\t- # cameras calibrated: " << sfmEngine.getSfMData().getValidViews().size() + << std::endl + << "\t- # poses: " << sfmEngine.getSfMData().getPoses().size() << std::endl + << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_lightingCalibration.cpp b/src/software/pipeline/main_lightingCalibration.cpp index 6ff3766e07..3c4a5c1a76 100644 --- a/src/software/pipeline/main_lightingCalibration.cpp +++ b/src/software/pipeline/main_lightingCalibration.cpp @@ -42,7 +42,7 @@ namespace fs = std::filesystem; using namespace aliceVision; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { system::Timer timer; diff --git a/src/software/pipeline/main_meshDecimate.cpp b/src/software/pipeline/main_meshDecimate.cpp index b7491c43c3..4de5148a3f 100644 --- a/src/software/pipeline/main_meshDecimate.cpp +++ b/src/software/pipeline/main_meshDecimate.cpp @@ -68,7 +68,7 @@ int aliceVision_main(int argc, char* argv[]) // clang-format on CmdLine cmdline("AliceVision meshDecimate"); - + cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -76,20 +76,19 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - fs::path outDirectory = fs::path(outputMeshPath).parent_path(); - if(!fs::is_directory(outDirectory)) + if (!fs::is_directory(outDirectory)) fs::create_directory(outDirectory); // Mesh type - typedef OpenMesh::TriMesh_ArrayKernelT<> Mesh; + typedef OpenMesh::TriMesh_ArrayKernelT<> Mesh; // Decimater type - typedef OpenMesh::Decimater::DecimaterT< Mesh > Decimater; + typedef OpenMesh::Decimater::DecimaterT Decimater; // Decimation Module Handle type - typedef OpenMesh::Decimater::ModQuadricT< Mesh >::Handle HModQuadric; + typedef OpenMesh::Decimater::ModQuadricT::Handle HModQuadric; Mesh mesh; - if(!OpenMesh::IO::read_mesh(mesh, inputMeshPath)) + if (!OpenMesh::IO::read_mesh(mesh, inputMeshPath)) { ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath); return EXIT_FAILURE; @@ -99,25 +98,25 @@ int aliceVision_main(int argc, char* argv[]) int nbInputPoints = mesh.n_vertices(); int nbOutputPoints = 0; - if(fixedNbVertices != 0) + if (fixedNbVertices != 0) { nbOutputPoints = fixedNbVertices; } else { - if(simplificationFactor != 0.0) + if (simplificationFactor != 0.0) { nbOutputPoints = simplificationFactor * nbInputPoints; } - if(minVertices != 0) + if (minVertices != 0) { - if(nbInputPoints > minVertices && nbOutputPoints < minVertices) - nbOutputPoints = minVertices; + if (nbInputPoints > minVertices && nbOutputPoints < minVertices) + nbOutputPoints = minVertices; } - if(maxVertices != 0) + if (maxVertices != 0) { - if(nbInputPoints > maxVertices && nbOutputPoints > maxVertices) - nbOutputPoints = maxVertices; + if (nbInputPoints > maxVertices && nbOutputPoints > maxVertices) + nbOutputPoints = maxVertices; } } @@ -126,7 +125,7 @@ int aliceVision_main(int argc, char* argv[]) { // a decimater object, connected to a mesh - Decimater decimater(mesh); + Decimater decimater(mesh); // use a quadric module HModQuadric hModQuadric; // register module at the decimater @@ -149,7 +148,7 @@ int aliceVision_main(int argc, char* argv[]) } ALICEVISION_LOG_INFO("Output mesh: " << mesh.n_vertices() << " vertices and " << mesh.n_faces() << " facets."); - if(mesh.n_faces() == 0) + if (mesh.n_faces() == 0) { ALICEVISION_LOG_ERROR("Failed: the output mesh is empty."); return EXIT_FAILURE; @@ -157,7 +156,7 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Save mesh."); // Save output mesh - if(!OpenMesh::IO::write_mesh(mesh, outputMeshPath)) + if (!OpenMesh::IO::write_mesh(mesh, outputMeshPath)) { ALICEVISION_LOG_ERROR("Failed to save mesh \"" << outputMeshPath << "\"."); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_meshDenoising.cpp b/src/software/pipeline/main_meshDenoising.cpp index 11c8d45cb4..946bded52e 100644 --- a/src/software/pipeline/main_meshDenoising.cpp +++ b/src/software/pipeline/main_meshDenoising.cpp @@ -40,7 +40,7 @@ int aliceVision_main(int argc, char* argv[]) std::string inputMeshPath; std::string outputMeshPath; - int denoisingIterations = 5; // OuterIterations + int denoisingIterations = 5; // OuterIterations float meshUpdateClosenessWeight = 0.001; int meshUpdateIterations = 20; int meshUpdateMethod = SDFilter::MeshFilterParameters::ITERATIVE_UPDATE; @@ -85,28 +85,26 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - fs::path outDirectory = fs::path(outputMeshPath).parent_path(); - if(!fs::is_directory(outDirectory)) + if (!fs::is_directory(outDirectory)) fs::create_directory(outDirectory); - TriMesh inMesh; - if(!OpenMesh::IO::read_mesh(inMesh, inputMeshPath)) + if (!OpenMesh::IO::read_mesh(inMesh, inputMeshPath)) { ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath); return EXIT_FAILURE; } - if(inMesh.n_vertices() == 0 || inMesh.n_faces() == 0) + if (inMesh.n_vertices() == 0 || inMesh.n_faces() == 0) { ALICEVISION_LOG_ERROR("Empty mesh from the file: " << inputMeshPath); ALICEVISION_LOG_ERROR("Input mesh: " << inMesh.n_vertices() << " vertices and " << inMesh.n_faces() << " facets."); return EXIT_FAILURE; } -// #ifdef USE_OPENMP + // #ifdef USE_OPENMP Eigen::initParallel(); -// #endif + // #endif // Load option file SDFilter::MeshDenoisingParameters param; @@ -119,17 +117,17 @@ int aliceVision_main(int argc, char* argv[]) param.mu = mu; param.nu = nu; -// enum LinearSolverType -// { -// CG, -// LDLT -// }; -// // Parameters related to termination criteria -// int max_iter; // Max number of iterations -// double avg_disp_eps; // Max average per-signal displacement threshold between two iterations for determining convergence -// bool normalize_iterates; // Normalization of the filtered normals in each iteration - - if(!param.valid_parameters()) + // enum LinearSolverType + // { + // CG, + // LDLT + // }; + // // Parameters related to termination criteria + // int max_iter; // Max number of iterations + // double avg_disp_eps; // Max average per-signal displacement threshold between two iterations for determining convergence + // bool normalize_iterates; // Normalization of the filtered normals in each iteration + + if (!param.valid_parameters()) { ALICEVISION_LOG_ERROR("Invalid filter options. Aborting..."); return EXIT_FAILURE; @@ -145,7 +143,7 @@ int aliceVision_main(int argc, char* argv[]) Eigen::Vector3d original_center; double original_scale; SDFilter::normalize_mesh(inMesh, original_center, original_scale); - if(true) + if (true) { ALICEVISION_LOG_INFO("Start mesh denoising."); // Filter the normals and construct the output mesh @@ -165,7 +163,7 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Output mesh: " << outMesh.n_vertices() << " vertices and " << outMesh.n_faces() << " facets."); - if(outMesh.n_faces() == 0) + if (outMesh.n_faces() == 0) { ALICEVISION_LOG_ERROR("Failed: the output mesh is empty."); return EXIT_FAILURE; @@ -173,7 +171,7 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Save mesh."); // Save output mesh - if(!OpenMesh::IO::write_mesh(outMesh, outputMeshPath)) + if (!OpenMesh::IO::write_mesh(outMesh, outputMeshPath)) { ALICEVISION_LOG_ERROR("Failed to save mesh file: \"" << outputMeshPath << "\"."); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_meshFiltering.cpp b/src/software/pipeline/main_meshFiltering.cpp index 44272eb300..1100b5ca78 100644 --- a/src/software/pipeline/main_meshFiltering.cpp +++ b/src/software/pipeline/main_meshFiltering.cpp @@ -52,13 +52,16 @@ std::string ESubsetType_informations() */ std::string ESubsetType_enumToString(ESubsetType subsetType) { - switch(subsetType) - { - case ESubsetType::ALL: return "all"; - case ESubsetType::SURFACE_BOUNDARIES: return "surface_boundaries"; - case ESubsetType::SURFACE_INNER_PART: return "surface_inner_part"; - } - throw std::out_of_range("Invalid SubsetType enum: " + std::to_string(int(subsetType))); + switch (subsetType) + { + case ESubsetType::ALL: + return "all"; + case ESubsetType::SURFACE_BOUNDARIES: + return "surface_boundaries"; + case ESubsetType::SURFACE_INNER_PART: + return "surface_inner_part"; + } + throw std::out_of_range("Invalid SubsetType enum: " + std::to_string(int(subsetType))); } /** @@ -68,13 +71,16 @@ std::string ESubsetType_enumToString(ESubsetType subsetType) */ ESubsetType ESubsetType_stringToEnum(const std::string& subsetType) { - std::string type = subsetType; - std::transform(type.begin(), type.end(), type.begin(), ::tolower); // tolower - - if(type == "all") return ESubsetType::ALL; - if(type == "surface_boundaries") return ESubsetType::SURFACE_BOUNDARIES; - if(type == "surface_inner_part") return ESubsetType::SURFACE_INNER_PART; - throw std::out_of_range("Invalid filterType: " + subsetType); + std::string type = subsetType; + std::transform(type.begin(), type.end(), type.begin(), ::tolower); // tolower + + if (type == "all") + return ESubsetType::ALL; + if (type == "surface_boundaries") + return ESubsetType::SURFACE_BOUNDARIES; + if (type == "surface_inner_part") + return ESubsetType::SURFACE_INNER_PART; + throw std::out_of_range("Invalid filterType: " + subsetType); } std::ostream& operator<<(std::ostream& os, const ESubsetType subsetType) @@ -163,20 +169,20 @@ int aliceVision_main(int argc, char* argv[]) const ESubsetType filteringSubsetType = ESubsetType_stringToEnum(filteringSubsetTypeName); fs::path outDirectory = fs::path(outputMeshPath).parent_path(); - if(!fs::is_directory(outDirectory)) + if (!fs::is_directory(outDirectory)) fs::create_directory(outDirectory); mesh::Texturing texturing; texturing.loadWithAtlas(inputMeshPath); mesh::Mesh* mesh = texturing.mesh; - if(!mesh) + if (!mesh) { ALICEVISION_LOG_ERROR("Unable to read input mesh from the file: " << inputMeshPath); return EXIT_FAILURE; } - if(mesh->pts.empty() || mesh->tris.empty()) + if (mesh->pts.empty() || mesh->tris.empty()) { ALICEVISION_LOG_ERROR("Error: empty mesh from the file " << inputMeshPath); ALICEVISION_LOG_ERROR("Input mesh: " << mesh->pts.size() << " vertices and " << mesh->tris.size() << " facets."); @@ -186,55 +192,56 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Mesh file: \"" << inputMeshPath << "\" loaded."); ALICEVISION_LOG_INFO("Input mesh: " << mesh->pts.size() << " vertices and " << mesh->tris.size() << " facets."); - StaticVectorBool ptsCanMove; // empty if smoothingSubsetType is ALL + StaticVectorBool ptsCanMove; // empty if smoothingSubsetType is ALL // lock filter subset vertices - switch(smoothingSubsetType) + switch (smoothingSubsetType) { - case ESubsetType::ALL: break; // nothing to lock + case ESubsetType::ALL: + break; // nothing to lock case ESubsetType::SURFACE_BOUNDARIES: - mesh->lockSurfaceBoundaries(smoothingBoundariesNeighbours, ptsCanMove, true); // invert = true (lock surface inner part) - break; + mesh->lockSurfaceBoundaries(smoothingBoundariesNeighbours, ptsCanMove, true); // invert = true (lock surface inner part) + break; case ESubsetType::SURFACE_INNER_PART: - mesh->lockSurfaceBoundaries(smoothingBoundariesNeighbours, ptsCanMove, false); // invert = false (lock surface boundaries) - break; + mesh->lockSurfaceBoundaries(smoothingBoundariesNeighbours, ptsCanMove, false); // invert = false (lock surface boundaries) + break; } - // filtering - if((filterLargeTrianglesFactor != 0.0) || (filterTrianglesRatio != 0.0)) + if ((filterLargeTrianglesFactor != 0.0) || (filterTrianglesRatio != 0.0)) { ALICEVISION_LOG_INFO("Start mesh filtering."); - for(int i = 0; i < filteringIterations; ++i) + for (int i = 0; i < filteringIterations; ++i) { - ALICEVISION_LOG_INFO("Mesh filtering: iteration " << i); + ALICEVISION_LOG_INFO("Mesh filtering: iteration " << i); - StaticVectorBool trisToStay(mesh->tris.size(), true); - StaticVectorBool trisInFilterSubset; // empty if filteringSubsetType is ALL + StaticVectorBool trisToStay(mesh->tris.size(), true); + StaticVectorBool trisInFilterSubset; // empty if filteringSubsetType is ALL - switch(filteringSubsetType) - { - case ESubsetType::ALL: break; // nothing to do + switch (filteringSubsetType) + { + case ESubsetType::ALL: + break; // nothing to do - case ESubsetType::SURFACE_BOUNDARIES: - mesh->getSurfaceBoundaries(trisInFilterSubset); // invert = false (get surface boundaries) - break; + case ESubsetType::SURFACE_BOUNDARIES: + mesh->getSurfaceBoundaries(trisInFilterSubset); // invert = false (get surface boundaries) + break; - case ESubsetType::SURFACE_INNER_PART: - mesh->getSurfaceBoundaries(trisInFilterSubset, true); // invert = true (get surface inner part) - break; - } + case ESubsetType::SURFACE_INNER_PART: + mesh->getSurfaceBoundaries(trisInFilterSubset, true); // invert = true (get surface inner part) + break; + } - if(filterLargeTrianglesFactor != 0.0) - mesh->filterLargeEdgeTriangles(filterLargeTrianglesFactor, trisInFilterSubset, trisToStay); + if (filterLargeTrianglesFactor != 0.0) + mesh->filterLargeEdgeTriangles(filterLargeTrianglesFactor, trisInFilterSubset, trisToStay); - if(filterTrianglesRatio != 0.0) - mesh->filterTrianglesByRatio(filterTrianglesRatio, trisInFilterSubset, trisToStay); + if (filterTrianglesRatio != 0.0) + mesh->filterTrianglesByRatio(filterTrianglesRatio, trisInFilterSubset, trisToStay); - mesh->letJustTringlesIdsInMesh(trisToStay); + mesh->letJustTringlesIdsInMesh(trisToStay); } ALICEVISION_LOG_INFO("Mesh filtering done: " << mesh->pts.size() << " vertices and " << mesh->tris.size() << " facets."); } @@ -250,15 +257,15 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Mesh smoothing done: " << meOpt.pts.size() << " vertices and " << meOpt.tris.size() << " facets."); } - if(keepLargestMeshOnly) + if (keepLargestMeshOnly) { StaticVector trisIdsToStay; meOpt.getLargestConnectedComponentTrisIds(trisIdsToStay); meOpt.letJustTringlesIdsInMesh(trisIdsToStay); ALICEVISION_LOG_INFO("Mesh after keepLargestMeshOnly: " << meOpt.pts.size() << " vertices and " << meOpt.tris.size() << " facets."); } - - // clear potential free points created by triangles removal in previous cleaning operations + + // clear potential free points created by triangles removal in previous cleaning operations StaticVector ptIdToNewPtId; meOpt.removeFreePointsFromMesh(ptIdToNewPtId); ptIdToNewPtId.clear(); @@ -268,7 +275,7 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_COUT("Output mesh: " << mesh->pts.size() << " vertices and " << mesh->tris.size() << " facets."); - if(outMesh.pts.empty() || outMesh.tris.empty()) + if (outMesh.pts.empty() || outMesh.tris.empty()) { ALICEVISION_CERR("Failed: the output mesh is empty."); ALICEVISION_LOG_INFO("Output mesh: " << outMesh.pts.size() << " vertices and " << outMesh.tris.size() << " facets."); diff --git a/src/software/pipeline/main_meshMasking.cpp b/src/software/pipeline/main_meshMasking.cpp index 1031ca3ab1..16ed3445ae 100644 --- a/src/software/pipeline/main_meshMasking.cpp +++ b/src/software/pipeline/main_meshMasking.cpp @@ -20,7 +20,6 @@ #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 @@ -33,24 +32,26 @@ namespace fs = std::filesystem; /** * @brief Basic cache system to manage masks. - * + * * It keeps the latest "maxSize" (defaut = 16) masks in memory. */ struct MaskCache { - MaskCache(const mvsUtils::MultiViewParams& mp, const std::vector& masksFolders, bool undistortMasks, - const std::string maskExtension, int maxSize = 16) - : _mp(mp) - , _masksFolders(masksFolders) - , _undistortMasks(undistortMasks) - , _maskExtension(maskExtension) - , _maxSize(maxSize) - { - } + MaskCache(const mvsUtils::MultiViewParams& mp, + const std::vector& masksFolders, + bool undistortMasks, + const std::string maskExtension, + int maxSize = 16) + : _mp(mp), + _masksFolders(masksFolders), + _undistortMasks(undistortMasks), + _maskExtension(maskExtension), + _maxSize(maxSize) + {} image::Image* lock(int camId) { - Item * item = findItem(camId); + Item* item = findItem(camId); if (item) { assert(item->locks >= 0); @@ -66,10 +67,10 @@ struct MaskCache tryFreeUnlockedItem(); } - _cache.push_back({ camId, std::make_unique>(), 0 }); + _cache.push_back({camId, std::make_unique>(), 0}); item = &_cache.back(); const IndexT viewId = _mp.getViewId(camId); - auto * const mask = item->mask.get(); + auto* const mask = item->mask.get(); const bool loaded = tryLoadMask(mask, _masksFolders, viewId, _mp.getImagePath(camId), _maskExtension); if (loaded) { @@ -109,7 +110,7 @@ struct MaskCache --item->locks; } -private: + private: struct Item { int camId; @@ -141,11 +142,11 @@ struct MaskCache { const auto it = findItemIt(camId); assert(it != _cache.end()); - std::rotate(it, it+1, _cache.end()); + std::rotate(it, it + 1, _cache.end()); return &_cache.back(); } -private: + private: mvsUtils::MultiViewParams _mp; std::vector _masksFolders; bool _undistortMasks; @@ -163,15 +164,13 @@ StaticVector computeDiffVisibilities(const StaticVector& A, const Stat return diff; } -Point3d findBoundaryVertex( - const mesh::Mesh& mesh, - int visibleVertexId, - int hiddenVertexId, - const mvsUtils::MultiViewParams& mp, - MaskCache & maskCache, - int threshold, - bool invert - ) +Point3d findBoundaryVertex(const mesh::Mesh& mesh, + int visibleVertexId, + int hiddenVertexId, + const mvsUtils::MultiViewParams& mp, + MaskCache& maskCache, + int threshold, + bool invert) { // find the cameras that make a difference, we only need to sample those const auto& visibleVertexVisibilities = mesh.pointsVisibilities[visibleVertexId]; @@ -185,8 +184,9 @@ Point3d findBoundaryVertex( // compute the visibility that is already acquired and that does not change along the edge. // <=> is in hiddenVertexVisibilities but not in diffVisibilities - const int baseVisibility = std::count_if(hiddenVertexVisibilities.begin(), hiddenVertexVisibilities.end(), - [&diffVisibilities] (int camId) { return diffVisibilities.indexOfSorted(camId) == -1; }); + const int baseVisibility = std::count_if(hiddenVertexVisibilities.begin(), hiddenVertexVisibilities.end(), [&diffVisibilities](int camId) { + return diffVisibilities.indexOfSorted(camId) == -1; + }); // preload masks std::map*> masks; @@ -199,8 +199,7 @@ Point3d findBoundaryVertex( // compute the minimal distance according to the mask resolution (it is our search stop condition) const Point3d leftPoint = mesh.pts[visibleVertexId]; const Point3d rightPoint = mesh.pts[hiddenVertexId]; - const float minDistance = [&] - { + const float minDistance = [&] { const int camId = diffVisibilities[0]; // use a single mask, supposing they are all equivalent Pixel leftPixel, rightPixel; mp.getPixelFor3DPoint(&leftPixel, leftPoint, camId); @@ -211,7 +210,7 @@ Point3d findBoundaryVertex( // binary search in continuous space along the edge Point3d result; - float left = 0.f; // is the visible area + float left = 0.f; // is the visible area float right = 1.f; // is the hidden area while (true) { @@ -255,17 +254,14 @@ Point3d findBoundaryVertex( return result; } -void smoothenBoundary( - mesh::Mesh & filteredMesh, - const StaticVector & filteredVertexVisibilityCounters, - const mvsUtils::MultiViewParams& mp, - MaskCache& maskCache, - int threshold, - bool invert - ) +void smoothenBoundary(mesh::Mesh& filteredMesh, + const StaticVector& filteredVertexVisibilityCounters, + const mvsUtils::MultiViewParams& mp, + MaskCache& maskCache, + int threshold, + bool invert) { - const auto isVertexVisible = [&filteredVertexVisibilityCounters, threshold](const int vertexId) - { + const auto isVertexVisible = [&filteredVertexVisibilityCounters, threshold](const int vertexId) { return filteredVertexVisibilityCounters[vertexId] >= threshold; }; @@ -291,8 +287,7 @@ void smoothenBoundary( } // move along the longest edge to avoid degenerate triangles - const auto visibleVertexId = [&] - { + const auto visibleVertexId = [&] { const auto visibleVertexId1 = triangle.v[(hiddenIdx + 1) % 3]; const double length1 = (filteredMesh.pts[visibleVertexId1] - filteredMesh.pts[hiddenVertexId]).size(); @@ -334,7 +329,7 @@ void smoothenBoundary( void removeCameraVisibility(mesh::Mesh& inputMesh, int camId) { - #pragma omp parallel for +#pragma omp parallel for for (int vertexId = 0; vertexId < inputMesh.pts.size(); ++vertexId) { auto& pointVisibilities = inputMesh.pointsVisibilities[vertexId]; @@ -346,18 +341,16 @@ void removeCameraVisibility(mesh::Mesh& inputMesh, int camId) } } -void meshMasking( - const mvsUtils::MultiViewParams & mp, - mesh::Mesh & inputMesh, - const std::vector & masksFolders, - const std::string & maskExtension, - const std::string & outputMeshPath, - const int threshold, - const bool invert, - const bool smoothBoundary, - const bool undistortMasks, - const bool usePointsVisibilities - ) +void meshMasking(const mvsUtils::MultiViewParams& mp, + mesh::Mesh& inputMesh, + const std::vector& masksFolders, + const std::string& maskExtension, + const std::string& outputMeshPath, + const int threshold, + const bool invert, + const bool smoothBoundary, + const bool undistortMasks, + const bool usePointsVisibilities) { MaskCache maskCache(mp, masksFolders, undistortMasks, maskExtension); @@ -390,7 +383,7 @@ void meshMasking( continue; } - #pragma omp parallel for +#pragma omp parallel for for (int vertexId = 0; vertexId < inputMesh.pts.size(); ++vertexId) { const auto& vertex = inputMesh.pts[vertexId]; @@ -406,8 +399,7 @@ void meshMasking( // project vertex on mask Pixel projectedPixel; mp.getPixelFor3DPoint(&projectedPixel, vertex, camId); - if (projectedPixel.x < 0 || projectedPixel.x >= mask.width() - || projectedPixel.y < 0 || projectedPixel.y >= mask.height()) + if (projectedPixel.x < 0 || projectedPixel.x >= mask.width() || projectedPixel.y < 0 || projectedPixel.y >= mask.height()) { if (usePointsVisibilities) { @@ -445,8 +437,7 @@ void meshMasking( StaticVector inputPtIdToFilteredPtId; { - const auto isVertexVisible = [&vertexVisibilityCounters, threshold] (const int vertexId) - { + const auto isVertexVisible = [&vertexVisibilityCounters, threshold](const int vertexId) { return vertexVisibilityCounters[vertexId] >= threshold; }; @@ -455,9 +446,8 @@ void meshMasking( for (int triangleId = 0; triangleId < inputMesh.tris.size(); ++triangleId) { const auto& triangle = inputMesh.tris[triangleId]; - const bool visible = smoothBoundary ? - std::any_of(std::begin(triangle.v), std::end(triangle.v), isVertexVisible) : - std::all_of(std::begin(triangle.v), std::end(triangle.v), isVertexVisible); + const bool visible = smoothBoundary ? std::any_of(std::begin(triangle.v), std::end(triangle.v), isVertexVisible) + : std::all_of(std::begin(triangle.v), std::end(triangle.v), isVertexVisible); if (visible) { visibleTriangles.push_back(triangleId); @@ -500,11 +490,10 @@ void meshMasking( ALICEVISION_LOG_INFO("Mesh file: \"" << outputMeshPath << "\" saved."); } - /** * @brief Write mask images from input images based on chosen algorithm. */ -int main(int argc, char **argv) +int main(int argc, char** argv) { // command-line parameters std::string sfmFilePath; @@ -557,7 +546,7 @@ int main(int argc, char **argv) } // check user choose at least one input option - if(sfmFilePath.empty()) + if (sfmFilePath.empty()) { ALICEVISION_LOG_ERROR("Program need -i option" << std::endl << "No input images."); return EXIT_FAILURE; @@ -569,21 +558,21 @@ int main(int argc, char **argv) inputMesh.load(inputMeshPath); // check sfm file - if(!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) + if (!sfmFilePath.empty() && !fs::exists(sfmFilePath) && !fs::is_regular_file(sfmFilePath)) { ALICEVISION_LOG_ERROR("The input sfm file doesn't exist"); return EXIT_FAILURE; } sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL_DENSE)) + if (!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL_DENSE)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); return EXIT_FAILURE; } // check output string - if(outputMeshPath.empty()) + if (outputMeshPath.empty()) { ALICEVISION_LOG_ERROR("Invalid output"); return EXIT_FAILURE; @@ -591,9 +580,9 @@ int main(int argc, char **argv) // ensure output folder exists fs::path outputDirectory = fs::path(outputMeshPath).parent_path(); - if(!outputDirectory.empty() && !fs::exists(outputDirectory)) + if (!outputDirectory.empty() && !fs::exists(outputDirectory)) { - if(!fs::create_directory(outputDirectory)) + if (!fs::create_directory(outputDirectory)) { ALICEVISION_LOG_ERROR("Cannot create output folder"); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_meshResampling.cpp b/src/software/pipeline/main_meshResampling.cpp index ff7e71d0a8..b877c90591 100644 --- a/src/software/pipeline/main_meshResampling.cpp +++ b/src/software/pipeline/main_meshResampling.cpp @@ -81,7 +81,7 @@ int aliceVision_main(int argc, char* argv[]) } fs::path outDirectory = fs::path(outputMeshPath).parent_path(); - if(!fs::is_directory(outDirectory)) + if (!fs::is_directory(outDirectory)) fs::create_directory(outDirectory); GEO::initialize(); @@ -90,7 +90,7 @@ int aliceVision_main(int argc, char* argv[]) GEO::Mesh M_in, M_out; { - if(!GEO::mesh_load(inputMeshPath, M_in)) + if (!GEO::mesh_load(inputMeshPath, M_in)) { ALICEVISION_LOG_ERROR("Failed to load mesh file: \"" << inputMeshPath << "\"."); return 1; @@ -101,25 +101,25 @@ int aliceVision_main(int argc, char* argv[]) int nbInputPoints = M_in.vertices.nb(); int nbOutputPoints = 0; - if(fixedNbVertices != 0) + if (fixedNbVertices != 0) { nbOutputPoints = fixedNbVertices; } else { - if(simplificationFactor != 0.0) + if (simplificationFactor != 0.0) { nbOutputPoints = simplificationFactor * nbInputPoints; } - if(minVertices != 0) + if (minVertices != 0) { - if(nbInputPoints > minVertices && nbOutputPoints < minVertices) - nbOutputPoints = minVertices; + if (nbInputPoints > minVertices && nbOutputPoints < minVertices) + nbOutputPoints = minVertices; } - if(maxVertices != 0) + if (maxVertices != 0) { - if(nbInputPoints > maxVertices && nbOutputPoints > maxVertices) - nbOutputPoints = maxVertices; + if (nbInputPoints > maxVertices && nbOutputPoints > maxVertices) + nbOutputPoints = maxVertices; } } @@ -128,7 +128,7 @@ int aliceVision_main(int argc, char* argv[]) { GEO::CmdLine::import_arg_group("standard"); - GEO::CmdLine::import_arg_group("remesh"); // needed for remesh_smooth + GEO::CmdLine::import_arg_group("remesh"); // needed for remesh_smooth GEO::CmdLine::import_arg_group("algo"); GEO::CmdLine::import_arg_group("post"); GEO::CmdLine::import_arg_group("opt"); @@ -138,33 +138,33 @@ int aliceVision_main(int argc, char* argv[]) const unsigned int newtonM = 0; ALICEVISION_LOG_INFO("Start mesh resampling."); - GEO::remesh_smooth( - M_in, M_out, - nbOutputPoints, - 3, // 3 dimensions - nbLloydIter, // Number of iterations for Lloyd pre-smoothing - nbNewtonIter, // Number of iterations for Newton-CVT - newtonM // Number of evaluations for Hessian approximation - ); + GEO::remesh_smooth(M_in, + M_out, + nbOutputPoints, + 3, // 3 dimensions + nbLloydIter, // Number of iterations for Lloyd pre-smoothing + nbNewtonIter, // Number of iterations for Newton-CVT + newtonM // Number of evaluations for Hessian approximation + ); ALICEVISION_LOG_INFO("Mesh resampling done."); } ALICEVISION_LOG_INFO("Output mesh: " << M_out.vertices.nb() << " vertices and " << M_out.facets.nb() << " facets."); - if(M_out.facets.nb() == 0) + if (M_out.facets.nb() == 0) { ALICEVISION_LOG_ERROR("The output mesh is empty."); return 1; } - if(flipNormals) + if (flipNormals) { - for(GEO::index_t i = 0; i < M_out.facets.nb(); ++i) + for (GEO::index_t i = 0; i < M_out.facets.nb(); ++i) { M_out.facets.flip(i); } } ALICEVISION_LOG_INFO("Save mesh."); - if(!GEO::mesh_save(M_out, outputMeshPath)) + if (!GEO::mesh_save(M_out, outputMeshPath)) { ALICEVISION_LOG_ERROR("Failed to save mesh file: \"" << outputMeshPath << "\"."); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_meshing.cpp b/src/software/pipeline/main_meshing.cpp index 8121463165..480a5d222e 100644 --- a/src/software/pipeline/main_meshing.cpp +++ b/src/software/pipeline/main_meshing.cpp @@ -48,9 +48,9 @@ enum EPartitioningMode EPartitioningMode EPartitioning_stringToEnum(const std::string& s) { - if(s == "singleBlock") + if (s == "singleBlock") return ePartitioningSingleBlock; - if(s == "auto") + if (s == "auto") return ePartitioningAuto; return ePartitioningUndefined; } @@ -71,7 +71,7 @@ enum ERepartitionMode ERepartitionMode ERepartitionMode_stringToEnum(const std::string& s) { - if(s == "multiResolution") + if (s == "multiResolution") return eRepartitionMultiResolution; return eRepartitionUndefined; } @@ -84,50 +84,51 @@ inline std::istream& operator>>(std::istream& in, ERepartitionMode& out_mode) return in; } -/// Create a dense SfMData based on reference \p sfmData, +/// Create a dense SfMData based on reference \p sfmData, /// using \p vertices as landmarks and \p ptCams as observations void createDenseSfMData(const sfmData::SfMData& sfmData, const mvsUtils::MultiViewParams& mp, const std::vector& vertices, const StaticVector>& ptsCams, - sfmData::SfMData& outSfmData -) + sfmData::SfMData& outSfmData) { - outSfmData = sfmData; - outSfmData.getLandmarks().clear(); - - const double unknownScale = 0.0; - for(std::size_t i = 0; i < vertices.size(); ++i) - { - const Point3d& point = vertices.at(i); - const Vec3 pt3D(point.x, point.y, point.z); - sfmData::Landmark landmark(pt3D, feature::EImageDescriberType::UNKNOWN); - // set landmark observations from ptsCams if any - if(!ptsCams[i].empty()) + outSfmData = sfmData; + outSfmData.getLandmarks().clear(); + + const double unknownScale = 0.0; + for (std::size_t i = 0; i < vertices.size(); ++i) { - for(int cam : ptsCams[i]) - { - const sfmData::View& view = sfmData.getView(mp.getViewId(cam)); - const camera::IntrinsicBase* intrinsicPtr = sfmData.getIntrinsicPtr(view.getIntrinsicId()); - const sfmData::Observation observation(intrinsicPtr->project(sfmData.getPose(view).getTransform(), pt3D.homogeneous(), true), UndefinedIndexT, unknownScale); // apply distortion - landmark.getObservations()[view.getViewId()] = observation; - } + const Point3d& point = vertices.at(i); + const Vec3 pt3D(point.x, point.y, point.z); + sfmData::Landmark landmark(pt3D, feature::EImageDescriberType::UNKNOWN); + // set landmark observations from ptsCams if any + if (!ptsCams[i].empty()) + { + for (int cam : ptsCams[i]) + { + const sfmData::View& view = sfmData.getView(mp.getViewId(cam)); + const camera::IntrinsicBase* intrinsicPtr = sfmData.getIntrinsicPtr(view.getIntrinsicId()); + const sfmData::Observation observation(intrinsicPtr->project(sfmData.getPose(view).getTransform(), pt3D.homogeneous(), true), + UndefinedIndexT, + unknownScale); // apply distortion + landmark.getObservations()[view.getViewId()] = observation; + } + } + outSfmData.getLandmarks()[i] = landmark; } - outSfmData.getLandmarks()[i] = landmark; - } } /// Remove all landmarks without observations from \p sfmData. void removeLandmarksWithoutObservations(sfmData::SfMData& sfmData) { - auto& landmarks = sfmData.getLandmarks(); - for(auto it = landmarks.begin(); it != landmarks.end();) - { - if(it->second.getObservations().empty()) - it = landmarks.erase(it); - else - ++it; - } + auto& landmarks = sfmData.getLandmarks(); + for (auto it = landmarks.begin(); it != landmarks.end();) + { + if (it->second.getObservations().empty()) + it = landmarks.erase(it); + else + ++it; + } } /// BoundingBox Structure stocking ordered values from the command line @@ -137,10 +138,7 @@ struct BoundingBox Eigen::Vector3d rotation = Eigen::Vector3d::Zero(); Eigen::Vector3d scale = Eigen::Vector3d::Zero(); - inline bool isInitialized() const - { - return scale(0) != 0.0; - } + inline bool isInitialized() const { return scale(0) != 0.0; } Eigen::Matrix4d modelMatrix() const { @@ -230,7 +228,7 @@ struct BoundingBox // Compute the translation Point3d cg(0., 0., 0.); - for(int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { cg += hexah[i]; } @@ -266,14 +264,14 @@ inline std::istream& operator>>(std::istream& in, BoundingBox& out_bbox) std::vector dataStr; boost::split(dataStr, s, boost::is_any_of(",")); - if(dataStr.size() != 9) + if (dataStr.size() != 9) { throw std::runtime_error("Invalid number of values for bounding box."); } std::vector data; data.reserve(9); - for (const std::string& elt : dataStr) + for (const std::string& elt : dataStr) { data.push_back(boost::lexical_cast(elt)); } @@ -285,7 +283,6 @@ inline std::istream& operator>>(std::istream& in, BoundingBox& out_bbox) return in; } - int aliceVision_main(int argc, char* argv[]) { system::Timer timer; @@ -434,31 +431,29 @@ int aliceVision_main(int argc, char* argv[]) return EXIT_FAILURE; } - - if(depthMapsFolder.empty()) + if (depthMapsFolder.empty()) { - if(depthMapsFolder.empty() && - repartitionMode == eRepartitionMultiResolution && - partitioningMode == ePartitioningSingleBlock) - { - meshingFromDepthMaps = false; - addLandmarksToTheDensePointCloud = true; - } - else - { - ALICEVISION_LOG_ERROR("Invalid input options:\n" - "- Meshing from depth maps require --depthMapsFolder option.\n" - "- Meshing from SfM require option --partitioning set to 'singleBlock' and option --repartition set to 'multiResolution'."); - return EXIT_FAILURE; - } + if (depthMapsFolder.empty() && repartitionMode == eRepartitionMultiResolution && partitioningMode == ePartitioningSingleBlock) + { + meshingFromDepthMaps = false; + addLandmarksToTheDensePointCloud = true; + } + else + { + ALICEVISION_LOG_ERROR( + "Invalid input options:\n" + "- Meshing from depth maps require --depthMapsFolder option.\n" + "- Meshing from SfM require option --partitioning set to 'singleBlock' and option --repartition set to 'multiResolution'."); + return EXIT_FAILURE; + } } // read the input SfM scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; } // initialization @@ -482,7 +477,7 @@ int aliceVision_main(int argc, char* argv[]) const auto baseDir = mp.userParams.get("LargeScale.baseDirName", "root01024"); fs::path outDirectory = fs::path(outputMesh).parent_path(); - if(!fs::is_directory(outDirectory)) + if (!fs::is_directory(outDirectory)) fs::create_directory(outDirectory); fs::path tmpDirectory = outDirectory / "tmp"; @@ -493,11 +488,11 @@ int aliceVision_main(int argc, char* argv[]) mesh::Mesh* mesh = nullptr; StaticVector> ptsCams; - switch(repartitionMode) + switch (repartitionMode) { case eRepartitionMultiResolution: { - switch(partitioningMode) + switch (partitioningMode) { case ePartitioningAuto: { @@ -513,10 +508,10 @@ int aliceVision_main(int argc, char* argv[]) if (boundingBox.isInitialized()) boundingBox.toHexahedron(&hexah[0]); - else if(meshingFromDepthMaps && (!estimateSpaceFromSfM || sfmData.getLandmarks().empty())) - fs.divideSpaceFromDepthMaps(&hexah[0], minPixSize); + else if (meshingFromDepthMaps && (!estimateSpaceFromSfM || sfmData.getLandmarks().empty())) + fs.divideSpaceFromDepthMaps(&hexah[0], minPixSize); else - fs.divideSpaceFromSfM(sfmData, &hexah[0], estimateSpaceMinObservations, estimateSpaceMinObservationAngle); + fs.divideSpaceFromSfM(sfmData, &hexah[0], estimateSpaceMinObservations, estimateSpaceMinObservationAngle); { const double length = hexah[0].x - hexah[1].x; @@ -529,7 +524,7 @@ int aliceVision_main(int argc, char* argv[]) BoundingBox bbox = BoundingBox::fromHexahedron(&hexah[0]); std::string filename = (outDirectory / "boundingBox.txt").string(); std::ofstream fs(filename, std::ios::out); - if(!fs.is_open()) + if (!fs.is_open()) { ALICEVISION_LOG_WARNING("Unable to create the bounding box file " << filename); } @@ -540,44 +535,48 @@ int aliceVision_main(int argc, char* argv[]) } StaticVector cams; - if(meshingFromDepthMaps) + if (meshingFromDepthMaps) { - cams = mp.findCamsWhichIntersectsHexahedron(&hexah[0]); + cams = mp.findCamsWhichIntersectsHexahedron(&hexah[0]); } else { - cams.resize(mp.getNbCameras()); - for(int i = 0; i < cams.size(); ++i) - cams[i] = i; + cams.resize(mp.getNbCameras()); + for (int i = 0; i < cams.size(); ++i) + cams[i] = i; } - if(cams.empty()) + if (cams.empty()) throw std::logic_error("No camera to make the reconstruction"); - + fuseCut::DelaunayGraphCut delaunayGC(mp); - delaunayGC.createDensePointCloud(&hexah[0], cams, addLandmarksToTheDensePointCloud ? &sfmData : nullptr, meshingFromDepthMaps ? &fuseParams : nullptr); - if(saveRawDensePointCloud) + delaunayGC.createDensePointCloud( + &hexah[0], cams, addLandmarksToTheDensePointCloud ? &sfmData : nullptr, meshingFromDepthMaps ? &fuseParams : nullptr); + if (saveRawDensePointCloud) { - ALICEVISION_LOG_INFO("Save dense point cloud before cut and filtering."); - StaticVector> ptsCams; - delaunayGC.createPtsCams(ptsCams); - sfmData::SfMData densePointCloud; - createDenseSfMData(sfmData, mp, delaunayGC._verticesCoords, ptsCams, densePointCloud); - removeLandmarksWithoutObservations(densePointCloud); - if(colorizeOutput) - sfmData::colorizeTracks(densePointCloud); - sfmDataIO::save(densePointCloud, (outDirectory/"densePointCloud_raw.abc").string(), sfmDataIO::ESfMData::ALL_DENSE); + ALICEVISION_LOG_INFO("Save dense point cloud before cut and filtering."); + StaticVector> ptsCams; + delaunayGC.createPtsCams(ptsCams); + sfmData::SfMData densePointCloud; + createDenseSfMData(sfmData, mp, delaunayGC._verticesCoords, ptsCams, densePointCloud); + removeLandmarksWithoutObservations(densePointCloud); + if (colorizeOutput) + sfmData::colorizeTracks(densePointCloud); + sfmDataIO::save(densePointCloud, (outDirectory / "densePointCloud_raw.abc").string(), sfmDataIO::ESfMData::ALL_DENSE); } - delaunayGC.createGraphCut(&hexah[0], cams, outDirectory.string() + "/", - outDirectory.string() + "/SpaceCamsTracks/", false, + delaunayGC.createGraphCut(&hexah[0], + cams, + outDirectory.string() + "/", + outDirectory.string() + "/SpaceCamsTracks/", + false, exportDebugTetrahedralization); - delaunayGC.graphCutPostProcessing(&hexah[0], outDirectory.string()+"/"); + delaunayGC.graphCutPostProcessing(&hexah[0], outDirectory.string() + "/"); mesh = delaunayGC.createMesh(maxNbConnectedHelperPoints); delaunayGC.createPtsCams(ptsCams); - mesh::meshPostProcessing(mesh, ptsCams, mp, outDirectory.string()+"/", nullptr, &hexah[0]); + mesh::meshPostProcessing(mesh, ptsCams, mp, outDirectory.string() + "/", nullptr, &hexah[0]); break; } @@ -592,32 +591,32 @@ int aliceVision_main(int argc, char* argv[]) throw std::invalid_argument("Repartition mode is not defined"); } - // Generate output files: + // Generate output files: // - dense point-cloud with observations as sfmData // - mesh as .obj - if(mesh == nullptr || mesh->pts.empty() || mesh->tris.empty()) - throw std::runtime_error("No valid mesh was generated."); + if (mesh == nullptr || mesh->pts.empty() || mesh->tris.empty()) + throw std::runtime_error("No valid mesh was generated."); - if(ptsCams.empty()) - throw std::runtime_error("Points visibilities data has not been initialized."); + if (ptsCams.empty()) + throw std::runtime_error("Points visibilities data has not been initialized."); sfmData::SfMData densePointCloud; createDenseSfMData(sfmData, mp, mesh->pts.getData(), ptsCams, densePointCloud); - if(colorizeOutput) + if (colorizeOutput) { - sfmData::colorizeTracks(densePointCloud); - // colorize output mesh before landmarks filtering - // to have a 1:1 mapping between points and mesh vertices - const auto& landmarks = densePointCloud.getLandmarks(); - std::vector& colors = mesh->colors(); - colors.resize(mesh->pts.size(), {0, 0, 0}); - for(std::size_t i = 0; i < mesh->pts.size(); ++i) - { - const auto& c = landmarks.at(i).rgb; - colors[i] = {c.r(), c.g(), c.b()}; - } + sfmData::colorizeTracks(densePointCloud); + // colorize output mesh before landmarks filtering + // to have a 1:1 mapping between points and mesh vertices + const auto& landmarks = densePointCloud.getLandmarks(); + std::vector& colors = mesh->colors(); + colors.resize(mesh->pts.size(), {0, 0, 0}); + for (std::size_t i = 0; i < mesh->pts.size(); ++i) + { + const auto& c = landmarks.at(i).rgb; + colors[i] = {c.r(), c.g(), c.b()}; + } } removeLandmarksWithoutObservations(densePointCloud); @@ -629,7 +628,6 @@ int aliceVision_main(int argc, char* argv[]) mesh->save(outputMesh); delete mesh; - ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_nodalSfM.cpp b/src/software/pipeline/main_nodalSfM.cpp index 9334bbd24b..f57311cd36 100644 --- a/src/software/pipeline/main_nodalSfM.cpp +++ b/src/software/pipeline/main_nodalSfM.cpp @@ -59,12 +59,11 @@ std::vector readJsons(std::istream& is, boost::json::error_c std::string line; std::size_t n = 0; - - while(true) + while (true) { - if(n == line.size()) + if (n == line.size()) { - if(!std::getline(is, line)) + if (!std::getline(is, line)) { break; } @@ -72,10 +71,10 @@ std::vector readJsons(std::istream& is, boost::json::error_c n = 0; } - //Consume at least part of the line - n += p.write_some( line.data() + n, line.size() - n, ec); + // Consume at least part of the line + n += p.write_some(line.data() + n, line.size() - n, ec); - //If the parser found a value, add it + // If the parser found a value, add it if (p.done()) { jvs.push_back(p.release()); @@ -85,7 +84,7 @@ std::vector readJsons(std::istream& is, boost::json::error_c if (!p.done()) { - //Try to extract the end + // Try to extract the end p.finish(ec); if (ec.failed()) { @@ -98,17 +97,16 @@ std::vector readJsons(std::istream& is, boost::json::error_c return jvs; } - -bool robustRotation(Mat3& R, std::vector& vecInliers, - const Mat& x1, const Mat& x2, +bool robustRotation(Mat3& R, + std::vector& vecInliers, + const Mat& x1, + const Mat& x2, std::mt19937& randomNumberGenerator, - const size_t maxIterationCount, const size_t minInliers) + const size_t maxIterationCount, + const size_t minInliers) { - using KernelType = multiview::RelativePoseSphericalKernel< - multiview::relativePose::Rotation3PSolver, - multiview::relativePose::RotationError, - robustEstimation::Mat3Model - >; + using KernelType = multiview:: + RelativePoseSphericalKernel; KernelType kernel(x1, x2); @@ -118,7 +116,7 @@ bool robustRotation(Mat3& R, std::vector& vecInliers, // robustly estimation of the Essential matrix and its precision robustEstimation::ACRANSAC(kernel, randomNumberGenerator, vecInliers, 1024, &model, std::numeric_limits::infinity()); - if(vecInliers.size() < minInliers) + if (vecInliers.size() < minInliers) { return false; } @@ -128,15 +126,18 @@ bool robustRotation(Mat3& R, std::vector& vecInliers, return true; } -void buildInitialWorld(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featuresPerView, const sfm::ReconstructedPair & pair, const track::TracksPerView & tracksPerView, const track::TracksMap & trackMap) +void buildInitialWorld(sfmData::SfMData& sfmData, + const feature::FeaturesPerView& featuresPerView, + const sfm::ReconstructedPair& pair, + const track::TracksPerView& tracksPerView, + const track::TracksMap& trackMap) { - - const sfmData::View & refView = sfmData.getView(pair.reference); - const sfmData::View & nextView = sfmData.getView(pair.next); + const sfmData::View& refView = sfmData.getView(pair.reference); + const sfmData::View& nextView = sfmData.getView(pair.next); - //Make sure initial camera pose is identity + // Make sure initial camera pose is identity sfmData.setPose(refView, sfmData::CameraPose()); - sfmData.setPose(nextView, sfmData::CameraPose(geometry::Pose3(pair.R,Vec3::Zero()))); + sfmData.setPose(nextView, sfmData::CameraPose(geometry::Pose3(pair.R, Vec3::Zero()))); std::shared_ptr refIntrinsics = sfmData.getIntrinsicsharedPtr(refView.getIntrinsicId()); std::shared_ptr nextIntrinsics = sfmData.getIntrinsicsharedPtr(nextView.getIntrinsicId()); @@ -144,21 +145,21 @@ void buildInitialWorld(sfmData::SfMData& sfmData, const feature::FeaturesPerView const feature::MapFeaturesPerDesc& refFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(pair.reference); const feature::MapFeaturesPerDesc& nextFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(pair.next); - //Get tracks of interest which is the intersection of both list of tracks + // Get tracks of interest which is the intersection of both list of tracks std::vector refTracks = tracksPerView.at(pair.reference); std::vector nextTracks = tracksPerView.at(pair.next); std::vector observedTracks; std::set_intersection(refTracks.begin(), refTracks.end(), nextTracks.begin(), nextTracks.end(), std::back_inserter(observedTracks)); - sfmData::Landmarks & landmarks = sfmData.getLandmarks(); + sfmData::Landmarks& landmarks = sfmData.getLandmarks(); for (IndexT id : observedTracks) { - const auto & track = trackMap.at(id); + const auto& track = trackMap.at(id); const feature::PointFeatures& refFeatures = refFeaturesPerDesc.at(track.descType); const feature::PointFeatures& nextFeatures = nextFeaturesPerDesc.at(track.descType); - + IndexT refFeatureId = track.featPerView.at(pair.reference).featureId; IndexT nextFeatureId = track.featPerView.at(pair.next).featureId; @@ -176,7 +177,6 @@ void buildInitialWorld(sfmData::SfMData& sfmData, const feature::FeaturesPerView continue; } - sfmData::Landmark l(track.descType); l.X = refP; l.getObservations()[pair.reference] = sfmData::Observation(refV, refFeatureId, refFeatures[refFeatureId].scale()); @@ -186,16 +186,21 @@ void buildInitialWorld(sfmData::SfMData& sfmData, const feature::FeaturesPerView } } -IndexT findBestNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featuresPerView, const track::TracksPerView & tracksPerView, const track::TracksMap & trackMap, const std::set & visitedViews) +IndexT findBestNext(sfmData::SfMData& sfmData, + const feature::FeaturesPerView& featuresPerView, + const track::TracksPerView& tracksPerView, + const track::TracksMap& trackMap, + const std::set& visitedViews) { - //Retrieve the set of tracks with an associated landmark + // Retrieve the set of tracks with an associated landmark std::set tracksWithPoint; - std::transform(sfmData.getLandmarks().begin(), sfmData.getLandmarks().end(), std::inserter(tracksWithPoint, tracksWithPoint.begin()), stl::RetrieveKey()); + std::transform( + sfmData.getLandmarks().begin(), sfmData.getLandmarks().end(), std::inserter(tracksWithPoint, tracksWithPoint.begin()), stl::RetrieveKey()); - //Find the view with most observed landmarks + // Find the view with most observed landmarks size_t bestCount = 0; IndexT bestId = UndefinedIndexT; - for (const auto & pV : sfmData.getViews()) + for (const auto& pV : sfmData.getViews()) { if (sfmData.isPoseAndIntrinsicDefined(pV.first)) { @@ -210,8 +215,9 @@ IndexT findBestNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & std::vector nextTracks = tracksPerView.at(pV.first); std::vector observedTracks; - std::set_intersection(tracksWithPoint.begin(), tracksWithPoint.end(), nextTracks.begin(), nextTracks.end(), std::back_inserter(observedTracks)); - + std::set_intersection( + tracksWithPoint.begin(), tracksWithPoint.end(), nextTracks.begin(), nextTracks.end(), std::back_inserter(observedTracks)); + if (observedTracks.size() > bestCount) { bestCount = observedTracks.size(); @@ -222,18 +228,22 @@ IndexT findBestNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & return bestId; } -bool localizeNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featuresPerView, const track::TracksPerView & tracksPerView, const track::TracksMap & trackMap, const IndexT newViewId) +bool localizeNext(sfmData::SfMData& sfmData, + const feature::FeaturesPerView& featuresPerView, + const track::TracksPerView& tracksPerView, + const track::TracksMap& trackMap, + const IndexT newViewId) { - //Retrieve the set of tracks with an associated landmark + // Retrieve the set of tracks with an associated landmark std::set tracksWithPoint; - std::transform(sfmData.getLandmarks().begin(), sfmData.getLandmarks().end(), std::inserter(tracksWithPoint, tracksWithPoint.begin()), stl::RetrieveKey()); + std::transform( + sfmData.getLandmarks().begin(), sfmData.getLandmarks().end(), std::inserter(tracksWithPoint, tracksWithPoint.begin()), stl::RetrieveKey()); std::vector nextTracks = tracksPerView.at(newViewId); std::vector observedTracks; std::set_intersection(tracksWithPoint.begin(), tracksWithPoint.end(), nextTracks.begin(), nextTracks.end(), std::back_inserter(observedTracks)); - - sfmData::Landmarks & landmarks = sfmData.getLandmarks(); + sfmData::Landmarks& landmarks = sfmData.getLandmarks(); const sfmData::View& newView = sfmData.getView(newViewId); std::shared_ptr newViewIntrinsics = sfmData.getIntrinsicsharedPtr(newView.getIntrinsicId()); @@ -245,13 +255,13 @@ bool localizeNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & fe int pos = 0; for (IndexT trackId : observedTracks) { - const track::Track & track = trackMap.at(trackId); + const track::Track& track = trackMap.at(trackId); const feature::PointFeatures& newViewFeatures = newViewFeaturesPerDesc.at(track.descType); IndexT newViewFeatureId = track.featPerView.at(newViewId).featureId; Vec2 nvV = newViewFeatures[newViewFeatureId].coords().cast(); Vec3 camP = newViewIntrinsics->toUnitSphere(newViewIntrinsics->ima2cam(newViewIntrinsics->get_ud_pixel(nvV))); - + refX.col(pos) = landmarks.at(trackId).X; newX.col(pos) = camP; @@ -263,33 +273,37 @@ bool localizeNext(sfmData::SfMData& sfmData, const feature::FeaturesPerView & fe const size_t minInliers = 35; std::mt19937 randomNumberGenerator(0); const bool relativeSuccess = robustRotation(R, vecInliers, refX, newX, randomNumberGenerator, 1024, minInliers); - if(!relativeSuccess) + if (!relativeSuccess) { return false; } - //Assign pose - sfmData.setPose(newView, sfmData::CameraPose(geometry::Pose3(R,Vec3::Zero()))); + // Assign pose + sfmData.setPose(newView, sfmData::CameraPose(geometry::Pose3(R, Vec3::Zero()))); std::cout << vecInliers.size() << std::endl; - //Add observations + // Add observations for (size_t pos : vecInliers) { IndexT trackId = observedTracks[pos]; - const track::Track & track = trackMap.at(trackId); + const track::Track& track = trackMap.at(trackId); const feature::PointFeatures& newViewFeatures = newViewFeaturesPerDesc.at(track.descType); IndexT newViewFeatureId = track.featPerView.at(newViewId).featureId; - auto & feat = newViewFeatures[newViewFeatureId]; + auto& feat = newViewFeatures[newViewFeatureId]; landmarks[trackId].getObservations()[newViewId] = sfmData::Observation(feat.coords().cast(), newViewFeatureId, feat.scale()); } return true; } -bool addPoints(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featuresPerView, const track::TracksPerView & tracksPerView, const track::TracksMap & trackMap, const IndexT newViewId) +bool addPoints(sfmData::SfMData& sfmData, + const feature::FeaturesPerView& featuresPerView, + const track::TracksPerView& tracksPerView, + const track::TracksMap& trackMap, + const IndexT newViewId) { - sfmData::Landmarks & landmarks = sfmData.getLandmarks(); + sfmData::Landmarks& landmarks = sfmData.getLandmarks(); std::set tracksWithPoint; std::transform(landmarks.begin(), landmarks.end(), std::inserter(tracksWithPoint, tracksWithPoint.begin()), stl::RetrieveKey()); @@ -297,15 +311,16 @@ bool addPoints(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featu std::vector nextTracks = tracksPerView.at(newViewId); std::vector nextTracksNotReconstructed; - std::set_difference(nextTracks.begin(), nextTracks.end(), tracksWithPoint.begin(), tracksWithPoint.end(), std::back_inserter(nextTracksNotReconstructed)); + std::set_difference( + nextTracks.begin(), nextTracks.end(), tracksWithPoint.begin(), tracksWithPoint.end(), std::back_inserter(nextTracksNotReconstructed)); const sfmData::View& newView = sfmData.getView(newViewId); std::shared_ptr newViewIntrinsics = sfmData.getIntrinsicsharedPtr(newView.getIntrinsicId()); const feature::MapFeaturesPerDesc& newViewFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(newViewId); const Eigen::Matrix3d new_R_world = sfmData.getPose(newView).getTransform().rotation(); - //For all reconstructed views - for (auto & pV : sfmData.getViews()) + // For all reconstructed views + for (auto& pV : sfmData.getViews()) { if (!sfmData.isPoseAndIntrinsicDefined(pV.first)) { @@ -315,20 +330,24 @@ bool addPoints(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featu std::vector refTracks = tracksPerView.at(pV.first); std::vector observedTracks; - std::set_intersection(refTracks.begin(), refTracks.end(), nextTracksNotReconstructed.begin(), nextTracksNotReconstructed.end(), std::back_inserter(observedTracks)); + std::set_intersection(refTracks.begin(), + refTracks.end(), + nextTracksNotReconstructed.begin(), + nextTracksNotReconstructed.end(), + std::back_inserter(observedTracks)); const sfmData::View& refView = sfmData.getView(pV.first); const Eigen::Matrix3d ref_R_world = sfmData.getPose(refView).getTransform().rotation(); std::shared_ptr refViewIntrinsics = sfmData.getIntrinsicsharedPtr(refView.getIntrinsicId()); const feature::MapFeaturesPerDesc& refViewFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(pV.first); - + Eigen::Matrix3d world_R_new = new_R_world.transpose(); Eigen::Matrix3d ref_R_new = ref_R_world * world_R_new; for (IndexT trackId : observedTracks) { - const track::Track & track = trackMap.at(trackId); + const track::Track& track = trackMap.at(trackId); const feature::PointFeatures& newViewFeatures = newViewFeaturesPerDesc.at(track.descType); const feature::PointFeatures& refViewFeatures = refViewFeaturesPerDesc.at(track.descType); @@ -336,8 +355,8 @@ bool addPoints(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featu IndexT newViewFeatureId = track.featPerView.at(newViewId).featureId; IndexT refViewFeatureId = track.featPerView.at(pV.first).featureId; - auto & newFeat = newViewFeatures[newViewFeatureId]; - auto & refFeat = refViewFeatures[refViewFeatureId]; + auto& newFeat = newViewFeatures[newViewFeatureId]; + auto& refFeat = refViewFeatures[refViewFeatureId]; Vec2 newV = newFeat.coords().cast(); Vec2 refV = refFeat.coords().cast(); @@ -357,7 +376,7 @@ bool addPoints(sfmData::SfMData& sfmData, const feature::FeaturesPerView & featu l.X = world_R_new * newP; l.getObservations()[newViewId] = sfmData::Observation(newV, newViewFeatureId, refViewFeatures[refViewFeatureId].scale()); l.getObservations()[pV.first] = sfmData::Observation(refV, refViewFeatureId, newViewFeatures[newViewFeatureId].scale()); - + landmarks[trackId] = l; } } @@ -403,7 +422,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("AliceVision Nodal SfM"); cmdline.add(requiredParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -411,24 +430,22 @@ int aliceVision_main(int argc, char** argv) // set maxThreads HardwareContext hwc = cmdline.getHardwareContext(); omp_set_num_threads(hwc.getMaxThreads()); - + // load input SfMData scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); return EXIT_FAILURE; } // get imageDescriber type - const std::vector describerTypes = - feature::EImageDescriberType_stringToEnums(describerTypesName); - + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); // features reading feature::FeaturesPerView featuresPerView; ALICEVISION_LOG_INFO("Load features"); - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Invalid features."); return EXIT_FAILURE; @@ -437,7 +454,7 @@ int aliceVision_main(int argc, char** argv) // Load tracks ALICEVISION_LOG_INFO("Load tracks"); std::ifstream tracksFile(tracksFilename); - if(tracksFile.is_open() == false) + if (tracksFile.is_open() == false) { ALICEVISION_LOG_ERROR("The input tracks file '" + tracksFilename + "' cannot be read."); return EXIT_FAILURE; @@ -452,33 +469,32 @@ int aliceVision_main(int argc, char** argv) // For easier access, and for eah view we build a list of tracks observed in this view ALICEVISION_LOG_INFO("Estimate tracks per view"); track::TracksPerView mapTracksPerView; - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { // create an entry in the map mapTracksPerView[viewIt.first]; } track::computeTracksPerView(mapTracks, mapTracksPerView); - // Because the reconstructed pairs information was processed in chunks // There are potentially multiple files describing the pairs. // Here we merge all the files in memory std::vector reconstructedPairs; - //Assuming the filename is pairs_ + a number with json extension + // Assuming the filename is pairs_ + a number with json extension const std::regex regex("pairs\\_[0-9]+\\.json"); - for(auto const& file : fs::directory_iterator{pairsDirectory}) + for (auto const& file : fs::directory_iterator{pairsDirectory}) { if (!std::regex_search(file.path().string(), regex)) { continue; } - //Load the file content - //This is a vector of sfm::ReconstructedPair - std::ifstream inputfile(file.path().string()); + // Load the file content + // This is a vector of sfm::ReconstructedPair + std::ifstream inputfile(file.path().string()); boost::json::error_code ec; std::vector values = readJsons(inputfile, ec); - for (const boost::json::value & value : values) + for (const boost::json::value& value : values) { std::vector localVector = boost::json::value_to>(value); reconstructedPairs.insert(reconstructedPairs.end(), localVector.begin(), localVector.end()); @@ -491,51 +507,50 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - //Sort reconstructedPairs by quality - std::sort(reconstructedPairs.begin(), reconstructedPairs.end(), - [](const sfm::ReconstructedPair& p1, const sfm::ReconstructedPair & p2) - { - return p1.score > p2.score; - } - ); + // Sort reconstructedPairs by quality + std::sort(reconstructedPairs.begin(), reconstructedPairs.end(), [](const sfm::ReconstructedPair& p1, const sfm::ReconstructedPair& p2) { + return p1.score > p2.score; + }); - //Using two views, create an initial map and pair of cameras + // Using two views, create an initial map and pair of cameras buildInitialWorld(sfmData, featuresPerView, reconstructedPairs[0], mapTracksPerView, mapTracks); - //Loop until termination of the process using the current boostrapped map + // Loop until termination of the process using the current boostrapped map std::set visited; while (1) { - //Find the optimal next view to localize + // Find the optimal next view to localize IndexT next = findBestNext(sfmData, featuresPerView, mapTracksPerView, mapTracks, visited); if (next == UndefinedIndexT) { break; } - //Localize the selected view + // Localize the selected view if (!localizeNext(sfmData, featuresPerView, mapTracksPerView, mapTracks, next)) { break; } - //Add points to the map in the frame of the first bootstrapping camera + // Add points to the map in the frame of the first bootstrapping camera if (!addPoints(sfmData, featuresPerView, mapTracksPerView, mapTracks, next)) { break; } } - - //Refinment options + // Refinment options sfm::BundleAdjustmentSymbolicCeres::CeresOptions options; - sfm::BundleAdjustment::ERefineOptions refineOptions = sfm::BundleAdjustment::REFINE_ROTATION | sfm::BundleAdjustment::REFINE_STRUCTURE | sfm::BundleAdjustment::REFINE_INTRINSICS_FOCAL | sfm::BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | sfm::BundleAdjustment::REFINE_INTRINSICS_DISTORTION | sfm::BundleAdjustment::REFINE_STRUCTURE_AS_NORMALS; + sfm::BundleAdjustment::ERefineOptions refineOptions = + sfm::BundleAdjustment::REFINE_ROTATION | sfm::BundleAdjustment::REFINE_STRUCTURE | sfm::BundleAdjustment::REFINE_INTRINSICS_FOCAL | + sfm::BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | sfm::BundleAdjustment::REFINE_INTRINSICS_DISTORTION | + sfm::BundleAdjustment::REFINE_STRUCTURE_AS_NORMALS; options.summary = true; - //Repeat until convergence - //Estimate the optimal parameters - //Remove the outliers - //If no outliers removed, exit the loop + // Repeat until convergence + // Estimate the optimal parameters + // Remove the outliers + // If no outliers removed, exit the loop int countRemoved = 0; do { @@ -543,8 +558,7 @@ int aliceVision_main(int argc, char** argv) const bool success = BA.adjust(sfmData, refineOptions); countRemoved = sfm::removeOutliersWithPixelResidualError(sfmData, sfm::EFeatureConstraint::SCALE, 2.0, 2); std::cout << countRemoved << std::endl; - } - while (countRemoved > 0); + } while (countRemoved > 0); sfmDataIO::save(sfmData, sfmDataOutputFilename, sfmDataIO::ESfMData::ALL); diff --git a/src/software/pipeline/main_normalIntegration.cpp b/src/software/pipeline/main_normalIntegration.cpp index 853d1078f2..f3ca746566 100644 --- a/src/software/pipeline/main_normalIntegration.cpp +++ b/src/software/pipeline/main_normalIntegration.cpp @@ -27,7 +27,7 @@ #include #include -//OpenCV +// OpenCV #include #include #include @@ -47,7 +47,7 @@ using namespace aliceVision; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { namespace po = boost::program_options; @@ -105,4 +105,3 @@ int aliceVision_main(int argc, char **argv) ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); return EXIT_SUCCESS; }; - diff --git a/src/software/pipeline/main_panoramaCompositing.cpp b/src/software/pipeline/main_panoramaCompositing.cpp index ef766a0678..5297f54d35 100644 --- a/src/software/pipeline/main_panoramaCompositing.cpp +++ b/src/software/pipeline/main_panoramaCompositing.cpp @@ -64,21 +64,23 @@ size_t getCompositingOptimalScale(int width, int height) */ const int gaussianFilterSize = 5; - //Avoid negative values on scale + // Avoid negative values on scale if (minsize < gaussianFilterSize) { return 0; } - + const size_t optimal_scale = size_t(floor(std::log2(double(minsize) / gaussianFilterSize))); return optimal_scale; } -std::unique_ptr buildMap(const sfmData::SfMData& sfmData, const std::string& inputPath, - const size_t borderSize, size_t forceMinPyramidLevels) +std::unique_ptr buildMap(const sfmData::SfMData& sfmData, + const std::string& inputPath, + const size_t borderSize, + size_t forceMinPyramidLevels) { - if(sfmData.getViews().empty()) + if (sfmData.getViews().empty()) { return nullptr; } @@ -87,9 +89,9 @@ std::unique_ptr buildMap(const sfmData::SfMData& sfmData, const std std::vector> listBoundingBox; std::pair panoramaSize; - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { - if(!sfmData.isPoseAndIntrinsicDefined(viewIt.first)) + if (!sfmData.isPoseAndIntrinsicDefined(viewIt.first)) continue; const std::string warpedPath = viewIt.second->getImage().getMetadata().at("AliceVision:warpedPath"); @@ -112,12 +114,12 @@ std::unique_ptr buildMap(const sfmData::SfMData& sfmData, const std bb.width = width; bb.height = height; - if(viewIt.first == 0) + if (viewIt.first == 0) continue; listBoundingBox.push_back(std::make_pair(viewIt.first, bb)); size_t scale = getCompositingOptimalScale(width, height); - if(scale > max_scale) + if (scale > max_scale) { max_scale = scale; } @@ -132,7 +134,7 @@ std::unique_ptr buildMap(const sfmData::SfMData& sfmData, const std } std::unique_ptr ret(new PanoramaMap(panoramaSize.first, panoramaSize.second, max_scale, borderSize)); - for(const auto& bbitem : listBoundingBox) + for (const auto& bbitem : listBoundingBox) { ret->append(bbitem.first, bbitem.second); } @@ -140,10 +142,17 @@ std::unique_ptr buildMap(const sfmData::SfMData& sfmData, const std return ret; } -bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmData, const std::string& compositerType, - const std::string& warpingFolder, const std::string& labelsFilePath, const std::string& outputFolder, - const image::EStorageDataType& storageDataType, IndexT viewReference, - const BoundingBox& referenceBoundingBox, bool showBorders, bool showSeams) +bool processImage(const PanoramaMap& panoramaMap, + const sfmData::SfMData& sfmData, + const std::string& compositerType, + const std::string& warpingFolder, + const std::string& labelsFilePath, + const std::string& outputFolder, + const image::EStorageDataType& storageDataType, + IndexT viewReference, + const BoundingBox& referenceBoundingBox, + bool showBorders, + bool showSeams) { // The laplacian pyramid must also contains some pixels outside of the bounding box to make sure // there is a continuity between all the "views" of the panorama. @@ -153,40 +162,37 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat bool needWeights; bool needSeams; std::unique_ptr compositer; - if(compositerType == "multiband") + if (compositerType == "multiband") { needWeights = false; needSeams = true; // Enlarge the panorama boundingbox to allow consider neighboor pixels even at small scale - panoramaBoundingBox = referenceBoundingBox.divide(panoramaMap.getScale()) - .dilate(panoramaMap.getBorderSize()) - .multiply(panoramaMap.getScale()); + panoramaBoundingBox = + referenceBoundingBox.divide(panoramaMap.getScale()).dilate(panoramaMap.getBorderSize()).multiply(panoramaMap.getScale()); panoramaBoundingBox.clampTop(); panoramaBoundingBox.clampBottom(panoramaMap.getHeight()); - compositer = std::unique_ptr( - new LaplacianCompositer(panoramaBoundingBox.width, panoramaBoundingBox.height, panoramaMap.getScale())); + compositer = + std::unique_ptr(new LaplacianCompositer(panoramaBoundingBox.width, panoramaBoundingBox.height, panoramaMap.getScale())); } - else if(compositerType == "alpha") + else if (compositerType == "alpha") { needWeights = true; needSeams = false; - compositer = - std::unique_ptr(new AlphaCompositer(referenceBoundingBox.width, referenceBoundingBox.height)); + compositer = std::unique_ptr(new AlphaCompositer(referenceBoundingBox.width, referenceBoundingBox.height)); } else { needWeights = false; needSeams = false; - compositer = - std::unique_ptr(new Compositer(referenceBoundingBox.width, referenceBoundingBox.height)); + compositer = std::unique_ptr(new Compositer(referenceBoundingBox.width, referenceBoundingBox.height)); } // Get the list of input which should be processed for this reference view bounding box std::vector overlappingViews; - if(!panoramaMap.getOverlaps(overlappingViews, referenceBoundingBox)) + if (!panoramaMap.getOverlaps(overlappingViews, referenceBoundingBox)) { ALICEVISION_LOG_ERROR("Problem analyzing neighboorhood"); return false; @@ -195,17 +201,17 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Compute the bounding box of the intersections with the reference bounding box // (which may be larger than the reference Bounding box because of dilatation) BoundingBox globalUnionBoundingBox; - for(IndexT viewCurrent : overlappingViews) + for (IndexT viewCurrent : overlappingViews) { // Compute list of intersection between this view and the reference view std::vector intersections; std::vector currentBoundingBoxes; - if(!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) + if (!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) { continue; } - for(BoundingBox& bb : intersections) + for (BoundingBox& bb : intersections) { globalUnionBoundingBox = globalUnionBoundingBox.unionWith(bb); } @@ -215,7 +221,7 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Building a map of visible pixels image::Image> visiblePixels(globalUnionBoundingBox.width, globalUnionBoundingBox.height, true); - for(IndexT viewCurrent : overlappingViews) + for (IndexT viewCurrent : overlappingViews) { const std::string warpedPath = sfmData.getViews().at(viewCurrent)->getImage().getMetadata().at("AliceVision:warpedPath"); @@ -228,33 +234,33 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Compute list of intersection between this view and the reference view std::vector intersections; std::vector currentBoundingBoxes; - if(!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) + if (!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) { continue; } - for(int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) + for (int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) { const BoundingBox& bbox = currentBoundingBoxes[indexIntersection]; const BoundingBox& bboxIntersect = intersections[indexIntersection]; - for(int i = 0; i < mask.height(); i++) + for (int i = 0; i < mask.height(); i++) { int y = bbox.top + i - globalUnionBoundingBox.top; - if(y < 0 || y >= globalUnionBoundingBox.height) + if (y < 0 || y >= globalUnionBoundingBox.height) { continue; } - for(int j = 0; j < mask.width(); j++) + for (int j = 0; j < mask.width(); j++) { - if(!mask(i, j)) + if (!mask(i, j)) { continue; } int x = bbox.left + j - globalUnionBoundingBox.left; - if(x < 0 || x >= globalUnionBoundingBox.width) + if (x < 0 || x >= globalUnionBoundingBox.width) { continue; } @@ -269,7 +275,7 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Compute initial seams image::Image referenceLabels; - if(needSeams) + if (needSeams) { image::Image panoramaLabels; image::readImageDirect(labelsFilePath, panoramaLabels); @@ -277,77 +283,76 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat const double scaleX = double(panoramaLabels.width()) / double(panoramaMap.getWidth()); const double scaleY = double(panoramaLabels.height()) / double(panoramaMap.getHeight()); - referenceLabels = - image::Image(globalUnionBoundingBox.width, globalUnionBoundingBox.height, true, UndefinedIndexT); + referenceLabels = image::Image(globalUnionBoundingBox.width, globalUnionBoundingBox.height, true, UndefinedIndexT); - for(int i = 0; i < globalUnionBoundingBox.height; i++) + for (int i = 0; i < globalUnionBoundingBox.height; i++) { const int y = i + globalUnionBoundingBox.top; const int scaledY = int(floor(scaleY * double(y))); - for(int j = 0; j < globalUnionBoundingBox.width; j++) + for (int j = 0; j < globalUnionBoundingBox.width; j++) { const int x = j + globalUnionBoundingBox.left; int scaledX = int(floor(scaleX * double(x))); - if(scaledX < 0) + if (scaledX < 0) { scaledX += panoramaLabels.width(); } - if(scaledX >= panoramaLabels.width()) + if (scaledX >= panoramaLabels.width()) { scaledX -= panoramaLabels.width(); } - if(scaledX < 0) + if (scaledX < 0) continue; - if(scaledX >= panoramaLabels.width()) + if (scaledX >= panoramaLabels.width()) continue; IndexT label = panoramaLabels(scaledY, scaledX); bool found = false; auto& listValid = visiblePixels(i, j); - for(auto item : listValid) + for (auto item : listValid) { - if(item == label) + if (item == label) { found = true; break; } } - if(found) + if (found) { referenceLabels(i, j) = label; continue; } found = false; - for(int k = -1; k <= 1; k++) + for (int k = -1; k <= 1; k++) { int nscaledY = scaledY + k; - if(nscaledY < 0) + if (nscaledY < 0) continue; - if(nscaledY >= panoramaLabels.height()) + if (nscaledY >= panoramaLabels.height()) continue; - for(int l = -1; l <= 1; l++) + for (int l = -1; l <= 1; l++) { - if(k == 0 && l == 0) + if (k == 0 && l == 0) continue; int nscaledX = scaledX + l; - if(nscaledX < 0) + if (nscaledX < 0) continue; - if(nscaledX >= panoramaLabels.width()) + if (nscaledX >= panoramaLabels.width()) continue; IndexT otherlabel = panoramaLabels(nscaledY, nscaledX); - for(auto item : listValid) + for (auto item : listValid) { - if(item == otherlabel) + if (item == otherlabel) { label = otherlabel; found = true; @@ -355,15 +360,15 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat } } - if(found) + if (found) break; } - if(found) + if (found) break; } - if(!found) + if (!found) { referenceLabels(i, j) = UndefinedIndexT; continue; @@ -383,7 +388,7 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat bbRoi.height = referenceBoundingBox.height; // Compositer initialization - if(!compositer->initialize(bbRoi)) + if (!compositer->initialize(bbRoi)) { ALICEVISION_LOG_ERROR("Error initializing panorama"); return false; @@ -394,20 +399,19 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Load metadata to get image color space std::string colorSpace = "Linear"; oiio::ParamValueList srcMetadata; - if(!overlappingViews.empty()) + if (!overlappingViews.empty()) { - const std::string warpedPath = - sfmData.getViews().at(overlappingViews[0])->getImage().getMetadata().at("AliceVision:warpedPath"); + const std::string warpedPath = sfmData.getViews().at(overlappingViews[0])->getImage().getMetadata().at("AliceVision:warpedPath"); const std::string firstImagePath = (fs::path(warpingFolder) / (warpedPath + ".exr")).string(); srcMetadata = image::readImageMetadata(firstImagePath); colorSpace = srcMetadata.get_string("AliceVision:ColorSpace", "Linear"); } #pragma omp parallel for - for(int posCurrent = 0; posCurrent < overlappingViews.size(); posCurrent++) + for (int posCurrent = 0; posCurrent < overlappingViews.size(); posCurrent++) { IndexT viewCurrent = overlappingViews[posCurrent]; - if(hasFailed) + if (hasFailed) { continue; } @@ -419,20 +423,20 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Compute list of intersection between this view and the reference view std::vector intersections; std::vector currentBoundingBoxes; - if(!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) + if (!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) { continue; } - if(intersections.empty()) + if (intersections.empty()) { continue; } ALICEVISION_LOG_TRACE("Effective processing"); - for(int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) + for (int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) { - if(hasFailed) + if (hasFailed) { continue; } @@ -454,20 +458,20 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat // Load weights image if needed image::Image weights; - if(needWeights) + if (needWeights) { const std::string weightsPath = (fs::path(warpingFolder) / (warpedPath + "_weight.exr")).string(); ALICEVISION_LOG_TRACE("Load weights with path " << weightsPath); image::readImage(weightsPath, weights, image::EImageColorSpace::NO_CONVERSION); } - if(needSeams) + if (needSeams) { int left = bboxIntersect.left - globalUnionBoundingBox.left; int top = bboxIntersect.top - globalUnionBoundingBox.top; weights = image::Image(bboxIntersect.width, bboxIntersect.height); - if(!getMaskFromLabels(weights, referenceLabels, viewCurrent, left, top)) + if (!getMaskFromLabels(weights, referenceLabels, viewCurrent, left, top)) { ALICEVISION_LOG_ERROR("Error estimating seams image"); hasFailed = true; @@ -479,7 +483,7 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat cutBoundingBox.top = bboxIntersect.top - bbox.top; cutBoundingBox.width = bboxIntersect.width; cutBoundingBox.height = bboxIntersect.height; - if(cutBoundingBox.isEmpty()) + if (cutBoundingBox.isEmpty()) { continue; } @@ -487,18 +491,17 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat image::Image subsource(cutBoundingBox.width, cutBoundingBox.height); image::Image submask(cutBoundingBox.width, cutBoundingBox.height); - subsource = - source.block(cutBoundingBox.top, cutBoundingBox.left, cutBoundingBox.height, cutBoundingBox.width); + subsource = source.block(cutBoundingBox.top, cutBoundingBox.left, cutBoundingBox.height, cutBoundingBox.width); submask = mask.block(cutBoundingBox.top, cutBoundingBox.left, cutBoundingBox.height, cutBoundingBox.width); source = image::Image(); mask = image::Image(); - if(!compositer->append(subsource, submask, weights, - referenceBoundingBox.left - panoramaBoundingBox.left + bboxIntersect.left - - referenceBoundingBox.left, - referenceBoundingBox.top - panoramaBoundingBox.top + bboxIntersect.top - - referenceBoundingBox.top)) + if (!compositer->append(subsource, + submask, + weights, + referenceBoundingBox.left - panoramaBoundingBox.left + bboxIntersect.left - referenceBoundingBox.left, + referenceBoundingBox.top - panoramaBoundingBox.top + bboxIntersect.top - referenceBoundingBox.top)) { ALICEVISION_LOG_INFO("Error in compositer append"); hasFailed = true; @@ -507,25 +510,25 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat } } - if(hasFailed) + if (hasFailed) { return false; } ALICEVISION_LOG_INFO("Terminate compositing for this view"); - if(!compositer->terminate()) + if (!compositer->terminate()) { ALICEVISION_LOG_ERROR("Error terminating panorama"); return false; } std::string warpedPath; - - if (viewReference==UndefinedIndexT) + + if (viewReference == UndefinedIndexT) { warpedPath = "panorama"; } - else + else { warpedPath = sfmData.getViews().at(viewReference)->getImage().getMetadata().at("AliceVision:warpedPath"); } @@ -533,11 +536,11 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat const std::string outputFilePath = (fs::path(outputFolder) / (warpedPath + ".exr")).string(); image::Image& output = compositer->getOutput(); - if(storageDataType == image::EStorageDataType::HalfFinite) + if (storageDataType == image::EStorageDataType::HalfFinite) { - for(int i = 0; i < output.height(); i++) + for (int i = 0; i < output.height(); i++) { - for(int j = 0; j < output.width(); j++) + for (int j = 0; j < output.width(); j++) { image::RGBAfColor ret; image::RGBAfColor c = output(i, j); @@ -554,29 +557,27 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat } } - if(showBorders) + if (showBorders) { ALICEVISION_LOG_INFO("Draw borders"); - for(IndexT viewCurrent : overlappingViews) + for (IndexT viewCurrent : overlappingViews) { // Compute list of intersection between this view and the reference view std::vector intersections; std::vector currentBoundingBoxes; - if(!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, - viewCurrent)) + if (!panoramaMap.getIntersectionsList(intersections, currentBoundingBoxes, referenceBoundingBox, viewCurrent)) { continue; } // Load mask - const std::string warpedPath = - sfmData.getViews().at(viewCurrent)->getImage().getMetadata().at("AliceVision:warpedPath"); + const std::string warpedPath = sfmData.getViews().at(viewCurrent)->getImage().getMetadata().at("AliceVision:warpedPath"); const std::string maskPath = (fs::path(warpingFolder) / (warpedPath + "_mask.exr")).string(); ALICEVISION_LOG_TRACE("Load mask with path " << maskPath); image::Image mask; image::readImageDirect(maskPath, mask); - for(int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) + for (int indexIntersection = 0; indexIntersection < intersections.size(); indexIntersection++) { const BoundingBox& bbox = currentBoundingBoxes[indexIntersection]; const BoundingBox& bboxIntersect = intersections[indexIntersection]; @@ -586,25 +587,23 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat cutBoundingBox.top = bboxIntersect.top - bbox.top; cutBoundingBox.width = bboxIntersect.width; cutBoundingBox.height = bboxIntersect.height; - if(cutBoundingBox.isEmpty()) + if (cutBoundingBox.isEmpty()) { continue; } image::Image submask(cutBoundingBox.width, cutBoundingBox.height); - submask = - mask.block(cutBoundingBox.top, cutBoundingBox.left, cutBoundingBox.height, cutBoundingBox.width); + submask = mask.block(cutBoundingBox.top, cutBoundingBox.left, cutBoundingBox.height, cutBoundingBox.width); - drawBorders(output, submask, bboxIntersect.left - referenceBoundingBox.left, - bboxIntersect.top - referenceBoundingBox.top); + drawBorders(output, submask, bboxIntersect.left - referenceBoundingBox.left, bboxIntersect.top - referenceBoundingBox.top); } } } - if(showSeams && needSeams) + if (showSeams && needSeams) { - drawSeams(output, referenceLabels, globalUnionBoundingBox.left - referenceBoundingBox.left, - globalUnionBoundingBox.top - referenceBoundingBox.top); + drawSeams( + output, referenceLabels, globalUnionBoundingBox.left - referenceBoundingBox.left, globalUnionBoundingBox.top - referenceBoundingBox.top); } oiio::ParamValueList metadata = srcMetadata; @@ -617,11 +616,12 @@ bool processImage(const PanoramaMap& panoramaMap, const sfmData::SfMData& sfmDat metadata.push_back(oiio::ParamValue("AliceVision:panoramaWidth", int(panoramaMap.getWidth()))); metadata.push_back(oiio::ParamValue("AliceVision:panoramaHeight", int(panoramaMap.getHeight()))); - image::writeImage(outputFilePath, output, + image::writeImage(outputFilePath, + output, image::ImageWriteOptions() - .fromColorSpace(image::EImageColorSpace_stringToEnum(colorSpace)) - .toColorSpace(image::EImageColorSpace_stringToEnum(colorSpace)) - .storageDataType(storageDataType), + .fromColorSpace(image::EImageColorSpace_stringToEnum(colorSpace)) + .toColorSpace(image::EImageColorSpace_stringToEnum(colorSpace)) + .storageDataType(storageDataType), metadata); return true; @@ -679,12 +679,11 @@ int aliceVision_main(int argc, char** argv) "Use tiling for compositing."); // clang-format on - CmdLine cmdline( - "Performs the panorama stiching of warped images, with an option to use constraints from precomputed seams maps.\n" - "AliceVision panoramaCompositing"); + CmdLine cmdline("Performs the panorama stiching of warped images, with an option to use constraints from precomputed seams maps.\n" + "AliceVision panoramaCompositing"); cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -692,18 +691,17 @@ int aliceVision_main(int argc, char** argv) // set maxThreads HardwareContext hwc = cmdline.getHardwareContext(); hwc.setUserCoresLimit(maxThreads); - + omp_set_num_threads(hwc.getMaxThreads()); oiio::attribute("threads", static_cast(hwc.getMaxThreads())); oiio::attribute("exr_threads", static_cast(hwc.getMaxThreads())); - - if(overlayType == "borders" || overlayType == "all") + if (overlayType == "borders" || overlayType == "all") { showBorders = true; } - if(overlayType == "seams" || overlayType == "all") + if (overlayType == "seams" || overlayType == "all") { showSeams = true; } @@ -716,8 +714,7 @@ int aliceVision_main(int argc, char** argv) // load input scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilepath, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS))) + if (!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilepath + "' cannot be read"); return EXIT_FAILURE; @@ -726,9 +723,9 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_TRACE("Sfm data loaded"); std::set uniquePreviousId; - for(const auto pv : sfmData.getViews()) + for (const auto pv : sfmData.getViews()) { - if(pv.second->getImage().getMetadata().find("AliceVision:previousViewId") == pv.second->getImage().getMetadata().end()) + if (pv.second->getImage().getMetadata().find("AliceVision:previousViewId") == pv.second->getImage().getMetadata().end()) { ALICEVISION_LOG_ERROR("You mixed different versions of alicevision."); ALICEVISION_LOG_ERROR("Warped images do not contain the required metadatas."); @@ -748,9 +745,9 @@ int aliceVision_main(int argc, char** argv) rangeIteration = 0; rangeSize = 1; } - if(rangeIteration != -1) + if (rangeIteration != -1) { - if(rangeIteration < 0 || rangeSize < 0) + if (rangeIteration < 0 || rangeSize < 0) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; @@ -758,7 +755,7 @@ int aliceVision_main(int argc, char** argv) const int countIterations = divideRoundUp(oldViewsCount, rangeSize); - if(rangeIteration >= countIterations) + if (rangeIteration >= countIterations) { // nothing to compute for this chunk return EXIT_SUCCESS; @@ -773,11 +770,11 @@ int aliceVision_main(int argc, char** argv) } size_t borderSize; - if(compositerType == "multiband") + if (compositerType == "multiband") { borderSize = 2; } - else if(compositerType == "alpha") + else if (compositerType == "alpha") { borderSize = 0; } @@ -789,7 +786,7 @@ int aliceVision_main(int argc, char** argv) // Build the map of inputs in the final panorama // This is mostly meant to compute overlaps between inputs std::unique_ptr panoramaMap = buildMap(sfmData, warpingFolder, borderSize, forceMinPyramidLevels); - if(viewsCount == 0) + if (viewsCount == 0) { ALICEVISION_LOG_ERROR("No valid views"); return EXIT_FAILURE; @@ -797,13 +794,13 @@ int aliceVision_main(int argc, char** argv) // Distribute more smartly inputs among chunks std::vector> chunks; - if(!panoramaMap->optimizeChunks(chunks, rangeSize)) + if (!panoramaMap->optimizeChunks(chunks, rangeSize)) { ALICEVISION_LOG_ERROR("Can't build chunks"); return EXIT_FAILURE; } - if(rangeIteration >= chunks.size()) + if (rangeIteration >= chunks.size()) { // nothing to compute for this chunk return EXIT_SUCCESS; @@ -815,45 +812,63 @@ int aliceVision_main(int argc, char** argv) if (useTiling) { - for(std::size_t posReference = 0; posReference < chunk.size(); posReference++) + for (std::size_t posReference = 0; posReference < chunk.size(); posReference++) { ALICEVISION_LOG_INFO("processing input region " << posReference + 1 << "/" << chunk.size()); const IndexT viewReference = chunk[posReference]; - if(!sfmData.isPoseAndIntrinsicDefined(viewReference)) + if (!sfmData.isPoseAndIntrinsicDefined(viewReference)) continue; BoundingBox referenceBoundingBox; - if(!panoramaMap->getBoundingBox(referenceBoundingBox, viewReference)) + if (!panoramaMap->getBoundingBox(referenceBoundingBox, viewReference)) { ALICEVISION_LOG_ERROR("Invalid view ID as reference"); return EXIT_FAILURE; } - if(!processImage(*panoramaMap, sfmData, compositerType, warpingFolder, labelsFilepath, outputFolder, - storageDataType, viewReference, referenceBoundingBox, showBorders, showSeams)) + if (!processImage(*panoramaMap, + sfmData, + compositerType, + warpingFolder, + labelsFilepath, + outputFolder, + storageDataType, + viewReference, + referenceBoundingBox, + showBorders, + showSeams)) { succeeded = false; continue; } } } - else + else { BoundingBox referenceBoundingBox; referenceBoundingBox.left = 0; referenceBoundingBox.top = 0; referenceBoundingBox.width = panoramaMap->getWidth(); referenceBoundingBox.height = panoramaMap->getHeight(); - - if(!processImage(*panoramaMap, sfmData, compositerType, warpingFolder, labelsFilepath, outputFolder, - storageDataType, UndefinedIndexT, referenceBoundingBox, showBorders, showSeams)) + + if (!processImage(*panoramaMap, + sfmData, + compositerType, + warpingFolder, + labelsFilepath, + outputFolder, + storageDataType, + UndefinedIndexT, + referenceBoundingBox, + showBorders, + showSeams)) { succeeded = false; } } - if(!succeeded) + if (!succeeded) { return EXIT_FAILURE; } diff --git a/src/software/pipeline/main_panoramaEstimation.cpp b/src/software/pipeline/main_panoramaEstimation.cpp index 5a5407a54d..09ba4a1558 100644 --- a/src/software/pipeline/main_panoramaEstimation.cpp +++ b/src/software/pipeline/main_panoramaEstimation.cpp @@ -112,20 +112,18 @@ int aliceVision_main(int argc, char** argv) "AliceVision panoramaEstimation"); cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } - if(params.eRotationAveragingMethod < sfm::ROTATION_AVERAGING_L1 || - params.eRotationAveragingMethod > sfm::ROTATION_AVERAGING_L2) + if (params.eRotationAveragingMethod < sfm::ROTATION_AVERAGING_L1 || params.eRotationAveragingMethod > sfm::ROTATION_AVERAGING_L2) { ALICEVISION_LOG_ERROR("Rotation averaging method is invalid"); return EXIT_FAILURE; } - if(params.eRelativeRotationMethod < sfm::RELATIVE_ROTATION_FROM_E || - params.eRelativeRotationMethod > sfm::RELATIVE_ROTATION_FROM_H) + if (params.eRelativeRotationMethod < sfm::RELATIVE_ROTATION_FROM_E || params.eRelativeRotationMethod > sfm::RELATIVE_ROTATION_FROM_H) { ALICEVISION_LOG_ERROR("Relative rotation method is invalid"); return EXIT_FAILURE; @@ -133,20 +131,19 @@ int aliceVision_main(int argc, char** argv) // load input SfMData scene sfmData::SfMData inputSfmData; - if(!sfmDataIO::load(inputSfmData, sfmDataFilename, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + if (!sfmDataIO::load(inputSfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); return EXIT_FAILURE; } - if(!inputSfmData.getLandmarks().empty()) + if (!inputSfmData.getLandmarks().empty()) { ALICEVISION_LOG_ERROR("Partially computed SfMData is not currently supported in PanoramaEstimation."); return EXIT_FAILURE; } - if(!inputSfmData.getRigs().empty()) + if (!inputSfmData.getRigs().empty()) { ALICEVISION_LOG_ERROR("Rigs are not currently supported in PanoramaEstimation."); return EXIT_FAILURE; @@ -155,18 +152,17 @@ int aliceVision_main(int argc, char** argv) /* Store the pose c1_R_o of the prior */ sfmData::Poses& initial_poses = inputSfmData.getPoses(); Eigen::Matrix3d c1_R_oprior = Eigen::Matrix3d::Identity(); - if(!initial_poses.empty()) + if (!initial_poses.empty()) { c1_R_oprior = initial_poses.begin()->second.getTransform().rotation(); } // get describerTypes - const std::vector describerTypes = - feature::EImageDescriberType_stringToEnums(describerTypesName); + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); // features reading feature::FeaturesPerView featuresPerView; - if(!sfm::loadFeaturesPerView(featuresPerView, inputSfmData, featuresFolders, describerTypes)) + if (!sfm::loadFeaturesPerView(featuresPerView, inputSfmData, featuresFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Invalid features"); return EXIT_FAILURE; @@ -175,7 +171,7 @@ int aliceVision_main(int argc, char** argv) // matches reading // Load the match file (try to read the two matches file formats). matching::PairwiseMatches pairwiseMatches; - if(!sfm::loadPairwiseMatches(pairwiseMatches, inputSfmData, matchesFolders, describerTypes)) + if (!sfm::loadPairwiseMatches(pairwiseMatches, inputSfmData, matchesFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Unable to load matches files from: " << matchesFolders); return EXIT_FAILURE; @@ -183,7 +179,7 @@ int aliceVision_main(int argc, char** argv) const std::string outDirectory = fs::path(outputSfMDataFilepath).parent_path().string(); - if(!fs::exists(outDirectory)) + if (!fs::exists(outDirectory)) { ALICEVISION_LOG_ERROR("Output folder does not exist: " << outDirectory); return EXIT_FAILURE; @@ -191,8 +187,7 @@ int aliceVision_main(int argc, char** argv) // Panorama reconstruction process aliceVision::system::Timer timer; - sfm::ReconstructionEngine_panorama sfmEngine(inputSfmData, params, outDirectory, - (fs::path(outDirectory) / "sfm_log.html").string()); + sfm::ReconstructionEngine_panorama sfmEngine(inputSfmData, params, outDirectory, (fs::path(outDirectory) / "sfm_log.html").string()); sfmEngine.initRandomSeed(randomSeed); @@ -200,12 +195,12 @@ int aliceVision_main(int argc, char** argv) sfmEngine.setFeaturesProvider(&featuresPerView); sfmEngine.setMatchesProvider(&pairwiseMatches); - if(filterMatches) + if (filterMatches) { sfmEngine.filterMatches(); } - if(!sfmEngine.process()) + if (!sfmEngine.process()) { return EXIT_FAILURE; } @@ -217,16 +212,14 @@ int aliceVision_main(int argc, char** argv) sfmEngine.getSfMData().setAbsolutePath(outputSfMDataFilepath); } - if(refine) + if (refine) { - sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(outDirectory) / "BA_before.abc").string(), - sfmDataIO::ESfMData::ALL); - if(!sfmEngine.adjust()) + sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(outDirectory) / "BA_before.abc").string(), sfmDataIO::ESfMData::ALL); + if (!sfmEngine.adjust()) { return EXIT_FAILURE; } - sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(outDirectory) / "BA_after.abc").string(), - sfmDataIO::ESfMData::ALL); + sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(outDirectory) / "BA_after.abc").string(), sfmDataIO::ESfMData::ALL); } sfmData::SfMData& outSfmData = sfmEngine.getSfMData(); @@ -235,13 +228,13 @@ int aliceVision_main(int argc, char** argv) // Otherwise take the middle view (sorted over time) sfmData::Poses& final_poses = outSfmData.getPoses(); - if(!final_poses.empty()) + if (!final_poses.empty()) { Eigen::Matrix3d ocur_R_oprior = Eigen::Matrix3d::Identity(); - if(initial_poses.empty()) + if (initial_poses.empty()) { - if(useAutomaticReferenceFrame) + if (useAutomaticReferenceFrame) { double S = 1.0; Eigen::Matrix3d R; @@ -254,7 +247,7 @@ int aliceVision_main(int argc, char** argv) std::vector> sorted_views; // Sort views per timestamps - for(auto v : outSfmData.getViews()) + for (auto v : outSfmData.getViews()) { int64_t t = v.second->getImage().getMetadataDateTimestamp(); sorted_views.push_back(std::make_pair(t, v.second->getPoseId())); @@ -275,7 +268,7 @@ int aliceVision_main(int argc, char** argv) ocur_R_oprior = c1_R_ocur.transpose() * c1_R_oprior; } - for(auto& pose : final_poses) + for (auto& pose : final_poses) { geometry::Pose3 p = pose.second.getTransform(); @@ -292,13 +285,11 @@ int aliceVision_main(int argc, char** argv) sfm::generateSfMReport(outSfmData, (fs::path(outDirectory) / "sfm_report.html").string()); // Add offsets to rotations - for(auto& pose : outSfmData.getPoses()) + for (auto& pose : outSfmData.getPoses()) { geometry::Pose3 p = pose.second.getTransform(); - Eigen::Matrix3d matLongitude = - Eigen::AngleAxisd(degreeToRadian(offsetLongitude), Vec3(0, 1, 0)).toRotationMatrix(); - Eigen::Matrix3d matLatitude = - Eigen::AngleAxisd(degreeToRadian(offsetLatitude), Vec3(1, 0, 0)).toRotationMatrix(); + Eigen::Matrix3d matLongitude = Eigen::AngleAxisd(degreeToRadian(offsetLongitude), Vec3(0, 1, 0)).toRotationMatrix(); + Eigen::Matrix3d matLatitude = Eigen::AngleAxisd(degreeToRadian(offsetLatitude), Vec3(1, 0, 0)).toRotationMatrix(); Eigen::Matrix3d newR = p.rotation() * matLongitude * matLatitude; p.setRotation(newR); pose.second.setTransform(p); @@ -309,20 +300,19 @@ int aliceVision_main(int argc, char** argv) { std::set viewsWithObservations; - for(const auto& landmarkIt : outSfmData.getLandmarks()) + for (const auto& landmarkIt : outSfmData.getLandmarks()) { - for(const auto& obsIt : landmarkIt.second.getObservations()) + for (const auto& obsIt : landmarkIt.second.getObservations()) { viewsWithObservations.insert(obsIt.first); } } - ALICEVISION_LOG_INFO("Panorama results:" - << std::endl - << "\t- # input images: " << outSfmData.getViews().size() << std::endl - << "\t- # cameras calibrated: " << outSfmData.getValidViews().size() << std::endl - << "\t- # cameras with observations: " << viewsWithObservations.size() << std::endl - << "\t- # landmarks: " << outSfmData.getLandmarks().size()); + ALICEVISION_LOG_INFO("Panorama results:" << std::endl + << "\t- # input images: " << outSfmData.getViews().size() << std::endl + << "\t- # cameras calibrated: " << outSfmData.getValidViews().size() << std::endl + << "\t- # cameras with observations: " << viewsWithObservations.size() << std::endl + << "\t- # landmarks: " << outSfmData.getLandmarks().size()); } // Export to disk computed scene (data & visualizable results) @@ -330,10 +320,10 @@ int aliceVision_main(int argc, char** argv) sfmDataIO::save(outSfmData, outputSfMDataFilepath, sfmDataIO::ESfMData::ALL); sfmDataIO::save(outSfmData, (fs::path(outDirectory) / "cloud_and_poses.ply").string(), sfmDataIO::ESfMData::ALL); - if(!outputViewsAndPosesFilepath.empty()) + if (!outputViewsAndPosesFilepath.empty()) { - sfmDataIO::save(outSfmData, outputViewsAndPosesFilepath, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); + sfmDataIO::save( + outSfmData, outputViewsAndPosesFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); } return EXIT_SUCCESS; diff --git a/src/software/pipeline/main_panoramaInit.cpp b/src/software/pipeline/main_panoramaInit.cpp index 9742be68fe..65f1aaf067 100644 --- a/src/software/pipeline/main_panoramaInit.cpp +++ b/src/software/pipeline/main_panoramaInit.cpp @@ -28,8 +28,7 @@ using namespace aliceVision; -namespace std -{ +namespace std { std::ostream& operator<<(std::ostream& os, const std::pair& v) { os << v.first << " " << v.second; @@ -45,7 +44,7 @@ std::istream& operator>>(std::istream& in, std::pair& v) v.second = boost::lexical_cast(token); return in; } -} // namespace std +} // namespace std namespace po = boost::program_options; namespace fs = std::filesystem; @@ -56,7 +55,7 @@ namespace pt = boost::property_tree; */ class PyramidFloat { -public: + public: PyramidFloat(size_t width, size_t height, size_t minimal_size) { // minimal_size * 2^n = minside @@ -66,9 +65,8 @@ class PyramidFloat size_t cwidth = width; size_t cheight = height; - for(int i = 0; i <= levels; i++) + for (int i = 0; i <= levels; i++) { - _levels.push_back(image::Image(cwidth, cheight)); cheight /= 2; @@ -80,9 +78,8 @@ class PyramidFloat { // First of all, build pyramid for filtering high frequencies _levels[0] = grayscale_input; - for(int level = 1; level < _levels.size(); level++) + for (int level = 1; level < _levels.size(); level++) { - size_t sw = _levels[level - 1].width(); size_t sh = _levels[level - 1].height(); size_t dw = _levels[level].width(); @@ -104,34 +101,32 @@ class PyramidFloat const image::Image& getLevel(size_t level) const { return _levels[level]; } -private: + private: std::vector> _levels; }; class CircleDetector { -public: + public: CircleDetector() = delete; CircleDetector(size_t width, size_t height, size_t minimal_size) - : _source_width(width) - , _source_height(height) - , _minimal_size(minimal_size) - , _radius(0) - { - } + : _source_width(width), + _source_height(height), + _minimal_size(minimal_size), + _radius(0) + {} void setDebugDirectory(const std::string& dir) { _debugDirectory = dir; } bool appendImage(const image::Image& grayscale_input) { - - if(grayscale_input.width() != _source_width) + if (grayscale_input.width() != _source_width) { return false; } - if(grayscale_input.height() != _source_height) + if (grayscale_input.height() != _source_height) { return false; } @@ -139,7 +134,7 @@ class CircleDetector // Store pyramid for this image, will be processed later // This way we ensure we do not loose intermediate information PyramidFloat pyramid(_source_width, _source_height, _minimal_size); - if(!pyramid.apply(grayscale_input)) + if (!pyramid.apply(grayscale_input)) { return false; } @@ -150,7 +145,7 @@ class CircleDetector bool preprocessLevel(const PyramidFloat& pyramid, size_t pyramid_id, size_t level) { - if(level >= pyramid.countLevels()) + if (level >= pyramid.countLevels()) { return false; } @@ -175,7 +170,7 @@ class CircleDetector // Build a polar image using the estimated circle center image::Image polarImage(max_radius_i, angles_bins, true, 0.0f); image::Image polarImageMask(max_radius_i, angles_bins, true, 0); - if(!buildPolarImage(polarImage, polarImageMask, source, level_centerx, level_centery)) + if (!buildPolarImage(polarImage, polarImageMask, source, level_centerx, level_centery)) { return false; } @@ -186,16 +181,16 @@ class CircleDetector // int max = pyramid.countLevels() - 1; // int diff = max - level; int min_radius = 8; - int radius = min_radius; // * pow(2, diff); + int radius = min_radius; // * pow(2, diff); image::Image gradientImage(max_radius_i, angles_bins); - if(!buildGradientImage(gradientImage, polarImage, polarImageMask, radius)) + if (!buildGradientImage(gradientImage, polarImage, polarImageMask, radius)) { return false; } debugImage(gradientImage, "gradientImage", pyramid_id, level); - if(_gradientImage.width() != gradientImage.width() || _gradientImage.height() != gradientImage.height()) + if (_gradientImage.width() != gradientImage.width() || _gradientImage.height() != gradientImage.height()) { _gradientImage = gradientImage; } @@ -209,7 +204,7 @@ class CircleDetector bool process() { - if(_pyramids.empty()) + if (_pyramids.empty()) { return false; } @@ -221,13 +216,13 @@ class CircleDetector size_t last_level_inliers = 0; int last_valid_level = -1; - for(int current_level = _pyramids[0].countLevels() - 1; current_level > 1; current_level--) + for (int current_level = _pyramids[0].countLevels() - 1; current_level > 1; current_level--) { // Compute gradients size_t current_pyramid_id = 0; - for(PyramidFloat& pyr : _pyramids) + for (PyramidFloat& pyr : _pyramids) { - if(!preprocessLevel(pyr, current_pyramid_id, current_level)) + if (!preprocessLevel(pyr, current_pyramid_id, current_level)) { return false; } @@ -236,7 +231,7 @@ class CircleDetector // Estimate the search area int uncertainty = 50; - if(current_level == _pyramids[0].countLevels() - 1) + if (current_level == _pyramids[0].countLevels() - 1) { uncertainty = std::max(_source_width, _source_height); } @@ -244,7 +239,7 @@ class CircleDetector debugImage(_gradientImage, "globalGradientImage", 0, current_level); // Perform estimation - if(!processLevel(current_level, uncertainty, last_level_inliers)) + if (!processLevel(current_level, uncertainty, last_level_inliers)) { break; } @@ -253,7 +248,7 @@ class CircleDetector } // Check that the circle was detected at some level - if(last_valid_level < 0) + if (last_valid_level < 0) { return false; } @@ -275,7 +270,7 @@ class CircleDetector // Extract maximas of response std::vector selected_points; - for(int y = 0; y < gradients.height(); y++) + for (int y = 0; y < gradients.height(); y++) { const double rangle = double(y) * (2.0 * M_PI / double(gradients.height())); const double cangle = cos(rangle); @@ -286,27 +281,27 @@ class CircleDetector const int end = std::min(gradients.width() - 1, int(level_radius) + uncertainty); // Remove non maximas - for(int x = start; x < end; x++) + for (int x = start; x < end; x++) { - if(gradients(y, x) < gradients(y, x + 1)) + if (gradients(y, x) < gradients(y, x + 1)) { gradients(y, x) = 0.0f; } } // Remove non maximas - for(int x = end; x > start; x--) + for (int x = end; x > start; x--) { - if(gradients(y, x) < gradients(y, x - 1)) + if (gradients(y, x) < gradients(y, x - 1)) { gradients(y, x) = 0.0f; } } // Store maximas - for(int x = start; x <= end; x++) + for (int x = start; x <= end; x++) { - if(gradients(y, x) > 0.0f) + if (gradients(y, x) > 0.0f) { const double nx = level_centerx + cangle * double(x); const double ny = level_centery + sangle * double(x); @@ -316,7 +311,7 @@ class CircleDetector } } - if(selected_points.size() < 3) + if (selected_points.size() < 3) { return false; } @@ -331,13 +326,13 @@ class CircleDetector size_t maxcount = 0; Eigen::Vector3d best_params; - for(int i = 0; i < 10000; i++) + for (int i = 0; i < 10000; i++) { const int id1 = distribution(generator); const int id2 = distribution(generator); const int id3 = distribution(generator); - if(id1 == id2 || id1 == id3 || id2 == id3) + if (id1 == id2 || id1 == id3 || id2 == id3) continue; const Eigen::Vector2d p1 = selected_points[id1]; @@ -345,33 +340,33 @@ class CircleDetector const Eigen::Vector2d p3 = selected_points[id3]; Eigen::Vector3d res; - if(!fitCircle(res, p1, p2, p3)) + if (!fitCircle(res, p1, p2, p3)) { continue; } size_t count = 0; - for(const auto& point : selected_points) + for (const auto& point : selected_points) { const double cx = point(0) - res(0); const double cy = point(1) - res(1); const double r = res(2); const double dist = std::abs(sqrt(cx * cx + cy * cy) - r); - if(dist < 3) + if (dist < 3) { count++; } } - if(count > maxcount) + if (count > maxcount) { maxcount = count; best_params = res; } } - if(maxcount < last_level_inliers) + if (maxcount < last_level_inliers) { return false; } @@ -385,10 +380,10 @@ class CircleDetector Eigen::Vector3d previous_good = best_params; double last_good_error = std::numeric_limits::max(); - for(int iter = 0; iter < 1000; iter++) + for (int iter = 0; iter < 1000; iter++) { double sum_error = 0.0; - for(int i = 0; i < selected_points.size(); i++) + for (int i = 0; i < selected_points.size(); i++) { const double cx = selected_points[i](0) - best_params(0); const double cy = selected_points[i](1) - best_params(1); @@ -396,7 +391,7 @@ class CircleDetector const double dist = pow(sqrt(cx * cx + cy * cy) - r, 2.0); double w = 0.0; - if(dist < c) + if (dist < c) { const double xoc = dist / c; const double hw = 1.0 - xoc * xoc; @@ -406,7 +401,7 @@ class CircleDetector sum_error += w * dist; } - if(sum_error > last_good_error) + if (sum_error > last_good_error) { best_params = previous_good; break; @@ -416,7 +411,7 @@ class CircleDetector Eigen::Matrix3d JtJ = Eigen::Matrix3d::Zero(); Eigen::Vector3d Jte = Eigen::Vector3d::Zero(); - for(auto& pt : selected_points) + for (auto& pt : selected_points) { const double cx = pt(0) - best_params(0); const double cy = pt(1) - best_params(1); @@ -426,7 +421,7 @@ class CircleDetector const double dist = norm - r; double w = 0.0; - if(dist < c) + if (dist < c) { double xoc = dist / c; double hw = 1.0 - xoc * xoc; @@ -434,7 +429,7 @@ class CircleDetector } Eigen::Vector3d J; - if(std::abs(normsq) < 1e-12) + if (std::abs(normsq) < 1e-12) { J.fill(0); J(2) = -w; @@ -446,9 +441,9 @@ class CircleDetector J(2) = -w; } - for(int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { - for(int j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) { JtJ(i, j) += J(i) * J(j); } @@ -468,8 +463,7 @@ class CircleDetector return true; } - bool fitCircle(Eigen::Vector3d& output, const Eigen::Vector2d& pt1, const Eigen::Vector2d& pt2, - const Eigen::Vector2d& pt3) + bool fitCircle(Eigen::Vector3d& output, const Eigen::Vector2d& pt1, const Eigen::Vector2d& pt2, const Eigen::Vector2d& pt3) { /* Solve : @@ -531,13 +525,13 @@ class CircleDetector const double D = 2.0 * (pt1(0) - pt3(0)); const double E = 2.0 * (pt1(1) - pt2(1)); const double F = 2.0 * (pt1(1) - pt3(1)); - if(std::abs(C) < 1e-12) + if (std::abs(C) < 1e-12) return false; const double G = -A / C; const double H = -E / C; - if(std::abs(D * H + F) < 1e-12) + if (std::abs(D * H + F) < 1e-12) return false; const double centery = -(B + D * G) / (D * H + F); @@ -550,20 +544,22 @@ class CircleDetector return true; } - bool buildPolarImage(image::Image& dst, image::Image& dstmask, const image::Image& src, - float center_x, float center_y) + bool buildPolarImage(image::Image& dst, + image::Image& dstmask, + const image::Image& src, + float center_x, + float center_y) { - image::Sampler2d sampler; const size_t count_angles = dst.height(); - for(int angle = 0; angle < count_angles; angle++) + for (int angle = 0; angle < count_angles; angle++) { const double rangle = angle * (2.0 * M_PI / double(count_angles)); const double cangle = cos(rangle); const double sangle = sin(rangle); - for(int amplitude = 0; amplitude < dst.width(); amplitude++) + for (int amplitude = 0; amplitude < dst.width(); amplitude++) { const double x = center_x + cangle * double(amplitude); const double y = center_y + sangle * double(amplitude); @@ -571,9 +567,9 @@ class CircleDetector dst(angle, amplitude) = 0; dstmask(angle, amplitude) = 0; - if(x < 0 || y < 0) + if (x < 0 || y < 0) continue; - if(x >= src.width() || y >= src.height()) + if (x >= src.width() || y >= src.height()) continue; dst(angle, amplitude) = sampler(src, y, x); dstmask(angle, amplitude) = 255; @@ -583,19 +579,18 @@ class CircleDetector return true; } - bool buildGradientImage(image::Image& dst, const image::Image& src, - const image::Image& srcMask, size_t radius_size) + bool buildGradientImage(image::Image& dst, const image::Image& src, const image::Image& srcMask, size_t radius_size) { // Build gradient for x coordinates image dst.fill(0); int kernel_radius = radius_size; - for(int angle = 0; angle < src.height(); angle++) + for (int angle = 0; angle < src.height(); angle++) { int start = radius_size; int end = src.width() - kernel_radius * 2; - for(int amplitude = start; amplitude < end; amplitude++) + for (int amplitude = start; amplitude < end; amplitude++) { dst(angle, amplitude) = 0.0; @@ -604,18 +599,18 @@ class CircleDetector unsigned char valid = 255; - for(int dx = -kernel_radius; dx < 0; dx++) + for (int dx = -kernel_radius; dx < 0; dx++) { sum_inside += src(angle, amplitude + dx); valid &= srcMask(angle, amplitude + dx); } - for(int dx = 1; dx <= kernel_radius * 2; dx++) + for (int dx = 1; dx <= kernel_radius * 2; dx++) { sum_outside += src(angle, amplitude + dx); valid &= srcMask(angle, amplitude + dx); } - if(valid) + if (valid) { dst(angle, amplitude) = std::max(0.0f, (sum_inside - sum_outside)); } @@ -635,20 +630,18 @@ class CircleDetector double getCircleRadius() const { return _radius; } - template + template void debugImage(const image::Image& toSave, const std::string& name, int pyramid_id, int level) { // Only export debug image if there is a debug output folder defined. - if(_debugDirectory.empty()) + if (_debugDirectory.empty()) return; - fs::path filepath = - fs::path(_debugDirectory) / - (name + "_" + std::to_string(pyramid_id) + "_" + std::to_string(level) + ".exr"); + fs::path filepath = fs::path(_debugDirectory) / (name + "_" + std::to_string(pyramid_id) + "_" + std::to_string(level) + ".exr"); image::writeImage(filepath.string(), toSave, image::ImageWriteOptions()); } -private: + private: std::vector _pyramids; image::Image _gradientImage; std::string _debugDirectory; @@ -665,14 +658,11 @@ class CircleDetector /** * @brief Utility function for resizing an image. */ -void resample(image::Image& output, - const image::Image& input) +void resample(image::Image& output, const image::Image& input) { - const oiio::ImageBuf inBuf(oiio::ImageSpec(input.width(), input.height(), 3, oiio::TypeDesc::FLOAT), - const_cast(input.data())); - - oiio::ImageBuf outBuf(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), - (image::RGBfColor*)output.data()); + const oiio::ImageBuf inBuf(oiio::ImageSpec(input.width(), input.height(), 3, oiio::TypeDesc::FLOAT), const_cast(input.data())); + + oiio::ImageBuf outBuf(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), (image::RGBfColor*)output.data()); oiio::ImageBufAlgo::resample(outBuf, inBuf, false); } @@ -680,15 +670,11 @@ void resample(image::Image& output, /** * @brief Utility function for rotating an image given its orientation metadata. */ -void applyOrientation(image::Image& output, - const image::Image& input, - sfmData::EEXIFOrientation orientation) +void applyOrientation(image::Image& output, const image::Image& input, sfmData::EEXIFOrientation orientation) { - const oiio::ImageBuf inBuf(oiio::ImageSpec(input.width(), input.height(), 3, oiio::TypeDesc::FLOAT), - const_cast(input.data())); - - oiio::ImageBuf outBuf(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), - (image::RGBfColor*)output.data()); + const oiio::ImageBuf inBuf(oiio::ImageSpec(input.width(), input.height(), 3, oiio::TypeDesc::FLOAT), const_cast(input.data())); + + oiio::ImageBuf outBuf(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), (image::RGBfColor*)output.data()); switch (orientation) { @@ -750,15 +736,16 @@ int orientedHeight(const Contact& contact) } bool buildContactSheetImage(image::Image& output, - const std::map>& contactSheetInfo, int contactSheetItemMaxSize) + const std::map>& contactSheetInfo, + int contactSheetItemMaxSize) { const int space = 10; // Compute ratio for resizing inputs int maxdim = 0; - for(const auto& rowpair : contactSheetInfo) + for (const auto& rowpair : contactSheetInfo) { - for(const auto& item : rowpair.second) + for (const auto& item : rowpair.second) { maxdim = std::max(maxdim, orientedWidth(item.second)); maxdim = std::max(maxdim, orientedHeight(item.second)); @@ -769,12 +756,12 @@ bool buildContactSheetImage(image::Image& output, // Compute output size int totalHeight = space; int maxWidth = 0; - for(const auto& rowpair : contactSheetInfo) + for (const auto& rowpair : contactSheetInfo) { int rowHeight = 0; int rowWidth = space; - for(const auto& item : rowpair.second) + for (const auto& item : rowpair.second) { int resizedHeight = int(ratioResize * double(orientedHeight(item.second))); int resizedWidth = int(ratioResize * double(orientedWidth(item.second))); @@ -787,7 +774,7 @@ bool buildContactSheetImage(image::Image& output, maxWidth = std::max(maxWidth, rowWidth); } - if(totalHeight == 0 || maxWidth == 0) + if (totalHeight == 0 || maxWidth == 0) { return false; } @@ -795,13 +782,13 @@ bool buildContactSheetImage(image::Image& output, int rowCount = 0; int posY = space; output = image::Image(maxWidth, totalHeight, true); - for(const auto& rowpair : contactSheetInfo) + for (const auto& rowpair : contactSheetInfo) { ALICEVISION_LOG_INFO("Build contact sheet row " << rowCount + 1 << "/" << contactSheetInfo.size()); int rowHeight = 0; int rowWidth = space; - for(const auto& item : rowpair.second) + for (const auto& item : rowpair.second) { int resizedHeight = int(ratioResize * double(orientedHeight(item.second))); int resizedWidth = int(ratioResize * double(orientedWidth(item.second))); @@ -814,7 +801,7 @@ bool buildContactSheetImage(image::Image& output, image::Image rowOutput(rowWidth, rowHeight, true); int posX = space; - for(const auto& item : rowpair.second) + for (const auto& item : rowpair.second) { int rawResizedHeight = int(ratioResize * double(item.second.height)); int rawResizedWidth = int(ratioResize * double(item.second.width)); @@ -914,20 +901,20 @@ int main(int argc, char* argv[]) cmdline.add(requiredParams); cmdline.add(motorizedHeadParams); cmdline.add(fisheyeParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } - if(inputAngleString == "rotate90") + if (inputAngleString == "rotate90") { additionalAngle = -M_PI_2; } - else if(inputAngleString == "rotate180") + else if (inputAngleString == "rotate180") { additionalAngle = -M_PI; } - else if(inputAngleString == "rotate270") + else if (inputAngleString == "rotate270") { additionalAngle = M_PI_2; } @@ -935,7 +922,7 @@ int main(int argc, char* argv[]) std::map> contactSheetInfo; sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::load(sfmData, sfmInputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmInputDataFilepath << "' cannot be read."); return EXIT_FAILURE; @@ -946,12 +933,10 @@ int main(int argc, char* argv[]) std::map rotations; boost::to_lower(initializeCameras); - if(initializeCameras == "no") + if (initializeCameras == "no") {} + else if (initializeCameras == "file" || (initializeCameras.empty() && !externalInfoFilepath.empty())) { - } - else if(initializeCameras == "file" || (initializeCameras.empty() && !externalInfoFilepath.empty())) - { - if(externalInfoFilepath.empty()) + if (externalInfoFilepath.empty()) { ALICEVISION_LOG_ERROR("Init cameras from file, but path is not set."); return EXIT_FAILURE; @@ -963,7 +948,7 @@ int main(int argc, char* argv[]) { pt::read_xml(externalInfoFilepath, tree); } - catch(...) + catch (...) { ALICEVISION_CERR("Error parsing input file"); return EXIT_FAILURE; @@ -973,10 +958,10 @@ int main(int argc, char* argv[]) // Get a set of unique ids std::set uniqueIds; - for(const auto it : shoot) + for (const auto it : shoot) { int id = it.second.get(".id"); - if(uniqueIds.find(id) != uniqueIds.end()) + if (uniqueIds.find(id) != uniqueIds.end()) { ALICEVISION_CERR("Multiple xml attributes with a same id: " << id); return EXIT_FAILURE; @@ -989,16 +974,16 @@ int main(int argc, char* argv[]) // note that set is ordered automatically. int pos = 0; std::map idToRank; - for(const auto id : uniqueIds) + for (const auto id : uniqueIds) { idToRank[id] = pos; pos++; } // Group shoots by "rows" (common pitch) assuming they are acquired row by row with a common pitch - if(buildContactSheet) + if (buildContactSheet) { - for(const auto it : shoot) + for (const auto it : shoot) { int id = it.second.get(".id"); int bracket = it.second.get(".bracket"); @@ -1007,7 +992,7 @@ int main(int argc, char* argv[]) const double yaw_degree = it.second.get("position..yaw"); const double pitch_degree = it.second.get("position..pitch"); - int ipitch_degree = -int(pitch_degree); // minus to be sure rows are going top to bottom + int ipitch_degree = -int(pitch_degree); // minus to be sure rows are going top to bottom int iyaw_degree = int(yaw_degree); // Store also the yaw to be able to sort left to right @@ -1015,7 +1000,7 @@ int main(int argc, char* argv[]) } } - for(const auto it : shoot) + for (const auto it : shoot) { int id = it.second.get(".id"); int bracket = it.second.get(".bracket"); @@ -1034,21 +1019,20 @@ int main(int argc, char* argv[]) const Eigen::AngleAxis Mroll(roll, Eigen::Vector3d::UnitZ()); const Eigen::AngleAxis Mimage(additionalAngle - M_PI_2, Eigen::Vector3d::UnitZ()); - const Eigen::Matrix3d oRc = Myaw.toRotationMatrix() * Mpitch.toRotationMatrix() * - Mroll.toRotationMatrix() * Mimage.toRotationMatrix(); + const Eigen::Matrix3d oRc = + Myaw.toRotationMatrix() * Mpitch.toRotationMatrix() * Mroll.toRotationMatrix() * Mimage.toRotationMatrix(); rotations[rank] = oRc.transpose(); } - if(sfmData.getViews().size() != rotations.size()) + if (sfmData.getViews().size() != rotations.size()) { - ALICEVISION_LOG_ERROR( - "The input SfMData has not the same number of views than the config file (sfmData views:" - << sfmData.getViews().size() << ", config file rotations: " << rotations.size() << ")."); + ALICEVISION_LOG_ERROR("The input SfMData has not the same number of views than the config file (sfmData views:" + << sfmData.getViews().size() << ", config file rotations: " << rotations.size() << ")."); return EXIT_FAILURE; } } - else if(boost::algorithm::contains(initializeCameras, "horizontal")) + else if (boost::algorithm::contains(initializeCameras, "horizontal")) { constexpr double zenithPitch = 0.5 * boost::math::constants::pi(); const Eigen::AngleAxis zenithMpitch(zenithPitch, Eigen::Vector3d::UnitX()); @@ -1056,21 +1040,20 @@ int main(int argc, char* argv[]) const Eigen::Matrix3d oRzenith = zenithMpitch.toRotationMatrix() * zenithMroll.toRotationMatrix(); const bool withZenith = boost::algorithm::contains(initializeCameras, "zenith"); - if(initializeCameras == "zenith+horizontal") + if (initializeCameras == "zenith+horizontal") { ALICEVISION_LOG_TRACE("Add zenith first"); rotations[rotations.size()] = oRzenith.transpose(); } const std::size_t nbHorizontalViews = sfmData.getViews().size() - int(withZenith); - for(int x = 0; x < nbHorizontalViews; ++x) + for (int x = 0; x < nbHorizontalViews; ++x) { double yaw = 0; - if(nbHorizontalViews > 1) + if (nbHorizontalViews > 1) { // Vary horizontally between -180 and +180 deg - yaw = (yawCW ? 1.0 : -1.0) * x * 2.0 * boost::math::constants::pi() / - double(nbHorizontalViews); + yaw = (yawCW ? 1.0 : -1.0) * x * 2.0 * boost::math::constants::pi() / double(nbHorizontalViews); } const Eigen::AngleAxis Myaw(yaw, Eigen::Vector3d::UnitY()); @@ -1081,15 +1064,15 @@ int main(int argc, char* argv[]) ALICEVISION_LOG_TRACE("Add rotation: yaw=" << yaw); rotations[rotations.size()] = oRc.transpose(); } - if(initializeCameras == "horizontal+zenith") + if (initializeCameras == "horizontal+zenith") { ALICEVISION_LOG_TRACE("Add zenith"); rotations[rotations.size()] = oRzenith.transpose(); } } - else if(initializeCameras == "spherical" || (initializeCameras.empty() && !nbViewsPerLineString.empty())) + else if (initializeCameras == "spherical" || (initializeCameras.empty() && !nbViewsPerLineString.empty())) { - if(nbViewsPerLineString.empty()) + if (nbViewsPerLineString.empty()) { ALICEVISION_LOG_ERROR("Init cameras from Sperical, but 'nbViewsPerLine' is not set."); return EXIT_FAILURE; @@ -1101,9 +1084,9 @@ int main(int argc, char* argv[]) std::vector nbViewsPerLine; int nbAutoSize = 0; int sum = 0; - for(const std::string& nbViewsStr : nbViewsStrPerLine) + for (const std::string& nbViewsStr : nbViewsStrPerLine) { - if(nbViewsStr == "*") + if (nbViewsStr == "*") { nbViewsPerLine.push_back(-1); ++nbAutoSize; @@ -1112,7 +1095,7 @@ int main(int argc, char* argv[]) { int v = boost::lexical_cast(nbViewsStr); nbViewsPerLine.push_back(v); - if(v == -1) + if (v == -1) { ++nbAutoSize; } @@ -1122,49 +1105,47 @@ int main(int argc, char* argv[]) } } } - if(sum > totalNbViews) + if (sum > totalNbViews) { ALICEVISION_LOG_ERROR("The input SfMData has less cameras declared than the number of cameras declared " "in the expression (sfmData views:" << sfmData.getViews().size() << ", expression sum: " << sum << ")."); return EXIT_FAILURE; } - if(nbAutoSize > 0) + if (nbAutoSize > 0) { std::replace(nbViewsPerLine.begin(), nbViewsPerLine.end(), -1, (totalNbViews - sum) / nbAutoSize); } - if(nbViewsPerLine.empty()) + if (nbViewsPerLine.empty()) { // If no expression assume that it is a pure rotation around one axis nbViewsPerLine.push_back(totalNbViews); } const std::size_t newSum = std::accumulate(nbViewsPerLine.begin(), nbViewsPerLine.end(), 0); - if(newSum != totalNbViews) + if (newSum != totalNbViews) { - ALICEVISION_LOG_ERROR( - "The number of cameras in the input SfMData does not match with the number of cameras declared " - "in the expression (sfmData views:" - << sfmData.getViews().size() << ", expression sum: " << newSum << ")."); + ALICEVISION_LOG_ERROR("The number of cameras in the input SfMData does not match with the number of cameras declared " + "in the expression (sfmData views:" + << sfmData.getViews().size() << ", expression sum: " << newSum << ")."); return EXIT_FAILURE; } int i = 0; - for(int y = 0; y < nbViewsPerLine.size(); ++y) + for (int y = 0; y < nbViewsPerLine.size(); ++y) { double pitch = 0; - if(nbViewsPerLine.size() > 1) + if (nbViewsPerLine.size() > 1) { // Vary vertically between -90 and +90 deg - pitch = (-0.5 * boost::math::constants::pi()) + - y * boost::math::constants::pi() / double(nbViewsPerLine.size()); + pitch = (-0.5 * boost::math::constants::pi()) + y * boost::math::constants::pi() / double(nbViewsPerLine.size()); } const int nbViews = nbViewsPerLine[y]; - for(int x = 0; x < nbViews; ++x) + for (int x = 0; x < nbViews; ++x) { double yaw = 0; - if(nbViews > 1) + if (nbViews > 1) { // Vary horizontally between -180 and +180 deg yaw = (yawCW ? 1.0 : -1.0) * x * 2.0 * boost::math::constants::pi() / double(nbViews); @@ -1175,11 +1156,9 @@ int main(int argc, char* argv[]) const Eigen::AngleAxis Mpitch(pitch, Eigen::Vector3d::UnitX()); const Eigen::AngleAxis Mroll(roll + additionalAngle, Eigen::Vector3d::UnitZ()); - const Eigen::Matrix3d oRc = - Myaw.toRotationMatrix() * Mpitch.toRotationMatrix() * Mroll.toRotationMatrix(); + const Eigen::Matrix3d oRc = Myaw.toRotationMatrix() * Mpitch.toRotationMatrix() * Mroll.toRotationMatrix(); - ALICEVISION_LOG_TRACE("Add rotation: yaw=" << yaw << ", pitch=" << pitch << ", roll=" << roll - << "."); + ALICEVISION_LOG_TRACE("Add rotation: yaw=" << yaw << ", pitch=" << pitch << ", roll=" << roll << "."); rotations[i++] = oRc.transpose(); } } @@ -1187,11 +1166,11 @@ int main(int argc, char* argv[]) std::map contacts; - if(!rotations.empty()) + if (!rotations.empty()) { ALICEVISION_LOG_TRACE("Apply rotations from nbViewsPerLine expressions: " << nbViewsPerLineString << "."); - if(rotations.size() != sfmData.getViews().size()) + if (rotations.size() != sfmData.getViews().size()) { ALICEVISION_LOG_ERROR("The number of cameras in the input SfMData does not match with the number of " "rotations to apply (sfmData nb views:" @@ -1203,7 +1182,7 @@ int main(int argc, char* argv[]) // The xml file describe rotations for views which are not correlated with AliceVision views. // We assume that the order of the xml view ids correspond to the lexicographic order of the image names. std::vector> namesWithRank; - for(const auto& v : sfmData.getViews()) + for (const auto& v : sfmData.getViews()) { fs::path path_image(v.second->getImage().getImagePath()); namesWithRank.push_back(std::make_pair(path_image.stem().string(), v.first)); @@ -1211,12 +1190,12 @@ int main(int argc, char* argv[]) std::sort(namesWithRank.begin(), namesWithRank.end()); // If we are trying to build a contact sheet - if(contactSheetInfo.size() > 0) + if (contactSheetInfo.size() > 0) { // Fill information in contact sheet - for(auto& rowpair : contactSheetInfo) + for (auto& rowpair : contactSheetInfo) { - for(auto& item : rowpair.second) + for (auto& item : rowpair.second) { int rank = item.second.rank; IndexT viewId = namesWithRank[rank].second; @@ -1231,16 +1210,16 @@ int main(int argc, char* argv[]) } image::Image contactSheetImage; - if(buildContactSheetImage(contactSheetImage, contactSheetInfo, contactSheetItemMaxSize)) + if (buildContactSheetImage(contactSheetImage, contactSheetInfo, contactSheetItemMaxSize)) { - image::writeImage( - (fs::path(sfmOutputDataFilepath).parent_path() / "contactSheetImage.jpg").string(), - contactSheetImage, image::ImageWriteOptions()); + image::writeImage((fs::path(sfmOutputDataFilepath).parent_path() / "contactSheetImage.jpg").string(), + contactSheetImage, + image::ImageWriteOptions()); } } size_t index = 0; - for(const auto& item_rotation : rotations) + for (const auto& item_rotation : rotations) { IndexT viewIdx = namesWithRank[index].second; const sfmData::View& v = sfmData.getView(viewIdx); @@ -1266,7 +1245,7 @@ int main(int argc, char* argv[]) const Eigen::Matrix3d viewRotation = Morientation.toRotationMatrix().transpose() * item_rotation.second; - if(viewRotation.trace() != 0) + if (viewRotation.trace() != 0) { sfmData::CameraPose pose(geometry::Pose3(viewRotation, Eigen::Vector3d::Zero())); sfmData.setAbsolutePose(viewIdx, pose); @@ -1276,26 +1255,22 @@ int main(int argc, char* argv[]) } } - if(useFisheye) + if (useFisheye) { sfmData::Intrinsics& intrinsics = sfmData.getIntrinsics(); - for(auto& intrinsic_pair : intrinsics) + for (auto& intrinsic_pair : intrinsics) { std::shared_ptr& intrinsic = intrinsic_pair.second; - std::shared_ptr intrinsicSO = - std::dynamic_pointer_cast(intrinsic); - std::shared_ptr equidistant = - std::dynamic_pointer_cast(intrinsic); + std::shared_ptr intrinsicSO = std::dynamic_pointer_cast(intrinsic); + std::shared_ptr equidistant = std::dynamic_pointer_cast(intrinsic); - if(intrinsicSO != nullptr && equidistant == nullptr) + if (intrinsicSO != nullptr && equidistant == nullptr) { - ALICEVISION_LOG_INFO("Replace intrinsic " << intrinsic_pair.first << " of type " - << intrinsic->getTypeStr() + ALICEVISION_LOG_INFO("Replace intrinsic " << intrinsic_pair.first << " of type " << intrinsic->getTypeStr() << " to an Equidistant camera model."); // convert non-Equidistant intrinsics to Equidistant std::shared_ptr newEquidistant = - std::dynamic_pointer_cast( - camera::createIntrinsic(camera::EINTRINSIC::EQUIDISTANT_CAMERA_RADIAL3)); + std::dynamic_pointer_cast(camera::createIntrinsic(camera::EINTRINSIC::EQUIDISTANT_CAMERA_RADIAL3)); newEquidistant->copyFrom(*intrinsicSO); // "radius" and "center" will be set later from the input parameters in another loop @@ -1309,44 +1284,44 @@ int main(int argc, char* argv[]) { int equidistantCount = 0; - if(useFisheye && estimateFisheyeCircle) + if (useFisheye && estimateFisheyeCircle) { - if(sfmData.getIntrinsics().size() != 1) + if (sfmData.getIntrinsics().size() != 1) { ALICEVISION_LOG_ERROR("Only one intrinsic allowed (" << sfmData.getIntrinsics().size() << " found)"); return EXIT_FAILURE; } std::shared_ptr intrinsic = sfmData.getIntrinsics().begin()->second; - if(!intrinsic) + if (!intrinsic) { ALICEVISION_LOG_ERROR("No valid intrinsic"); return EXIT_FAILURE; } - if(camera::isEquidistant(intrinsic->getType())) + if (camera::isEquidistant(intrinsic->getType())) { CircleDetector detector(intrinsic->w(), intrinsic->h(), 256); - if(debugFisheyeCircleEstimation) + if (debugFisheyeCircleEstimation) { fs::path path(sfmOutputDataFilepath); detector.setDebugDirectory(path.parent_path().string()); } - for(const auto& v : sfmData.getViews()) + for (const auto& v : sfmData.getViews()) { // Read original image image::Image grayscale; image::readImage(v.second->getImage().getImagePath(), grayscale, image::EImageColorSpace::SRGB); const bool res = detector.appendImage(grayscale); - if(!res) + if (!res) { ALICEVISION_LOG_ERROR("Image is incompatible with fisheye detection"); return EXIT_FAILURE; } } - if(!detector.process()) + if (!detector.process()) { ALICEVISION_LOG_ERROR("Failed to find circle"); return EXIT_FAILURE; @@ -1368,24 +1343,21 @@ int main(int argc, char* argv[]) } sfmData::Intrinsics& intrinsics = sfmData.getIntrinsics(); - for(const auto& intrinsic_pair : intrinsics) + for (const auto& intrinsic_pair : intrinsics) { std::shared_ptr intrinsic = intrinsic_pair.second; - std::shared_ptr equidistant = - std::dynamic_pointer_cast(intrinsic); - if(!equidistant) + std::shared_ptr equidistant = std::dynamic_pointer_cast(intrinsic); + if (!equidistant) { // skip non equidistant cameras continue; } - ALICEVISION_LOG_INFO("Update Equidistant camera intrinsic " << intrinsic_pair.first - << " with center and offset."); + ALICEVISION_LOG_INFO("Update Equidistant camera intrinsic " << intrinsic_pair.first << " with center and offset."); equidistant->setCircleCenterX(double(equidistant->w()) / 2.0 + fisheyeCenterOffset(0)); equidistant->setCircleCenterY(double(equidistant->h()) / 2.0 + fisheyeCenterOffset(1)); - equidistant->setCircleRadius(fisheyeRadius / 100.0 * 0.5 * - std::min(double(equidistant->w()), double(equidistant->h()))); + equidistant->setCircleRadius(fisheyeRadius / 100.0 * 0.5 * std::min(double(equidistant->w()), double(equidistant->h()))); ++equidistantCount; } @@ -1393,7 +1365,7 @@ int main(int argc, char* argv[]) } ALICEVISION_LOG_INFO("Export SfM: " << sfmOutputDataFilepath); - if(!sfmDataIO::save(sfmData, sfmOutputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::save(sfmData, sfmOutputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The output SfMData file '" << sfmOutputDataFilepath << "' cannot be write."); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_panoramaMerging.cpp b/src/software/pipeline/main_panoramaMerging.cpp index 8d2700e3e9..cd253232c2 100644 --- a/src/software/pipeline/main_panoramaMerging.cpp +++ b/src/software/pipeline/main_panoramaMerging.cpp @@ -67,9 +67,8 @@ int aliceVision_main(int argc, char** argv) "Use tiling for compositing."); // clang-format on - CmdLine cmdline( - "Merges all the image tiles created by the PanoramaCompositing.\n" - "AliceVision panoramaMerging"); + CmdLine cmdline("Merges all the image tiles created by the PanoramaCompositing.\n" + "AliceVision panoramaMerging"); cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -83,7 +82,7 @@ int aliceVision_main(int argc, char** argv) // load input scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS))) + if (!sfmDataIO::load(sfmData, sfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_ERROR("The input file '" + sfmDataFilepath + "' cannot be read"); return EXIT_FAILURE; @@ -91,15 +90,14 @@ int aliceVision_main(int argc, char** argv) bool clampHalf = false; oiio::TypeDesc typeColor = oiio::TypeDesc::FLOAT; - if (storageDataType == image::EStorageDataType::Half || storageDataType == image::EStorageDataType::HalfFinite) + if (storageDataType == image::EStorageDataType::Half || storageDataType == image::EStorageDataType::HalfFinite) { typeColor = oiio::TypeDesc::HALF; - if (storageDataType == image::EStorageDataType::HalfFinite) + if (storageDataType == image::EStorageDataType::HalfFinite) { clampHalf = true; } - } - + } std::vector> sourcesList; if (useTiling) @@ -107,7 +105,7 @@ int aliceVision_main(int argc, char** argv) for (auto viewItem : sfmData.getViews()) { IndexT viewId = viewItem.first; - if(!sfmData.isPoseAndIntrinsicDefined(viewId)) + if (!sfmData.isPoseAndIntrinsicDefined(viewId)) { continue; } @@ -159,13 +157,13 @@ int aliceVision_main(int argc, char** argv) { offsetX += panoramaWidth; } - + int left = std::floor(double(offsetX) / double(tileSize)); int top = std::floor(double(offsetY) / double(tileSize)); int right = std::ceil(double(offsetX + width - 1) / double(tileSize)); int bottom = std::ceil(double(offsetY + height - 1) / double(tileSize)); - //Loop over all tiles of this input + // Loop over all tiles of this input for (int ty = top; ty <= bottom; ty++) { for (int tx = left; tx <= right; tx++) @@ -174,11 +172,15 @@ int aliceVision_main(int argc, char** argv) int bright = (tx + 1) * tileSize - 1; int btop = ty * tileSize; int bbottom = (ty + 1) * tileSize - 1; - - if (bleft < offsetX) continue; - if (bright >= offsetX + width) continue; - if (btop < offsetY) continue; - if (bbottom >= offsetY + height) continue; + + if (bleft < offsetX) + continue; + if (bright >= offsetX + width) + continue; + if (btop < offsetY) + continue; + if (bbottom >= offsetY + height) + continue; std::pair pos; pos.first = tx; @@ -195,22 +197,21 @@ int aliceVision_main(int argc, char** argv) std::unique_ptr panorama = oiio::ImageOutput::create(outputPanoramaPath); oiio::ImageSpec spec_panorama(panoramaWidth, panoramaHeight, 4, typeColor); spec_panorama.tile_width = tileSize; - spec_panorama.tile_height = tileSize; - spec_panorama.attribute("compression", "zips"); + spec_panorama.tile_height = tileSize; + spec_panorama.attribute("compression", "zips"); spec_panorama.attribute("openexr:lineOrder", "randomY"); metadata["openexr:lineOrder"] = "randomY"; - spec_panorama.extra_attribs = metadata; - - panorama->open(outputPanoramaPath, spec_panorama); + spec_panorama.extra_attribs = metadata; + panorama->open(outputPanoramaPath, spec_panorama); struct TileInfo { bool filed = false; size_t used = 0; std::shared_ptr> tileContent = nullptr; - }; + }; image::Image tiles(tileCountWidth, tileCountHeight, true, {false, 0, nullptr}); for (auto sourceItem : sourcesList) @@ -227,7 +228,7 @@ int aliceVision_main(int argc, char** argv) { offsetX += panoramaWidth; } - + image::Image source; image::readImage(imagePath, source, image::EImageColorSpace::NO_CONVERSION); @@ -236,16 +237,16 @@ int aliceVision_main(int argc, char** argv) int right = std::ceil(double(offsetX + width - 1) / double(tileSize)); int bottom = std::ceil(double(offsetY + height - 1) / double(tileSize)); - //Loop over all tiles of this input + // Loop over all tiles of this input for (int ty = top; ty <= bottom; ty++) { - if (ty < 0 || ty >= tileCountHeight) + if (ty < 0 || ty >= tileCountHeight) { continue; } int y = ty * tileSize; - + for (int iter_tx = left; iter_tx <= right; iter_tx++) { int tx = iter_tx; @@ -254,17 +255,17 @@ int aliceVision_main(int argc, char** argv) if (tx >= tileCountWidth) { tx = tx - tileCountWidth; - offset_loop = - panoramaWidth; + offset_loop = -panoramaWidth; } - - if (tx < 0 || tx >= tileCountWidth) + + if (tx < 0 || tx >= tileCountWidth) { continue; } int x = tx * tileSize; - //If this view is not registered as the main view, ignore + // If this view is not registered as the main view, ignore std::pair pos; pos.first = tx; pos.second = ty; @@ -282,10 +283,10 @@ int aliceVision_main(int argc, char** argv) continue; } - if (ti.tileContent == nullptr) { - ti.tileContent = std::make_shared>(tileSize, tileSize, true, image::RGBAfColor(0.0f, 0.0f, 0.0f, 0.0f)); + ti.tileContent = + std::make_shared>(tileSize, tileSize, true, image::RGBAfColor(0.0f, 0.0f, 0.0f, 0.0f)); } for (int py = 0; py < tileSize; py++) @@ -297,7 +298,6 @@ int aliceVision_main(int argc, char** argv) { continue; } - for (int px = 0; px < tileSize; px++) { @@ -320,8 +320,8 @@ int aliceVision_main(int argc, char** argv) continue; } - //Check if the pixel is already written - image::RGBAfColor & dpix = ti.tileContent->operator()(py, px); + // Check if the pixel is already written + image::RGBAfColor& dpix = ti.tileContent->operator()(py, px); image::RGBAfColor pix = source(source_y, source_x); if (pix.a() > 0.9) { @@ -336,9 +336,9 @@ int aliceVision_main(int argc, char** argv) } } - if (ti.used >= tileSize*tileSize) + if (ti.used >= tileSize * tileSize) { - panorama->write_tile (tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); + panorama->write_tile(tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); ti.tileContent = nullptr; ti.filed = true; continue; @@ -349,12 +349,16 @@ int aliceVision_main(int argc, char** argv) int btop = ty * tileSize; int bbottom = (ty + 1) * tileSize - 1; - if (bleft < offsetX) continue; - if (bright >= offsetX + width) continue; - if (btop < offsetY) continue; - if (bbottom >= offsetY + height) continue; + if (bleft < offsetX) + continue; + if (bright >= offsetX + width) + continue; + if (btop < offsetY) + continue; + if (bbottom >= offsetY + height) + continue; - panorama->write_tile (tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); + panorama->write_tile(tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); ti.tileContent = nullptr; ti.filed = true; } @@ -372,23 +376,21 @@ int aliceVision_main(int argc, char** argv) { continue; } - + if (ti.tileContent) - { - panorama->write_tile (tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); + { + panorama->write_tile(tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, ti.tileContent->data()); } else - { - panorama->write_tile (tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, vide.data()); + { + panorama->write_tile(tx * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, vide.data()); } ti.filed = true; } } - panorama->close(); - return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_panoramaPostProcessing.cpp b/src/software/pipeline/main_panoramaPostProcessing.cpp index e868ae5f92..f4468f28d7 100644 --- a/src/software/pipeline/main_panoramaPostProcessing.cpp +++ b/src/software/pipeline/main_panoramaPostProcessing.cpp @@ -4,7 +4,6 @@ // 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/. - // Image #include @@ -29,7 +28,6 @@ #include #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 2 @@ -40,7 +38,7 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -bool downscaleTriangle(image::Image & smaller, const image::Image & source) +bool downscaleTriangle(image::Image& smaller, const image::Image& source) { int sw = source.width(); int sh = source.height(); @@ -54,16 +52,20 @@ bool downscaleTriangle(image::Image & smaller, const image::I int di = i * 2; int pi = di - 1; int ni = di + 1; - if (pi < 0) pi = ni; - if (ni >= sh) ni = pi; + if (pi < 0) + pi = ni; + if (ni >= sh) + ni = pi; for (int j = 0; j < nw; j++) { int dj = j * 2; int pj = dj - 1; int nj = dj + 1; - if (pj < 0) pj = nj; - if (nj >= sw) nj = pj; + if (pj < 0) + pj = nj; + if (nj >= sw) + nj = pj; image::RGBAfColor c1 = (source(pi, pj) * 0.25f) + (source(di, pj) * 0.5f) + (source(ni, pj) * 0.25f); image::RGBAfColor c2 = (source(pi, dj) * 0.25f) + (source(di, dj) * 0.5f) + (source(ni, dj) * 0.25f); @@ -85,10 +87,10 @@ bool downscaleTriangle(image::Image & smaller, const image::I return true; } -bool readFullTile(image::Image & output, std::unique_ptr & input, int tx, int ty) +bool readFullTile(image::Image& output, std::unique_ptr& input, int tx, int ty) { - const oiio::ImageSpec &inputSpec = input->spec(); - const int tileSize = inputSpec.tile_width; + const oiio::ImageSpec& inputSpec = input->spec(); + const int tileSize = inputSpec.tile_width; const int width = inputSpec.width; const int height = inputSpec.height; @@ -96,11 +98,7 @@ bool readFullTile(image::Image & output, std::unique_ptr & buf, - int txLeft, - int xOutput, int xBuf, int sliceWidth) -> bool - { + auto readTilePartial = [&](image::Image& buf, int txLeft, int xOutput, int xBuf, int sliceWidth) -> bool { if (!input->read_tile(txLeft * tileSize, ty * tileSize, 0, oiio::TypeDesc::FLOAT, buf.data())) { return false; @@ -133,12 +131,14 @@ bool readFullTile(image::Image & output, std::unique_ptr buf(tileSize, tileSize); // Load left part tile - if (!readTilePartial(buf, txLeft, 0, rightside, leftside)) return false; + if (!readTilePartial(buf, txLeft, 0, rightside, leftside)) + return false; // Load right part tile to complete filling output if (rightside > 0) { - if (!readTilePartial(buf, txLeft + 1, leftside, 0, rightside)) return false; + if (!readTilePartial(buf, txLeft + 1, leftside, 0, rightside)) + return false; } } else if (tx == countWidth - 1) @@ -150,12 +150,14 @@ bool readFullTile(image::Image & output, std::unique_ptr buf(tileSize, tileSize); // Load last tile (which may be incomplete) - if (!readTilePartial(buf, tx, 0, 0, leftside)) return false; + if (!readTilePartial(buf, tx, 0, 0, leftside)) + return false; // Load first tile to complete filling output if (rightside > 0) { - if (!readTilePartial(buf, 0, leftside, 0, rightside)) return false; + if (!readTilePartial(buf, 0, leftside, 0, rightside)) + return false; } } else if (tx == countWidth) @@ -171,12 +173,14 @@ bool readFullTile(image::Image & output, std::unique_ptr buf(tileSize, tileSize); // Load left part tile - if (!readTilePartial(buf, txLeft, 0, rightside, leftside)) return false; + if (!readTilePartial(buf, txLeft, 0, rightside, leftside)) + return false; // Load right part tile to complete filling output if (rightside > 0) { - if (!readTilePartial(buf, txLeft + 1, leftside, 0, rightside)) return false; + if (!readTilePartial(buf, txLeft + 1, leftside, 0, rightside)) + return false; } } else if (tx >= 0 && tx < countWidth - 1) @@ -191,11 +195,16 @@ bool readFullTile(image::Image & output, std::unique_ptr& inputImage, image::EImageColorSpace fromColorSpace, image::EImageColorSpace toColorSpace, image::DCPProfile dcpProf, image::DCPProfile::Triple neutral) +void colorSpaceTransform(image::Image& inputImage, + image::EImageColorSpace fromColorSpace, + image::EImageColorSpace toColorSpace, + image::DCPProfile dcpProf, + image::DCPProfile::Triple neutral) { const int width = inputImage.width(); const int tileSize = inputImage.height(); - oiio::ImageBuf inBuf = oiio::ImageBuf(oiio::ImageSpec(width, tileSize, 4, oiio::TypeDesc::FLOAT), const_cast(inputImage.data())); + oiio::ImageBuf inBuf = + oiio::ImageBuf(oiio::ImageSpec(width, tileSize, 4, oiio::TypeDesc::FLOAT), const_cast(inputImage.data())); oiio::ImageBuf* outBuf = &inBuf; if (fromColorSpace == image::EImageColorSpace::NO_CONVERSION) @@ -205,7 +214,8 @@ void colorSpaceTransform(image::Image& inputImage, image::EIm fromColorSpace = image::EImageColorSpace::ACES2065_1; } - oiio::ImageBuf colorspaceBuf = oiio::ImageBuf(oiio::ImageSpec(width, tileSize, 4, oiio::TypeDesc::FLOAT), const_cast(inputImage.data())); // buffer for image colorspace modification + oiio::ImageBuf colorspaceBuf = oiio::ImageBuf(oiio::ImageSpec(width, tileSize, 4, oiio::TypeDesc::FLOAT), + const_cast(inputImage.data())); // buffer for image colorspace modification if ((fromColorSpace == toColorSpace) || (toColorSpace == image::EImageColorSpace::NO_CONVERSION)) { // Do nothing. Note that calling imageAlgo::colorconvert() will copy the source buffer @@ -219,14 +229,20 @@ void colorSpaceTransform(image::Image& inputImage, image::EIm throw std::runtime_error("ALICEVISION_ROOT is not defined, OCIO config file cannot be accessed."); } oiio::ColorConfig colorConfig(colorConfigPath); - oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, - EImageColorSpace_enumToOIIOString(fromColorSpace), - EImageColorSpace_enumToOIIOString(toColorSpace), true, "", "", &colorConfig); + oiio::ImageBufAlgo::colorconvert(colorspaceBuf, + *outBuf, + EImageColorSpace_enumToOIIOString(fromColorSpace), + EImageColorSpace_enumToOIIOString(toColorSpace), + true, + "", + "", + &colorConfig); outBuf = &colorspaceBuf; } else { - oiio::ImageBufAlgo::colorconvert(colorspaceBuf, *outBuf, EImageColorSpace_enumToOIIOString(fromColorSpace), EImageColorSpace_enumToOIIOString(toColorSpace)); + oiio::ImageBufAlgo::colorconvert( + colorspaceBuf, *outBuf, EImageColorSpace_enumToOIIOString(fromColorSpace), EImageColorSpace_enumToOIIOString(toColorSpace)); outBuf = &colorspaceBuf; } @@ -292,16 +308,16 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - //Open input panorama + // Open input panorama std::unique_ptr panoramaInput = oiio::ImageInput::open(inputPanoramaPath); if (!panoramaInput) { return EXIT_FAILURE; } - //Get information about input panorama - const oiio::ImageSpec &inputSpec = panoramaInput->spec(); - const int tileWidth = inputSpec.tile_width; + // Get information about input panorama + const oiio::ImageSpec& inputSpec = panoramaInput->spec(); + const int tileWidth = inputSpec.tile_width; const int tileHeight = inputSpec.tile_height; image::EImageColorSpace fromColorSpace = image::EImageColorSpace_stringToEnum(inputSpec.get_string_attribute("AliceVision:ColorSpace", "linear")); @@ -315,13 +331,14 @@ int aliceVision_main(int argc, char** argv) std::map imageMetadata = image::getMapFromMetadata(image::readImageMetadata(inputPanoramaPath, tmpWidth, tmpHeight)); image::DCPProfile dcpProf; - image::DCPProfile::Triple neutral = { 1.0,1.0,1.0 }; + image::DCPProfile::Triple neutral = {1.0, 1.0, 1.0}; if (fromColorSpace == image::EImageColorSpace::NO_CONVERSION) { // load DCP metadata dcpProf.Load(imageMetadata); - std::string cam_mul = map_has_non_empty_value(imageMetadata, "raw:cam_mul") ? imageMetadata.at("raw:cam_mul") : imageMetadata.at("AliceVision:raw:cam_mul"); + std::string cam_mul = + map_has_non_empty_value(imageMetadata, "raw:cam_mul") ? imageMetadata.at("raw:cam_mul") : imageMetadata.at("AliceVision:raw:cam_mul"); std::vector v_mult; size_t last = 0; size_t next = 1; @@ -338,7 +355,7 @@ int aliceVision_main(int argc, char** argv) } } - //Create output panorama + // Create output panorama std::unique_ptr panoramaOutput = oiio::ImageOutput::create(outputPanoramaPath); oiio::ImageSpec outputSpec(inputSpec); outputSpec.tile_width = 0; @@ -365,10 +382,10 @@ int aliceVision_main(int argc, char** argv) } } } - outputSpec.attribute("compression", compressionMethod_str); + outputSpec.attribute("compression", compressionMethod_str); outputSpec.extra_attribs.remove("openexr:lineOrder"); - outputSpec.attribute("AliceVision:ColorSpace",image::EImageColorSpace_enumToString(outputColorSpace)); + outputSpec.attribute("AliceVision:ColorSpace", image::EImageColorSpace_enumToString(outputColorSpace)); if (!panoramaOutput->open(outputPanoramaPath, outputSpec)) { @@ -376,7 +393,7 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - //Compute various sizes + // Compute various sizes const int tileSize = tileWidth; const int width = inputSpec.width; const int height = inputSpec.height; @@ -396,11 +413,8 @@ int aliceVision_main(int argc, char** argv) int previewCurrentRow = 0; // Create image outputs for downscaled panorama levels - const int nbLevels = exportLevels ? - static_cast(std::min( - std::floor(std::log2(tileSize)), - std::ceil(std::log2(width) - std::log2(lastLevelMaxSize)))) - : 0; + const int nbLevels = + exportLevels ? static_cast(std::min(std::floor(std::log2(tileSize)), std::ceil(std::log2(width) - std::log2(lastLevelMaxSize)))) : 0; std::vector> levelOutputs; ALICEVISION_LOG_INFO("Number of downscaled panorama levels to generate: " << nbLevels); @@ -409,8 +423,8 @@ int aliceVision_main(int argc, char** argv) for (int levelIdx = 1; levelIdx <= nbLevels; ++levelIdx) { const int levelWidth = width / (1 << levelIdx); - - fs::path levelPath = outputFSPath.parent_path() / (outputFSPath.stem().string() + "_level_" + std::to_string(levelWidth) + ".exr"); + + fs::path levelPath = outputFSPath.parent_path() / (outputFSPath.stem().string() + "_level_" + std::to_string(levelWidth) + ".exr"); levelOutputs.push_back(std::move(oiio::ImageOutput::create(levelPath.string()))); oiio::ImageSpec levelSpec(outputSpec); @@ -419,7 +433,7 @@ int aliceVision_main(int argc, char** argv) levelSpec.full_width /= (1 << levelIdx); levelSpec.full_height /= (1 << levelIdx); - if (!levelOutputs[levelIdx-1]->open(levelPath.string(), levelSpec)) + if (!levelOutputs[levelIdx - 1]->open(levelPath.string(), levelSpec)) { ALICEVISION_LOG_ERROR("Impossible to write downscaled panorama at level " << levelIdx); return EXIT_FAILURE; @@ -429,27 +443,27 @@ int aliceVision_main(int argc, char** argv) if (fillHoles) { ALICEVISION_LOG_INFO("Reduce image (" << width << "x" << height << ")"); - - //Downscale such that each tile == 1 pixel + + // Downscale such that each tile == 1 pixel image::Image smallImage(countWidth, countHeight); bool error = false; - #pragma omp parallel for +#pragma omp parallel for for (int ty = 0; ty < countHeight; ty++) { - //Build subimage - image::Image region(rowSize * tileSize, 3 * tileSize); - image::Image tile(tileWidth, tileWidth); - + // Build subimage + image::Image region(rowSize * tileSize, 3 * tileSize); + image::Image tile(tileWidth, tileWidth); + for (int ry = 0; ry < 3; ry++) { int dy = ry - 1; - + for (int rx = 0; rx < rowSize; rx++) { int dx = rx - 1; - if (!readFullTile(tile, panoramaInput, dx, ty + dy)) + if (!readFullTile(tile, panoramaInput, dx, ty + dy)) { ALICEVISION_LOG_ERROR("Invalid tile"); error = true; @@ -493,7 +507,7 @@ int aliceVision_main(int argc, char** argv) { return EXIT_FAILURE; } - + ALICEVISION_LOG_INFO("Process fill holes for reduced image (" << smallImage.width() << "x" << smallImage.height() << ")"); image::Image smallFiled(smallImage.width(), smallImage.height()); oiio::ImageBuf inBuf(oiio::ImageSpec(smallImage.width(), smallImage.height(), 4, oiio::TypeDesc::FLOAT), smallImage.data()); @@ -501,24 +515,24 @@ int aliceVision_main(int argc, char** argv) oiio::ImageBufAlgo::fillholes_pushpull(outBuf, inBuf); ALICEVISION_LOG_INFO("Upscaling and filling holes"); - //Process one full row of tiles each iteration + // Process one full row of tiles each iteration for (int ty = 0; ty < countHeight; ty++) { int ybegin = ty * tileSize; int yend = (ty + 1) * tileSize - 1; - - //Build subimage + + // Build subimage image::Image region(tileSize * rowSize, tileSize * 3); image::Image subFiled(rowSize, 3, true, image::RGBAfColor(0.0f, 0.0f, 0.0f, 0.0f)); image::Image final(width, tileSize, true, image::RGBAfColor(0.0f, 0.0f, 0.0f, 0.0f)); - - //Build a region + + // Build a region for (int ry = 0; ry < 3; ry++) { int dy = ry - 1; int cy = ty + dy; - - #pragma omp parallel for + +#pragma omp parallel for for (int rx = 0; rx < rowSize; rx++) { int dx = rx - 1; @@ -552,11 +566,11 @@ int aliceVision_main(int argc, char** argv) } } - //First level is original image + // First level is original image std::vector> pyramid; pyramid.push_back(region); - //Build pyramid for tile + // Build pyramid for tile int cs = tileSize * rowSize; while (cs != rowSize) { @@ -568,32 +582,36 @@ int aliceVision_main(int argc, char** argv) cs = ns; } - pyramid[pyramid.size() - 1] = subFiled; + pyramid[pyramid.size() - 1] = subFiled; for (int level = pyramid.size() - 2; level >= 0; level--) { - const image::Image & source = pyramid[level + 1]; - - image::Image & dest = pyramid[level]; + const image::Image& source = pyramid[level + 1]; + + image::Image& dest = pyramid[level]; - #pragma omp parallel for +#pragma omp parallel for for (int i = 0; i < source.height(); i++) { int di = i * 2; int pi = i - 1; int ni = i + 1; - if (pi < 0) pi = -pi; - if (ni >= source.height()) ni = i; - + if (pi < 0) + pi = -pi; + if (ni >= source.height()) + ni = i; + for (int j = 0; j < source.width(); j++) { int dj = j * 2; int pj = j - 1; int nj = j + 1; - if (pj < 0) pj = nj; - if (nj >= source.width()) nj = j; + if (pj < 0) + pj = nj; + if (nj >= source.width()) + nj = j; image::RGBAfColor c11 = source(pi, pj); image::RGBAfColor c12 = source(pi, j); @@ -617,22 +635,25 @@ int aliceVision_main(int argc, char** argv) image::RGBAfColor d12 = f1 * 0.25f + f2 * 0.75f; image::RGBAfColor d22 = f3 * 0.25f + f2 * 0.75f; - - //Here we normalize without checking for alpha validity, - //as we assume it is not possible to have zero alpha if the algorithm did its job. - - if (dest(di, dj).a() != 1.0f) dest(di, dj) = d11 / d11.a(); - if (dest(di, dj + 1).a() != 1.0f) dest(di, dj + 1) = d12 / d12.a(); - if (dest(di + 1, dj).a() != 1.0f) dest(di + 1, dj) = d21 / d21.a(); - if (dest(di + 1, dj + 1).a() != 1.0f) dest(di + 1, dj + 1) = d22 / d22.a(); + // Here we normalize without checking for alpha validity, + // as we assume it is not possible to have zero alpha if the algorithm did its job. + + if (dest(di, dj).a() != 1.0f) + dest(di, dj) = d11 / d11.a(); + if (dest(di, dj + 1).a() != 1.0f) + dest(di, dj + 1) = d12 / d12.a(); + if (dest(di + 1, dj).a() != 1.0f) + dest(di + 1, dj) = d21 / d21.a(); + if (dest(di + 1, dj + 1).a() != 1.0f) + dest(di + 1, dj + 1) = d22 / d22.a(); } } } - const image::Image & finalTile = pyramid[0]; + const image::Image& finalTile = pyramid[0]; final.block(0, 0, tileSize, width) = finalTile.block(tileSize, tileSize, tileSize, width); - //Fill preview image + // Fill preview image while (previewCurrentRow < previewImage.rows()) { double finalY = ratioPreview * double(previewCurrentRow) - ybegin; @@ -658,19 +679,19 @@ int aliceVision_main(int argc, char** argv) // Write downscaled panorama levels for (int levelIdx = 1; levelIdx <= nbLevels; ++levelIdx) { - image::Image & levelTile = pyramid[levelIdx]; + image::Image& levelTile = pyramid[levelIdx]; const int levelTileSize = tileSize / (1 << levelIdx); const int levelWidth = width / (1 << levelIdx); image::Image level(levelWidth, levelTileSize); level.block(0, 0, levelTileSize, levelWidth) = levelTile.block(levelTileSize, levelTileSize, levelTileSize, levelWidth); colorSpaceTransform(level, fromColorSpace, outputColorSpace, dcpProf, neutral); - levelOutputs[levelIdx-1]->write_scanlines(ty * levelTileSize, (ty + 1) * levelTileSize, 0, oiio::TypeDesc::FLOAT, level.data()); + levelOutputs[levelIdx - 1]->write_scanlines(ty * levelTileSize, (ty + 1) * levelTileSize, 0, oiio::TypeDesc::FLOAT, level.data()); } } } - else + else { - //Process one full row of tiles each iteration + // Process one full row of tiles each iteration for (int ty = 0; ty < countHeight; ty++) { int ybegin = ty * tileSize; @@ -680,13 +701,13 @@ int aliceVision_main(int argc, char** argv) image::Image region(tileSize * rowSize, tileSize * 3); image::Image final(width, tileSize, true, image::RGBAfColor(0.0f, 0.0f, 0.0f, 0.0f)); - //Build a region + // Build a region for (int ry = 0; ry < 3; ry++) { int dy = ry - 1; int cy = ty + dy; - - #pragma omp parallel for + +#pragma omp parallel for for (int rx = 0; rx < rowSize; rx++) { int dx = rx - 1; @@ -703,11 +724,11 @@ int aliceVision_main(int argc, char** argv) } } - //First level is original image + // First level is original image std::vector> pyramid; pyramid.push_back(region); - //Build pyramid for tile + // Build pyramid for tile int cs = tileSize * rowSize; while (cs != rowSize) { @@ -719,10 +740,10 @@ int aliceVision_main(int argc, char** argv) cs = ns; } - const image::Image & finalTile = pyramid[0]; + const image::Image& finalTile = pyramid[0]; final.block(0, 0, tileSize, width) = finalTile.block(tileSize, tileSize, tileSize, width); - //Fill preview image + // Fill preview image while (previewCurrentRow < previewImage.rows()) { double finalY = ratioPreview * double(previewCurrentRow) - ybegin; @@ -748,13 +769,13 @@ int aliceVision_main(int argc, char** argv) // Write downscaled panorama levels for (int levelIdx = 1; levelIdx <= nbLevels; ++levelIdx) { - image::Image & levelTile = pyramid[levelIdx]; + image::Image& levelTile = pyramid[levelIdx]; const int levelTileSize = tileSize / (1 << levelIdx); const int levelWidth = width / (1 << levelIdx); image::Image level(levelWidth, levelTileSize); level.block(0, 0, levelTileSize, levelWidth) = levelTile.block(levelTileSize, levelTileSize, levelTileSize, levelWidth); colorSpaceTransform(level, fromColorSpace, outputColorSpace, dcpProf, neutral); - levelOutputs[levelIdx-1]->write_scanlines(ty * levelTileSize, (ty + 1) * levelTileSize, 0, oiio::TypeDesc::FLOAT, level.data()); + levelOutputs[levelIdx - 1]->write_scanlines(ty * levelTileSize, (ty + 1) * levelTileSize, 0, oiio::TypeDesc::FLOAT, level.data()); } } } @@ -763,7 +784,7 @@ int aliceVision_main(int argc, char** argv) panoramaOutput->close(); for (int levelIdx = 1; levelIdx <= nbLevels; ++levelIdx) { - levelOutputs[levelIdx-1]->close(); + levelOutputs[levelIdx - 1]->close(); } if (outputPanoramaPreviewPath != "") diff --git a/src/software/pipeline/main_panoramaPrepareImages.cpp b/src/software/pipeline/main_panoramaPrepareImages.cpp index 5e63f0c507..b7cc635a43 100644 --- a/src/software/pipeline/main_panoramaPrepareImages.cpp +++ b/src/software/pipeline/main_panoramaPrepareImages.cpp @@ -42,7 +42,7 @@ Eigen::Matrix3d getRotationForCode(int code) { Eigen::Matrix3d R_metadata = Eigen::Matrix3d::Identity(); - switch(code) + switch (code) { case 0: R_metadata = Eigen::Matrix3d::Identity(); @@ -94,39 +94,39 @@ int aliceVision_main(int argc, char* argv[]) // Read sfm data sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmInputDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmInputDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmInputDataFilename << "' cannot be read."); return EXIT_FAILURE; } size_t countImages = sfmData.getViews().size(); - if(countImages == 0) + if (countImages == 0) { ALICEVISION_LOG_ERROR("The input SfMData contains no input !"); return EXIT_FAILURE; } // Make sure there is only one kind of image in dataset - if(sfmData.getIntrinsics().size() > 2) + if (sfmData.getIntrinsics().size() > 2) { ALICEVISION_LOG_ERROR("Only one intrinsic allowed (" << sfmData.getIntrinsics().size() << " found)"); return EXIT_FAILURE; } - if(sfmData.getIntrinsics().size() > 1) + if (sfmData.getIntrinsics().size() > 1) { const unsigned int refw = sfmData.getIntrinsics().begin()->second->w(); const unsigned int refh = sfmData.getIntrinsics().begin()->second->h(); - for(const auto& item : sfmData.getIntrinsics()) + for (const auto& item : sfmData.getIntrinsics()) { - if(item.second->w() == refw && item.second->h() == refh) + if (item.second->w() == refw && item.second->h() == refh) { continue; } - if(item.second->w() == refh && item.second->h() == refw) + if (item.second->w() == refh && item.second->h() == refw) { continue; } @@ -142,9 +142,9 @@ int aliceVision_main(int argc, char* argv[]) // Read the flip values from metadata, or create it if necessary std::map count_flips; - for(auto& v : views) + for (auto& v : views) { - if(v.second->getImage().hasMetadata({"raw:flip"})) + if (v.second->getImage().hasMetadata({"raw:flip"})) { std::string str = v.second->getImage().getMetadata({"raw:flip"}); int flip_code = std::stoi(str); @@ -155,7 +155,7 @@ int aliceVision_main(int argc, char* argv[]) // Add fake raw:flip if needed std::size_t width = v.second->getImage().getWidth(); std::size_t height = v.second->getImage().getHeight(); - if(width > height) + if (width > height) { v.second->getImage().addMetadata("raw:flip", "0"); count_flips[0]++; @@ -168,19 +168,19 @@ int aliceVision_main(int argc, char* argv[]) } } - if(count_flips.size() <= 1 && sfmData.getIntrinsics().size() == 2) + if (count_flips.size() <= 1 && sfmData.getIntrinsics().size() == 2) { - ALICEVISION_LOG_ERROR("Only one intrinsic allowed (" << sfmData.getIntrinsics().size() - << " found, count flips: " << count_flips.size() << ")"); + ALICEVISION_LOG_ERROR("Only one intrinsic allowed (" << sfmData.getIntrinsics().size() << " found, count flips: " << count_flips.size() + << ")"); return EXIT_FAILURE; } // Decide which rotation is the most used int max_flip = -1; size_t max_count = 0; - for(const auto& item : count_flips) + for (const auto& item : count_flips) { - if(item.second > max_count) + if (item.second > max_count) { max_flip = item.first; max_count = item.second; @@ -189,16 +189,16 @@ int aliceVision_main(int argc, char* argv[]) // Get the intrinsic of the best flip IndexT refIntrinsic = UndefinedIndexT; - for(const auto& v : views) + for (const auto& v : views) { // Now, all views have "raw:flip" const std::string str = v.second->getImage().getMetadata({"raw:flip"}); const int flip_code = std::stoi(str); - if(flip_code == max_flip) + if (flip_code == max_flip) { const IndexT intid = v.second->getIntrinsicId(); - if(refIntrinsic != intid && refIntrinsic != UndefinedIndexT) + if (refIntrinsic != intid && refIntrinsic != UndefinedIndexT) { ALICEVISION_LOG_ERROR("Multiple intrinsics for the correct flip code !"); return EXIT_FAILURE; @@ -209,26 +209,26 @@ int aliceVision_main(int argc, char* argv[]) } // Remove all other intrinsics - for(sfmData::Intrinsics::iterator it = sfmData.getIntrinsics().begin(); it != sfmData.getIntrinsics().end(); ++it) + for (sfmData::Intrinsics::iterator it = sfmData.getIntrinsics().begin(); it != sfmData.getIntrinsics().end(); ++it) { - if(it->first != refIntrinsic) + if (it->first != refIntrinsic) { it = sfmData.getIntrinsics().erase(it); } } - for(auto& v : views) + for (auto& v : views) { // Now, all views have raw:flip const std::string str = v.second->getImage().getMetadata({"raw:flip"}); const int flip_code = std::stoi(str); - if(flip_code == max_flip) + if (flip_code == max_flip) { continue; } - if(refIntrinsic != v.second->getIntrinsicId()) + if (refIntrinsic != v.second->getIntrinsicId()) { v.second->setIntrinsicId(refIntrinsic); } @@ -238,7 +238,7 @@ int aliceVision_main(int argc, char* argv[]) Eigen::Vector3d axis = aa.axis(); double angle = aa.angle(); - if(axis(2) < -0.99) + if (axis(2) < -0.99) { axis(2) = 1.0; angle = -angle; @@ -260,41 +260,36 @@ int aliceVision_main(int argc, char* argv[]) options.colorProfileFileName = v.second->getImage().getColorProfileFileName(); image::readImage(v.second->getImage().getImagePath(), originalImage, options); - oiio::ImageBuf bufInput( - oiio::ImageSpec(originalImage.width(), originalImage.height(), 3, oiio::TypeDesc::FLOAT), - originalImage.data()); + oiio::ImageBuf bufInput(oiio::ImageSpec(originalImage.width(), originalImage.height(), 3, oiio::TypeDesc::FLOAT), originalImage.data()); // Find the correct operation to perform bool validTransform = false; - if(axis(2) > 0.99) + if (axis(2) > 0.99) { - if(std::abs(angle - M_PI_2) < 1e-4) + if (std::abs(angle - M_PI_2) < 1e-4) { validTransform = true; output.resize(originalImage.height(), originalImage.width()); - oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), - output.data()); + oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), output.data()); oiio::ImageBufAlgo::rotate90(bufOutput, bufInput); } - else if(std::abs(angle + M_PI_2) < 1e-4) + else if (std::abs(angle + M_PI_2) < 1e-4) { validTransform = true; output.resize(originalImage.height(), originalImage.width()); - oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), - output.data()); + oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), output.data()); oiio::ImageBufAlgo::rotate90(bufOutput, bufInput); } - else if(std::abs(std::abs(angle) - M_PI) < 1e-4) + else if (std::abs(std::abs(angle) - M_PI) < 1e-4) { validTransform = true; output.resize(originalImage.width(), originalImage.height()); - oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), - output.data()); + oiio::ImageBuf bufOutput(oiio::ImageSpec(output.width(), output.height(), 3, oiio::TypeDesc::FLOAT), output.data()); oiio::ImageBufAlgo::rotate180(bufOutput, bufInput); } } - if(validTransform == false) + if (validTransform == false) { ALICEVISION_LOG_ERROR("Unrecognized intermediate transformation : "); ALICEVISION_LOG_ERROR(axis.transpose()); @@ -309,7 +304,7 @@ int aliceVision_main(int argc, char* argv[]) } // Export output sfmData - if(!sfmDataIO::save(sfmData, sfmOutputDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::save(sfmData, sfmOutputDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("Can not save output sfm file at " << sfmOutputDataFilename); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_panoramaSeams.cpp b/src/software/pipeline/main_panoramaSeams.cpp index 0055d175b4..1c2511d337 100644 --- a/src/software/pipeline/main_panoramaSeams.cpp +++ b/src/software/pipeline/main_panoramaSeams.cpp @@ -4,7 +4,6 @@ // 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 // Input and geometry @@ -43,7 +42,11 @@ namespace po = boost::program_options; namespace bpt = boost::property_tree; namespace fs = std::filesystem; -bool computeWTALabels(image::Image & labels, const std::vector> & views, const std::string & inputPath, const std::pair & panoramaSize, int downscale) +bool computeWTALabels(image::Image& labels, + const std::vector>& views, + const std::string& inputPath, + const std::pair& panoramaSize, + int downscale) { ALICEVISION_LOG_INFO("Estimating initial labels for panorama"); @@ -79,7 +82,7 @@ bool computeWTALabels(image::Image & labels, const std::vector & labels, const std::vector& labels, const std::vector>& views, - const std::string& inputPath, std::pair& panoramaSize, int smallestViewScale, +bool computeGCLabels(image::Image& labels, + const std::vector>& views, + const std::string& inputPath, + std::pair& panoramaSize, + int smallestViewScale, int downscale) { ALICEVISION_LOG_INFO("Estimating smart seams for panorama"); @@ -101,7 +107,7 @@ bool computeGCLabels(image::Image& labels, const std::vector& labels, const std::vector> paths_per_view; - for (auto & iter : fs::directory_iterator(p)) - { + for (auto& iter : fs::directory_iterator(p)) + { if (!fs::is_regular_file(iter)) { continue; @@ -250,21 +255,21 @@ int aliceVision_main(int argc, char** argv) IndexT index = std::stol(m[1].str()); paths_per_view[index].push_back(iter.path().stem().string()); - } + } auto copyviews = sfmData.getViews(); sfmData.getViews().clear(); for (auto pv : copyviews) { - std::vector & images = paths_per_view[pv.first]; + std::vector& images = paths_per_view[pv.first]; if (images.empty()) continue; for (int idx = 0; idx < images.size(); idx++) { std::shared_ptr newView(pv.second->clone()); - + newView->getImage().addMetadata("AliceVision:previousViewId", std::to_string(pv.first)); newView->getImage().addMetadata("AliceVision:imageCounter", std::to_string(idx)); newView->getImage().addMetadata("AliceVision:warpedPath", images[idx]); @@ -274,7 +279,7 @@ int aliceVision_main(int argc, char** argv) sfmData.getViews().emplace(newIndex, newView); } } - + sfmDataIO::save(sfmData, sfmOutDataFilepath, sfmDataIO::ESfMData::ALL); int tileSize; @@ -292,19 +297,19 @@ int aliceVision_main(int argc, char** argv) panoramaSize.second = metadata.find("AliceVision:panoramaHeight")->get_int(); tileSize = metadata.find("AliceVision:tileSize")->get_int(); - if(panoramaSize.first == 0 || panoramaSize.second == 0) + if (panoramaSize.first == 0 || panoramaSize.second == 0) { ALICEVISION_LOG_ERROR("The output panorama size is empty."); return EXIT_FAILURE; } - if(tileSize == 0) + if (tileSize == 0) { ALICEVISION_LOG_ERROR("no information on tileSize"); return EXIT_FAILURE; } - if(maxPanoramaWidth > 0 && panoramaSize.first > maxPanoramaWidth) + if (maxPanoramaWidth > 0 && panoramaSize.first > maxPanoramaWidth) { downscaleFactor = divideRoundUp(panoramaSize.first, maxPanoramaWidth); } @@ -318,12 +323,12 @@ int aliceVision_main(int argc, char** argv) // Get a list of views ordered by their image scale int smallestScale = 10000; std::vector> views; - for (auto it : sfmData.getViews()) + for (auto it : sfmData.getViews()) { auto view = it.second; IndexT viewId = view->getViewId(); - if(!sfmData.isPoseAndIntrinsicDefined(view.get())) + if (!sfmData.isPoseAndIntrinsicDefined(view.get())) { // skip unreconstructed views continue; @@ -348,7 +353,7 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO(views.size() << " views to process"); image::Image labels; - if(!computeWTALabels(labels, views, warpingFolder, panoramaSize, downscaleFactor)) + if (!computeWTALabels(labels, views, warpingFolder, panoramaSize, downscaleFactor)) { ALICEVISION_LOG_ERROR("Error computing initial labels"); return EXIT_FAILURE; @@ -356,15 +361,14 @@ int aliceVision_main(int argc, char** argv) if (useGraphCut) { - if(!computeGCLabels(labels, views, warpingFolder, panoramaSize, smallestScale, downscaleFactor)) + if (!computeGCLabels(labels, views, warpingFolder, panoramaSize, smallestScale, downscaleFactor)) { ALICEVISION_LOG_ERROR("Error computing graph cut labels"); return EXIT_FAILURE; } } - image::writeImage(outputLabels, labels, - image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); + image::writeImage(outputLabels, labels, image::ImageWriteOptions().toColorSpace(image::EImageColorSpace::NO_CONVERSION)); return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_panoramaWarping.cpp b/src/software/pipeline/main_panoramaWarping.cpp index a0ff875a8c..2b2f36a441 100644 --- a/src/software/pipeline/main_panoramaWarping.cpp +++ b/src/software/pipeline/main_panoramaWarping.cpp @@ -34,8 +34,7 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -bool computeOptimalPanoramaSize(std::pair& optimalSize, const sfmData::SfMData& sfmData, - const float ratioUpscale) +bool computeOptimalPanoramaSize(std::pair& optimalSize, const sfmData::SfMData& sfmData, const float ratioUpscale) { // We use a small panorama for probing optimalSize.first = 512; @@ -43,12 +42,11 @@ bool computeOptimalPanoramaSize(std::pair& optimalSize, const sfmData: // Loop over views to estimate best scale std::vector scales; - for(auto& viewIt : sfmData.getViews()) + for (auto& viewIt : sfmData.getViews()) { - // Ignore non positionned views const sfmData::View& view = *viewIt.second.get(); - if(!sfmData.isPoseAndIntrinsicDefined(&view)) + if (!sfmData.isPoseAndIntrinsicDefined(&view)) { continue; } @@ -59,19 +57,19 @@ bool computeOptimalPanoramaSize(std::pair& optimalSize, const sfmData: // Compute coarse bounding box BoundingBox coarseBbox; - if(!computeCoarseBB(coarseBbox, optimalSize, camPose, intrinsic)) + if (!computeCoarseBB(coarseBbox, optimalSize, camPose, intrinsic)) { continue; } CoordinatesMap map; - if(!map.build(optimalSize, camPose, intrinsic, coarseBbox)) + if (!map.build(optimalSize, camPose, intrinsic, coarseBbox)) { continue; } double scale; - if(!map.computeScale(scale, ratioUpscale)) + if (!map.computeScale(scale, ratioUpscale)) { continue; } @@ -79,7 +77,7 @@ bool computeOptimalPanoramaSize(std::pair& optimalSize, const sfmData: scales.push_back(scale); } - if(scales.empty()) + if (scales.empty()) { return false; } @@ -144,7 +142,7 @@ int aliceVision_main(int argc, char** argv) "AliceVision panoramaWarping"); cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -155,10 +153,10 @@ int aliceVision_main(int argc, char** argv) bool clampHalf = false; oiio::TypeDesc typeColor = oiio::TypeDesc::FLOAT; - if(storageDataType == image::EStorageDataType::Half || storageDataType == image::EStorageDataType::HalfFinite) + if (storageDataType == image::EStorageDataType::Half || storageDataType == image::EStorageDataType::HalfFinite) { typeColor = oiio::TypeDesc::HALF; - if(storageDataType == image::EStorageDataType::HalfFinite) + if (storageDataType == image::EStorageDataType::HalfFinite) { clampHalf = true; } @@ -169,8 +167,7 @@ int aliceVision_main(int argc, char** argv) // Camera intrinsics // Camera extrinsics sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); return EXIT_FAILURE; @@ -178,28 +175,28 @@ int aliceVision_main(int argc, char** argv) // Order views by their image names for easier debugging std::vector> viewsOrderedByName; - for(auto& viewIt : sfmData.getViews()) + for (auto& viewIt : sfmData.getViews()) { viewsOrderedByName.push_back(viewIt.second); } - std::sort(viewsOrderedByName.begin(), viewsOrderedByName.end(), - [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool - { - if(a == nullptr || b == nullptr) + std::sort(viewsOrderedByName.begin(), + viewsOrderedByName.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) -> bool { + if (a == nullptr || b == nullptr) return true; return (a->getImage().getImagePath() < b->getImage().getImagePath()); }); // Define range to compute - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0 || std::size_t(rangeStart) > viewsOrderedByName.size()) + if (rangeStart < 0 || rangeSize < 0 || std::size_t(rangeStart) > viewsOrderedByName.size()) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; } - if(std::size_t(rangeStart + rangeSize) > viewsOrderedByName.size()) + if (std::size_t(rangeStart + rangeSize) > viewsOrderedByName.size()) { rangeSize = int(viewsOrderedByName.size()) - rangeStart; } @@ -213,12 +210,12 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_DEBUG("Range to compute: rangeStart=" << rangeStart << ", rangeSize=" << rangeSize); // If panorama width is undefined, estimate it - if(panoramaSize.first <= 0) + if (panoramaSize.first <= 0) { const float ratioUpscale = clamp(float(percentUpscale) / 100.0f, 0.0f, 1.0f); std::pair optimalPanoramaSize; - if(computeOptimalPanoramaSize(optimalPanoramaSize, sfmData, ratioUpscale)) + if (computeOptimalPanoramaSize(optimalPanoramaSize, sfmData, ratioUpscale)) { panoramaSize = optimalPanoramaSize; } @@ -228,7 +225,7 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - if(maxPanoramaWidth != 0 && panoramaSize.first > maxPanoramaWidth) + if (maxPanoramaWidth != 0 && panoramaSize.first > maxPanoramaWidth) { ALICEVISION_LOG_INFO("The optimal size of the panorama exceeds the maximum size (estimated width: " << panoramaSize.first << ", max width: " << maxPanoramaWidth << ")."); @@ -246,19 +243,19 @@ int aliceVision_main(int argc, char** argv) std::memset(empty_char.get(), 0, tileSize * tileSize * sizeof(char)); // Preprocessing per view - for(std::size_t i = std::size_t(rangeStart); i < std::size_t(rangeStart + rangeSize); ++i) + for (std::size_t i = std::size_t(rangeStart); i < std::size_t(rangeStart + rangeSize); ++i) { const std::shared_ptr& viewIt = viewsOrderedByName[i]; // Retrieve view const sfmData::View& view = *viewIt; - if(!sfmData.isPoseAndIntrinsicDefined(&view)) + if (!sfmData.isPoseAndIntrinsicDefined(&view)) { continue; } - ALICEVISION_LOG_INFO("[" << int(i) + 1 - rangeStart << "/" << rangeSize << "] Processing view " - << view.getViewId() << " (" << i + 1 << "/" << viewsOrderedByName.size() << ")"); + ALICEVISION_LOG_INFO("[" << int(i) + 1 - rangeStart << "/" << rangeSize << "] Processing view " << view.getViewId() << " (" << i + 1 << "/" + << viewsOrderedByName.size() << ")"); // Get intrinsics and extrinsics geometry::Pose3 camPose = sfmData.getPose(view).getTransform(); @@ -266,19 +263,19 @@ int aliceVision_main(int argc, char** argv) // Compute coarse bounding box to make computations faster BoundingBox coarseBboxInitial; - if(!computeCoarseBB(coarseBboxInitial, panoramaSize, camPose, *(intrinsic.get()))) + if (!computeCoarseBB(coarseBboxInitial, panoramaSize, camPose, *(intrinsic.get()))) { continue; } std::vector coarsesBbox; - if(coarseBboxInitial.width > coarseBboxInitial.height * 2.0) + if (coarseBboxInitial.width > coarseBboxInitial.height * 2.0) { const int count = int(double(coarseBboxInitial.width) / double(coarseBboxInitial.height)); const int width = coarseBboxInitial.width / count; int pos = 0; - for(int id = 0; id < count; id++) + for (int id = 0; id < count; id++) { BoundingBox subCoarseBbox; subCoarseBbox.left = coarseBboxInitial.left + pos; @@ -301,7 +298,7 @@ int aliceVision_main(int argc, char** argv) image::Image source; image::readImage(imagePath, source, workingColorSpace); - for(int idsub = 0; idsub < coarsesBbox.size(); idsub++) + for (int idsub = 0; idsub < coarsesBbox.size(); idsub++) { const auto coarseBbox = coarsesBbox[idsub]; @@ -317,12 +314,12 @@ int aliceVision_main(int argc, char** argv) // Search for first non empty box starting from the top bool found = false; - for(int y = 0; y < snappedCoarseBbox.height; y += tileSize) + for (int y = 0; y < snappedCoarseBbox.height; y += tileSize) { #pragma omp parallel for - for(int x = 0; x < snappedCoarseBbox.width; x += tileSize) + for (int x = 0; x < snappedCoarseBbox.width; x += tileSize) { - if(found) + if (found) { continue; } @@ -338,14 +335,14 @@ int aliceVision_main(int argc, char** argv) // Prepare coordinates map CoordinatesMap map; - if(!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) + if (!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) { continue; } #pragma omp critical { - if(!map.getBoundingBox().isEmpty()) + if (!map.getBoundingBox().isEmpty()) { globalBbox = globalBbox.unionWith(map.getBoundingBox()); found = true; @@ -353,7 +350,7 @@ int aliceVision_main(int argc, char** argv) } } - if(found) + if (found) { break; } @@ -363,12 +360,12 @@ int aliceVision_main(int argc, char** argv) { // Search for first non empty box starting from the bottom bool found = false; - for(int y = snappedCoarseBbox.height - 1; y >= 0; y -= tileSize) + for (int y = snappedCoarseBbox.height - 1; y >= 0; y -= tileSize) { #pragma omp parallel for - for(int x = 0; x < snappedCoarseBbox.width; x += tileSize) + for (int x = 0; x < snappedCoarseBbox.width; x += tileSize) { - if(found) + if (found) { continue; } @@ -384,14 +381,14 @@ int aliceVision_main(int argc, char** argv) // Prepare coordinates map CoordinatesMap map; - if(!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) + if (!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) { continue; } #pragma omp critical { - if(!map.getBoundingBox().isEmpty()) + if (!map.getBoundingBox().isEmpty()) { globalBbox = globalBbox.unionWith(map.getBoundingBox()); found = true; @@ -399,7 +396,7 @@ int aliceVision_main(int argc, char** argv) } } - if(found) + if (found) { break; } @@ -409,12 +406,12 @@ int aliceVision_main(int argc, char** argv) { // Search for first non empty box starting from the left bool found = false; - for(int x = 0; x < snappedCoarseBbox.width; x += tileSize) + for (int x = 0; x < snappedCoarseBbox.width; x += tileSize) { #pragma omp parallel for - for(int y = 0; y < snappedCoarseBbox.height; y += tileSize) + for (int y = 0; y < snappedCoarseBbox.height; y += tileSize) { - if(found) + if (found) { continue; } @@ -430,14 +427,14 @@ int aliceVision_main(int argc, char** argv) // Prepare coordinates map CoordinatesMap map; - if(!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) + if (!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) { continue; } #pragma omp critical { - if(!map.getBoundingBox().isEmpty()) + if (!map.getBoundingBox().isEmpty()) { globalBbox = globalBbox.unionWith(map.getBoundingBox()); found = true; @@ -445,7 +442,7 @@ int aliceVision_main(int argc, char** argv) } } - if(found) + if (found) { break; } @@ -455,12 +452,12 @@ int aliceVision_main(int argc, char** argv) { // Search for first non empty box starting from the left bool found = false; - for(int x = snappedCoarseBbox.width - 1; x >= 0; x -= tileSize) + for (int x = snappedCoarseBbox.width - 1; x >= 0; x -= tileSize) { #pragma omp parallel for - for(int y = 0; y < snappedCoarseBbox.height; y += tileSize) + for (int y = 0; y < snappedCoarseBbox.height; y += tileSize) { - if(found) + if (found) { continue; } @@ -476,14 +473,14 @@ int aliceVision_main(int argc, char** argv) // Prepare coordinates map CoordinatesMap map; - if(!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) + if (!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) { continue; } #pragma omp critical { - if(!map.getBoundingBox().isEmpty()) + if (!map.getBoundingBox().isEmpty()) { globalBbox = globalBbox.unionWith(map.getBoundingBox()); found = true; @@ -491,7 +488,7 @@ int aliceVision_main(int argc, char** argv) } } - if(found) + if (found) { break; } @@ -499,7 +496,7 @@ int aliceVision_main(int argc, char** argv) } // Rare case ... When all boxes valid are after the loop - if(globalBbox.left >= panoramaSize.first) + if (globalBbox.left >= panoramaSize.first) { globalBbox.left -= panoramaSize.first; } @@ -526,12 +523,9 @@ int aliceVision_main(int argc, char** argv) // Define output paths const std::string viewIdStr = std::to_string(view.getViewId()); const std::string subIdStr = std::to_string(idsub); - const std::string viewFilepath = - (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + ".exr")).string(); - const std::string maskFilepath = - (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + "_mask.exr")).string(); - const std::string weightFilepath = - (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + "_weight.exr")).string(); + const std::string viewFilepath = (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + ".exr")).string(); + const std::string maskFilepath = (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + "_mask.exr")).string(); + const std::string weightFilepath = (fs::path(outputDirectory) / (viewIdStr + "_" + subIdStr + "_weight.exr")).string(); // Create output images std::unique_ptr out_view = oiio::ImageOutput::create(viewFilepath); @@ -561,16 +555,16 @@ int aliceVision_main(int argc, char** argv) out_weights->open(weightFilepath, spec_weights); GaussianPyramidNoMask pyramid(source.width(), source.height()); - if(!pyramid.process(source)) + if (!pyramid.process(source)) { ALICEVISION_LOG_ERROR("Problem creating pyramid."); continue; } std::vector boxes; - for(int y = 0; y < globalBbox.height; y += tileSize) + for (int y = 0; y < globalBbox.height; y += tileSize) { - for(int x = 0; x < globalBbox.width; x += tileSize) + for (int x = 0; x < globalBbox.width; x += tileSize) { BoundingBox localBbox; localBbox.left = x + globalBbox.left; @@ -582,7 +576,7 @@ int aliceVision_main(int argc, char** argv) } #pragma omp parallel for - for(int boxId = 0; boxId < boxes.size(); boxId++) + for (int boxId = 0; boxId < boxes.size(); boxId++) { BoundingBox localBbox = boxes[boxId]; @@ -591,21 +585,21 @@ int aliceVision_main(int argc, char** argv) // Prepare coordinates map CoordinatesMap map; - if(!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) + if (!map.build(panoramaSize, camPose, *(intrinsic.get()), localBbox)) { continue; } // Warp image GaussianWarper warper; - if(!warper.warp(map, pyramid, clampHalf)) + if (!warper.warp(map, pyramid, clampHalf)) { continue; } // Alpha mask aliceVision::image::Image weights; - if(!distanceToCenter(weights, map, intrinsic->w(), intrinsic->h())) + if (!distanceToCenter(weights, map, intrinsic->w(), intrinsic->h())) { continue; } diff --git a/src/software/pipeline/main_photometricStereo.cpp b/src/software/pipeline/main_photometricStereo.cpp index 3a947debe1..0fd4da18b4 100644 --- a/src/software/pipeline/main_photometricStereo.cpp +++ b/src/software/pipeline/main_photometricStereo.cpp @@ -27,7 +27,7 @@ #include #include -//OpenCV +// OpenCV #include #include #include @@ -51,7 +51,7 @@ namespace fs = std::filesystem; using namespace aliceVision; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { system::Timer timer; @@ -113,13 +113,13 @@ int aliceVision_main(int argc, char **argv) else { sfmData::SfMData sfmData; - if (!sfmDataIO::load(sfmData, inputPath, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) + if (!sfmDataIO::load(sfmData, inputPath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) { ALICEVISION_LOG_ERROR("The input file '" + inputPath + "' cannot be read."); return EXIT_FAILURE; } - photometricStereo::photometricStereo(sfmData, pathToLightData, maskPath, outputPath, PSParameters, normalsIm, albedoIm); + photometricStereo::photometricStereo(sfmData, pathToLightData, maskPath, outputPath, PSParameters, normalsIm, albedoIm); } ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); diff --git a/src/software/pipeline/main_prepareDenseScene.cpp b/src/software/pipeline/main_prepareDenseScene.cpp index 5181358c1d..bd15188639 100644 --- a/src/software/pipeline/main_prepareDenseScene.cpp +++ b/src/software/pipeline/main_prepareDenseScene.cpp @@ -41,16 +41,22 @@ using namespace aliceVision::sfmDataIO; namespace po = boost::program_options; namespace fs = std::filesystem; -template -void process(const std::string &dstColorImage, const IntrinsicBase* cam, const oiio::ParamValueList & metadata, const std::string & srcImage, bool evCorrection, float exposureCompensation, MaskFuncT && maskFunc) +template +void process(const std::string& dstColorImage, + const IntrinsicBase* cam, + const oiio::ParamValueList& metadata, + const std::string& srcImage, + bool evCorrection, + float exposureCompensation, + MaskFuncT&& maskFunc) { ImageT image, image_ud; readImage(srcImage, image, image::EImageColorSpace::LINEAR); // exposure correction - if(evCorrection) + if (evCorrection) { - for(int pix = 0; pix < image.width() * image.height(); ++pix) + for (int pix = 0; pix < image.width() * image.height(); ++pix) { image(pix)[0] *= exposureCompensation; image(pix)[1] *= exposureCompensation; @@ -62,7 +68,7 @@ void process(const std::string &dstColorImage, const IntrinsicBase* cam, const o maskFunc(image); // undistort - if(cam->isValid() && cam->hasDistortion()) + if (cam->isValid() && cam->hasDistortion()) { // undistort the image and save it using Pix = typename ImageT::Tpixel; @@ -94,7 +100,7 @@ bool prepareDenseScene(const SfMData& sfmData, sfmData::Views::const_iterator itViewBegin = sfmData.getViews().begin(); sfmData::Views::const_iterator itViewEnd = sfmData.getViews().end(); - if(endIndex > 0) + if (endIndex > 0) { itViewEnd = itViewBegin; std::advance(itViewEnd, endIndex); @@ -103,7 +109,7 @@ bool prepareDenseScene(const SfMData& sfmData, std::advance(itViewBegin, (beginIndex < 0) ? 0 : beginIndex); // export valid views as projective cameras - for(auto it = itViewBegin; it != itViewEnd; ++it) + for (auto it = itViewBegin; it != itViewEnd; ++it) { const View* view = it->second.get(); if (!sfmData.isPoseAndIntrinsicDefined(view)) @@ -111,20 +117,19 @@ bool prepareDenseScene(const SfMData& sfmData, viewIds.insert(view->getViewId()); } - if((outputFileType != image::EImageFileType::EXR) && saveMetadata) + if ((outputFileType != image::EImageFileType::EXR) && saveMetadata) ALICEVISION_LOG_WARNING("Cannot save informations in images metadata.\n" "Choose '.exr' file type if you want AliceVision custom metadata"); // export data - auto progressDisplay = system::createConsoleProgressDisplay(viewIds.size(), std::cout, - "Exporting Scene Undistorted Images\n"); + auto progressDisplay = system::createConsoleProgressDisplay(viewIds.size(), std::cout, "Exporting Scene Undistorted Images\n"); // for exposure correction const double medianCameraExposure = sfmData.getMedianCameraExposureSetting().getExposure(); - ALICEVISION_LOG_INFO("Median Camera Exposure: " << medianCameraExposure << ", Median EV: " << std::log2(1.0/medianCameraExposure)); + ALICEVISION_LOG_INFO("Median Camera Exposure: " << medianCameraExposure << ", Median EV: " << std::log2(1.0 / medianCameraExposure)); - #pragma omp parallel for num_threads(3) - for(int i = 0; i < viewIds.size(); ++i) +#pragma omp parallel for num_threads(3) + for (int i = 0; i < viewIds.size(); ++i) { auto itView = viewIds.begin(); std::advance(itView, i); @@ -137,19 +142,21 @@ bool prepareDenseScene(const SfMData& sfmData, // we have a valid view with a corresponding camera & pose const std::string baseFilename = std::to_string(viewId); - // get metadata from source image to be sure we get all metadata. We don't use the metadatas from the Views inside the SfMData to avoid type conversion problems with string maps. + // get metadata from source image to be sure we get all metadata. We don't use the metadatas from the Views inside the SfMData to avoid type + // conversion problems with string maps. std::string srcImage = view->getImage().getImagePath(); oiio::ParamValueList metadata = image::readImageMetadata(srcImage); // export camera - if(saveMetadata || saveMatricesFiles) + if (saveMetadata || saveMatricesFiles) { // get camera pose / projection const Pose3 pose = sfmData.getPose(*view).getTransform(); std::shared_ptr cam = iterIntrinsic->second; std::shared_ptr camPinHole = std::dynamic_pointer_cast(cam); - if (!camPinHole) { + if (!camPinHole) + { ALICEVISION_LOG_ERROR("Camera is not pinhole in filter"); continue; } @@ -161,18 +168,16 @@ bool prepareDenseScene(const SfMData& sfmData, const Mat3& R = pose.rotation(); const Vec3& t = pose.translation(); - if(saveMatricesFiles) + if (saveMatricesFiles) { std::ofstream fileP((fs::path(outFolder) / (baseFilename + "_P.txt")).string()); - fileP << std::setprecision(10) - << P(0, 0) << " " << P(0, 1) << " " << P(0, 2) << " " << P(0, 3) << "\n" - << P(1, 0) << " " << P(1, 1) << " " << P(1, 2) << " " << P(1, 3) << "\n" - << P(2, 0) << " " << P(2, 1) << " " << P(2, 2) << " " << P(2, 3) << "\n"; + fileP << std::setprecision(10) << P(0, 0) << " " << P(0, 1) << " " << P(0, 2) << " " << P(0, 3) << "\n" + << P(1, 0) << " " << P(1, 1) << " " << P(1, 2) << " " << P(1, 3) << "\n" + << P(2, 0) << " " << P(2, 1) << " " << P(2, 2) << " " << P(2, 3) << "\n"; fileP.close(); std::ofstream fileKRt((fs::path(outFolder) / (baseFilename + "_KRt.txt")).string()); - fileKRt << std::setprecision(10) - << K(0, 0) << " " << K(0, 1) << " " << K(0, 2) << "\n" + fileKRt << std::setprecision(10) << K(0, 0) << " " << K(0, 1) << " " << K(0, 2) << "\n" << K(1, 0) << " " << K(1, 1) << " " << K(1, 2) << "\n" << K(2, 0) << " " << K(2, 1) << " " << K(2, 2) << "\n" << "\n" @@ -184,14 +189,12 @@ bool prepareDenseScene(const SfMData& sfmData, fileKRt.close(); } - if(saveMetadata) + if (saveMetadata) { // convert to 44 matix Mat4 projectionMatrix; - projectionMatrix << P(0, 0), P(0, 1), P(0, 2), P(0, 3), - P(1, 0), P(1, 1), P(1, 2), P(1, 3), - P(2, 0), P(2, 1), P(2, 2), P(2, 3), - 0, 0, 0, 1; + projectionMatrix << P(0, 0), P(0, 1), P(0, 2), P(0, 3), P(1, 0), P(1, 1), P(1, 2), P(1, 3), P(2, 0), P(2, 1), P(2, 2), P(2, 3), 0, 0, + 0, 1; // convert matrices to rowMajor std::vector vP(projectionMatrix.size()); @@ -213,25 +216,26 @@ bool prepareDenseScene(const SfMData& sfmData, } // export undistort image - { - if(!imagesFolders.empty()) + { + if (!imagesFolders.empty()) { std::vector paths = sfmDataIO::viewPathsFromFolders(*view, imagesFolders); // if path was not found - if(paths.empty()) + if (paths.empty()) { throw std::runtime_error("Cannot find view '" + std::to_string(view->getViewId()) + "' image file in given folder(s)"); } - else if(paths.size() > 1) + else if (paths.size() > 1) { - throw std::runtime_error( "Ambiguous case: Multiple source image files found in given folder(s) for the view '" + - std::to_string(view->getViewId()) + "'."); + throw std::runtime_error("Ambiguous case: Multiple source image files found in given folder(s) for the view '" + + std::to_string(view->getViewId()) + "'."); } srcImage = paths[0]; } - const std::string dstColorImage = (fs::path(outFolder) / (baseFilename + "." + image::EImageFileType_enumToString(outputFileType))).string(); + const std::string dstColorImage = + (fs::path(outFolder) / (baseFilename + "." + image::EImageFileType_enumToString(outputFileType))).string(); const IntrinsicBase* cam = iterIntrinsic->second.get(); // add exposure values to images metadata @@ -241,32 +245,33 @@ bool prepareDenseScene(const SfMData& sfmData, metadata.push_back(oiio::ParamValue("AliceVision:EV", float(ev))); metadata.push_back(oiio::ParamValue("AliceVision:EVComp", exposureCompensation)); - if(evCorrection) + if (evCorrection) { - ALICEVISION_LOG_INFO("image " << viewId << ", exposure: " << cameraExposure << ", Ev " << ev << " Ev compensation: " + std::to_string(exposureCompensation)); + ALICEVISION_LOG_INFO("image " << viewId << ", exposure: " << cameraExposure << ", Ev " << ev + << " Ev compensation: " + std::to_string(exposureCompensation)); } image::Image mask; - if(tryLoadMask(&mask, masksFolders, viewId, srcImage, maskExtension)) + if (tryLoadMask(&mask, masksFolders, viewId, srcImage, maskExtension)) { - process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, [&mask] (Image & image) - { - if(image.width() * image.height() != mask.width() * mask.height()) - { - ALICEVISION_LOG_WARNING("Invalid image mask size: mask is ignored."); - return; - } - - for(int pix = 0; pix < image.width() * image.height(); ++pix) - { - const bool masked = (mask(pix) == 0); - image(pix).a() = masked ? 0.f : 1.f; - } - }); + process>( + dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, [&mask](Image& image) { + if (image.width() * image.height() != mask.width() * mask.height()) + { + ALICEVISION_LOG_WARNING("Invalid image mask size: mask is ignored."); + return; + } + + for (int pix = 0; pix < image.width() * image.height(); ++pix) + { + const bool masked = (mask(pix) == 0); + image(pix).a() = masked ? 0.f : 1.f; + } + }); } else { - const auto noMaskingFunc = [] (Image & image) {}; + const auto noMaskingFunc = [](Image& image) {}; process>(dstColorImage, cam, metadata, srcImage, evCorrection, exposureCompensation, noMaskingFunc); } } @@ -277,7 +282,7 @@ bool prepareDenseScene(const SfMData& sfmData, return true; } -int aliceVision_main(int argc, char *argv[]) +int aliceVision_main(int argc, char* argv[]) { // command-line parameters @@ -332,18 +337,18 @@ int aliceVision_main(int argc, char *argv[]) if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; - } + } // set output file type image::EImageFileType outputFileType = image::EImageFileType_stringToEnum(outImageFileTypeName); // Create output dir - if(!fs::exists(outFolder)) + if (!fs::exists(outFolder)) fs::create_directory(outFolder); // Read the input SfM scene SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); return EXIT_FAILURE; @@ -352,20 +357,20 @@ int aliceVision_main(int argc, char *argv[]) int rangeEnd = sfmData.getViews().size(); // set range - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0) + if (rangeStart < 0 || rangeSize < 0) { ALICEVISION_LOG_ERROR("Range is incorrect"); return EXIT_FAILURE; } - if(rangeStart + rangeSize > sfmData.getViews().size()) + if (rangeStart + rangeSize > sfmData.getViews().size()) rangeSize = sfmData.getViews().size() - rangeStart; rangeEnd = rangeStart + rangeSize; - if(rangeSize <= 0) + if (rangeSize <= 0) { ALICEVISION_LOG_WARNING("Nothing to compute."); return EXIT_SUCCESS; @@ -377,8 +382,17 @@ int aliceVision_main(int argc, char *argv[]) } // export - if(prepareDenseScene(sfmData, imagesFolders, masksFolders, maskExtension, rangeStart, rangeEnd, - outFolder, outputFileType, saveMetadata, saveMatricesTxtFiles, evCorrection)) + if (prepareDenseScene(sfmData, + imagesFolders, + masksFolders, + maskExtension, + rangeStart, + rangeEnd, + outFolder, + outputFileType, + saveMetadata, + saveMatricesTxtFiles, + evCorrection)) return EXIT_SUCCESS; return EXIT_FAILURE; diff --git a/src/software/pipeline/main_relativePoseEstimating.cpp b/src/software/pipeline/main_relativePoseEstimating.cpp index e18aa64356..862846df3c 100644 --- a/src/software/pipeline/main_relativePoseEstimating.cpp +++ b/src/software/pipeline/main_relativePoseEstimating.cpp @@ -50,9 +50,15 @@ using namespace aliceVision; namespace po = boost::program_options; - -bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vector& newVecInliers, const Mat3& E, - const std::vector& vecInliers, const Mat3& K1, const Mat3& K2, const Mat& x1, +bool getPoseStructure(Mat3& R, + Vec3& t, + std::vector& structure, + std::vector& newVecInliers, + const Mat3& E, + const std::vector& vecInliers, + const Mat3& K1, + const Mat3& K2, + const Mat& x1, const Mat& x2) { // Find set of analytical solutions @@ -67,7 +73,7 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto size_t bestCoundValid = 0; - for(int it = 0; it < Rs.size(); it++) + for (int it = 0; it < Rs.size(); it++) { const Mat3& R2 = Rs[it]; const Vec3& t2 = ts[it]; @@ -78,7 +84,7 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto std::vector updatedInliers; size_t countValid = 0; - for(size_t k = 0; k < vecInliers.size(); ++k) + for (size_t k = 0; k < vecInliers.size(); ++k) { const Vec2& pt1 = x1.col(vecInliers[k]); const Vec2& pt2 = x2.col(vecInliers[k]); @@ -87,7 +93,7 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto multiview::TriangulateDLT(P1, pt1, P2, pt2, X); // Test if point is front to the two cameras. - if(Depth(R1, t1, X) > 0 && Depth(R2, t2, X) > 0) + if (Depth(R1, t1, X) > 0 && Depth(R2, t2, X) > 0) { countValid++; } @@ -96,7 +102,7 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto points.push_back(X); } - if(countValid > bestCoundValid) + if (countValid > bestCoundValid) { bestCoundValid = countValid; structure = points; @@ -106,7 +112,7 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto } } - if(newVecInliers.size() < 10) + if (newVecInliers.size() < 10) { return false; } @@ -114,18 +120,24 @@ bool getPoseStructure(Mat3& R, Vec3& t, std::vector& structure, std::vecto return true; } -bool robustEssential(Mat3& E, std::vector& vecInliers, const Mat3& K1, const Mat3& K2, const Mat& x1, - const Mat& x2, const std::pair& size_ima1, - const std::pair& size_ima2, std::mt19937& randomNumberGenerator, - const size_t maxIterationCount, const size_t minInliers) +bool robustEssential(Mat3& E, + std::vector& vecInliers, + const Mat3& K1, + const Mat3& K2, + const Mat& x1, + const Mat& x2, + const std::pair& size_ima1, + const std::pair& size_ima2, + std::mt19937& randomNumberGenerator, + const size_t maxIterationCount, + const size_t minInliers) { // use the 5 point solver to estimate E using SolverT = multiview::relativePose::Essential5PSolver; // define the kernel using KernelT = - multiview::RelativePoseKernel_K; + multiview::RelativePoseKernel_K; KernelT kernel(x1, size_ima1.first, size_ima1.second, x2, size_ima2.first, size_ima2.second, K1, K2); @@ -134,9 +146,9 @@ bool robustEssential(Mat3& E, std::vector& vecInliers, const Mat3& K1, c // robustly estimation of the Essential matrix and its precision const std::pair acRansacOut = - robustEstimation::ACRANSAC(kernel, randomNumberGenerator, vecInliers, maxIterationCount, &model, 4.0); + robustEstimation::ACRANSAC(kernel, randomNumberGenerator, vecInliers, maxIterationCount, &model, 4.0); - if(vecInliers.size() < minInliers) + if (vecInliers.size() < minInliers) { return false; } @@ -146,16 +158,16 @@ bool robustEssential(Mat3& E, std::vector& vecInliers, const Mat3& K1, c return true; } -bool robustRotation(Mat3& R, std::vector& vecInliers, - const Mat& x1, const Mat& x2, +bool robustRotation(Mat3& R, + std::vector& vecInliers, + const Mat& x1, + const Mat& x2, std::mt19937& randomNumberGenerator, - const size_t maxIterationCount, const size_t minInliers) + const size_t maxIterationCount, + const size_t minInliers) { - using KernelType = multiview::RelativePoseSphericalKernel< - multiview::relativePose::Rotation3PSolver, - multiview::relativePose::RotationError, - robustEstimation::Mat3Model - >; + using KernelType = multiview:: + RelativePoseSphericalKernel; KernelType kernel(x1, x2); @@ -165,7 +177,7 @@ bool robustRotation(Mat3& R, std::vector& vecInliers, // robustly estimation of the Essential matrix and its precision robustEstimation::ACRANSAC(kernel, randomNumberGenerator, vecInliers, 1024, &model, std::numeric_limits::infinity()); - if(vecInliers.size() < minInliers) + if (vecInliers.size() < minInliers) { return false; } @@ -177,20 +189,20 @@ bool robustRotation(Mat3& R, std::vector& vecInliers, void computeCovisibility(std::map& covisibility, const track::TracksMap& mapTracks) { - for(const auto& item : mapTracks) + for (const auto& item : mapTracks) { const auto& track = item.second; - for(auto it = track.featPerView.begin(); it != track.featPerView.end(); it++) + for (auto it = track.featPerView.begin(); it != track.featPerView.end(); it++) { Pair p; p.first = it->first; - for(auto next = std::next(it); next != track.featPerView.end(); next++) + for (auto next = std::next(it); next != track.featPerView.end(); next++) { p.second = next->first; - if(covisibility.find(p) == covisibility.end()) + if (covisibility.find(p) == covisibility.end()) { covisibility[p] = 0; } @@ -203,12 +215,7 @@ void computeCovisibility(std::map& covisibility, const track } } -double computeAreaScore( - const std::vector & refPts, - const std::vector & nextPts, - double refArea, - double nextArea -) +double computeAreaScore(const std::vector& refPts, const std::vector& nextPts, double refArea, double nextArea) { namespace bg = boost::geometry; @@ -219,8 +226,8 @@ double computeAreaScore( for (int idx = 0; idx < refPts.size(); idx++) { - const auto & refPt = refPts[idx]; - const auto & nextPt = nextPts[idx]; + const auto& refPt = refPts[idx]; + const auto& nextPt = nextPts[idx]; boost::geometry::append(mpt1, point_t(refPt(0), refPt(1))); boost::geometry::append(mpt2, point_t(nextPt(0), nextPt(1))); @@ -232,7 +239,7 @@ double computeAreaScore( double area1 = boost::geometry::area(hull1); double area2 = boost::geometry::area(hull1); double score = (area1 + area2) / (refArea + nextArea); - + return score; } @@ -281,7 +288,7 @@ int aliceVision_main(int argc, char** argv) cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -289,30 +296,30 @@ int aliceVision_main(int argc, char** argv) // set maxThreads HardwareContext hwc = cmdline.getHardwareContext(); omp_set_num_threads(hwc.getMaxThreads()); - + std::mt19937 randomNumberGenerator(randomSeed); // load input SfMData scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); return EXIT_FAILURE; } // Define range to compute - if(rangeStart != -1) + if (rangeStart != -1) { - if(rangeStart < 0 || rangeSize < 0 || rangeStart > sfmData.getViews().size()) - { - ALICEVISION_LOG_ERROR("Range is incorrect"); - return EXIT_FAILURE; - } + if (rangeStart < 0 || rangeSize < 0 || rangeStart > sfmData.getViews().size()) + { + ALICEVISION_LOG_ERROR("Range is incorrect"); + return EXIT_FAILURE; + } - if(rangeStart + rangeSize > sfmData.getViews().size()) - { - rangeSize = sfmData.getViews().size() - rangeStart; - } + if (rangeStart + rangeSize > sfmData.getViews().size()) + { + rangeSize = sfmData.getViews().size() - rangeStart; + } } else { @@ -321,17 +328,13 @@ int aliceVision_main(int argc, char** argv) } ALICEVISION_LOG_DEBUG("Range to compute: rangeStart=" << rangeStart << ", rangeSize=" << rangeSize); - - // get imageDescriber type - const std::vector describerTypes = - feature::EImageDescriberType_stringToEnums(describerTypesName); - + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); // features reading feature::FeaturesPerView featuresPerView; ALICEVISION_LOG_INFO("Load features"); - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Invalid features."); return EXIT_FAILURE; @@ -340,7 +343,7 @@ int aliceVision_main(int argc, char** argv) // Load tracks ALICEVISION_LOG_INFO("Load tracks"); std::ifstream tracksFile(tracksFilename); - if(tracksFile.is_open() == false) + if (tracksFile.is_open() == false) { ALICEVISION_LOG_ERROR("The input tracks file '" + tracksFilename + "' cannot be read."); return EXIT_FAILURE; @@ -353,7 +356,7 @@ int aliceVision_main(int argc, char** argv) // Compute tracks per view ALICEVISION_LOG_INFO("Estimate tracks per view"); track::TracksPerView mapTracksPerView; - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { // create an entry in the map mapTracksPerView[viewIt.first]; @@ -363,8 +366,7 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Compute co-visibility"); std::map covisibility; computeCovisibility(covisibility, mapTracks); - - + ALICEVISION_LOG_INFO("Process co-visibility"); std::stringstream ss; ss << outputDirectory << "/pairs_" << rangeStart << ".json"; @@ -376,14 +378,14 @@ int aliceVision_main(int argc, char** argv) int chunkStart = int(double(rangeStart) * ratioChunk); int chunkEnd = int(double(rangeStart + rangeSize) * ratioChunk); - //For each covisible pair + // For each covisible pair #pragma omp parallel for - for(int posPairs = chunkStart; posPairs < chunkEnd; posPairs++) + for (int posPairs = chunkStart; posPairs < chunkEnd; posPairs++) { auto iterPairs = covisibility.begin(); std::advance(iterPairs, posPairs); - //Retrieve pair information + // Retrieve pair information IndexT refImage = iterPairs->first.first; IndexT nextImage = iterPairs->first.second; @@ -391,8 +393,7 @@ int aliceVision_main(int argc, char** argv) const sfmData::View& nextView = sfmData.getView(nextImage); std::shared_ptr refIntrinsics = sfmData.getIntrinsicsharedPtr(refView.getIntrinsicId()); - std::shared_ptr nextIntrinsics = - sfmData.getIntrinsicsharedPtr(nextView.getIntrinsicId()); + std::shared_ptr nextIntrinsics = sfmData.getIntrinsicsharedPtr(nextView.getIntrinsicId()); std::shared_ptr refPinhole = std::dynamic_pointer_cast(refIntrinsics); std::shared_ptr nextPinhole = std::dynamic_pointer_cast(nextIntrinsics); @@ -402,12 +403,12 @@ int aliceVision_main(int argc, char** argv) feature::MapFeaturesPerDesc& refFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(refImage); feature::MapFeaturesPerDesc& nextFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(nextImage); - //Build features coordinates matrices + // Build features coordinates matrices const std::size_t n = mapTracksCommon.size(); Mat refX(2, n); Mat nextX(2, n); IndexT pos = 0; - for(const auto& commonItem : mapTracksCommon) + for (const auto& commonItem : mapTracksCommon) { const track::Track& track = commonItem.second; @@ -428,20 +429,20 @@ int aliceVision_main(int argc, char** argv) if (enforcePureRotation) { - //Transform to vector + // Transform to vector Mat refVecs(3, n); Mat nextVecs(3, n); for (int idx = 0; idx < n; idx++) { - //Lift to unit sphere + // Lift to unit sphere refVecs.col(idx) = refIntrinsics->toUnitSphere(refIntrinsics->removeDistortion(refIntrinsics->ima2cam(refX.col(idx)))); nextVecs.col(idx) = nextIntrinsics->toUnitSphere(nextIntrinsics->removeDistortion(nextIntrinsics->ima2cam(nextX.col(idx)))); } - //Try to fit an essential matrix (we assume we are approx. calibrated) + // Try to fit an essential matrix (we assume we are approx. calibrated) Mat3 R; const bool relativeSuccess = robustRotation(R, vecInliers, refVecs, nextVecs, randomNumberGenerator, 1024, minInliers); - if(!relativeSuccess) + if (!relativeSuccess) { continue; } @@ -453,15 +454,21 @@ int aliceVision_main(int argc, char** argv) } else { - //Try to fit an essential matrix (we assume we are approx. calibrated) + // Try to fit an essential matrix (we assume we are approx. calibrated) Mat3 E; std::vector inliers; - const bool essentialSuccess = - robustEssential(E, inliers, refPinhole->K(), nextPinhole->K(), refX, nextX, - std::make_pair(refPinhole->w(), refPinhole->h()), - std::make_pair(nextPinhole->w(), nextPinhole->h()), - randomNumberGenerator, 1024, minInliers); - if(!essentialSuccess) + const bool essentialSuccess = robustEssential(E, + inliers, + refPinhole->K(), + nextPinhole->K(), + refX, + nextX, + std::make_pair(refPinhole->w(), refPinhole->h()), + std::make_pair(nextPinhole->w(), nextPinhole->h()), + randomNumberGenerator, + 1024, + minInliers); + if (!essentialSuccess) { continue; } @@ -470,10 +477,8 @@ int aliceVision_main(int argc, char** argv) reconstructed.reference = refImage; reconstructed.next = nextImage; - if(!getPoseStructure(reconstructed.R, reconstructed.t, - structure, vecInliers, E, inliers, - refPinhole->K(), nextPinhole->K(), - refX, nextX)) + if (!getPoseStructure( + reconstructed.R, reconstructed.t, structure, vecInliers, E, inliers, refPinhole->K(), nextPinhole->K(), refX, nextX)) { continue; } @@ -486,24 +491,23 @@ int aliceVision_main(int argc, char** argv) nextpts.push_back(nextX.col(id)); } - //Compute matched points coverage of image + // Compute matched points coverage of image double areaRef = refPinhole->w() * refPinhole->h(); double areaNext = nextPinhole->w() * nextPinhole->h(); double areaScore = computeAreaScore(refpts, nextpts, areaRef, areaNext); - //Compute ratio of matched points + // Compute ratio of matched points double iunion = n; double iinter = vecInliers.size(); double score = iinter / iunion; reconstructed.score = 0.5 * score + 0.5 * areaScore; - - //Buffered output to avoid lo - #pragma omp critical +// Buffered output to avoid lo +#pragma omp critical { reconstructedPairs.push_back(reconstructed); - if(reconstructedPairs.size() > 1000) + if (reconstructedPairs.size() > 1000) { boost::json::value jv = boost::json::value_from(reconstructedPairs); of << boost::json::serialize(jv); @@ -512,7 +516,7 @@ int aliceVision_main(int argc, char** argv) } } - //Serialize last pairs + // Serialize last pairs { boost::json::value jv = boost::json::value_from(reconstructedPairs); of << boost::json::serialize(jv); diff --git a/src/software/pipeline/main_rigCalibration.cpp b/src/software/pipeline/main_rigCalibration.cpp index ad2f125b12..54e6446188 100644 --- a/src/software/pipeline/main_rigCalibration.cpp +++ b/src/software/pipeline/main_rigCalibration.cpp @@ -7,7 +7,7 @@ #include #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) -#include + #include #endif #include #include @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -37,8 +37,8 @@ #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) -#include -#endif // ALICEVISION_HAVE_ALEMBIC + #include +#endif // ALICEVISION_HAVE_ALEMBIC // These constants define the current software version. // They must be updated when the command line is changed. @@ -53,64 +53,63 @@ namespace po = boost::program_options; int aliceVision_main(int argc, char** argv) { - // common parameters - /// the AliceVision .json/abc data file - std::string sfmFilePath; - /// the the folder containing the descriptors - std::string descriptorsFolder; - /// the media file to localize - std::vector mediaPath; - /// the calibration file for each camera - std::vector cameraIntrinsics; - /// the name of the file where to store the calibration data - std::string outputFile; - /// the describer types name to use for the matching - std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - /// the preset for the feature extractor - feature::ConfigurationPreset featDescPreset; - /// the describer types to use for the matching - std::vector matchDescTypes; - /// the estimator to use for resection - robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the estimator to use for matching - robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the possible choices for the estimators as strings - const std::string str_estimatorChoices = ""+robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) - +","+robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); - bool refineIntrinsics = false; - - /// the maximum error allowed for resection - double resectionErrorMax = 4.0; - /// the maximum error allowed for image matching with geometric validation - double matchingErrorMax = 4.0; - /// the maximum number of frames in input - std::size_t maxInputFrames = 0; - - - // parameters for voctree localizer - /// whether to use the voctreeLocalizer or cctagLocalizer - bool useVoctreeLocalizer = true; - /// the vocabulary tree file - std::string vocTreeFilepath; - /// the vocabulary tree weights file - std::string weightsFilepath; - /// the localization algorithm to use for the voctree localizer - std::string algostring = "AllResults"; - /// number of documents to search when querying the voctree - std::size_t numResults = 4; - /// maximum number of matching documents to retain - std::size_t maxResults = 10; - - // parameters for cctag localizer - std::size_t nNearestKeyFrames = 5; + // common parameters + /// the AliceVision .json/abc data file + std::string sfmFilePath; + /// the the folder containing the descriptors + std::string descriptorsFolder; + /// the media file to localize + std::vector mediaPath; + /// the calibration file for each camera + std::vector cameraIntrinsics; + /// the name of the file where to store the calibration data + std::string outputFile; + /// the describer types name to use for the matching + std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + /// the preset for the feature extractor + feature::ConfigurationPreset featDescPreset; + /// the describer types to use for the matching + std::vector matchDescTypes; + /// the estimator to use for resection + robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the estimator to use for matching + robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the possible choices for the estimators as strings + const std::string str_estimatorChoices = "" + robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) + + "," + robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); + bool refineIntrinsics = false; + + /// the maximum error allowed for resection + double resectionErrorMax = 4.0; + /// the maximum error allowed for image matching with geometric validation + double matchingErrorMax = 4.0; + /// the maximum number of frames in input + std::size_t maxInputFrames = 0; + + // parameters for voctree localizer + /// whether to use the voctreeLocalizer or cctagLocalizer + bool useVoctreeLocalizer = true; + /// the vocabulary tree file + std::string vocTreeFilepath; + /// the vocabulary tree weights file + std::string weightsFilepath; + /// the localization algorithm to use for the voctree localizer + std::string algostring = "AllResults"; + /// number of documents to search when querying the voctree + std::size_t numResults = 4; + /// maximum number of matching documents to retain + std::size_t maxResults = 10; + + // parameters for cctag localizer + std::size_t nNearestKeyFrames = 5; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - /// the export file - std::string exportFile = "trackedcameras.abc"; + /// the export file + std::string exportFile = "trackedcameras.abc"; #endif - int randomSeed = std::mt19937::default_seed; + int randomSeed = std::mt19937::default_seed; - std::size_t numCameras = 0; + std::size_t numCameras = 0; // clang-format off po::options_description ioParams("Required input and output parameters"); @@ -184,290 +183,276 @@ int aliceVision_main(int argc, char** argv) #endif ; // clang-format on - - CmdLine cmdline("This program is used to calibrate a camera rig composed of internally calibrated cameras." - "It takes as input a synchronized sequence of N cameras and it saves the estimated " - "rig calibration to a text file.\n" - "AliceVision rigCalibration"); - cmdline.add(ioParams); - cmdline.add(outputParams); - cmdline.add(commonParams); - cmdline.add(voctreeParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - const double defaultLoRansacMatchingError = 4.0; - const double defaultLoRansacResectionError = 4.0; - if(!adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || - !adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) - { - return EXIT_FAILURE; - } - - // check that we have the same number of feeds as the intrinsics - if((mediaPath.size() != cameraIntrinsics.size())) - { - ALICEVISION_CERR("The number of intrinsics and the number of cameras are not the same." << std::endl); - return EXIT_FAILURE; - } - numCameras = mediaPath.size(); - - // Init descTypes from command-line string - matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); - - // decide the localizer to use based on the type of feature + + CmdLine cmdline("This program is used to calibrate a camera rig composed of internally calibrated cameras." + "It takes as input a synchronized sequence of N cameras and it saves the estimated " + "rig calibration to a text file.\n" + "AliceVision rigCalibration"); + cmdline.add(ioParams); + cmdline.add(outputParams); + cmdline.add(commonParams); + cmdline.add(voctreeParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + const double defaultLoRansacMatchingError = 4.0; + const double defaultLoRansacResectionError = 4.0; + if (!adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || + !adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) + { + return EXIT_FAILURE; + } + + // check that we have the same number of feeds as the intrinsics + if ((mediaPath.size() != cameraIntrinsics.size())) + { + ALICEVISION_CERR("The number of intrinsics and the number of cameras are not the same." << std::endl); + return EXIT_FAILURE; + } + numCameras = mediaPath.size(); + + // Init descTypes from command-line string + matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); + + // decide the localizer to use based on the type of feature #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - useVoctreeLocalizer = !(matchDescTypes.size() == 1 && - ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || - (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); + useVoctreeLocalizer = !(matchDescTypes.size() == 1 && ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || + (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); #endif + std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); + + std::unique_ptr param; + + std::unique_ptr localizer; + + // load SfMData + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); + return EXIT_FAILURE; + } - std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); - - std::unique_ptr param; - - std::unique_ptr localizer; - - // load SfMData - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); - return EXIT_FAILURE; - } - - // initialize the localizer according to the chosen type of describer - if(useVoctreeLocalizer) - { - ALICEVISION_COUT("Calibrating sequence using the voctree localizer"); - localization::VoctreeLocalizer* tmpLoc = new localization::VoctreeLocalizer(sfmData, - descriptorsFolder, - vocTreeFilepath, - weightsFilepath, - matchDescTypes); - - localizer.reset(tmpLoc); - - localization::VoctreeLocalizer::Parameters *tmpParam = new localization::VoctreeLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring);; - tmpParam->_numResults = numResults; - tmpParam->_maxResults = maxResults; - tmpParam->_ccTagUseCuda = false; - tmpParam->_matchingError = matchingErrorMax; - - } + // initialize the localizer according to the chosen type of describer + if (useVoctreeLocalizer) + { + ALICEVISION_COUT("Calibrating sequence using the voctree localizer"); + localization::VoctreeLocalizer* tmpLoc = + new localization::VoctreeLocalizer(sfmData, descriptorsFolder, vocTreeFilepath, weightsFilepath, matchDescTypes); + + localizer.reset(tmpLoc); + + localization::VoctreeLocalizer::Parameters* tmpParam = new localization::VoctreeLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring); + ; + tmpParam->_numResults = numResults; + tmpParam->_maxResults = maxResults; + tmpParam->_ccTagUseCuda = false; + tmpParam->_matchingError = matchingErrorMax; + } #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - else - { - localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); - localizer.reset(tmpLoc); - - localization::CCTagLocalizer::Parameters *tmpParam = new localization::CCTagLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_nNearestKeyFrames = nNearestKeyFrames; - } -#endif - - assert(localizer); - assert(param); - - // set other common parameters - param->_featurePreset = featDescPreset; - param->_refineIntrinsics = refineIntrinsics; - param->_errorMax = resectionErrorMax; - param->_resectionEstimator = resectionEstimator; - param->_matchingEstimator = matchingEstimator; - - if(!localizer->isInit()) - { - ALICEVISION_CERR("ERROR while initializing the localizer!"); - return EXIT_FAILURE; - } + else + { + localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); + localizer.reset(tmpLoc); + + localization::CCTagLocalizer::Parameters* tmpParam = new localization::CCTagLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_nNearestKeyFrames = nNearestKeyFrames; + } +#endif + + assert(localizer); + assert(param); + + // set other common parameters + param->_featurePreset = featDescPreset; + param->_refineIntrinsics = refineIntrinsics; + param->_errorMax = resectionErrorMax; + param->_resectionEstimator = resectionEstimator; + param->_matchingEstimator = matchingEstimator; + + if (!localizer->isInit()) + { + ALICEVISION_CERR("ERROR while initializing the localizer!"); + return EXIT_FAILURE; + } #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - sfmDataIO::AlembicExporter exporter(exportFile); - exporter.addLandmarks(localizer->getSfMData().getLandmarks()); + sfmDataIO::AlembicExporter exporter(exportFile); + exporter.addLandmarks(localizer->getSfMData().getLandmarks()); #endif - // Create a camera rig - rig::Rig rig; - - // Loop over all cameras of the rig - for(std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) - { - ALICEVISION_COUT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - ALICEVISION_COUT("CAMERA " << idCamera); - ALICEVISION_COUT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); - const std::string &calibFile = cameraIntrinsics[idCamera]; - const std::string &feedPath = mediaPath[idCamera]; - // contains the folder where the video, the images or the filelist is - const std::string subMediaFilepath = - fs::is_directory(fs::path(mediaPath[idCamera])) ? - (mediaPath[idCamera]) : - (fs::path(mediaPath[idCamera]).parent_path().string()); - - // create the feedProvider - dataio::FeedProvider feed(feedPath, calibFile); - if(!feed.isInit()) + // Create a camera rig + rig::Rig rig; + + // Loop over all cameras of the rig + for (std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) { - ALICEVISION_CERR("ERROR while initializing the FeedProvider!"); - return EXIT_FAILURE; + ALICEVISION_COUT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + ALICEVISION_COUT("CAMERA " << idCamera); + ALICEVISION_COUT("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"); + const std::string& calibFile = cameraIntrinsics[idCamera]; + const std::string& feedPath = mediaPath[idCamera]; + // contains the folder where the video, the images or the filelist is + const std::string subMediaFilepath = + fs::is_directory(fs::path(mediaPath[idCamera])) ? (mediaPath[idCamera]) : (fs::path(mediaPath[idCamera]).parent_path().string()); + + // create the feedProvider + dataio::FeedProvider feed(feedPath, calibFile); + if (!feed.isInit()) + { + ALICEVISION_CERR("ERROR while initializing the FeedProvider!"); + return EXIT_FAILURE; + } + + double step = 1.0; + const int nbFrames = feed.nbFrames(); + int nbFramesToProcess = nbFrames; + + // Compute the discretization's step + if (maxInputFrames && feed.nbFrames() > maxInputFrames) + { + step = feed.nbFrames() / (double)maxInputFrames; + nbFramesToProcess = maxInputFrames; + } + ALICEVISION_COUT("Input stream length is " << feed.nbFrames() << "."); + + // std::string featureFile, cameraResultFile, pointsFile; + // featureFile = subMediaFilepath + "/cctag" + std::to_string(nRings) + "CC.out"; + // cameraResultFile = inputFolder + "/" + std::to_string(i) + "/cameras.txt"; + // std::ofstream result; + // result.open(cameraResultFile); + // pointsFile = inputFolder + "/points.txt"; + + image::Image imageGrey; + std::shared_ptr queryIntrinsics = + std::dynamic_pointer_cast(camera::createIntrinsic(camera::PINHOLE_CAMERA_RADIAL3)); + bool hasIntrinsics = false; + + std::size_t iInputFrame = 0; + std::string currentImgName; + + // Define an accumulator set for computing the mean and the + // standard deviation of the time taken for localization + bacc::accumulator_set> stats; + + // used to collect the match data result + std::vector vLocalizationResults; + std::size_t currentFrame = 0; + while (feed.readImage(imageGrey, *queryIntrinsics, currentImgName, hasIntrinsics)) + { + ALICEVISION_COUT("******************************"); + ALICEVISION_COUT("Stream " << idCamera << " Frame " << utils::toStringZeroPadded(currentFrame, 4) << "/" << nbFrames << " : (" + << iInputFrame << "/" << nbFramesToProcess << ")"); + ALICEVISION_COUT("******************************"); + auto detect_start = std::chrono::steady_clock::now(); + localization::LocalizationResult localizationResult; + localizer->setCudaPipe(idCamera); + const bool ok = localizer->localize( + imageGrey, param.get(), randomNumberGenerator, hasIntrinsics /*useInputIntrinsics*/, *queryIntrinsics, localizationResult); + assert(ok == localizationResult.isValid()); + vLocalizationResults.emplace_back(localizationResult); + sfmData::CameraPose pose(localizationResult.getPose()); + auto detect_end = std::chrono::steady_clock::now(); + auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("Localization took " << detect_elapsed.count() << " [ms]"); + stats(detect_elapsed.count()); + +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) + if (localizationResult.isValid()) + { + exporter.addCamera("camera" + std::to_string(idCamera) + "." + utils::toStringZeroPadded(currentFrame, 4), + sfmData::View(subMediaFilepath, currentFrame, currentFrame), + &pose, + queryIntrinsics); + } + else + { + // @fixme for now just add a fake camera so that it still can be see in MAYA + exporter.addCamera("camera" + std::to_string(idCamera) + ".V." + utils::toStringZeroPadded(currentFrame, 4), + sfmData::View(subMediaFilepath, currentFrame, currentFrame), + &pose, + queryIntrinsics); + } +#endif + ++iInputFrame; + currentFrame = std::floor(iInputFrame * step); + feed.goToFrame(currentFrame); + } + + rig.setTrackingResult(vLocalizationResults, idCamera); + + // print out some time stats + ALICEVISION_COUT("\n\n******************************"); + ALICEVISION_COUT("Processed " << iInputFrame << " images for camera " << idCamera); + ALICEVISION_COUT("Processing took " << bacc::sum(stats) / 1000 << " [s] overall"); + ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); + ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); + ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); } - double step = 1.0; - const int nbFrames = feed.nbFrames(); - int nbFramesToProcess = nbFrames; + { + // just for statistics purposes + const std::size_t numRigCam = rig.nCams(); + ALICEVISION_COUT("\n\n******************************"); + for (std::size_t idCam = 0; idCam < numRigCam; ++idCam) + { + auto& currResult = rig.getLocalizationResults(idCam); + std::size_t numLocalized = 0; + for (const auto& curr : currResult) + { + if (curr.isValid()) + ++numLocalized; + } + ALICEVISION_COUT("Camera " << idCam << " localized " << numLocalized << "/" << currResult.size()); + } + } - // Compute the discretization's step - if (maxInputFrames && feed.nbFrames() > maxInputFrames) + ALICEVISION_COUT("Rig calibration initialization..."); + if (!rig.initializeCalibration()) { - step = feed.nbFrames() / (double) maxInputFrames; - nbFramesToProcess = maxInputFrames; + ALICEVISION_CERR("Unable to find a proper initialization for the relative poses! Aborting..."); + return EXIT_FAILURE; } - ALICEVISION_COUT("Input stream length is " << feed.nbFrames() << "."); - - //std::string featureFile, cameraResultFile, pointsFile; - //featureFile = subMediaFilepath + "/cctag" + std::to_string(nRings) + "CC.out"; - //cameraResultFile = inputFolder + "/" + std::to_string(i) + "/cameras.txt"; - //std::ofstream result; - //result.open(cameraResultFile); - //pointsFile = inputFolder + "/points.txt"; - - image::Image imageGrey; - std::shared_ptr queryIntrinsics = std::dynamic_pointer_cast( - camera::createIntrinsic(camera::PINHOLE_CAMERA_RADIAL3)); - bool hasIntrinsics = false; - - std::size_t iInputFrame = 0; - std::string currentImgName; - - // Define an accumulator set for computing the mean and the - // standard deviation of the time taken for localization - bacc::accumulator_set > stats; - - // used to collect the match data result - std::vector vLocalizationResults; - std::size_t currentFrame = 0; - while(feed.readImage(imageGrey, *queryIntrinsics, currentImgName, hasIntrinsics)) + ALICEVISION_COUT("Rig calibration optimization..."); + if (!rig.optimizeCalibration()) { - ALICEVISION_COUT("******************************"); - ALICEVISION_COUT("Stream " << idCamera << " Frame " << utils::toStringZeroPadded(currentFrame, 4) - << "/" << nbFrames << " : (" << iInputFrame << "/" << nbFramesToProcess << ")"); - ALICEVISION_COUT("******************************"); - auto detect_start = std::chrono::steady_clock::now(); - localization::LocalizationResult localizationResult; - localizer->setCudaPipe( idCamera ); - const bool ok = localizer->localize(imageGrey, - param.get(), - randomNumberGenerator, - hasIntrinsics/*useInputIntrinsics*/, - *queryIntrinsics, - localizationResult); - assert( ok == localizationResult.isValid() ); - vLocalizationResults.emplace_back(localizationResult); - sfmData::CameraPose pose(localizationResult.getPose()); - auto detect_end = std::chrono::steady_clock::now(); - auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("Localization took " << detect_elapsed.count() << " [ms]"); - stats(detect_elapsed.count()); - -#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - if(localizationResult.isValid()) - { - exporter.addCamera("camera"+std::to_string(idCamera)+"." + utils::toStringZeroPadded(currentFrame, 4), - sfmData::View(subMediaFilepath, currentFrame, currentFrame), - &pose, - queryIntrinsics); - } - else - { - // @fixme for now just add a fake camera so that it still can be see in MAYA - exporter.addCamera("camera"+std::to_string(idCamera)+".V." + utils::toStringZeroPadded(currentFrame, 4), - sfmData::View(subMediaFilepath, currentFrame, currentFrame), - &pose, - queryIntrinsics); - } -#endif - ++iInputFrame; - currentFrame = std::floor(iInputFrame * step); - feed.goToFrame(currentFrame); + ALICEVISION_CERR("Unable to optimize the relative poses! Aborting..."); + return EXIT_FAILURE; } - rig.setTrackingResult(vLocalizationResults, idCamera); - - // print out some time stats - ALICEVISION_COUT("\n\n******************************"); - ALICEVISION_COUT("Processed " << iInputFrame << " images for camera " << idCamera); - ALICEVISION_COUT("Processing took " << bacc::sum(stats) / 1000 << " [s] overall"); - ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); - ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); - ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); - } - - { - // just for statistics purposes - const std::size_t numRigCam = rig.nCams(); - ALICEVISION_COUT("\n\n******************************"); - for(std::size_t idCam = 0; idCam < numRigCam; ++idCam) + // save the rig calibration (subposes) + rig.saveCalibration(outputFile); + + // just print out the results + // the first rig pose + if (rig.getPosesSize() > 0) { - auto & currResult = rig.getLocalizationResults(idCam); - std::size_t numLocalized = 0; - for(const auto &curr : currResult) - { - if(curr.isValid()) - ++numLocalized; - } - ALICEVISION_COUT("Camera " << idCam << " localized " - << numLocalized << "/" << currResult.size()); + ALICEVISION_COUT("First pose of the rig"); + const geometry::Pose3& pose = rig.getPose(0); + ALICEVISION_COUT("R\n" << pose.rotation()); + ALICEVISION_COUT("center\n" << pose.center()); + ALICEVISION_COUT("t\n" << pose.translation()); } - - } - - ALICEVISION_COUT("Rig calibration initialization..."); - if(!rig.initializeCalibration()) - { - ALICEVISION_CERR("Unable to find a proper initialization for the relative poses! Aborting..."); - return EXIT_FAILURE; - } - ALICEVISION_COUT("Rig calibration optimization..."); - if(!rig.optimizeCalibration()) - { - ALICEVISION_CERR("Unable to optimize the relative poses! Aborting..."); - return EXIT_FAILURE; - } - - // save the rig calibration (subposes) - rig.saveCalibration(outputFile); - - - // just print out the results - // the first rig pose - if(rig.getPosesSize() > 0) - { - ALICEVISION_COUT("First pose of the rig"); - const geometry::Pose3 &pose = rig.getPose(0); - ALICEVISION_COUT("R\n" << pose.rotation()); - ALICEVISION_COUT("center\n" << pose.center()); - ALICEVISION_COUT("t\n" << pose.translation()); - } - - // get the subposes of the cameras inside the rig - const std::vector& subposes = rig.getRelativePoses(); - assert(numCameras-1 == subposes.size()); - for(std::size_t i = 0; i < subposes.size(); ++i) - { - const geometry::Pose3 &pose = subposes[i]; - ALICEVISION_COUT("--------------------"); - ALICEVISION_COUT("Subpose p0" << i+1); // from camera 0 to camera i+1 - ALICEVISION_COUT("R\n" << pose.rotation()); - ALICEVISION_COUT("center\n" << pose.center()); - ALICEVISION_COUT("t\n" << pose.translation()); - ALICEVISION_COUT("--------------------\n"); - } - - return EXIT_SUCCESS; + + // get the subposes of the cameras inside the rig + const std::vector& subposes = rig.getRelativePoses(); + assert(numCameras - 1 == subposes.size()); + for (std::size_t i = 0; i < subposes.size(); ++i) + { + const geometry::Pose3& pose = subposes[i]; + ALICEVISION_COUT("--------------------"); + ALICEVISION_COUT("Subpose p0" << i + 1); // from camera 0 to camera i+1 + ALICEVISION_COUT("R\n" << pose.rotation()); + ALICEVISION_COUT("center\n" << pose.center()); + ALICEVISION_COUT("t\n" << pose.translation()); + ALICEVISION_COUT("--------------------\n"); + } + + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_rigLocalization.cpp b/src/software/pipeline/main_rigLocalization.cpp index d38086a082..bd4d6648fc 100644 --- a/src/software/pipeline/main_rigLocalization.cpp +++ b/src/software/pipeline/main_rigLocalization.cpp @@ -7,7 +7,7 @@ #include #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) -#include + #include #endif #include #include @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -39,7 +39,7 @@ #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) -#include + #include #endif // These constants define the current software version. @@ -55,64 +55,63 @@ namespace po = boost::program_options; int aliceVision_main(int argc, char** argv) { - // common parameters - /// the AliceVision .json/abc data file - std::string sfmFilePath; - /// the the folder containing the descriptors - std::string descriptorsFolder; - /// the media file to localize - std::vector mediaPath; - /// the calibration file for each camera - std::vector cameraIntrinsics; - /// the file containing the calibration data for the file (subposes) - std::string rigCalibPath; - - /// the describer types name to use for the matching - std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - /// the preset for the feature extractor - feature::ConfigurationPreset featDescPreset; - /// the describer types to use for the matching - std::vector matchDescTypes; - /// the estimator to use for resection - robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the estimator to use for matching - robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; - /// the possible choices for the estimators as strings - const std::string str_estimatorChoices = robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) - +", "+robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); - bool refineIntrinsics = false; - bool useLocalizeRigNaive = false; - /// the maximum error allowed for resection - double resectionErrorMax = 4.0; - /// the maximum error allowed for image matching with geometric validation - double matchingErrorMax = 4.0; - /// the maximum angular error allowed for rig resectioning (in degrees) - double angularThreshold = 0.1; - - - // parameters for voctree localizer - /// whether to use the voctreeLocalizer or cctagLocalizer - bool useVoctreeLocalizer = true; - /// the vocabulary tree file - std::string vocTreeFilepath; - /// the vocabulary tree weights file - std::string weightsFilepath; - /// the localization algorithm to use for the voctree localizer - std::string algostring = "AllResults"; - /// number of documents to search when querying the voctree - std::size_t numResults = 4; - /// maximum number of matching documents to retain - std::size_t maxResults = 10; - - // parameters for cctag localizer - std::size_t nNearestKeyFrames = 5; - - /// the Alembic export file - std::string exportAlembicFile = "trackedcameras.abc"; - - std::size_t numCameras = 0; - - int randomSeed = std::mt19937::default_seed; + // common parameters + /// the AliceVision .json/abc data file + std::string sfmFilePath; + /// the the folder containing the descriptors + std::string descriptorsFolder; + /// the media file to localize + std::vector mediaPath; + /// the calibration file for each camera + std::vector cameraIntrinsics; + /// the file containing the calibration data for the file (subposes) + std::string rigCalibPath; + + /// the describer types name to use for the matching + std::string matchDescTypeNames = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + /// the preset for the feature extractor + feature::ConfigurationPreset featDescPreset; + /// the describer types to use for the matching + std::vector matchDescTypes; + /// the estimator to use for resection + robustEstimation::ERobustEstimator resectionEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the estimator to use for matching + robustEstimation::ERobustEstimator matchingEstimator = robustEstimation::ERobustEstimator::ACRANSAC; + /// the possible choices for the estimators as strings + const std::string str_estimatorChoices = robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::ACRANSAC) + ", " + + robustEstimation::ERobustEstimator_enumToString(robustEstimation::ERobustEstimator::LORANSAC); + bool refineIntrinsics = false; + bool useLocalizeRigNaive = false; + /// the maximum error allowed for resection + double resectionErrorMax = 4.0; + /// the maximum error allowed for image matching with geometric validation + double matchingErrorMax = 4.0; + /// the maximum angular error allowed for rig resectioning (in degrees) + double angularThreshold = 0.1; + + // parameters for voctree localizer + /// whether to use the voctreeLocalizer or cctagLocalizer + bool useVoctreeLocalizer = true; + /// the vocabulary tree file + std::string vocTreeFilepath; + /// the vocabulary tree weights file + std::string weightsFilepath; + /// the localization algorithm to use for the voctree localizer + std::string algostring = "AllResults"; + /// number of documents to search when querying the voctree + std::size_t numResults = 4; + /// maximum number of matching documents to retain + std::size_t maxResults = 10; + + // parameters for cctag localizer + std::size_t nNearestKeyFrames = 5; + + /// the Alembic export file + std::string exportAlembicFile = "trackedcameras.abc"; + + std::size_t numCameras = 0; + + int randomSeed = std::mt19937::default_seed; // clang-format off po::options_description inputParams("Required input parameters"); @@ -191,278 +190,269 @@ int aliceVision_main(int argc, char** argv) ; // clang-format on - CmdLine cmdline("This program is used to localize a camera rig composed of internally calibrated cameras.\n" - "AliceVision rigLocalization"); - cmdline.add(inputParams); - cmdline.add(outputParams); - cmdline.add(commonParams); - cmdline.add(voctreeParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); - - const double defaultLoRansacMatchingError = 4.0; - const double defaultLoRansacResectionError = 4.0; - if(!adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || - !adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) - { - return EXIT_FAILURE; - } - - // check that we have the same number of feeds as the intrinsics - if((mediaPath.size() != cameraIntrinsics.size())) - { - ALICEVISION_CERR("The number of intrinsics and the number of cameras are not the same." << std::endl); - return EXIT_FAILURE; - } - numCameras = mediaPath.size(); - - // Init descTypes from command-line string - matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); + CmdLine cmdline("This program is used to localize a camera rig composed of internally calibrated cameras.\n" + "AliceVision rigLocalization"); + cmdline.add(inputParams); + cmdline.add(outputParams); + cmdline.add(commonParams); + cmdline.add(voctreeParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + std::mt19937 randomNumberGenerator(randomSeed == -1 ? std::random_device()() : randomSeed); + + const double defaultLoRansacMatchingError = 4.0; + const double defaultLoRansacResectionError = 4.0; + if (!adjustRobustEstimatorThreshold(matchingEstimator, matchingErrorMax, defaultLoRansacMatchingError) || + !adjustRobustEstimatorThreshold(resectionEstimator, resectionErrorMax, defaultLoRansacResectionError)) + { + return EXIT_FAILURE; + } + + // check that we have the same number of feeds as the intrinsics + if ((mediaPath.size() != cameraIntrinsics.size())) + { + ALICEVISION_CERR("The number of intrinsics and the number of cameras are not the same." << std::endl); + return EXIT_FAILURE; + } + numCameras = mediaPath.size(); + + // Init descTypes from command-line string + matchDescTypes = feature::EImageDescriberType_stringToEnums(matchDescTypeNames); #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - useVoctreeLocalizer = !(matchDescTypes.size() == 1 && - ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || - (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); + useVoctreeLocalizer = !(matchDescTypes.size() == 1 && ((matchDescTypes.front() == feature::EImageDescriberType::CCTAG3) || + (matchDescTypes.front() == feature::EImageDescriberType::CCTAG4))); #endif + std::unique_ptr param; - std::unique_ptr param; - - std::unique_ptr localizer; - - // load SfMData - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); - return EXIT_FAILURE; - } - - // initialize the localizer according to the chosen type of describer - if(useVoctreeLocalizer) - { - ALICEVISION_COUT("Localizing sequence using the voctree localizer"); - localization::VoctreeLocalizer* tmpLoc = new localization::VoctreeLocalizer(sfmData, - descriptorsFolder, - vocTreeFilepath, - weightsFilepath, - matchDescTypes - ); - localizer.reset(tmpLoc); - - localization::VoctreeLocalizer::Parameters *tmpParam = new localization::VoctreeLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring);; - tmpParam->_numResults = numResults; - tmpParam->_maxResults = maxResults; - tmpParam->_ccTagUseCuda = false; - tmpParam->_matchingError = matchingErrorMax; - - } + std::unique_ptr localizer; + + // load SfMData + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmFilePath, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmFilePath + "' cannot be read."); + return EXIT_FAILURE; + } + + // initialize the localizer according to the chosen type of describer + if (useVoctreeLocalizer) + { + ALICEVISION_COUT("Localizing sequence using the voctree localizer"); + localization::VoctreeLocalizer* tmpLoc = + new localization::VoctreeLocalizer(sfmData, descriptorsFolder, vocTreeFilepath, weightsFilepath, matchDescTypes); + localizer.reset(tmpLoc); + + localization::VoctreeLocalizer::Parameters* tmpParam = new localization::VoctreeLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_algorithm = localization::VoctreeLocalizer::initFromString(algostring); + ; + tmpParam->_numResults = numResults; + tmpParam->_maxResults = maxResults; + tmpParam->_ccTagUseCuda = false; + tmpParam->_matchingError = matchingErrorMax; + } #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - else - { - localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); - localizer.reset(tmpLoc); - - localization::CCTagLocalizer::Parameters *tmpParam = new localization::CCTagLocalizer::Parameters(); - param.reset(tmpParam); - tmpParam->_nNearestKeyFrames = nNearestKeyFrames; - } -#endif - - assert(localizer); - assert(param); - - // set other common parameters - param->_featurePreset = featDescPreset; - param->_refineIntrinsics = refineIntrinsics; - param->_errorMax = resectionErrorMax; - param->_resectionEstimator = resectionEstimator; - param->_matchingEstimator = matchingEstimator; - param->_useLocalizeRigNaive = useLocalizeRigNaive; - param->_angularThreshold = degreeToRadian(angularThreshold); - - if(!localizer->isInit()) - { - ALICEVISION_CERR("ERROR while initializing the localizer!"); - return EXIT_FAILURE; - } + else + { + localization::CCTagLocalizer* tmpLoc = new localization::CCTagLocalizer(sfmData, descriptorsFolder); + localizer.reset(tmpLoc); -#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - sfmDataIO::AlembicExporter exporter(exportAlembicFile); - exporter.initAnimatedCamera("rig"); - exporter.addLandmarks(localizer->getSfMData().getLandmarks()); - - boost::ptr_vector cameraExporters; - cameraExporters.reserve(numCameras); - - // this contains the full path and the root name of the file without the extension - const std::string basename = (fs::path(exportAlembicFile).parent_path() / fs::path(exportAlembicFile).stem()).string(); - - for(std::size_t i = 0; i < numCameras; ++i) - { - auto camIndexStr = utils::toStringZeroPadded(i, 2); - cameraExporters.push_back(new sfmDataIO::AlembicExporter(basename + ".cam" + camIndexStr + ".abc")); - cameraExporters.back().initAnimatedCamera("cam" + camIndexStr); - } + localization::CCTagLocalizer::Parameters* tmpParam = new localization::CCTagLocalizer::Parameters(); + param.reset(tmpParam); + tmpParam->_nNearestKeyFrames = nNearestKeyFrames; + } #endif - std::vector feeders(numCameras); - std::vector subMediaFilepath(numCameras); - - // Init the feeder for each camera - for(std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) - { - const std::string &calibFile = cameraIntrinsics[idCamera]; - const std::string &feedPath = mediaPath[idCamera]; - // contains the folder where the video, the images or the filelist is - subMediaFilepath[idCamera] = - fs::is_directory(fs::path(mediaPath[idCamera])) ? - (mediaPath[idCamera]) : - (fs::path(mediaPath[idCamera]).parent_path().string()); - - // create the feedProvider - feeders[idCamera] = new dataio::FeedProvider(feedPath, calibFile); - if(!feeders[idCamera]->isInit()) + assert(localizer); + assert(param); + + // set other common parameters + param->_featurePreset = featDescPreset; + param->_refineIntrinsics = refineIntrinsics; + param->_errorMax = resectionErrorMax; + param->_resectionEstimator = resectionEstimator; + param->_matchingEstimator = matchingEstimator; + param->_useLocalizeRigNaive = useLocalizeRigNaive; + param->_angularThreshold = degreeToRadian(angularThreshold); + + if (!localizer->isInit()) { - ALICEVISION_CERR("ERROR while initializing the FeedProvider for the camera " - << idCamera << " " << feedPath); - return EXIT_FAILURE; + ALICEVISION_CERR("ERROR while initializing the localizer!"); + return EXIT_FAILURE; } - } - - - bool haveImage = true; - std::size_t frameCounter = 0; - std::size_t numLocalizedFrames = 0; - - // load the subposes - std::vector vec_subPoses; - if(numCameras > 1) - rig::loadRigCalibration(rigCalibPath, vec_subPoses); - assert(vec_subPoses.size() == numCameras-1); - geometry::Pose3 rigPose; - - // Define an accumulator set for computing the mean and the - // standard deviation of the time taken for localization - bacc::accumulator_set > stats; - - // store the result - std::vector< std::vector > rigResultPerFrame; - - while(haveImage) - { - // @fixme It's better to have arrays of pointers... - std::vector > vec_imageGrey; - std::vector vec_queryIntrinsics; - vec_imageGrey.reserve(numCameras); - vec_queryIntrinsics.reserve(numCameras); - - // for each camera get the image and the associated internal parameters - for(std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) + +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) + sfmDataIO::AlembicExporter exporter(exportAlembicFile); + exporter.initAnimatedCamera("rig"); + exporter.addLandmarks(localizer->getSfMData().getLandmarks()); + + boost::ptr_vector cameraExporters; + cameraExporters.reserve(numCameras); + + // this contains the full path and the root name of the file without the extension + const std::string basename = (fs::path(exportAlembicFile).parent_path() / fs::path(exportAlembicFile).stem()).string(); + + for (std::size_t i = 0; i < numCameras; ++i) { - image::Image imageGrey; - camera::Pinhole queryIntrinsics; - bool hasIntrinsics = false; - std::string currentImgName; - haveImage = feeders[idCamera]->readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics); - feeders[idCamera]->goToNextFrame(); - - if(!haveImage) - { - if(idCamera > 0) - { - // this is quite odd, it means that eg the fist camera has an image but - // one of the others has not image - ALICEVISION_CERR("This is weird... Camera " << idCamera << " seems not to have any available images while some other cameras do..."); - return EXIT_FAILURE; // a bit harsh but if we are here it's cheesy to say the less - } - break; - } - - // for now let's suppose that the cameras are calibrated internally too - if(!hasIntrinsics) - { - ALICEVISION_CERR("For now only internally calibrated cameras are supported!" - << "\nCamera " << idCamera << " does not have calibration for image " << currentImgName); - return EXIT_FAILURE; // a bit harsh but if we are here it's cheesy to say the less - } - - vec_imageGrey.push_back(imageGrey); - vec_queryIntrinsics.push_back(queryIntrinsics); + auto camIndexStr = utils::toStringZeroPadded(i, 2); + cameraExporters.push_back(new sfmDataIO::AlembicExporter(basename + ".cam" + camIndexStr + ".abc")); + cameraExporters.back().initAnimatedCamera("cam" + camIndexStr); } - - if(!haveImage) +#endif + + std::vector feeders(numCameras); + std::vector subMediaFilepath(numCameras); + + // Init the feeder for each camera + for (std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) { - // no more images are available - break; + const std::string& calibFile = cameraIntrinsics[idCamera]; + const std::string& feedPath = mediaPath[idCamera]; + // contains the folder where the video, the images or the filelist is + subMediaFilepath[idCamera] = + fs::is_directory(fs::path(mediaPath[idCamera])) ? (mediaPath[idCamera]) : (fs::path(mediaPath[idCamera]).parent_path().string()); + + // create the feedProvider + feeders[idCamera] = new dataio::FeedProvider(feedPath, calibFile); + if (!feeders[idCamera]->isInit()) + { + ALICEVISION_CERR("ERROR while initializing the FeedProvider for the camera " << idCamera << " " << feedPath); + return EXIT_FAILURE; + } } - - ALICEVISION_COUT("******************************"); - ALICEVISION_COUT("FRAME " << utils::toStringZeroPadded(frameCounter, 4)); - ALICEVISION_COUT("******************************"); - auto detect_start = std::chrono::steady_clock::now(); - std::vector localizationResults; - const bool isLocalized = localizer->localizeRig(vec_imageGrey, - param.get(), - randomNumberGenerator, - vec_queryIntrinsics, - vec_subPoses, - rigPose, - localizationResults); - auto detect_end = std::chrono::steady_clock::now(); - auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("Localization took " << detect_elapsed.count() << " [ms]"); - stats(detect_elapsed.count()); - - rigResultPerFrame.push_back(localizationResults); - - if(isLocalized) + + bool haveImage = true; + std::size_t frameCounter = 0; + std::size_t numLocalizedFrames = 0; + + // load the subposes + std::vector vec_subPoses; + if (numCameras > 1) + rig::loadRigCalibration(rigCalibPath, vec_subPoses); + assert(vec_subPoses.size() == numCameras - 1); + geometry::Pose3 rigPose; + + // Define an accumulator set for computing the mean and the + // standard deviation of the time taken for localization + bacc::accumulator_set> stats; + + // store the result + std::vector> rigResultPerFrame; + + while (haveImage) { - ++numLocalizedFrames; + // @fixme It's better to have arrays of pointers... + std::vector> vec_imageGrey; + std::vector vec_queryIntrinsics; + vec_imageGrey.reserve(numCameras); + vec_queryIntrinsics.reserve(numCameras); + + // for each camera get the image and the associated internal parameters + for (std::size_t idCamera = 0; idCamera < numCameras; ++idCamera) + { + image::Image imageGrey; + camera::Pinhole queryIntrinsics; + bool hasIntrinsics = false; + std::string currentImgName; + haveImage = feeders[idCamera]->readImage(imageGrey, queryIntrinsics, currentImgName, hasIntrinsics); + feeders[idCamera]->goToNextFrame(); + + if (!haveImage) + { + if (idCamera > 0) + { + // this is quite odd, it means that eg the fist camera has an image but + // one of the others has not image + ALICEVISION_CERR("This is weird... Camera " << idCamera + << " seems not to have any available images while some other cameras do..."); + return EXIT_FAILURE; // a bit harsh but if we are here it's cheesy to say the less + } + break; + } + + // for now let's suppose that the cameras are calibrated internally too + if (!hasIntrinsics) + { + ALICEVISION_CERR("For now only internally calibrated cameras are supported!" + << "\nCamera " << idCamera << " does not have calibration for image " << currentImgName); + return EXIT_FAILURE; // a bit harsh but if we are here it's cheesy to say the less + } + + vec_imageGrey.push_back(imageGrey); + vec_queryIntrinsics.push_back(queryIntrinsics); + } + + if (!haveImage) + { + // no more images are available + break; + } + + ALICEVISION_COUT("******************************"); + ALICEVISION_COUT("FRAME " << utils::toStringZeroPadded(frameCounter, 4)); + ALICEVISION_COUT("******************************"); + auto detect_start = std::chrono::steady_clock::now(); + std::vector localizationResults; + const bool isLocalized = + localizer->localizeRig(vec_imageGrey, param.get(), randomNumberGenerator, vec_queryIntrinsics, vec_subPoses, rigPose, localizationResults); + auto detect_end = std::chrono::steady_clock::now(); + auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("Localization took " << detect_elapsed.count() << " [ms]"); + stats(detect_elapsed.count()); + + rigResultPerFrame.push_back(localizationResults); + + if (isLocalized) + { + ++numLocalizedFrames; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - // save the position of the main camera - exporter.addCameraKeyframe(rigPose, &vec_queryIntrinsics[0], subMediaFilepath[0], frameCounter, frameCounter); - assert(cameraExporters.size()==numCameras); - assert(localizationResults.size()==numCameras); - assert(vec_queryIntrinsics.size()==numCameras); - // save the position of all cameras of the rig - for(std::size_t camIDX = 0; camIDX < numCameras; ++camIDX) - { - ALICEVISION_COUT("cam pose" << camIDX << "\n" << localizationResults[camIDX].getPose().rotation() << "\n" << localizationResults[camIDX].getPose().center()); - if(camIDX > 0) - ALICEVISION_COUT("cam subpose" << camIDX-1 << "\n" << vec_subPoses[camIDX-1].rotation() << "\n" << vec_subPoses[camIDX-1].center()); - cameraExporters[camIDX].addCameraKeyframe(localizationResults[camIDX].getPose(), &vec_queryIntrinsics[camIDX], subMediaFilepath[camIDX], frameCounter, frameCounter); - } + // save the position of the main camera + exporter.addCameraKeyframe(rigPose, &vec_queryIntrinsics[0], subMediaFilepath[0], frameCounter, frameCounter); + assert(cameraExporters.size() == numCameras); + assert(localizationResults.size() == numCameras); + assert(vec_queryIntrinsics.size() == numCameras); + // save the position of all cameras of the rig + for (std::size_t camIDX = 0; camIDX < numCameras; ++camIDX) + { + ALICEVISION_COUT("cam pose" << camIDX << "\n" + << localizationResults[camIDX].getPose().rotation() << "\n" + << localizationResults[camIDX].getPose().center()); + if (camIDX > 0) + ALICEVISION_COUT("cam subpose" << camIDX - 1 << "\n" + << vec_subPoses[camIDX - 1].rotation() << "\n" + << vec_subPoses[camIDX - 1].center()); + cameraExporters[camIDX].addCameraKeyframe( + localizationResults[camIDX].getPose(), &vec_queryIntrinsics[camIDX], subMediaFilepath[camIDX], frameCounter, frameCounter); + } #endif - } - else - { - ALICEVISION_CERR("Unable to localize frame " << frameCounter); + } + else + { + ALICEVISION_CERR("Unable to localize frame " << frameCounter); #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) - exporter.jumpKeyframe(); - assert(cameraExporters.size()==numCameras); - for(std::size_t camIDX = 0; camIDX < numCameras; ++camIDX) - { - cameraExporters[camIDX].jumpKeyframe(); - } + exporter.jumpKeyframe(); + assert(cameraExporters.size() == numCameras); + for (std::size_t camIDX = 0; camIDX < numCameras; ++camIDX) + { + cameraExporters[camIDX].jumpKeyframe(); + } #endif + } + + ++frameCounter; } - ++frameCounter; - } - - // print out some time stats - ALICEVISION_COUT("\n\n******************************"); - ALICEVISION_COUT("Localized " << numLocalizedFrames << " / " << frameCounter << " images"); - ALICEVISION_COUT("Processing took " << bacc::sum(stats) / 1000 << " [s] overall"); - ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); - ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); - ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); - - return EXIT_SUCCESS; + // print out some time stats + ALICEVISION_COUT("\n\n******************************"); + ALICEVISION_COUT("Localized " << numLocalizedFrames << " / " << frameCounter << " images"); + ALICEVISION_COUT("Processing took " << bacc::sum(stats) / 1000 << " [s] overall"); + ALICEVISION_COUT("Mean time for localization: " << bacc::mean(stats) << " [ms]"); + ALICEVISION_COUT("Max time for localization: " << bacc::max(stats) << " [ms]"); + ALICEVISION_COUT("Min time for localization: " << bacc::min(stats) << " [ms]"); + + return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_sfmBootstraping.cpp b/src/software/pipeline/main_sfmBootstraping.cpp index 6000098dfb..071a92bd87 100644 --- a/src/software/pipeline/main_sfmBootstraping.cpp +++ b/src/software/pipeline/main_sfmBootstraping.cpp @@ -48,12 +48,11 @@ std::vector readJsons(std::istream& is, boost::json::error_c std::string line; std::size_t n = 0; - - while(true) + while (true) { - if(n == line.size()) + if (n == line.size()) { - if(!std::getline(is, line)) + if (!std::getline(is, line)) { break; } @@ -61,10 +60,10 @@ std::vector readJsons(std::istream& is, boost::json::error_c n = 0; } - //Consume at least part of the line - n += p.write_some( line.data() + n, line.size() - n, ec); + // Consume at least part of the line + n += p.write_some(line.data() + n, line.size() - n, ec); - //If the parser found a value, add it + // If the parser found a value, add it if (p.done()) { jvs.push_back(p.release()); @@ -74,7 +73,7 @@ std::vector readJsons(std::istream& is, boost::json::error_c if (!p.done()) { - //Try to extract the end + // Try to extract the end p.finish(ec); if (ec.failed()) { @@ -87,7 +86,14 @@ std::vector readJsons(std::istream& is, boost::json::error_c return jvs; } -bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pair, const track::TracksMap& tracksMap, const track::TracksPerView & tracksPerView, const feature::FeaturesPerView & featuresPerView, const double maxDistance, double & resultAngle, std::vector & usedTracks) +bool estimatePairAngle(const sfmData::SfMData& sfmData, + const sfm::ReconstructedPair& pair, + const track::TracksMap& tracksMap, + const track::TracksPerView& tracksPerView, + const feature::FeaturesPerView& featuresPerView, + const double maxDistance, + double& resultAngle, + std::vector& usedTracks) { usedTracks.clear(); @@ -99,11 +105,11 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::Reconstructe std::shared_ptr refPinhole = std::dynamic_pointer_cast(refIntrinsics); std::shared_ptr nextPinhole = std::dynamic_pointer_cast(nextIntrinsics); - if (refPinhole==nullptr || nextPinhole == nullptr) + if (refPinhole == nullptr || nextPinhole == nullptr) { return false; } - + aliceVision::track::TracksMap mapTracksCommon; track::getCommonTracksInImagesFast({pair.reference, pair.next}, tracksMap, tracksPerView, mapTracksCommon); @@ -115,15 +121,15 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::Reconstructe Eigen::Matrix3d F = Knext.inverse().transpose() * CrossProductMatrix(pair.t) * pair.R * Kref.inverse(); - Eigen::Vector3d c = - pair.R.transpose() * pair.t; + Eigen::Vector3d c = -pair.R.transpose() * pair.t; Mat34 P1, P2; P_from_KRt(Kref, Mat3::Identity(), Vec3::Zero(), P1); P_from_KRt(Knext, pair.R, pair.t, P2); - + size_t count = 0; std::vector angles; - for(const auto& commonItem : mapTracksCommon) + for (const auto& commonItem : mapTracksCommon) { const track::Track& track = commonItem.second; const feature::PointFeatures& refFeatures = refFeaturesPerDesc.at(track.descType); @@ -138,7 +144,7 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::Reconstructe Vec3 line = F * refptu.homogeneous(); - //Make sure line normal is normalized + // Make sure line normal is normalized line = line * (1.0 / line.head<2>().norm()); double distance = nextptu.homogeneous().dot(line); if (distance > maxDistance) @@ -148,13 +154,13 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::Reconstructe Vec3 X; multiview::TriangulateDLT(P1, refptu, P2, nextptu, X); - + if (X(2) < 0.0) { continue; } - const Vec3 ray1 = - X; + 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); @@ -170,7 +176,11 @@ bool estimatePairAngle(const sfmData::SfMData & sfmData, const sfm::Reconstructe return true; } -double computeScore(const feature::FeaturesPerView & featuresPerView, const track::TracksMap & tracksMap, const std::vector & usedTracks, const IndexT viewId, const size_t maxLevel) +double computeScore(const feature::FeaturesPerView& featuresPerView, + const track::TracksMap& tracksMap, + const std::vector& usedTracks, + const IndexT viewId, + const size_t maxLevel) { const feature::MapFeaturesPerDesc& featuresPerDesc = featuresPerView.getFeaturesPerDesc(viewId); @@ -178,10 +188,10 @@ double computeScore(const feature::FeaturesPerView & featuresPerView, const trac for (auto trackId : usedTracks) { - auto & track = tracksMap.at(trackId); + auto& track = tracksMap.at(trackId); const feature::PointFeatures& features = featuresPerDesc.at(track.descType); - + const IndexT featureId = track.featPerView.at(viewId).featureId; const Vec2 pt = features[featureId].coords().cast(); @@ -195,7 +205,7 @@ double computeScore(const feature::FeaturesPerView & featuresPerView, const trac uniques[shift - 1].insert(std::make_pair(lptx, lpty)); } - } + } double sum = 0.0; for (unsigned int shift = 1; shift < maxLevel; shift++) @@ -213,8 +223,11 @@ double computeScore(const feature::FeaturesPerView & featuresPerView, const trac return sum; } - -bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pair, const track::TracksMap& tracksMap, const feature::FeaturesPerView & featuresPerView, const std::vector & usedTracks) +bool buildSfmData(sfmData::SfMData& sfmData, + const sfm::ReconstructedPair& pair, + const track::TracksMap& tracksMap, + const feature::FeaturesPerView& featuresPerView, + const std::vector& usedTracks) { const sfmData::View& refView = sfmData.getView(pair.reference); const sfmData::View& nextView = sfmData.getView(pair.next); @@ -224,11 +237,11 @@ bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pai std::shared_ptr refPinhole = std::dynamic_pointer_cast(refIntrinsics); std::shared_ptr nextPinhole = std::dynamic_pointer_cast(nextIntrinsics); - if (refPinhole==nullptr || nextPinhole == nullptr) + if (refPinhole == nullptr || nextPinhole == nullptr) { return false; } - + const feature::MapFeaturesPerDesc& refFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(pair.reference); const feature::MapFeaturesPerDesc& nextFeaturesPerDesc = featuresPerView.getFeaturesPerDesc(pair.next); @@ -238,10 +251,10 @@ bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pai Mat34 P1, P2; P_from_KRt(Kref, Mat3::Identity(), Vec3::Zero(), P1); P_from_KRt(Knext, pair.R, pair.t, P2); - + size_t count = 0; std::vector angles; - for(const auto& trackId : usedTracks) + for (const auto& trackId : usedTracks) { const track::Track& track = tracksMap.at(trackId); @@ -257,7 +270,7 @@ bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pai Vec3 X; multiview::TriangulateDLT(P1, refptu, P2, nextptu, X); - + if (X(2) < 0.0) { continue; @@ -266,7 +279,7 @@ bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pai sfmData::Landmark landmark; landmark.descType = track.descType; landmark.X = X; - + sfmData::Observation refObs; refObs.setFeatureId(refFeatureId); refObs.setScale(refFeatures[refFeatureId].scale()); @@ -279,7 +292,7 @@ bool buildSfmData(sfmData::SfMData & sfmData, const sfm::ReconstructedPair & pai landmark.getObservations()[pair.reference] = refObs; landmark.getObservations()[pair.next] = nextObs; - + sfmData.getLandmarks()[trackId] = landmark; } @@ -324,7 +337,7 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("AliceVision SfM Bootstraping"); cmdline.add(requiredParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -332,32 +345,28 @@ int aliceVision_main(int argc, char** argv) // set maxThreads HardwareContext hwc = cmdline.getHardwareContext(); omp_set_num_threads(hwc.getMaxThreads()); - + // load input SfMData scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); return EXIT_FAILURE; } - if (sfmData.getValidViews().size() >= 2) { ALICEVISION_LOG_INFO("SfmData has already an initialization"); return EXIT_SUCCESS; } - // get imageDescriber type - const std::vector describerTypes = - feature::EImageDescriberType_stringToEnums(describerTypesName); - + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); // features reading feature::FeaturesPerView featuresPerView; ALICEVISION_LOG_INFO("Load features"); - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Invalid features."); return EXIT_FAILURE; @@ -366,7 +375,7 @@ int aliceVision_main(int argc, char** argv) // Load tracks ALICEVISION_LOG_INFO("Load tracks"); std::ifstream tracksFile(tracksFilename); - if(tracksFile.is_open() == false) + if (tracksFile.is_open() == false) { ALICEVISION_LOG_ERROR("The input tracks file '" + tracksFilename + "' cannot be read."); return EXIT_FAILURE; @@ -379,37 +388,35 @@ int aliceVision_main(int argc, char** argv) // Compute tracks per view ALICEVISION_LOG_INFO("Estimate tracks per view"); track::TracksPerView mapTracksPerView; - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { // create an entry in the map mapTracksPerView[viewIt.first]; } track::computeTracksPerView(mapTracks, mapTracksPerView); - - //Result of pair estimations are stored in multiple files + // Result of pair estimations are stored in multiple files std::vector reconstructedPairs; const std::regex regex("pairs\\_[0-9]+\\.json"); - for(auto const& file : fs::directory_iterator{pairsDirectory}) + for (auto const& file : fs::directory_iterator{pairsDirectory}) { if (!std::regex_search(file.path().string(), regex)) { continue; } - std::ifstream inputfile(file.path().string()); + std::ifstream inputfile(file.path().string()); boost::json::error_code ec; std::vector values = readJsons(inputfile, ec); - for (const boost::json::value & value : values) + for (const boost::json::value& value : values) { std::vector localVector = boost::json::value_to>(value); reconstructedPairs.insert(reconstructedPairs.end(), localVector.begin(), localVector.end()); } } - - //Check all pairs + // Check all pairs ALICEVISION_LOG_INFO("Give a score to all pairs"); int count = 0; @@ -417,7 +424,7 @@ int aliceVision_main(int argc, char** argv) sfm::ReconstructedPair bestPair; std::vector bestUsedTracks; - for (const sfm::ReconstructedPair & pair: reconstructedPairs) + for (const sfm::ReconstructedPair& pair : reconstructedPairs) { std::vector usedTracks; double angle = 0.0; diff --git a/src/software/pipeline/main_sfmTriangulation.cpp b/src/software/pipeline/main_sfmTriangulation.cpp index 694ad8b88a..6d83ade084 100644 --- a/src/software/pipeline/main_sfmTriangulation.cpp +++ b/src/software/pipeline/main_sfmTriangulation.cpp @@ -103,7 +103,7 @@ int aliceVision_main(int argc, char** argv) ("randomSeed", po::value(&randomSeed)->default_value(randomSeed), "This seed value will generate a sequence using a linear random generator. Set -1 to use a random seed."); // clang-format on - + CmdLine cmdline("AliceVision SfM Triangulation"); cmdline.add(requiredParams); cmdline.add(optionalParams); @@ -117,7 +117,8 @@ int aliceVision_main(int argc, char** argv) omp_set_num_threads(hwc.getMaxThreads()); const double defaultLoRansacLocalizationError = 4.0; - if (!robustEstimation::adjustRobustEstimatorThreshold(sfmParams.localizerEstimator, sfmParams.localizerEstimatorError, defaultLoRansacLocalizationError)) + if (!robustEstimation::adjustRobustEstimatorThreshold( + sfmParams.localizerEstimator, sfmParams.localizerEstimatorError, defaultLoRansacLocalizationError)) { return EXIT_FAILURE; } @@ -143,7 +144,8 @@ int aliceVision_main(int argc, char** argv) // matches reading matching::PairwiseMatches pairwiseMatches; - if (!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) + if (!sfm::loadPairwiseMatches( + pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) { ALICEVISION_LOG_ERROR("Unable to load matches."); return EXIT_FAILURE; @@ -164,12 +166,8 @@ int aliceVision_main(int argc, char** argv) sfmParams.minNbObservationsForTriangulation = 0; } - //instantiate an sfmEngine for triangulation only - sfm::ReconstructionEngine_sequentialSfM sfmEngine( - sfmData, - sfmParams, - extraInfoFolder, - (fs::path(extraInfoFolder) / "sfm_log.html").string()); + // instantiate an sfmEngine for triangulation only + sfm::ReconstructionEngine_sequentialSfM sfmEngine(sfmData, sfmParams, extraInfoFolder, (fs::path(extraInfoFolder) / "sfm_log.html").string()); sfmEngine.initRandomSeed(randomSeed); @@ -177,7 +175,7 @@ int aliceVision_main(int argc, char** argv) sfmEngine.setFeatures(&featuresPerView); sfmEngine.setMatches(&pairwiseMatches); - //run the triangulation + // run the triangulation sfmEngine.fuseMatchesIntoTracks(); std::set reconstructedViews = sfmData.getValidViews(); sfmEngine.triangulate({}, reconstructedViews); @@ -198,11 +196,12 @@ int aliceVision_main(int argc, char** argv) // export to disk computed scene (data & visualizable results) ALICEVISION_LOG_INFO("Export SfMData to disk: " + outputSfM); - sfmDataIO::save(sfmEngine.getSfMData(), (fs::path(extraInfoFolder) / ("cloud_and_poses" + sfmParams.sfmStepFileExtension)).string(), sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS | sfmDataIO::STRUCTURE)); + sfmDataIO::save(sfmEngine.getSfMData(), + (fs::path(extraInfoFolder) / ("cloud_and_poses" + sfmParams.sfmStepFileExtension)).string(), + sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS | sfmDataIO::STRUCTURE)); sfmDataIO::save(sfmEngine.getSfMData(), outputSfM, sfmDataIO::ESfMData::ALL); - ALICEVISION_LOG_INFO("Triangulation Done" << std::endl - << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); + ALICEVISION_LOG_INFO("Triangulation Done" << std::endl << "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size()); return EXIT_SUCCESS; } diff --git a/src/software/pipeline/main_sphereDetection.cpp b/src/software/pipeline/main_sphereDetection.cpp index 99b9a10ead..313244693d 100644 --- a/src/software/pipeline/main_sphereDetection.cpp +++ b/src/software/pipeline/main_sphereDetection.cpp @@ -85,7 +85,7 @@ int aliceVision_main(int argc, char** argv) // Load SFMData file sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, inputSfMDataPath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + if (!sfmDataIO::load(sfmData, inputSfMDataPath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_ERROR("The input file '" + inputSfMDataPath + "' cannot be read"); return EXIT_FAILURE; diff --git a/src/software/pipeline/main_texturing.cpp b/src/software/pipeline/main_texturing.cpp index f93cda9248..f887e6c2d7 100644 --- a/src/software/pipeline/main_texturing.cpp +++ b/src/software/pipeline/main_texturing.cpp @@ -36,10 +36,7 @@ using namespace aliceVision; namespace fs = std::filesystem; namespace po = boost::program_options; -fs::path absolutePathNoExt(const fs::path& p) -{ - return p.parent_path() / p.stem(); -} +fs::path absolutePathNoExt(const fs::path& p) { return p.parent_path() / p.stem(); } int aliceVision_main(int argc, char* argv[]) { @@ -47,8 +44,8 @@ int aliceVision_main(int argc, char* argv[]) std::string sfmDataFilename; - std::string inputMeshFilepath; // Model to texture (HighPoly for diffuse, LowPoly for Diffuse+Normal) - std::string inputRefMeshFilepath; // HighPoly for NormalMap + std::string inputMeshFilepath; // Model to texture (HighPoly for diffuse, LowPoly for Diffuse+Normal) + std::string inputRefMeshFilepath; // HighPoly for NormalMap aliceVision::mesh::EFileType outputMeshFileType; std::string outputFolder; @@ -166,17 +163,20 @@ int aliceVision_main(int argc, char* argv[]) texParams.outputColorSpace = outputColorSpace; texParams.correctEV = mvsUtils::ECorrectEV::NO_CORRECTION; - if(correctEV) { texParams.correctEV = mvsUtils::ECorrectEV::APPLY_CORRECTION; } + if (correctEV) + { + texParams.correctEV = mvsUtils::ECorrectEV::APPLY_CORRECTION; + } // read the input SfM scene sfmData::SfMData sfmData; - if(!sfmDataFilename.empty()) + if (!sfmDataFilename.empty()) { ALICEVISION_LOG_INFO("Load dense point cloud."); - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL_DENSE)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL_DENSE)) { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; } } @@ -199,7 +199,7 @@ int aliceVision_main(int argc, char* argv[]) mvsUtils::createRefMeshFromDenseSfMData(refMesh, sfmData, mp); // generate UVs if necessary - if(!mesh.hasUVs()) + if (!mesh.hasUVs()) { // Need visibilities to compute unwrap mesh.remapVisibilities(texParams.visibilityRemappingMethod, mp, refMesh); @@ -208,7 +208,7 @@ int aliceVision_main(int argc, char* argv[]) ALICEVISION_LOG_INFO("Unwrapping done."); } - if(texParams.subdivisionTargetRatio > 0) + if (texParams.subdivisionTargetRatio > 0) { const bool remapVisibilities = false; const int nbSubdiv = mesh.mesh->subdivideMesh(refMesh, texParams.subdivisionTargetRatio, remapVisibilities); @@ -220,7 +220,7 @@ int aliceVision_main(int argc, char* argv[]) mesh.mesh->pointsVisibilities.clear(); } - if(mesh.mesh->pointsVisibilities.empty()) + if (mesh.mesh->pointsVisibilities.empty()) { mesh.remapVisibilities(texParams.visibilityRemappingMethod, mp, refMesh); @@ -229,16 +229,15 @@ int aliceVision_main(int argc, char* argv[]) } // generate diffuse textures - if(!inputMeshFilepath.empty() && !sfmDataFilename.empty() && texParams.textureFileType != image::EImageFileType::NONE) + if (!inputMeshFilepath.empty() && !sfmDataFilename.empty() && texParams.textureFileType != image::EImageFileType::NONE) { ALICEVISION_LOG_INFO("Generate textures."); mesh.generateTextures(mp, outputFolder, hwc.getMaxMemory(), texParams.textureFileType); } - - if(!inputRefMeshFilepath.empty() && !inputMeshFilepath.empty() && - (bumpMappingParams.bumpMappingFileType != image::EImageFileType::NONE || - bumpMappingParams.displacementFileType != image::EImageFileType::NONE)) + if (!inputRefMeshFilepath.empty() && !inputMeshFilepath.empty() && + (bumpMappingParams.bumpMappingFileType != image::EImageFileType::NONE || + bumpMappingParams.displacementFileType != image::EImageFileType::NONE)) { ALICEVISION_LOG_INFO("Generate height and normal maps."); @@ -249,7 +248,7 @@ int aliceVision_main(int argc, char* argv[]) } // save final obj file - if(!inputMeshFilepath.empty()) + if (!inputMeshFilepath.empty()) { mesh.saveAs(outputFolder, "texturedMesh", outputMeshFileType); } diff --git a/src/software/pipeline/main_tracksBuilding.cpp b/src/software/pipeline/main_tracksBuilding.cpp index 7ec25e01a9..32aa055e45 100644 --- a/src/software/pipeline/main_tracksBuilding.cpp +++ b/src/software/pipeline/main_tracksBuilding.cpp @@ -33,7 +33,6 @@ using namespace aliceVision; namespace po = boost::program_options; - int aliceVision_main(int argc, char** argv) { // command-line parameters @@ -86,7 +85,7 @@ int aliceVision_main(int argc, char** argv) cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } @@ -97,20 +96,19 @@ int aliceVision_main(int argc, char** argv) // load input SfMData scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); return EXIT_FAILURE; } - // get imageDescriber type const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); // features reading feature::FeaturesPerView featuresPerView; ALICEVISION_LOG_INFO("Load features"); - if(!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) + if (!sfm::loadFeaturesPerView(featuresPerView, sfmData, featuresFolders, describerTypes)) { ALICEVISION_LOG_ERROR("Invalid features."); return EXIT_FAILURE; @@ -119,13 +117,14 @@ int aliceVision_main(int argc, char** argv) // matches reading matching::PairwiseMatches pairwiseMatches; ALICEVISION_LOG_INFO("Load features matches"); - if(!sfm::loadPairwiseMatches(pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) + if (!sfm::loadPairwiseMatches( + pairwiseMatches, sfmData, matchesFolders, describerTypes, maxNbMatches, minNbMatches, useOnlyMatchesFromInputFolder)) { ALICEVISION_LOG_ERROR("Unable to load matches."); return EXIT_FAILURE; } - //Create tracks + // Create tracks track::TracksBuilder tracksBuilder; ALICEVISION_LOG_INFO("Track building"); tracksBuilder.build(pairwiseMatches); diff --git a/src/software/utils/main_applyCalibration.cpp b/src/software/utils/main_applyCalibration.cpp index 48b6201295..3f8ff34281 100644 --- a/src/software/utils/main_applyCalibration.cpp +++ b/src/software/utils/main_applyCalibration.cpp @@ -26,7 +26,7 @@ namespace po = boost::program_options; using namespace aliceVision; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { // command-line parameters std::string sfmDataFilename; @@ -43,7 +43,7 @@ int aliceVision_main(int argc, char **argv) ("calibration,c", po::value(&sfmDataCalibratedFilename)->required(), "Calibrated SfMData scene."); // clang-format on - + CmdLine cmdline("AliceVision applyCalibration"); cmdline.add(requiredParams); if (!cmdline.execute(argc, argv)) @@ -120,7 +120,8 @@ int aliceVision_main(int argc, char **argv) // Copy calibrated sub-poses for (std::size_t idx = 0; idx < subPoses.size(); ++idx) { - if (calibratedSubPoses[idx].status != sfmData::ERigSubPoseStatus::CONSTANT) continue; + if (calibratedSubPoses[idx].status != sfmData::ERigSubPoseStatus::CONSTANT) + continue; subPoses[idx] = calibratedSubPoses[idx]; subPoses[idx].status = sfmData::ERigSubPoseStatus::ESTIMATED; @@ -153,7 +154,8 @@ int aliceVision_main(int argc, char **argv) IndexT subPoseId = UndefinedIndexT; for (const auto& [viewId, view] : sfmData.getViews()) { - if (view->getIntrinsicId() != intrinsicId) continue; + if (view->getIntrinsicId() != intrinsicId) + continue; subPoseId = view->getSubPoseId(); break; @@ -162,13 +164,16 @@ int aliceVision_main(int argc, char **argv) bool found = false; for (const auto& [calibIntrId, calibIntr] : calibratedIntrinsics) { - if (found) break; + if (found) + break; for (const auto& [viewId, view] : sfmDataCalibrated.getViews()) { - if (view->getIntrinsicId() != calibIntrId) continue; + if (view->getIntrinsicId() != calibIntrId) + continue; - if (view->getSubPoseId() != subPoseId) continue; + if (view->getSubPoseId() != subPoseId) + continue; calibratedIntrinsic = std::dynamic_pointer_cast(calibIntr); found = true; @@ -176,17 +181,18 @@ int aliceVision_main(int argc, char **argv) } } } - + if (!calibratedIntrinsic) { ALICEVISION_LOG_ERROR("Unable to find a corresponding calibrated intrinsic"); return EXIT_FAILURE; } - + const bool isIntrinsicCalibrated = calibratedIntrinsic->getInitializationMode() == camera::EInitMode::CALIBRATED; const bool isDistortionCalibrated = calibratedIntrinsic->getDistortionInitializationMode() == camera::EInitMode::CALIBRATED; - if (!isIntrinsicCalibrated && !isDistortionCalibrated) continue; + if (!isIntrinsicCalibrated && !isDistortionCalibrated) + continue; // Aspect ratio of input intrinsic const unsigned int width = intrinsic->w(); @@ -206,9 +212,7 @@ int aliceVision_main(int argc, char **argv) } // Copy original intrinsic - auto newIntrinsic = - std::dynamic_pointer_cast( - camera::createIntrinsic(intrinsic->getType())); + auto newIntrinsic = std::dynamic_pointer_cast(camera::createIntrinsic(intrinsic->getType())); newIntrinsic->assign(*intrinsic); if (isIntrinsicCalibrated) @@ -230,8 +234,7 @@ int aliceVision_main(int argc, char **argv) // Use calibrated distortion newIntrinsic->setDistortionObject(nullptr); auto calibratedUndistortion = calibratedIntrinsic->getUndistortion(); - auto undistortion = camera::createUndistortion( - calibratedUndistortion->getType()); + auto undistortion = camera::createUndistortion(calibratedUndistortion->getType()); undistortion->setSize(width, height); undistortion->setParameters(calibratedUndistortion->getParameters()); newIntrinsic->setUndistortionObject(undistortion); diff --git a/src/software/utils/main_colorCheckerCorrection.cpp b/src/software/utils/main_colorCheckerCorrection.cpp index 80719d7628..4f6a1da0b6 100644 --- a/src/software/utils/main_colorCheckerCorrection.cpp +++ b/src/software/utils/main_colorCheckerCorrection.cpp @@ -58,7 +58,7 @@ struct CChecker _colorData = cv::Mat::zeros(24, 1, CV_64FC3); int i = 0; - for(const bpt::ptree::value_type& row : ccheckerPTree.second.get_child("colors")) + for (const bpt::ptree::value_type& row : ccheckerPTree.second.get_child("colors")) { cv::Vec3d* rowPtr = _colorData.ptr(i); cv::Vec3d& matPixel = rowPtr[0]; @@ -78,19 +78,20 @@ void processColorCorrection(image::Image& image, cv::Mat& ref cv::ccm::ColorCorrectionModel model(refColors, cv::ccm::COLORCHECKER_Macbeth); model.run(); - + model.setColorSpace(cv::ccm::COLOR_SPACE_sRGB); - //model.setCCM_TYPE(cv::ccm::CCM_3x3); - //model.setDistance(cv::ccm::DISTANCE_CIE2000); - //model.setLinear(cv::ccm::LINEARIZATION_GAMMA); - //model.setLinearGamma(2.2); - //model.setLinearDegree(3); // to prevent overfitting - + // model.setCCM_TYPE(cv::ccm::CCM_3x3); + // model.setDistance(cv::ccm::DISTANCE_CIE2000); + // model.setLinear(cv::ccm::LINEARIZATION_GAMMA); + // model.setLinearGamma(2.2); + // model.setLinearDegree(3); // to prevent overfitting + cv::Mat img; cvtColor(imageBGR, img, cv::COLOR_BGR2RGB); img.convertTo(img, CV_64F); - cv::Mat calibratedImage = model.infer(img, true); // make correction using cc matrix and assuming images are in linear color space (as RAW for example) + cv::Mat calibratedImage = + model.infer(img, true); // make correction using cc matrix and assuming images are in linear color space (as RAW for example) calibratedImage.convertTo(calibratedImage, CV_32FC3); @@ -100,8 +101,9 @@ void processColorCorrection(image::Image& image, cv::Mat& ref image::cvMatBGRToImageRGBA(outImg, image); } - -void saveImage(image::Image& image, const std::string& inputPath, const std::string& outputPath, +void saveImage(image::Image& image, + const std::string& inputPath, + const std::string& outputPath, const image::EStorageDataType storageDataType) { // Read metadata path @@ -109,7 +111,7 @@ void saveImage(image::Image& image, const std::string& inputP const std::string outExtension = boost::to_lower_copy(fs::path(outputPath).extension().string()); const bool isEXR = (outExtension == ".exr"); - + // Metadata are extracted from the original images metadataFilePath = inputPath; @@ -118,7 +120,7 @@ void saveImage(image::Image& image, const std::string& inputP image::ImageWriteOptions options; - if(isEXR) + if (isEXR) { // Select storage data type options.storageDataType(storageDataType); @@ -130,7 +132,6 @@ void saveImage(image::Image& image, const std::string& inputP image::writeImage(outputPath, image, options, metadata); } - int aliceVision_main(int argc, char** argv) { // command-line parameters @@ -169,14 +170,14 @@ int aliceVision_main(int argc, char** argv) } // check user choose an input - if(inputExpression.empty()) + if (inputExpression.empty()) { ALICEVISION_LOG_ERROR("Program need --input option." << std::endl << "No input images here."); return EXIT_FAILURE; } - if(fs::exists(inputData)) + if (fs::exists(inputData)) { // checkers collection std::vector ccheckers; @@ -185,7 +186,7 @@ int aliceVision_main(int argc, char** argv) bpt::ptree data; bpt::read_json(inputData, data); - for(const bpt::ptree::value_type& ccheckerPTree : data.get_child("checkers")) + for (const bpt::ptree::value_type& ccheckerPTree : data.get_child("checkers")) ccheckers.push_back(CChecker(ccheckerPTree)); // for now the program behaves as if all the images to process are sharing the same properties @@ -197,11 +198,11 @@ int aliceVision_main(int argc, char** argv) // Check if sfmInputDataFilename exist and is recognized as sfm data file const std::string inputExt = boost::to_lower_copy(fs::path(inputExpression).extension().string()); static const std::array sfmSupportedExtensions = {".sfm", ".json", ".abc"}; - if(!inputExpression.empty() && std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), - inputExt) != sfmSupportedExtensions.end()) + if (!inputExpression.empty() && + std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), inputExt) != sfmSupportedExtensions.end()) { sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, inputExpression, sfmDataIO::VIEWS)) + if (!sfmDataIO::load(sfmData, inputExpression, sfmDataIO::VIEWS)) { ALICEVISION_LOG_ERROR("The input SfMData file '" << inputExpression << "' cannot be read."); return EXIT_FAILURE; @@ -211,7 +212,7 @@ int aliceVision_main(int argc, char** argv) std::unordered_map ViewPaths; // Iterate over all views - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { const sfmData::View& view = *(viewIt.second); @@ -221,7 +222,7 @@ int aliceVision_main(int argc, char** argv) const int size = ViewPaths.size(); int i = 0; - for(auto& viewIt : ViewPaths) + for (auto& viewIt : ViewPaths) { const IndexT viewId = viewIt.first; const std::string viewPath = viewIt.second; @@ -230,8 +231,7 @@ int aliceVision_main(int argc, char** argv) const fs::path fsPath = viewPath; const std::string fileExt = fsPath.extension().string(); const std::string outputExt = extension.empty() ? fileExt : (std::string(".") + extension); - const std::string outputfilePath = - (fs::path(outputPath) / (std::to_string(viewId) + outputExt)).generic_string(); + const std::string outputfilePath = (fs::path(outputPath) / (std::to_string(viewId) + outputExt)).generic_string(); ALICEVISION_LOG_INFO(++i << "/" << size << " - Process view '" << viewId << "' for color correction."); @@ -256,9 +256,8 @@ int aliceVision_main(int argc, char** argv) } // Save sfmData with modified path to images - const std::string sfmfilePath = - (fs::path(outputPath) / fs::path(inputExpression).filename()).generic_string(); - if(!sfmDataIO::save(sfmData, sfmfilePath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + const std::string sfmfilePath = (fs::path(outputPath) / fs::path(inputExpression).filename()).generic_string(); + if (!sfmDataIO::save(sfmData, sfmfilePath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The output SfMData file '" << sfmfilePath << "' cannot be written."); @@ -271,7 +270,7 @@ int aliceVision_main(int argc, char** argv) const fs::path inputPath(inputExpression); std::vector filesStrPaths; - if(fs::is_regular_file(inputPath)) + if (fs::is_regular_file(inputPath)) { filesStrPaths.push_back(inputPath.string()); } @@ -281,20 +280,17 @@ int aliceVision_main(int argc, char** argv) const std::regex regex = utils::filterToRegex(inputExpression); // Get supported files in inputPath directory which matches our regex filter - filesStrPaths = utils::getFilesPathsFromFolder( - inputPath.parent_path().generic_string(), [®ex](const fs::path& path) { - return image::isSupported(path.extension().string()) && - std::regex_match(path.generic_string(), regex); - }); + filesStrPaths = utils::getFilesPathsFromFolder(inputPath.parent_path().generic_string(), [®ex](const fs::path& path) { + return image::isSupported(path.extension().string()) && std::regex_match(path.generic_string(), regex); + }); } const int size = filesStrPaths.size(); - if(!size) + if (!size) { ALICEVISION_LOG_ERROR("Any images was found."); - ALICEVISION_LOG_ERROR("Input folders or input expression '" << inputExpression - << "' may be incorrect ?"); + ALICEVISION_LOG_ERROR("Input folders or input expression '" << inputExpression << "' may be incorrect ?"); return EXIT_FAILURE; } else @@ -303,7 +299,7 @@ int aliceVision_main(int argc, char** argv) } int i = 0; - for(const std::string& inputFilePath : filesStrPaths) + for (const std::string& inputFilePath : filesStrPaths) { const fs::path path = fs::path(inputFilePath); const std::string filename = path.stem().string(); @@ -311,8 +307,7 @@ int aliceVision_main(int argc, char** argv) const std::string outputExt = extension.empty() ? fileExt : (std::string(".") + extension); const std::string outputFilePath = (fs::path(outputPath) / (filename + outputExt)).generic_string(); - ALICEVISION_LOG_INFO(++i << "/" << size << " - Process image '" << filename << fileExt - << "' for color correction."); + ALICEVISION_LOG_INFO(++i << "/" << size << " - Process image '" << filename << fileExt << "' for color correction."); // Read original image image::Image image; diff --git a/src/software/utils/main_colorCheckerDetection.cpp b/src/software/utils/main_colorCheckerDetection.cpp index bb7274f711..61ca36fbad 100644 --- a/src/software/utils/main_colorCheckerDetection.cpp +++ b/src/software/utils/main_colorCheckerDetection.cpp @@ -44,16 +44,17 @@ namespace po = boost::program_options; // Match values used in OpenCV MCC // See https://github.com/opencv/opencv_contrib/blob/342f8924cca88fe6ce979024b7776f6815c89978/modules/mcc/src/dictionary.hpp#L72 -const std::vector< cv::Point2f > MACBETH_CCHART_CORNERS_POS = { - {0.f, 0.f}, {1675.f, 0.f}, {1675.f, 1125.f}, {0.f, 1125.f}, +const std::vector MACBETH_CCHART_CORNERS_POS = { + {0.f, 0.f}, + {1675.f, 0.f}, + {1675.f, 1125.f}, + {0.f, 1125.f}, }; -const std::vector< cv::Point2f > MACBETH_CCHART_CELLS_POS_CENTER = { - {150.f, 150.f}, {425.f, 150.f}, {700.f, 150.f}, {975.f, 150.f}, {1250.f, 150.f}, {1525.f, 150.f}, - {150.f, 425.f}, {425.f, 425.f}, {700.f, 425.f}, {975.f, 425.f}, {1250.f, 425.f}, {1525.f, 425.f}, - {150.f, 700.f}, {425.f, 700.f}, {700.f, 700.f}, {975.f, 700.f}, {1250.f, 700.f}, {1525.f, 700.f}, - {150.f, 975.f}, {425.f, 975.f}, {700.f, 975.f}, {975.f, 975.f}, {1250.f, 975.f}, {1525.f, 975.f} -}; +const std::vector MACBETH_CCHART_CELLS_POS_CENTER = { + {150.f, 150.f}, {425.f, 150.f}, {700.f, 150.f}, {975.f, 150.f}, {1250.f, 150.f}, {1525.f, 150.f}, {150.f, 425.f}, {425.f, 425.f}, + {700.f, 425.f}, {975.f, 425.f}, {1250.f, 425.f}, {1525.f, 425.f}, {150.f, 700.f}, {425.f, 700.f}, {700.f, 700.f}, {975.f, 700.f}, + {1250.f, 700.f}, {1525.f, 700.f}, {150.f, 975.f}, {425.f, 975.f}, {700.f, 975.f}, {975.f, 975.f}, {1250.f, 975.f}, {1525.f, 975.f}}; const float MACBETH_CCHART_CELLS_SIZE = 250.f * .5f; @@ -71,7 +72,7 @@ struct QuadSVG ALICEVISION_LOG_ERROR("Invalid color checker box: size is not equal to 4"); exit(EXIT_FAILURE); } - for(const auto& p : points) + for (const auto& p : points) { xCoords.push_back(p.x); yCoords.push_back(p.y); @@ -93,24 +94,24 @@ struct QuadSVG } }; -void drawSVG(const cv::Ptr &checker, const std::string& outputPath) +void drawSVG(const cv::Ptr& checker, const std::string& outputPath) { - std::vector< QuadSVG > quadsToDraw; + std::vector quadsToDraw; // Push back the quad representing the color checker - quadsToDraw.push_back( QuadSVG(checker->getBox()) ); + quadsToDraw.push_back(QuadSVG(checker->getBox())); // Transform matrix from 'theoric' to 'measured' - cv::Matx33f tMatrix = cv::getPerspectiveTransform(MACBETH_CCHART_CORNERS_POS,checker->getBox()); + cv::Matx33f tMatrix = cv::getPerspectiveTransform(MACBETH_CCHART_CORNERS_POS, checker->getBox()); // Push back quads representing color checker cells for (const auto& center : MACBETH_CCHART_CELLS_POS_CENTER) { QuadSVG quad({ - cv::Point2f( center.x - MACBETH_CCHART_CELLS_SIZE * .5, center.y - MACBETH_CCHART_CELLS_SIZE * .5 ), - cv::Point2f( center.x + MACBETH_CCHART_CELLS_SIZE * .5, center.y - MACBETH_CCHART_CELLS_SIZE * .5 ), - cv::Point2f( center.x + MACBETH_CCHART_CELLS_SIZE * .5, center.y + MACBETH_CCHART_CELLS_SIZE * .5 ), - cv::Point2f( center.x - MACBETH_CCHART_CELLS_SIZE * .5, center.y + MACBETH_CCHART_CELLS_SIZE * .5 ), + cv::Point2f(center.x - MACBETH_CCHART_CELLS_SIZE * .5, center.y - MACBETH_CCHART_CELLS_SIZE * .5), + cv::Point2f(center.x + MACBETH_CCHART_CELLS_SIZE * .5, center.y - MACBETH_CCHART_CELLS_SIZE * .5), + cv::Point2f(center.x + MACBETH_CCHART_CELLS_SIZE * .5, center.y + MACBETH_CCHART_CELLS_SIZE * .5), + cv::Point2f(center.x - MACBETH_CCHART_CELLS_SIZE * .5, center.y + MACBETH_CCHART_CELLS_SIZE * .5), }); quad.transform(tMatrix); quadsToDraw.push_back(quad); @@ -119,10 +120,7 @@ void drawSVG(const cv::Ptr &checker, const std::string& outpu svg::svgDrawer svgSurface; for (const auto& quad : quadsToDraw) { - svgSurface.drawPolyline( - quad.xCoords.begin(), quad.xCoords.end(), - quad.yCoords.begin(), quad.yCoords.end(), - svg::svgStyle().stroke("red", 2)); + svgSurface.drawPolyline(quad.xCoords.begin(), quad.xCoords.end(), quad.yCoords.begin(), quad.yCoords.end(), svg::svgStyle().stroke("red", 2)); } std::ofstream svgFile(outputPath); @@ -130,8 +128,8 @@ void drawSVG(const cv::Ptr &checker, const std::string& outpu svgFile.close(); } - -struct ImageOptions { +struct ImageOptions +{ fs::path imgFsPath; std::string viewId; std::string lensSerialNumber; @@ -139,15 +137,14 @@ struct ImageOptions { image::ImageReadOptions readOptions; }; - -struct CCheckerDetectionSettings { +struct CCheckerDetectionSettings +{ cv::mcc::TYPECHART typechart; unsigned int maxCountByImage; std::string outputData; bool debug; }; - struct Quad { struct BoundingBox @@ -156,8 +153,8 @@ struct Quad cv::Point2f max; BoundingBox() - : min(cv::Point2f(std::numeric_limits::infinity(), std::numeric_limits::infinity())) - , max(cv::Point2f(0.f, 0.f)) + : min(cv::Point2f(std::numeric_limits::infinity(), std::numeric_limits::infinity())), + max(cv::Point2f(0.f, 0.f)) {} float sizeX() const { return max.x - min.x; } @@ -166,52 +163,52 @@ struct Quad bool contains(cv::Point2f p) const { return contains(p.x, p.y); } }; - std::vector< cv::Point > _corners; + std::vector _corners; BoundingBox _bbox; Quad() = default; Quad(const std::vector& corners) { - if(corners.size() != 4) + if (corners.size() != 4) { ALICEVISION_LOG_ERROR("Invalid points count, expected 4."); exit(EXIT_FAILURE); } - for(const auto& c : corners) + for (const auto& c : corners) _corners.push_back(c); updateBBox(); } Quad(cv::Point2f center, float halfHeight, float halfWidth) - : _corners( std::vector< cv::Point >(4) ) + : _corners(std::vector(4)) { - _corners[0] = cv::Point2f( center.x - halfWidth, center.y - halfHeight ); - _corners[1] = cv::Point2f( center.x + halfWidth, center.y - halfHeight ); - _corners[2] = cv::Point2f( center.x + halfWidth, center.y + halfHeight ); - _corners[3] = cv::Point2f( center.x - halfWidth, center.y + halfHeight ); + _corners[0] = cv::Point2f(center.x - halfWidth, center.y - halfHeight); + _corners[1] = cv::Point2f(center.x + halfWidth, center.y - halfHeight); + _corners[2] = cv::Point2f(center.x + halfWidth, center.y + halfHeight); + _corners[3] = cv::Point2f(center.x - halfWidth, center.y + halfHeight); updateBBox(); } void updateBBox() { - for(auto const &c : _corners) + for (auto const& c : _corners) { - if(c.x < _bbox.min.x) + if (c.x < _bbox.min.x) _bbox.min.x = c.x; - if(c.y < _bbox.min.y) + if (c.y < _bbox.min.y) _bbox.min.y = c.y; - if(c.x > _bbox.max.x) + if (c.x > _bbox.max.x) _bbox.max.x = c.x; - if(c.y > _bbox.max.y) + if (c.y > _bbox.max.y) _bbox.max.y = c.y; } } void transform(const cv::Matx33f& transformMatrix) { - for (auto &c : _corners) + for (auto& c : _corners) { cv::Point3f p(c.x, c.y, 1.); p = transformMatrix * p; @@ -222,7 +219,7 @@ struct Quad void translate(float dx, float dy) { - for (auto &c : _corners) + for (auto& c : _corners) { c.x += dx; c.y += dy; @@ -230,37 +227,32 @@ struct Quad } }; - -struct MacbethCCheckerQuad : Quad { +struct MacbethCCheckerQuad : Quad +{ cv::Ptr _cchecker; cv::Mat _colorData; cv::Mat _transformMat; - std::vector< cv::Mat > _cellMasks; + std::vector _cellMasks; ImageOptions _imgOpt; MacbethCCheckerQuad() = default; - MacbethCCheckerQuad( - cv::Ptr cchecker, - const image::Image &img, - const ImageOptions& imgOpt) - : Quad(cchecker->getBox()) - , _cchecker(cchecker) - , _imgOpt(imgOpt) - , _cellMasks(std::vector< cv::Mat >(24)) + MacbethCCheckerQuad(cv::Ptr cchecker, const image::Image& img, const ImageOptions& imgOpt) + : Quad(cchecker->getBox()), + _cchecker(cchecker), + _imgOpt(imgOpt), + _cellMasks(std::vector(24)) { // Transform matrix from 'theoric' to 'measured' _transformMat = cv::getPerspectiveTransform(MACBETH_CCHART_CORNERS_POS, _cchecker->getBox()); // Create an image boolean mask for each cchecker cell - for(int i = 0; i < _cellMasks.size(); ++i) + for (int i = 0; i < _cellMasks.size(); ++i) { _cellMasks[i] = cv::Mat::zeros(_bbox.sizeY(), _bbox.sizeX(), CV_8UC1); - Quad q(MACBETH_CCHART_CELLS_POS_CENTER[i], - MACBETH_CCHART_CELLS_SIZE * .5, - MACBETH_CCHART_CELLS_SIZE * .5); + Quad q(MACBETH_CCHART_CELLS_POS_CENTER[i], MACBETH_CCHART_CELLS_SIZE * .5, MACBETH_CCHART_CELLS_SIZE * .5); q.transform(_transformMat); - q.translate(- _bbox.min.x, - _bbox.min.y); + q.translate(-_bbox.min.x, -_bbox.min.y); cv::fillConvexPoly(_cellMasks[i], q._corners.data(), 4, cv::Scalar(255)); } @@ -268,25 +260,25 @@ struct MacbethCCheckerQuad : Quad { _colorData = cv::Mat::zeros(24, 1, CV_64FC3); cv::Mat pixelsCount = cv::Mat::zeros(24, 1, CV_32S); - for(int y = 0; y < (int) _bbox.sizeY(); ++y) + for (int y = 0; y < (int)_bbox.sizeY(); ++y) { - for(int x = 0; x < (int) _bbox.sizeX(); ++x) + for (int x = 0; x < (int)_bbox.sizeX(); ++x) { - for(int i = 0; i < _cellMasks.size(); ++i) + for (int i = 0; i < _cellMasks.size(); ++i) { // Check current pixel for the current image mask - if(_cellMasks[i].at(y,x) == 255) + if (_cellMasks[i].at(y, x) == 255) { const image::RGBAfColor& px = img(y + _bbox.min.y, x + _bbox.min.x); - _colorData.at< cv::Vec3d >(i,0) += cv::Vec3d(px.r(), px.g(), px.b()); + _colorData.at(i, 0) += cv::Vec3d(px.r(), px.g(), px.b()); ++pixelsCount.at(i); } } } } - for(int i = 0; i < _colorData.rows; ++i) - _colorData.at(i, 0) /= pixelsCount.at(i); // average value + for (int i = 0; i < _colorData.rows; ++i) + _colorData.at(i, 0) /= pixelsCount.at(i); // average value } bpt::ptree ptree() const @@ -304,7 +296,7 @@ struct MacbethCCheckerQuad : Quad { bpt::ptree ptPoint; ptPoint.put("x", point.x); ptPoint.put("y", point.y); - ptPositions.push_back( std::make_pair("", ptPoint) ); + ptPositions.push_back(std::make_pair("", ptPoint)); } pt.add_child("positions", ptPositions); @@ -312,13 +304,13 @@ struct MacbethCCheckerQuad : Quad { for (int i = 0; i < _transformMat.rows; ++i) { bpt::ptree row; - for(int j = 0; j < _transformMat.cols; ++j) + for (int j = 0; j < _transformMat.cols; ++j) { bpt::ptree cell; cell.put_value(_transformMat.at(i, j)); row.push_back(std::make_pair("", cell)); } - ptTransform.push_back( std::make_pair("", row) ); + ptTransform.push_back(std::make_pair("", row)); } pt.add_child("transform", ptTransform); @@ -326,10 +318,10 @@ struct MacbethCCheckerQuad : Quad { for (int i = 0; i < _colorData.rows; ++i) { bpt::ptree ptColor; - ptColor.put("r", _colorData.at(i,0)[0]); - ptColor.put("g", _colorData.at(i,0)[1]); - ptColor.put("b", _colorData.at(i,0)[2]); - ptColors.push_back( std::make_pair("", ptColor) ); + ptColor.put("r", _colorData.at(i, 0)[0]); + ptColor.put("g", _colorData.at(i, 0)[1]); + ptColor.put("b", _colorData.at(i, 0)[2]); + ptColors.push_back(std::make_pair("", ptColor)); } pt.add_child("colors", ptColors); @@ -337,11 +329,7 @@ struct MacbethCCheckerQuad : Quad { } }; - -void detectColorChecker( - std::vector &detectedCCheckers, - ImageOptions& imgOpt, - CCheckerDetectionSettings &settings) +void detectColorChecker(std::vector& detectedCCheckers, ImageOptions& imgOpt, CCheckerDetectionSettings& settings) { const std::string outputFolder = fs::path(settings.outputData).parent_path().string() + "/"; const std::string imgSrcPath = imgOpt.imgFsPath.string(); @@ -353,15 +341,16 @@ void detectColorChecker( image::readImage(imgSrcPath, img, imgOpt.readOptions); cv::Mat imgBGR = image::imageRGBAToCvMatBGR(img, CV_8UC3); - if(imgBGR.cols == 0 || imgBGR.rows == 0) + if (imgBGR.cols == 0 || imgBGR.rows == 0) { - ALICEVISION_LOG_ERROR("Image at: '" << imgSrcPath << "'.\n" << "is empty."); + ALICEVISION_LOG_ERROR("Image at: '" << imgSrcPath << "'.\n" + << "is empty."); exit(EXIT_FAILURE); } cv::Ptr detector = cv::mcc::CCheckerDetector::create(); - if(!detector->process(imgBGR, settings.typechart, settings.maxCountByImage)) + if (!detector->process(imgBGR, settings.typechart, settings.maxCountByImage)) { ALICEVISION_LOG_INFO("Checker not detected in image at: '" << imgSrcPath << "'"); return; @@ -369,16 +358,16 @@ void detectColorChecker( int counter = 0; - for(const cv::Ptr cchecker : detector->getListColorChecker()) + for (const cv::Ptr cchecker : detector->getListColorChecker()) { const std::string counterStr = "_" + std::to_string(++counter); - ALICEVISION_LOG_INFO("Checker #" << counter <<" successfully detected in '" << imgSrcStem << "'"); + ALICEVISION_LOG_INFO("Checker #" << counter << " successfully detected in '" << imgSrcStem << "'"); MacbethCCheckerQuad ccq(cchecker, img, imgOpt); detectedCCheckers.push_back(ccq); - - if(settings.debug) + + if (settings.debug) { // Output debug data drawSVG(cchecker, outputFolder + imgDestStem + counterStr + ".svg"); @@ -396,7 +385,6 @@ void detectColorChecker( } } - int aliceVision_main(int argc, char** argv) { // command-line parameters @@ -439,12 +427,12 @@ int aliceVision_main(int argc, char** argv) settings.outputData = outputData; settings.debug = debug; - std::vector< MacbethCCheckerQuad > detectedCCheckers; + std::vector detectedCCheckers; // Check if inputExpression is recognized as sfm data file const std::string inputExt = boost::to_lower_copy(fs::path(inputExpression).extension().string()); static const std::array sfmSupportedExtensions = {".sfm", ".abc"}; - if(std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), inputExt) != sfmSupportedExtensions.end()) + if (std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), inputExt) != sfmSupportedExtensions.end()) { // load input as sfm data file sfmData::SfMData sfmData; @@ -457,21 +445,19 @@ int aliceVision_main(int argc, char** argv) int counter = 0; // Detect color checker for each images - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { const sfmData::View& view = *(viewIt.second); ALICEVISION_LOG_INFO(++counter << "/" << sfmData.getViews().size() << " - Process image at: '" << view.getImage().getImagePath() << "'."); - ImageOptions imgOpt = { - view.getImage().getImagePath(), - std::to_string(view.getViewId()), - view.getImage().getMetadataBodySerialNumber(), - view.getImage().getMetadataLensSerialNumber() }; + ImageOptions imgOpt = {view.getImage().getImagePath(), + std::to_string(view.getViewId()), + view.getImage().getMetadataBodySerialNumber(), + view.getImage().getMetadataLensSerialNumber()}; imgOpt.readOptions.workingColorSpace = image::EImageColorSpace::SRGB; imgOpt.readOptions.rawColorInterpretation = image::ERawColorInterpretation_stringToEnum(view.getImage().getRawColorInterpretation()); detectColorChecker(detectedCCheckers, imgOpt, settings); } - } else { @@ -479,7 +465,7 @@ int aliceVision_main(int argc, char** argv) const fs::path inputPath(inputExpression); std::vector filesStrPaths; - if(fs::is_regular_file(inputPath)) + if (fs::is_regular_file(inputPath)) { filesStrPaths.push_back(inputPath.string()); } @@ -489,16 +475,14 @@ int aliceVision_main(int argc, char** argv) const std::regex regex = utils::filterToRegex(inputExpression); // Get supported files in inputPath directory which matches our regex filter - filesStrPaths = utils::getFilesPathsFromFolder(inputPath.parent_path().generic_string(), - [®ex](const fs::path& path) { - return image::isSupported(path.extension().string()) && std::regex_match(path.generic_string(), regex); - } - ); + filesStrPaths = utils::getFilesPathsFromFolder(inputPath.parent_path().generic_string(), [®ex](const fs::path& path) { + return image::isSupported(path.extension().string()) && std::regex_match(path.generic_string(), regex); + }); } const int size = filesStrPaths.size(); - if(!size) + if (!size) { ALICEVISION_LOG_ERROR("Any images was found."); ALICEVISION_LOG_ERROR("Input folders or input expression '" << inputExpression << "' may be incorrect ?"); @@ -510,7 +494,7 @@ int aliceVision_main(int argc, char** argv) } int counter = 0; - for(const std::string& imgSrcPath : filesStrPaths) + for (const std::string& imgSrcPath : filesStrPaths) { ALICEVISION_LOG_INFO(++counter << "/" << size << " - Process image at: '" << imgSrcPath << "'."); ImageOptions imgOpt; @@ -518,7 +502,6 @@ int aliceVision_main(int argc, char** argv) imgOpt.readOptions.workingColorSpace = image::EImageColorSpace::SRGB; detectColorChecker(detectedCCheckers, imgOpt, settings); } - } if (detectedCCheckers.empty()) diff --git a/src/software/utils/main_computeUncertainty.cpp b/src/software/utils/main_computeUncertainty.cpp index 79cecaee30..7949913fb3 100644 --- a/src/software/utils/main_computeUncertainty.cpp +++ b/src/software/utils/main_computeUncertainty.cpp @@ -31,15 +31,14 @@ using namespace aliceVision::sfmData; using namespace aliceVision::sfmDataIO; namespace po = boost::program_options; - -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outSfMDataFilename; - std::string outputStats; - std::string algorithm = cov::EAlgorithm_enumToString(cov::eAlgorithmSvdTaylorExpansion); - bool debug = false; + // command-line parameters + std::string sfmDataFilename; + std::string outSfMDataFilename; + std::string outputStats; + std::string algorithm = cov::EAlgorithm_enumToString(cov::eAlgorithmSvdTaylorExpansion); + bool debug = false; // clang-format off params.add_options() @@ -57,104 +56,103 @@ int aliceVision_main(int argc, char **argv) "Verbosity level (fatal, error, warning, info, debug, trace)."); // clang-format on - CmdLine cmdline("AliceVision computeUncertainty"); - cmdline.add(params); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if (sfmDataFilename.empty() || - outSfMDataFilename.empty()) - { - std::cerr << "Invalid input or output filename." << std::endl; - return EXIT_FAILURE; - } - - // Load input scene - SfMData sfmData; - if (!Load(sfmData, sfmDataFilename, ESfMData(ALL))) - { - std::cerr << std::endl - << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; - return EXIT_FAILURE; - } - - ceres::CRSMatrix jacobian; - { - BundleAdjustmentCeres bundleAdjustmentObj; - BundleAdjustment::ERefineOptions refineOptions = BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION | BundleAdjustment::REFINE_STRUCTURE; - bundleAdjustmentObj.createJacobian(sfmData, refineOptions, jacobian); - } - - { - cov::Options options; - // Configure covariance engine (find the indexes of the most distatnt points etc.) - // setPts2Fix(opt, mutable_points.size() / 3, mutable_points.data()); - options._numCams = sfmData.getValidViews().size(); - options._camParams = 6; - options._numPoints = sfmData.getLandmarks().size(); - options._numObs = jacobian.num_rows / 2; - options._algorithm = cov::EAlgorithm_stringToEnum(algorithm); - options._epsilon = 1e-10; - options._lambda = -1; - options._svdRemoveN = -1; - options._maxIterTE = -1; - options._debug = debug; - - cov::Statistic statistic; - std::vector points3D; - points3D.reserve(sfmData.getLandmarks().size() * 3); - for(auto& landmarkIt: sfmData.getLandmarks()) + CmdLine cmdline("AliceVision computeUncertainty"); + cmdline.add(params); + if (!cmdline.execute(argc, argv)) { - double* p = landmarkIt.second.X.data(); - points3D.push_back(p[0]); - points3D.push_back(p[1]); - points3D.push_back(p[2]); + return EXIT_FAILURE; } - cov::Uncertainty uncertainty; + if (sfmDataFilename.empty() || outSfMDataFilename.empty()) + { + std::cerr << "Invalid input or output filename." << std::endl; + return EXIT_FAILURE; + } - getCovariances(options, statistic, jacobian, &points3D[0], uncertainty); + // Load input scene + SfMData sfmData; + if (!Load(sfmData, sfmDataFilename, ESfMData(ALL))) + { + std::cerr << std::endl << "The input SfMData file \"" << sfmDataFilename << "\" cannot be read." << std::endl; + return EXIT_FAILURE; + } - if(!outputStats.empty()) - saveResults(outputStats, options, statistic, uncertainty); + ceres::CRSMatrix jacobian; + { + BundleAdjustmentCeres bundleAdjustmentObj; + BundleAdjustment::ERefineOptions refineOptions = + BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION | BundleAdjustment::REFINE_STRUCTURE; + bundleAdjustmentObj.createJacobian(sfmData, refineOptions, jacobian); + } { - const std::vector posesUncertainty = uncertainty.getCamerasUncEigenValues(); - - std::size_t indexPose = 0; - for (Poses::const_iterator itPose = sfmData.getPoses().begin(); itPose != sfmData.getPoses().end(); ++itPose, ++indexPose) - { - const IndexT idPose = itPose->first; - Vec6& u = sfmData._posesUncertainty[idPose]; // create uncertainty entry - const double* uIn = &posesUncertainty[indexPose*6]; - u << uIn[0], uIn[1], uIn[2], uIn[3], uIn[4], uIn[5]; - } + cov::Options options; + // Configure covariance engine (find the indexes of the most distatnt points etc.) + // setPts2Fix(opt, mutable_points.size() / 3, mutable_points.data()); + options._numCams = sfmData.getValidViews().size(); + options._camParams = 6; + options._numPoints = sfmData.getLandmarks().size(); + options._numObs = jacobian.num_rows / 2; + options._algorithm = cov::EAlgorithm_stringToEnum(algorithm); + options._epsilon = 1e-10; + options._lambda = -1; + options._svdRemoveN = -1; + options._maxIterTE = -1; + options._debug = debug; + + cov::Statistic statistic; + std::vector points3D; + points3D.reserve(sfmData.getLandmarks().size() * 3); + for (auto& landmarkIt : sfmData.getLandmarks()) + { + double* p = landmarkIt.second.X.data(); + points3D.push_back(p[0]); + points3D.push_back(p[1]); + points3D.push_back(p[2]); + } + + cov::Uncertainty uncertainty; + + getCovariances(options, statistic, jacobian, &points3D[0], uncertainty); + + if (!outputStats.empty()) + saveResults(outputStats, options, statistic, uncertainty); + + { + const std::vector posesUncertainty = uncertainty.getCamerasUncEigenValues(); + + std::size_t indexPose = 0; + for (Poses::const_iterator itPose = sfmData.getPoses().begin(); itPose != sfmData.getPoses().end(); ++itPose, ++indexPose) + { + const IndexT idPose = itPose->first; + Vec6& u = sfmData._posesUncertainty[idPose]; // create uncertainty entry + const double* uIn = &posesUncertainty[indexPose * 6]; + u << uIn[0], uIn[1], uIn[2], uIn[3], uIn[4], uIn[5]; + } + } + { + const std::vector landmarksUncertainty = uncertainty.getPointsUncEigenValues(); + + std::size_t indexLandmark = 0; + for (Landmarks::const_iterator itLandmark = sfmData.getLandmarks().begin(); itLandmark != sfmData.getLandmarks().end(); + ++itLandmark, ++indexLandmark) + { + const IndexT idLandmark = itLandmark->first; + Vec3& u = sfmData._landmarksUncertainty[idLandmark]; // create uncertainty entry + const double* uIn = &landmarksUncertainty[indexLandmark * 3]; + u << uIn[0], uIn[1], uIn[2]; + } + } } + + std::cout << "Save into \"" << outSfMDataFilename << "\"" << std::endl; + + // Export the SfMData scene in the expected format + if (!Save(sfmData, outSfMDataFilename, ESfMData(ALL))) { - const std::vector landmarksUncertainty = uncertainty.getPointsUncEigenValues(); - - std::size_t indexLandmark = 0; - for (Landmarks::const_iterator itLandmark = sfmData.getLandmarks().begin(); itLandmark != sfmData.getLandmarks().end(); ++itLandmark, ++indexLandmark) - { - const IndexT idLandmark = itLandmark->first; - Vec3& u = sfmData._landmarksUncertainty[idLandmark]; // create uncertainty entry - const double* uIn = &landmarksUncertainty[indexLandmark*3]; - u << uIn[0], uIn[1], uIn[2]; - } + std::cerr << std::endl << "An error occurred while trying to save \"" << outSfMDataFilename << "\"." << std::endl; + return EXIT_FAILURE; } - } - - std::cout << "Save into \"" << outSfMDataFilename << "\"" << std::endl; - - // Export the SfMData scene in the expected format - if (!Save(sfmData, outSfMDataFilename, ESfMData(ALL))) - { - std::cerr << std::endl - << "An error occurred while trying to save \"" << outSfMDataFilename << "\"." << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_frustumFiltering.cpp b/src/software/utils/main_frustumFiltering.cpp index c8ea73123f..1c40de68ef 100644 --- a/src/software/utils/main_frustumFiltering.cpp +++ b/src/software/utils/main_frustumFiltering.cpp @@ -33,47 +33,45 @@ namespace fs = std::filesystem; /// Build a list of pair that share visibility content from the SfMData structure PairSet BuildPairsFromStructureObservations(const sfmData::SfMData& sfmData) { - PairSet pairs; + PairSet pairs; - for (sfmData::Landmarks::const_iterator itL = sfmData.getLandmarks().begin(); - itL != sfmData.getLandmarks().end(); ++itL) - { - const sfmData::Landmark & landmark = itL->second; - for(const auto& iterI : landmark.getObservations()) + for (sfmData::Landmarks::const_iterator itL = sfmData.getLandmarks().begin(); itL != sfmData.getLandmarks().end(); ++itL) { - const IndexT id_viewI = iterI.first; - sfmData::Observations::const_iterator iterJ = landmark.getObservations().begin(); - std::advance(iterJ, 1); - for (; iterJ != landmark.getObservations().end(); ++iterJ) - { - const IndexT id_viewJ = iterJ->first; - pairs.insert( std::make_pair(id_viewI,id_viewJ)); - } + const sfmData::Landmark& landmark = itL->second; + for (const auto& iterI : landmark.getObservations()) + { + const IndexT id_viewI = iterI.first; + sfmData::Observations::const_iterator iterJ = landmark.getObservations().begin(); + std::advance(iterJ, 1); + for (; iterJ != landmark.getObservations().end(); ++iterJ) + { + const IndexT id_viewJ = iterJ->first; + pairs.insert(std::make_pair(id_viewI, id_viewJ)); + } + } } - } - return pairs; + return pairs; } /// Build a list of pair from the camera frusta intersections -PairSet BuildPairsFromFrustumsIntersections( - const sfmData::SfMData & sfmData, - const double z_near = -1., // default near plane - const double z_far = -1., // default far plane - const std::string& sOutDirectory = "") // output folder to save frustums as PLY +PairSet BuildPairsFromFrustumsIntersections(const sfmData::SfMData& sfmData, + const double z_near = -1., // default near plane + const double z_far = -1., // default far plane + const std::string& sOutDirectory = "") // output folder to save frustums as PLY { - const FrustumFilter frustum_filter(sfmData, z_near, z_far); - if (!sOutDirectory.empty()) - frustum_filter.exportPly((fs::path(sOutDirectory) / "frustums.ply").string()); - return frustum_filter.getFrustumIntersectionPairs(); + const FrustumFilter frustum_filter(sfmData, z_near, z_far); + if (!sOutDirectory.empty()) + frustum_filter.exportPly((fs::path(sOutDirectory) / "frustums.ply").string()); + return frustum_filter.getFrustumIntersectionPairs(); } -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFilename; - double zNear = -1.; - double zFar = -1.; + // command-line parameters + std::string sfmDataFilename; + std::string outputFilename; + double zNear = -1.; + double zFar = -1.; // clang-format off po::options_description requiredParams("Required parameters"); @@ -91,39 +89,39 @@ int aliceVision_main(int argc, char **argv) "Distance of the far camera plane."); // clang-format on - CmdLine cmdline("This program computes camera cones that share some putative visual content.\n" - "AliceVision frustumFiltering"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // check that we can create the output folder - if(!fs::exists(fs::path(outputFilename).parent_path())) - if(!fs::exists(fs::path(outputFilename).parent_path())) - return EXIT_FAILURE; - - // load input SfMData scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - aliceVision::system::Timer timer; - - const PairSet pairs = BuildPairsFromFrustumsIntersections(sfmData, zNear, zFar, fs::path(outputFilename).parent_path().string()); - /*const PairSet pairs = BuildPairsFromStructureObservations(sfm_data); */ - - ALICEVISION_LOG_INFO("# pairs: " << pairs.size()); - ALICEVISION_LOG_INFO("Pair filtering took: " << timer.elapsed() << " s"); - - // export pairs on disk - if (matchingImageCollection::savePairsToFile(outputFilename, pairs)) - return EXIT_SUCCESS; - else - return EXIT_FAILURE; + CmdLine cmdline("This program computes camera cones that share some putative visual content.\n" + "AliceVision frustumFiltering"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // check that we can create the output folder + if (!fs::exists(fs::path(outputFilename).parent_path())) + if (!fs::exists(fs::path(outputFilename).parent_path())) + return EXIT_FAILURE; + + // load input SfMData scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; + } + + aliceVision::system::Timer timer; + + const PairSet pairs = BuildPairsFromFrustumsIntersections(sfmData, zNear, zFar, fs::path(outputFilename).parent_path().string()); + /*const PairSet pairs = BuildPairsFromStructureObservations(sfm_data); */ + + ALICEVISION_LOG_INFO("# pairs: " << pairs.size()); + ALICEVISION_LOG_INFO("Pair filtering took: " << timer.elapsed() << " s"); + + // export pairs on disk + if (matchingImageCollection::savePairsToFile(outputFilename, pairs)) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; } diff --git a/src/software/utils/main_generateSampleScene.cpp b/src/software/utils/main_generateSampleScene.cpp index a62e3ea3cc..bc73ae830f 100644 --- a/src/software/utils/main_generateSampleScene.cpp +++ b/src/software/utils/main_generateSampleScene.cpp @@ -27,8 +27,8 @@ namespace po = boost::program_options; int aliceVision_main(int argc, char** argv) { // command-line parameters - std::string sfmOutputDataFilepath; // output folder for splited images - + std::string sfmOutputDataFilepath; // output folder for splited images + // clang-format off po::options_description requiredParams("Required parameters"); requiredParams.add_options() @@ -48,7 +48,7 @@ int aliceVision_main(int argc, char** argv) sfmDataIO::generateSampleScene(sfmData); ALICEVISION_LOG_INFO("Export SfM: " << sfmOutputDataFilepath); - if(!sfmDataIO::save(sfmData, sfmOutputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::save(sfmData, sfmOutputDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The output SfMData file '" << sfmOutputDataFilepath << "' cannot be write."); return EXIT_FAILURE; diff --git a/src/software/utils/main_hardwareResources.cpp b/src/software/utils/main_hardwareResources.cpp index bd6aebe61e..ceb161739c 100644 --- a/src/software/utils/main_hardwareResources.cpp +++ b/src/software/utils/main_hardwareResources.cpp @@ -17,7 +17,6 @@ #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 @@ -27,40 +26,38 @@ using namespace aliceVision; namespace po = boost::program_options; - -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - CmdLine cmdline("AliceVision hardwareResources"); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // print GPU Information - ALICEVISION_LOG_INFO(gpu::gpuInformationCUDA()); - - system::MemoryInfo memoryInformation = system::getMemoryInfo(); - - ALICEVISION_LOG_INFO("Memory information: " << std::endl << memoryInformation); - - if(memoryInformation.availableRam == 0) - { - ALICEVISION_LOG_WARNING("Cannot find available system memory, this can be due to OS limitation.\n" - "Use only one thread for CPU feature extraction."); - } - else - { - const double oneGB = 1024.0 * 1024.0 * 1024.0; - if(memoryInformation.availableRam < 0.5 * memoryInformation.totalRam) - { - ALICEVISION_LOG_WARNING("More than half of the RAM is used by other applications. It would be more efficient to close them."); - ALICEVISION_LOG_WARNING(" => " - << std::size_t(std::round(double(memoryInformation.totalRam - memoryInformation.availableRam) / oneGB)) - << " GB are used by other applications for a total RAM capacity of " - << std::size_t(std::round(double(memoryInformation.totalRam) / oneGB)) << " GB."); - } - } - - return EXIT_SUCCESS; + // command-line parameters + CmdLine cmdline("AliceVision hardwareResources"); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // print GPU Information + ALICEVISION_LOG_INFO(gpu::gpuInformationCUDA()); + + system::MemoryInfo memoryInformation = system::getMemoryInfo(); + + ALICEVISION_LOG_INFO("Memory information: " << std::endl << memoryInformation); + + if (memoryInformation.availableRam == 0) + { + ALICEVISION_LOG_WARNING("Cannot find available system memory, this can be due to OS limitation.\n" + "Use only one thread for CPU feature extraction."); + } + else + { + const double oneGB = 1024.0 * 1024.0 * 1024.0; + if (memoryInformation.availableRam < 0.5 * memoryInformation.totalRam) + { + ALICEVISION_LOG_WARNING("More than half of the RAM is used by other applications. It would be more efficient to close them."); + ALICEVISION_LOG_WARNING(" => " << std::size_t(std::round(double(memoryInformation.totalRam - memoryInformation.availableRam) / oneGB)) + << " GB are used by other applications for a total RAM capacity of " + << std::size_t(std::round(double(memoryInformation.totalRam) / oneGB)) << " GB."); + } + } + + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_imageProcessing.cpp b/src/software/utils/main_imageProcessing.cpp index fa45e9234d..593099ad94 100644 --- a/src/software/utils/main_imageProcessing.cpp +++ b/src/software/utils/main_imageProcessing.cpp @@ -24,8 +24,8 @@ #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_OPENCV) -#include -#include + #include + #include #endif #include @@ -41,7 +41,6 @@ #include #include - // These constants define the current software version. // They must be updated when the command line is changed. #define ALICEVISION_SOFTWARE_VERSION_MAJOR 3 @@ -107,7 +106,7 @@ std::istream& operator>>(std::istream& in, SharpenParams& sParams) in >> token; std::vector splitParams; boost::split(splitParams, token, boost::algorithm::is_any_of(":")); - if(splitParams.size() != 4) + if (splitParams.size() != 4) throw std::invalid_argument("Failed to parse SharpenParams from: " + token); sParams.enabled = boost::to_lower_copy(splitParams[0]) == "true"; sParams.width = boost::lexical_cast(splitParams[1]); @@ -119,7 +118,7 @@ std::istream& operator>>(std::istream& in, SharpenParams& sParams) inline std::ostream& operator<<(std::ostream& os, const SharpenParams& sParams) { - os << sParams.enabled << ":" << sParams.width << ":" << sParams.contrast << ":"<< sParams.threshold; + os << sParams.enabled << ":" << sParams.width << ":" << sParams.contrast << ":" << sParams.threshold; return os; } @@ -137,7 +136,7 @@ std::istream& operator>>(std::istream& in, BilateralFilterParams& bfParams) in >> token; std::vector splitParams; boost::split(splitParams, token, boost::algorithm::is_any_of(":")); - if(splitParams.size() != 4) + if (splitParams.size() != 4) throw std::invalid_argument("Failed to parse BilateralFilterParams from: " + token); bfParams.enabled = boost::to_lower_copy(splitParams[0]) == "true"; bfParams.distance = boost::lexical_cast(splitParams[1]); @@ -166,7 +165,7 @@ std::istream& operator>>(std::istream& in, ClaheFilterParams& cfParams) in >> token; std::vector splitParams; boost::split(splitParams, token, boost::algorithm::is_any_of(":")); - if(splitParams.size() != 3) + if (splitParams.size() != 3) throw std::invalid_argument("Failed to parse ClaheFilterParams from: " + token); cfParams.enabled = boost::to_lower_copy(splitParams[0]) == "true"; cfParams.clipLimit = boost::lexical_cast(splitParams[1]); @@ -181,15 +180,23 @@ inline std::ostream& operator<<(std::ostream& os, const ClaheFilterParams& cfPar return os; } -enum class ENoiseMethod { uniform, gaussian, salt }; +enum class ENoiseMethod +{ + uniform, + gaussian, + salt +}; inline std::string ENoiseMethod_enumToString(ENoiseMethod noiseMethod) { - switch(noiseMethod) + switch (noiseMethod) { - case ENoiseMethod::uniform: return "uniform"; - case ENoiseMethod::gaussian: return "gaussian"; - case ENoiseMethod::salt: return "salt"; + case ENoiseMethod::uniform: + return "uniform"; + case ENoiseMethod::gaussian: + return "gaussian"; + case ENoiseMethod::salt: + return "salt"; } throw std::invalid_argument("Invalid ENoiseMethod Enum"); } @@ -197,9 +204,12 @@ inline std::string ENoiseMethod_enumToString(ENoiseMethod noiseMethod) inline ENoiseMethod ENoiseMethod_stringToEnum(std::string noiseMethod) { boost::to_lower(noiseMethod); - if(noiseMethod == "uniform") return ENoiseMethod::uniform; - if(noiseMethod == "gaussian") return ENoiseMethod::gaussian; - if(noiseMethod == "salt") return ENoiseMethod::salt; + if (noiseMethod == "uniform") + return ENoiseMethod::uniform; + if (noiseMethod == "gaussian") + return ENoiseMethod::gaussian; + if (noiseMethod == "salt") + return ENoiseMethod::salt; throw std::invalid_argument("Unrecognized noise method '" + noiseMethod + "'"); } @@ -219,7 +229,7 @@ std::istream& operator>>(std::istream& in, NoiseFilterParams& nfParams) in >> token; std::vector splitParams; boost::split(splitParams, token, boost::algorithm::is_any_of(":")); - if(splitParams.size() != 5) + if (splitParams.size() != 5) throw std::invalid_argument("Failed to parse NoiseFilterParams from: " + token); nfParams.enabled = boost::to_lower_copy(splitParams[0]) == "true"; nfParams.method = ENoiseMethod_stringToEnum(splitParams[1]); @@ -231,20 +241,27 @@ std::istream& operator>>(std::istream& in, NoiseFilterParams& nfParams) inline std::ostream& operator<<(std::ostream& os, const NoiseFilterParams& nfParams) { - os << nfParams.enabled << ":" << ENoiseMethod_enumToString(nfParams.method) << ":" << nfParams.A << ":" << nfParams.B - << ":" << nfParams.mono; + os << nfParams.enabled << ":" << ENoiseMethod_enumToString(nfParams.method) << ":" << nfParams.A << ":" << nfParams.B << ":" << nfParams.mono; return os; } -enum class EImageFormat { RGBA, RGB, Grayscale }; +enum class EImageFormat +{ + RGBA, + RGB, + Grayscale +}; inline std::string EImageFormat_enumToString(EImageFormat imageFormat) { - switch(imageFormat) + switch (imageFormat) { - case EImageFormat::RGBA: return "rgba"; - case EImageFormat::RGB: return "rgb"; - case EImageFormat::Grayscale: return "grayscale"; + case EImageFormat::RGBA: + return "rgba"; + case EImageFormat::RGB: + return "rgb"; + case EImageFormat::Grayscale: + return "grayscale"; } throw std::invalid_argument("Invalid EImageFormat Enum"); } @@ -252,17 +269,17 @@ inline std::string EImageFormat_enumToString(EImageFormat imageFormat) inline EImageFormat EImageFormat_stringToEnum(std::string imageFormat) { boost::to_lower(imageFormat); - if(imageFormat == "rgba") return EImageFormat::RGBA; - if(imageFormat == "rgb") return EImageFormat::RGB; - if(imageFormat == "grayscale") return EImageFormat::Grayscale; + if (imageFormat == "rgba") + return EImageFormat::RGBA; + if (imageFormat == "rgb") + return EImageFormat::RGB; + if (imageFormat == "grayscale") + return EImageFormat::Grayscale; throw std::invalid_argument("Unrecognized image format '" + imageFormat + "'"); } -inline std::ostream& operator<<(std::ostream& os, EImageFormat e) -{ - return os << EImageFormat_enumToString(e); -} +inline std::ostream& operator<<(std::ostream& os, EImageFormat e) { return os << EImageFormat_enumToString(e); } inline std::istream& operator>>(std::istream& in, EImageFormat& e) { @@ -287,7 +304,7 @@ std::istream& operator>>(std::istream& in, NLMeansFilterParams& nlmParams) in >> token; std::vector splitParams; boost::split(splitParams, token, boost::algorithm::is_any_of(":")); - if(splitParams.size() != 5) + if (splitParams.size() != 5) throw std::invalid_argument("Failed to parse NLMeansFilterParams from: " + token); nlmParams.enabled = boost::to_lower_copy(splitParams[0]) == "true"; nlmParams.filterStrength = boost::lexical_cast(splitParams[1]); @@ -300,8 +317,8 @@ std::istream& operator>>(std::istream& in, NLMeansFilterParams& nlmParams) inline std::ostream& operator<<(std::ostream& os, const NLMeansFilterParams& nlmParams) { - os << nlmParams.enabled << ":" << nlmParams.filterStrength << ":" << nlmParams.filterStrengthColor << ":" - << nlmParams.templateWindowSize << ":" << nlmParams.searchWindowSize; + os << nlmParams.enabled << ":" << nlmParams.filterStrength << ":" << nlmParams.filterStrengthColor << ":" << nlmParams.templateWindowSize << ":" + << nlmParams.searchWindowSize; return os; } @@ -358,52 +375,47 @@ struct ProcessingParams double correlatedColorTemperature = -1.0; bool reorient = false; - LensCorrectionParams lensCorrection = - { - false, // enable - false, // geometry - false, // vignetting - false // chromatic aberration + LensCorrectionParams lensCorrection = { + false, // enable + false, // geometry + false, // vignetting + false // chromatic aberration }; - SharpenParams sharpen = - { - false, // enable - 3, // width - 1.0f, // contrast - 0.0f // threshold + SharpenParams sharpen = { + false, // enable + 3, // width + 1.0f, // contrast + 0.0f // threshold }; - BilateralFilterParams bilateralFilter = - { - false, // enable - 0, // distance - 0.0f, // sigmaColor - 0.0f // sigmaSpace + BilateralFilterParams bilateralFilter = { + false, // enable + 0, // distance + 0.0f, // sigmaColor + 0.0f // sigmaSpace }; - ClaheFilterParams claheFilter = - { - false, // enable - 4.0f, // clipLimit - 8 // tileGridSize + ClaheFilterParams claheFilter = { + false, // enable + 4.0f, // clipLimit + 8 // tileGridSize }; NoiseFilterParams noise = { - false, // enable - ENoiseMethod::uniform, // method - 0.0f, // A - 1.0f, // B - true // mono + false, // enable + ENoiseMethod::uniform, // method + 0.0f, // A + 1.0f, // B + true // mono }; - NLMeansFilterParams nlmFilter = - { - false, // enable - 5.0f, // filterStrength - 10.0f, // filterStrengthColor - 7, // templateWindowSize - 21 // searchWindowSize + NLMeansFilterParams nlmFilter = { + false, // enable + 5.0f, // filterStrength + 10.0f, // filterStrengthColor + 7, // templateWindowSize + 21 // searchWindowSize }; pixelAspectRatioParams par = { @@ -425,9 +437,10 @@ void undistortVignetting(aliceVision::image::Image& img_ud, const image::RGBAfColor fillcolor) { - if(!model.isEmpty && model.FocalLengthX != 0.0 && model.FocalLengthY != 0.0) + if (!model.isEmpty && model.FocalLengthX != 0.0 && model.FocalLengthY != 0.0) { img_ud.resize(img.width(), img.height(), true, fillcolor); const image::Sampler2d sampler; @@ -461,9 +474,9 @@ void undistortRectilinearGeometryLCP(const aliceVision::image::Image& img, - RectilinearModel& greenModel, RectilinearModel& blueGreenModel, + RectilinearModel& greenModel, + RectilinearModel& blueGreenModel, RectilinearModel& redGreenModel, aliceVision::image::Image& img_ud, - const image::RGBAfColor fillcolor, bool undistortGeometry = false) + const image::RGBAfColor fillcolor, + bool undistortGeometry = false) { - if(!greenModel.isEmpty && greenModel.FocalLengthX != 0.0 && greenModel.FocalLengthY != 0.0) + if (!greenModel.isEmpty && greenModel.FocalLengthX != 0.0 && greenModel.FocalLengthY != 0.0) { img_ud.resize(img.width(), img.height(), true, fillcolor); const image::Sampler2d sampler; @@ -502,17 +517,17 @@ void undistortChromaticAberrations(const aliceVision::image::Image& image, ProcessingParams& pParams, - std::map& imageMetadata, std::shared_ptr cam) +void processImage(image::Image& image, + ProcessingParams& pParams, + std::map& imageMetadata, + std::shared_ptr cam) { const unsigned int nchannels = 4; @@ -577,15 +594,15 @@ void processImage(image::Image& image, ProcessingParams& pPar { const image::RGBAfColor FBLACK_A(.0f, .0f, .0f, 1.0f); image::Image image_ud; - undistortChromaticAberrations(image, pParams.lensCorrection.caGModel, pParams.lensCorrection.caBGModel, - pParams.lensCorrection.caRGModel, image_ud, FBLACK_A, false); + undistortChromaticAberrations( + image, pParams.lensCorrection.caGModel, pParams.lensCorrection.caBGModel, pParams.lensCorrection.caRGModel, image_ud, FBLACK_A, false); image = image_ud; } - else if(pParams.lensCorrection.chromaticAberration && pParams.lensCorrection.caGModel.isEmpty) + else if (pParams.lensCorrection.chromaticAberration && pParams.lensCorrection.caGModel.isEmpty) { ALICEVISION_LOG_WARNING("No distortion model available for lens chromatic aberration correction."); } - + if (pParams.lensCorrection.geometry && cam != NULL && cam->hasDistortion()) { const image::RGBAfColor FBLACK_A(.0f, .0f, .0f, 1.0f); @@ -609,11 +626,10 @@ void processImage(image::Image& image, ProcessingParams& pPar } const float sfw = - (pParams.maxWidth != 0 && pParams.maxWidth < image.width()) ? - static_cast(pParams.maxWidth) / static_cast(image.width()) : 1.0; - const float sfh = - (pParams.maxHeight != 0 && pParams.maxHeight < image.height()) ? - static_cast(pParams.maxHeight) / static_cast(image.height()) : 1.0; + (pParams.maxWidth != 0 && pParams.maxWidth < image.width()) ? static_cast(pParams.maxWidth) / static_cast(image.width()) : 1.0; + const float sfh = (pParams.maxHeight != 0 && pParams.maxHeight < image.height()) + ? static_cast(pParams.maxHeight) / static_cast(image.height()) + : 1.0; const float scaleFactor = std::min(pParams.scaleFactor, std::min(sfw, sfh)); if (scaleFactor != 1.0f || pParams.par.enabled) @@ -642,7 +658,7 @@ void processImage(image::Image& image, ProcessingParams& pPar image.swap(rescaled); } - + if ((pParams.reorient) && (imageMetadata.find("Orientation") != imageMetadata.end())) { oiio::ImageBuf inBuf(oiio::ImageSpec(image.width(), image.height(), nchannels, oiio::TypeDesc::FLOAT), image.data()); @@ -691,21 +707,22 @@ void processImage(image::Image& image, ProcessingParams& pPar image.swap(filtered); } - + if (pParams.bilateralFilter.enabled) { #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_OPENCV) - // Create temporary OpenCV Mat (keep only 3 Channels) to handled Eigen data of our image - cv::Mat openCVMatIn = image::imageRGBAToCvMatBGR(image, CV_32FC3); - cv::Mat openCVMatOut(image.width(), image.height(), CV_32FC3); + // Create temporary OpenCV Mat (keep only 3 Channels) to handled Eigen data of our image + cv::Mat openCVMatIn = image::imageRGBAToCvMatBGR(image, CV_32FC3); + cv::Mat openCVMatOut(image.width(), image.height(), CV_32FC3); - cv::bilateralFilter(openCVMatIn, openCVMatOut, pParams.bilateralFilter.distance, pParams.bilateralFilter.sigmaColor, pParams.bilateralFilter.sigmaSpace); + cv::bilateralFilter( + openCVMatIn, openCVMatOut, pParams.bilateralFilter.distance, pParams.bilateralFilter.sigmaColor, pParams.bilateralFilter.sigmaSpace); + + // Copy filtered data from openCV Mat(3 channels) to our image(keep the alpha channel unfiltered) + image::cvMatBGRToImageRGBA(openCVMatOut, image); - // Copy filtered data from openCV Mat(3 channels) to our image(keep the alpha channel unfiltered) - image::cvMatBGRToImageRGBA(openCVMatOut, image); - #else - throw std::invalid_argument("Unsupported mode! If you intended to use a bilateral filter, please add OpenCV support."); + throw std::invalid_argument("Unsupported mode! If you intended to use a bilateral filter, please add OpenCV support."); #endif } @@ -720,38 +737,39 @@ void processImage(image::Image& image, ProcessingParams& pPar cv::Mat labImg; cv::cvtColor(BGRMat, labImg, cv::COLOR_LBGR2Lab); - // Extract the L channel + // Extract the L channel cv::Mat L; cv::extractChannel(labImg, L, 0); // normalise L channel from [0, 100] to [0, 1] std::for_each(L.begin(), L.end(), [](float& pixel) { pixel /= 100.0; }); - // Convert float image to 16bit + // Convert float image to 16bit L.convertTo(L, CV_16U, 65535.0); // apply Clahe algorithm to the L channel { - const cv::Ptr clahe = cv::createCLAHE(pParams.claheFilter.clipLimit, cv::Size(pParams.claheFilter.tileGridSize, pParams.claheFilter.tileGridSize)); + const cv::Ptr clahe = + cv::createCLAHE(pParams.claheFilter.clipLimit, cv::Size(pParams.claheFilter.tileGridSize, pParams.claheFilter.tileGridSize)); clahe->apply(L, L); } - // Convert 16bit image to float + // Convert 16bit image to float L.convertTo(L, CV_32F, 1.0 / 65535.0); // normalise back L channel from [0, 1] to [0, 100] std::for_each(L.begin(), L.end(), [](float& pixel) { pixel *= 100.0; }); - + // Merge back Lab colors channels cv::insertChannel(L, labImg, 0); // Convert Lab format to BGR format cv::cvtColor(labImg, BGRMat, cv::COLOR_Lab2LBGR); - // Copy filtered data from openCV Mat to our alicevision image(keep the alpha channel unfiltered) + // Copy filtered data from openCV Mat to our alicevision image(keep the alpha channel unfiltered) image::cvMatBGRToImageRGBA(BGRMat, image); #else - throw std::invalid_argument( "Unsupported mode! If you intended to use a Clahe filter, please add OpenCV support."); + throw std::invalid_argument("Unsupported mode! If you intended to use a Clahe filter, please add OpenCV support."); #endif } if (pParams.fillHoles) @@ -768,7 +786,7 @@ void processImage(image::Image& image, ProcessingParams& pPar } if (pParams.noise.enabled) - { + { oiio::ImageBuf inBuf(oiio::ImageSpec(image.width(), image.height(), nchannels, oiio::TypeDesc::FLOAT), image.data()); oiio::ImageBufAlgo::noise(inBuf, ENoiseMethod_enumToString(pParams.noise.method), pParams.noise.A, pParams.noise.B, pParams.noise.mono); } @@ -780,16 +798,18 @@ void processImage(image::Image& image, ProcessingParams& pPar cv::Mat openCVMatIn = image::imageRGBAToCvMatBGR(image, CV_8UC3); cv::Mat openCVMatOut(image.width(), image.height(), CV_8UC3); - cv::fastNlMeansDenoisingColored(openCVMatIn, openCVMatOut, pParams.nlmFilter.filterStrength, - pParams.nlmFilter.filterStrengthColor, pParams.nlmFilter.templateWindowSize, + cv::fastNlMeansDenoisingColored(openCVMatIn, + openCVMatOut, + pParams.nlmFilter.filterStrength, + pParams.nlmFilter.filterStrengthColor, + pParams.nlmFilter.templateWindowSize, pParams.nlmFilter.searchWindowSize); // Copy filtered data from OpenCV Mat(3 channels) to our image (keep the alpha channel unfiltered) image::cvMatBGRToImageRGBA(openCVMatOut, image); #else - throw std::invalid_argument( - "Unsupported mode! If you intended to use a non-local means filter, please add OpenCV support."); + throw std::invalid_argument("Unsupported mode! If you intended to use a non-local means filter, please add OpenCV support."); #endif } @@ -810,8 +830,7 @@ void processImage(image::Image& image, ProcessingParams& pPar ALICEVISION_LOG_INFO("Matrix Number : " << colorMatrixNb << " ; " << fwdMatrixNb); - dcpMetadataOK = !((colorMatrixNb == 0) || - ((colorMatrixNb > 0) && !map_has_non_empty_value(imageMetadata, "AliceVision:DCP:ColorMat1")) || + dcpMetadataOK = !((colorMatrixNb == 0) || ((colorMatrixNb > 0) && !map_has_non_empty_value(imageMetadata, "AliceVision:DCP:ColorMat1")) || ((colorMatrixNb > 1) && !map_has_non_empty_value(imageMetadata, "AliceVision:DCP:ColorMat2")) || ((fwdMatrixNb > 0) && !map_has_non_empty_value(imageMetadata, "AliceVision:DCP:ForwardMat1")) || ((fwdMatrixNb > 1) && !map_has_non_empty_value(imageMetadata, "AliceVision:DCP:ForwardMat2"))); @@ -851,7 +870,8 @@ void processImage(image::Image& image, ProcessingParams& pPar dcpProf.setMatricesFromStrings("forward", v_str); } - std::string cam_mul = map_has_non_empty_value(imageMetadata, "raw:cam_mul") ? imageMetadata.at("raw:cam_mul") : imageMetadata.at("AliceVision:raw:cam_mul"); + std::string cam_mul = + map_has_non_empty_value(imageMetadata, "raw:cam_mul") ? imageMetadata.at("raw:cam_mul") : imageMetadata.at("AliceVision:raw:cam_mul"); std::vector v_mult; size_t last = 0; size_t next = 1; @@ -889,26 +909,31 @@ void processImage(image::Image& image, ProcessingParams& pPar } } -void saveImage(image::Image& image, const std::string& inputPath, const std::string& outputPath, std::map inputMetadata, - const std::vector& metadataFolders, const EImageFormat outputFormat, const image::ImageWriteOptions options) +void saveImage(image::Image& image, + const std::string& inputPath, + const std::string& outputPath, + std::map inputMetadata, + const std::vector& metadataFolders, + const EImageFormat outputFormat, + const image::ImageWriteOptions options) { // Read metadata path std::string metadataFilePath; - + const std::string filename = fs::path(inputPath).filename().string(); const std::string outExtension = boost::to_lower_copy(fs::path(outputPath).extension().string()); const bool isEXR = (outExtension == ".exr"); oiio::ParamValueList metadata; - - if (!inputMetadata.empty()) // If metadata are provided as input + + if (!inputMetadata.empty()) // If metadata are provided as input { // metadata name in "raw" domain must be updated otherwise values are discarded by oiio when writing exr format // we want to propagate them so we replace the domain "raw" with "AliceVision:raw" - for (const auto & meta : inputMetadata) + for (const auto& meta : inputMetadata) { if (meta.first.compare(0, 3, "raw") == 0) { - metadata.add_or_replace(oiio::ParamValue("AliceVision:"+meta.first, meta.second)); + metadata.add_or_replace(oiio::ParamValue("AliceVision:" + meta.first, meta.second)); } else { @@ -916,26 +941,22 @@ void saveImage(image::Image& image, const std::string& inputP } } } - else if (!metadataFolders.empty()) // If metadataFolders is specified + else if (!metadataFolders.empty()) // If metadataFolders is specified { // The file must match the file name and extension to be used as a metadata replacement. - const std::vector metadataFilePaths = utils::getFilesPathsFromFolders( - metadataFolders, [&filename](const fs::path& path) - { - return path.filename().string() == filename; - } - ); + const std::vector metadataFilePaths = + utils::getFilesPathsFromFolders(metadataFolders, [&filename](const fs::path& path) { return path.filename().string() == filename; }); - if(metadataFilePaths.size() > 1) + if (metadataFilePaths.size() > 1) { ALICEVISION_LOG_ERROR("Ambiguous case: Multiple path corresponding to this file was found for metadata replacement."); throw std::invalid_argument("Ambiguous case: Multiple path corresponding to this file was found for metadata replacement"); } - if(metadataFilePaths.empty()) + if (metadataFilePaths.empty()) { - ALICEVISION_LOG_WARNING("Metadata folders was specified but there is no matching for this image: "<< filename - << ". The default metadata will be used instead for this image."); + ALICEVISION_LOG_WARNING("Metadata folders was specified but there is no matching for this image: " + << filename << ". The default metadata will be used instead for this image."); metadataFilePath = inputPath; } else @@ -953,27 +974,27 @@ void saveImage(image::Image& image, const std::string& inputP // Save image ALICEVISION_LOG_TRACE("Export image: '" << outputPath << "'."); - - if(outputFormat == EImageFormat::Grayscale) + + if (outputFormat == EImageFormat::Grayscale) { image::Image outputImage; image::ConvertPixelType(image, &outputImage); image::writeImage(outputPath, outputImage, options, metadata); } - else if(outputFormat == EImageFormat::RGB) + else if (outputFormat == EImageFormat::RGB) { image::Image outputImage; image::ConvertPixelType(image, &outputImage); image::writeImage(outputPath, outputImage, options, metadata); } - else + else { // Already in RGBAf image::writeImage(outputPath, image, options, metadata); } } -int aliceVision_main(int argc, char * argv[]) +int aliceVision_main(int argc, char* argv[]) { std::string inputExpression; std::vector inputFolders; @@ -1201,17 +1222,16 @@ int aliceVision_main(int argc, char * argv[]) } // check user choose at least one input option - if(inputExpression.empty() && inputFolders.empty()) + if (inputExpression.empty() && inputFolders.empty()) { ALICEVISION_LOG_ERROR("Program need at least --input or --inputFolders option." << std::endl << "No input images here."); return EXIT_FAILURE; } #if !ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_OPENCV) - if(pParams.bilateralFilter.enabled || pParams.claheFilter.enabled || pParams.nlmFilter.enabled) + if (pParams.bilateralFilter.enabled || pParams.claheFilter.enabled || pParams.nlmFilter.enabled) { - ALICEVISION_LOG_ERROR( - "Invalid option: BilateralFilter, claheFilter and nlmFilter can't be used without openCV !"); + ALICEVISION_LOG_ERROR("Invalid option: BilateralFilter, claheFilter and nlmFilter can't be used without openCV !"); return EXIT_FAILURE; } #endif @@ -1225,7 +1245,7 @@ int aliceVision_main(int argc, char * argv[]) // Check if sfmInputDataFilename exist and is recognized as sfm data file const std::string inputExt = boost::to_lower_copy(fs::path(inputExpression).extension().string()); static const std::array sfmSupportedExtensions = {".sfm", ".abc"}; - if(!inputExpression.empty() && std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), inputExt) != sfmSupportedExtensions.end()) + if (!inputExpression.empty() && std::find(sfmSupportedExtensions.begin(), sfmSupportedExtensions.end(), inputExt) != sfmSupportedExtensions.end()) { sfmData::SfMData sfmData; if (!sfmDataIO::load(sfmData, inputExpression, sfmDataIO::ALL)) @@ -1239,30 +1259,31 @@ int aliceVision_main(int argc, char * argv[]) const bool checkInputFolders = !inputFolders.empty(); // Iterate over all views - for(const auto& viewIt : sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { const sfmData::View& view = *(viewIt.second); // Only valid views if needed - if(pParams.reconstructedViewsOnly && !sfmData.isPoseAndIntrinsicDefined(&view)) + if (pParams.reconstructedViewsOnly && !sfmData.isPoseAndIntrinsicDefined(&view)) { continue; } // if inputFolders are used - if(checkInputFolders) + if (checkInputFolders) { const std::vector foundViewPaths = sfmDataIO::viewPathsFromFolders(view, inputFolders); // Checks if a file associated with a given view is found in the inputfolders - if(foundViewPaths.empty()) + if (foundViewPaths.empty()) { ALICEVISION_LOG_ERROR("Some views from SfmData cannot be found in folders passed in the parameters.\n" - << "Use only SfmData input, use reconstructedViewsOnly or specify the correct inputFolders."); + << "Use only SfmData input, use reconstructedViewsOnly or specify the correct inputFolders."); return EXIT_FAILURE; } - else if(foundViewPaths.size() > 1) + else if (foundViewPaths.size() > 1) { - ALICEVISION_LOG_ERROR("Ambiguous case: Multiple paths found in input folders for the corresponding view '" << view.getViewId() << "'.\n" - << "Make sure that only one match can be found in input folders."); + ALICEVISION_LOG_ERROR("Ambiguous case: Multiple paths found in input folders for the corresponding view '" + << view.getViewId() << "'.\n" + << "Make sure that only one match can be found in input folders."); return EXIT_FAILURE; } @@ -1280,7 +1301,7 @@ int aliceVision_main(int argc, char * argv[]) const int size = ViewPaths.size(); int i = 0; - for(auto& viewIt : ViewPaths) + for (auto& viewIt : ViewPaths) { const IndexT viewId = viewIt.first; const std::string viewPath = viewIt.second; @@ -1292,7 +1313,8 @@ int aliceVision_main(int argc, char * argv[]) const std::string fileName = fsPath.stem().string(); const std::string fileExt = fsPath.extension().string(); const std::string outputExt = extension.empty() ? (isRAW ? ".exr" : fileExt) : (std::string(".") + extension); - const std::string outputfilePath = (fs::path(outputPath) / ((pParams.keepImageFilename ? fileName : std::to_string(viewId)) + outputExt)).generic_string(); + const std::string outputfilePath = + (fs::path(outputPath) / ((pParams.keepImageFilename ? fileName : std::to_string(viewId)) + outputExt)).generic_string(); ALICEVISION_LOG_INFO(++i << "/" << size << " - Process view '" << viewId << "'."); @@ -1353,27 +1375,26 @@ int aliceVision_main(int argc, char * argv[]) if (pParams.lensCorrection.enabled && pParams.lensCorrection.chromaticAberration) { std::vector caGParams, caBGParams, caRGParams; - view.getImage().getChromaticAberrationParams(caGParams,caBGParams,caRGParams); + view.getImage().getChromaticAberrationParams(caGParams, caBGParams, caRGParams); pParams.lensCorrection.caGModel.init3(caGParams); pParams.lensCorrection.caBGModel.init3(caBGParams); pParams.lensCorrection.caRGModel.init3(caRGParams); - if(pParams.lensCorrection.caGModel.FocalLengthX == 0.0) + if (pParams.lensCorrection.caGModel.FocalLengthX == 0.0) { float sensorWidth = view.getImage().getSensorWidth(); pParams.lensCorrection.caGModel.FocalLengthX = view.getImage().getWidth() * view.getImage().getMetadataFocalLength() / sensorWidth / std::max(view.getImage().getWidth(), view.getImage().getHeight()); } - if(pParams.lensCorrection.caGModel.FocalLengthY == 0.0) + if (pParams.lensCorrection.caGModel.FocalLengthY == 0.0) { float sensorHeight = view.getImage().getSensorHeight(); pParams.lensCorrection.caGModel.FocalLengthY = view.getImage().getHeight() * view.getImage().getMetadataFocalLength() / sensorHeight / std::max(view.getImage().getWidth(), view.getImage().getHeight()); } - if((pParams.lensCorrection.caGModel.FocalLengthX <= 0.0) || - (pParams.lensCorrection.caGModel.FocalLengthY <= 0.0)) + if ((pParams.lensCorrection.caGModel.FocalLengthX <= 0.0) || (pParams.lensCorrection.caGModel.FocalLengthY <= 0.0)) { pParams.lensCorrection.caGModel.reset(); pParams.lensCorrection.caBGModel.reset(); @@ -1447,7 +1468,7 @@ int aliceVision_main(int argc, char * argv[]) if (viewMetadata.find("Orientation") != viewMetadata.end()) view.getImage().addMetadata("Orientation", viewMetadata.at("Orientation")); - if (pParams.reorient && image.width() != cam->w() && image.width() == cam->h()) // The image has been rotated by automatic reorientation + if (pParams.reorient && image.width() != cam->w() && image.width() == cam->h()) // The image has been rotated by automatic reorientation { camera::IntrinsicBase* cam2 = cam->clone(); @@ -1477,7 +1498,7 @@ int aliceVision_main(int argc, char * argv[]) // Save sfmData with modified path to images const std::string sfmfilePath = (fs::path(outputPath) / fs::path(inputExpression).filename()).generic_string(); - if(!sfmDataIO::save(sfmData, sfmfilePath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::save(sfmData, sfmfilePath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The output SfMData file '" << sfmfilePath << "' cannot be written."); return EXIT_FAILURE; @@ -1489,22 +1510,21 @@ int aliceVision_main(int argc, char * argv[]) std::vector filesStrPaths; // If sfmInputDataFilename is empty use imageFolders instead - if(inputExpression.empty()) + if (inputExpression.empty()) { // Get supported files - filesStrPaths = utils::getFilesPathsFromFolders(inputFolders, [](const fs::path& path) { - return image::isSupported(path.extension().string()); - }); + filesStrPaths = + utils::getFilesPathsFromFolders(inputFolders, [](const fs::path& path) { return image::isSupported(path.extension().string()); }); } else { // If you try to use both a regex-like filter expression and folders as input - if(!inputFolders.empty()) + if (!inputFolders.empty()) { ALICEVISION_LOG_WARNING("InputFolders and filter expression cannot be used at the same time, InputFolders are ignored here."); } - if(fs::is_regular_file(inputPath)) + if (fs::is_regular_file(inputPath)) { filesStrPaths.push_back(inputPath.string()); } @@ -1514,17 +1534,15 @@ int aliceVision_main(int argc, char * argv[]) const std::regex regex = utils::filterToRegex(inputExpression); // Get supported files in inputPath directory which matches our regex filter - filesStrPaths = utils::getFilesPathsFromFolder(inputPath.parent_path().generic_string(), - [®ex](const fs::path& path) { - return image::isSupported(path.extension().string()) && std::regex_match(path.generic_string(), regex); - } - ); + filesStrPaths = utils::getFilesPathsFromFolder(inputPath.parent_path().generic_string(), [®ex](const fs::path& path) { + return image::isSupported(path.extension().string()) && std::regex_match(path.generic_string(), regex); + }); } } const int size = filesStrPaths.size(); - if(!size) + if (!size) { ALICEVISION_LOG_ERROR("Any images was found."); ALICEVISION_LOG_ERROR("Input folders or input expression '" << inputExpression << "' may be incorrect ?"); @@ -1547,8 +1565,7 @@ int aliceVision_main(int argc, char * argv[]) const auto root = image::getAliceVisionRoot(); if (root.empty()) { - ALICEVISION_LOG_WARNING( - "ALICEVISION_ROOT is not defined, default sensor database cannot be accessed."); + ALICEVISION_LOG_WARNING("ALICEVISION_ROOT is not defined, default sensor database cannot be accessed."); } else { @@ -1557,8 +1574,7 @@ int aliceVision_main(int argc, char * argv[]) } if (!sensorDatabasePath.empty() && !sensorDB::parseDatabase(sensorDatabasePath, sensorDatabase)) { - ALICEVISION_LOG_ERROR("Invalid input sensor database '" << sensorDatabasePath - << "', please specify a valid file."); + ALICEVISION_LOG_ERROR("Invalid input sensor database '" << sensorDatabasePath << "', please specify a valid file."); return EXIT_FAILURE; } } @@ -1588,7 +1604,7 @@ int aliceVision_main(int argc, char * argv[]) { outputFilePath = (fs::path(outputPath).parent_path() / (filename + outputExt)).generic_string(); ALICEVISION_LOG_WARNING("Extension " << userExt << " is not supported! Output image saved in " << outputFilePath); - } + } } else { @@ -1596,7 +1612,7 @@ int aliceVision_main(int argc, char * argv[]) } image::DCPProfile dcpProf; - sfmData::View view; // used to extract and complete metadata + sfmData::View view; // used to extract and complete metadata view.getImage().setImagePath(inputFilePath); int width, height; const auto metadata = image::readImageMetadata(inputFilePath, width, height); @@ -1632,7 +1648,7 @@ int aliceVision_main(int argc, char * argv[]) view.getImage().addDCPMetadata(dcpProf); } - if(isRAW && pParams.lensCorrection.enabled && + if (isRAW && pParams.lensCorrection.enabled && (pParams.lensCorrection.geometry || pParams.lensCorrection.vignetting || pParams.lensCorrection.chromaticAberration)) { // try to find an appropriate Lens Correction Profile @@ -1649,7 +1665,7 @@ int aliceVision_main(int argc, char * argv[]) if (!make.empty() && !lensModel.empty()) { - #pragma omp critical(lcp) +#pragma omp critical(lcp) lcpData = lcpStore.findLCP(make, model, lensModel, lensID, 1); } } @@ -1673,12 +1689,10 @@ int aliceVision_main(int argc, char * argv[]) if (lensParam.hasVignetteParams() && !lensParam.vignParams.isEmpty && pParams.lensCorrection.vignetting) { - float FocX = lensParam.vignParams.FocalLengthX != 0.0 - ? lensParam.vignParams.FocalLengthX - : width * focalLengthmm / sensorWidth / std::max(width, height); - float FocY = lensParam.vignParams.FocalLengthY != 0.0 - ? lensParam.vignParams.FocalLengthY - : height * focalLengthmm / sensorHeight / std::max(width, height); + float FocX = lensParam.vignParams.FocalLengthX != 0.0 ? lensParam.vignParams.FocalLengthX + : width * focalLengthmm / sensorWidth / std::max(width, height); + float FocY = lensParam.vignParams.FocalLengthY != 0.0 ? lensParam.vignParams.FocalLengthY + : height * focalLengthmm / sensorHeight / std::max(width, height); pParams.lensCorrection.vParams.clear(); @@ -1702,24 +1716,20 @@ int aliceVision_main(int argc, char * argv[]) { if (lensParam.ChromaticGreenParams.FocalLengthX == 0.0) { - lensParam.ChromaticGreenParams.FocalLengthX = - width * focalLengthmm / sensorWidth / std::max(width, height); + lensParam.ChromaticGreenParams.FocalLengthX = width * focalLengthmm / sensorWidth / std::max(width, height); } if (lensParam.ChromaticGreenParams.FocalLengthY == 0.0) { - lensParam.ChromaticGreenParams.FocalLengthY = - height * focalLengthmm / sensorHeight / std::max(width, height); + lensParam.ChromaticGreenParams.FocalLengthY = height * focalLengthmm / sensorHeight / std::max(width, height); } - if(lensParam.ChromaticGreenParams.FocalLengthX == 0.0 || - lensParam.ChromaticGreenParams.FocalLengthY == 0.0) + if (lensParam.ChromaticGreenParams.FocalLengthX == 0.0 || lensParam.ChromaticGreenParams.FocalLengthY == 0.0) { pParams.lensCorrection.caGModel.reset(); pParams.lensCorrection.caBGModel.reset(); pParams.lensCorrection.caRGModel.reset(); - ALICEVISION_LOG_WARNING( - "Chromatic Aberration correction is requested but cannot be applied due to missing info."); + ALICEVISION_LOG_WARNING("Chromatic Aberration correction is requested but cannot be applied due to missing info."); } else { @@ -1739,9 +1749,17 @@ int aliceVision_main(int argc, char * argv[]) const double defaultFocalRatio = 1.0; const double defaultOffsetX = 0.0; const double defaultOffsetY = 0.0; - intrinsicBase = sfmDataIO::getViewIntrinsic( - view, focalLengthmm, sensorWidth, defaultFocalLength, defaultFieldOfView, defaultFocalRatio, - defaultOffsetX, defaultOffsetY, &lensParam, defaultCameraModel, allowedCameraModels); + intrinsicBase = sfmDataIO::getViewIntrinsic(view, + focalLengthmm, + sensorWidth, + defaultFocalLength, + defaultFieldOfView, + defaultFocalRatio, + defaultOffsetX, + defaultOffsetY, + &lensParam, + defaultCameraModel, + allowedCameraModels); pParams.lensCorrection.geometryModel = lensParam.perspParams; } @@ -1770,9 +1788,8 @@ int aliceVision_main(int argc, char * argv[]) if (isRAW) { readOptions.colorProfileFileName = dcpProf.info.filename; - if (dcpProf.info.filename.empty() && - ((rawColorInterpretation == image::ERawColorInterpretation::DcpLinearProcessing) || - (rawColorInterpretation == image::ERawColorInterpretation::DcpMetadata))) + if (dcpProf.info.filename.empty() && ((rawColorInterpretation == image::ERawColorInterpretation::DcpLinearProcessing) || + (rawColorInterpretation == image::ERawColorInterpretation::DcpMetadata))) { // Fallback case of missing profile but no error requested readOptions.rawColorInterpretation = image::ERawColorInterpretation::LibRawWhiteBalancing; diff --git a/src/software/utils/main_importMiddlebury.cpp b/src/software/utils/main_importMiddlebury.cpp index db2ea1b55b..fbf859e709 100644 --- a/src/software/utils/main_importMiddlebury.cpp +++ b/src/software/utils/main_importMiddlebury.cpp @@ -20,19 +20,16 @@ #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 fs = std::filesystem; - /* * This program generate an SfMData from the configuration files of the Middlebury dataset */ @@ -71,8 +68,9 @@ int aliceVision_main(int argc, char** argv) "Set the poses to locked, so they will not be refined in the SfM step."); // clang-format on - CmdLine cmdline("This program generates an SfMData from the configuration files of the Middlebury dataset: https://vision.middlebury.edu/mview/data\n" - "AliceVision importMiddlebury"); + CmdLine cmdline( + "This program generates an SfMData from the configuration files of the Middlebury dataset: https://vision.middlebury.edu/mview/data\n" + "AliceVision importMiddlebury"); cmdline.add(requiredParams); cmdline.add(optionalParams); if (!cmdline.execute(argc, argv)) @@ -81,7 +79,7 @@ int aliceVision_main(int argc, char** argv) } // check input file exist - if(!exists(fs::path(middleburyFile))) + if (!exists(fs::path(middleburyFile))) { ALICEVISION_LOG_ERROR("File " << middleburyFile << " does not exist"); return EXIT_FAILURE; @@ -91,10 +89,9 @@ int aliceVision_main(int argc, char** argv) const auto basePath = fs::path(middleburyFile).parent_path().string(); // parse file - const auto sfmData = - sfmDataIO::middleburySceneToSfmData(middleburyFile, basePath, uniqueIntrinsics, importPoses, lockIntrinsics, lockPoses); + const auto sfmData = sfmDataIO::middleburySceneToSfmData(middleburyFile, basePath, uniqueIntrinsics, importPoses, lockIntrinsics, lockPoses); - if(!sfmDataIO::save(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::save(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("Unable to save " << sfmDataFilename); return EXIT_FAILURE; diff --git a/src/software/utils/main_keyframeSelection.cpp b/src/software/utils/main_keyframeSelection.cpp index 5436f184aa..f215500d0d 100644 --- a/src/software/utils/main_keyframeSelection.cpp +++ b/src/software/utils/main_keyframeSelection.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include @@ -31,14 +31,14 @@ const std::string supportedExtensions = "none, exr, jpg, png"; int aliceVision_main(int argc, char** argv) { // Command-line parameters - std::vector inputPaths; // media or SfMData file path list - std::vector brands; // media brand list - std::vector models; // media model list - std::vector mmFocals; // media focal (mm) list - std::string sensorDbPath; // camera sensor width database - std::string outputFolder; // output folder for keyframes - std::string outputSfMDataKeyframes; // output SfMData file containing the selected keyframes - std::string outputSfMDataFrames; // output SfMData file containing the rejected frames + std::vector inputPaths; // media or SfMData file path list + std::vector brands; // media brand list + std::vector models; // media model list + std::vector mmFocals; // media focal (mm) list + std::string sensorDbPath; // camera sensor width database + std::string outputFolder; // output folder for keyframes + std::string outputSfMDataKeyframes; // output SfMData file containing the selected keyframes + std::string outputSfMDataFrames; // output SfMData file containing the rejected frames // Algorithm variables bool useSmartSelection = true; // enable the smart selection instead of the regular one @@ -53,19 +53,19 @@ int aliceVision_main(int argc, char** argv) std::size_t flowCellSize = 90; // size of the cells within a frame used to compute the optical flow (smart selection) std::string outputExtension = "exr"; // file extension of the written keyframes (keyframes will not be written if set to "none") image::EStorageDataType exrDataType = // storage data type for EXR output files - image::EStorageDataType::Float; - bool renameKeyframes = false; // name selected keyframes as consecutive frames instead of using their index as a name - std::size_t minBlockSize = 10; // minimum number of frames in a block for multi-threading - std::vector maskPaths; // masks path list + image::EStorageDataType::Float; + bool renameKeyframes = false; // name selected keyframes as consecutive frames instead of using their index as a name + std::size_t minBlockSize = 10; // minimum number of frames in a block for multi-threading + std::vector maskPaths; // masks path list // Debug options - bool exportScores = false; // export the sharpness and optical flow scores to a CSV file - std::string csvFilename = "scores.csv"; // name of the CSV file containing the scores - bool exportSelectedFrames = false; // export the selected frames (1 for selected, 0 for not selected) - bool skipSelection = false; // only compute the scores and do not proceed with the selection - bool exportFlowVisualisation = false; // export optical flow visualisation for all the frames - bool flowVisualisationOnly = false; // export optical flow visualisation for all the frames but do not compute scores - bool skipSharpnessComputation = false; // skip sharpness score computations + bool exportScores = false; // export the sharpness and optical flow scores to a CSV file + std::string csvFilename = "scores.csv"; // name of the CSV file containing the scores + bool exportSelectedFrames = false; // export the selected frames (1 for selected, 0 for not selected) + bool skipSelection = false; // only compute the scores and do not proceed with the selection + bool exportFlowVisualisation = false; // export optical flow visualisation for all the frames + bool flowVisualisationOnly = false; // export optical flow visualisation for all the frames but do not compute scores + bool skipSharpnessComputation = false; // skip sharpness score computations // clang-format off po::options_description inputParams("Required parameters"); @@ -172,7 +172,8 @@ int aliceVision_main(int argc, char** argv) cmdline.add(regularAlgorithmParams); cmdline.add(smartAlgorithmParams); cmdline.add(debugParams); - if (!cmdline.execute(argc, argv)) { + if (!cmdline.execute(argc, argv)) + { return EXIT_FAILURE; } @@ -183,37 +184,42 @@ int aliceVision_main(int argc, char** argv) { const fs::path outDir = fs::absolute(outputFolder); outputFolder = outDir.string(); - if (!fs::is_directory(outDir)) { + if (!fs::is_directory(outDir)) + { ALICEVISION_LOG_ERROR("Cannot find folder: " << outputFolder); return EXIT_FAILURE; } } - if (nbCameras < 1) { + if (nbCameras < 1) + { ALICEVISION_LOG_ERROR("At least one media path needs to be provided."); return EXIT_FAILURE; } - if (nbMasks > 0 && nbMasks != nbCameras) { + if (nbMasks > 0 && nbMasks != nbCameras) + { ALICEVISION_LOG_ERROR("The number of provided mask directories does not match the number of media paths."); return EXIT_FAILURE; } - if (maxFrameStep > 0 && minFrameStep >= maxFrameStep) { + if (maxFrameStep > 0 && minFrameStep >= maxFrameStep) + { ALICEVISION_LOG_ERROR("Setting 'minFrameStep' should be less than setting 'maxFrameStep'."); return EXIT_FAILURE; } - if (minNbOutFrames < 1) { + if (minNbOutFrames < 1) + { ALICEVISION_LOG_ERROR("The minimum number of output keyframes cannot be less than 1."); return EXIT_FAILURE; } // Convert the provided output extension to lowercase before performing any comparison std::transform(outputExtension.begin(), outputExtension.end(), outputExtension.begin(), ::tolower); - if (supportedExtensions.find(outputExtension) == std::string::npos) { - ALICEVISION_LOG_ERROR("Unsupported extension for the output file. Supported extensions are: " - << supportedExtensions); + if (supportedExtensions.find(outputExtension) == std::string::npos) + { + ALICEVISION_LOG_ERROR("Unsupported extension for the output file. Supported extensions are: " << supportedExtensions); return EXIT_FAILURE; } @@ -228,11 +234,12 @@ int aliceVision_main(int argc, char** argv) else ALICEVISION_LOG_INFO("Camera rig of " << nbCameras << " cameras."); - for (std::size_t i = 0; i < nbCameras; ++i) { - ALICEVISION_LOG_INFO("Camera: " << inputPaths.at(i) << std::endl - << "\t - brand: " << brands.at(i) << std::endl - << "\t - model: " << models.at(i) << std::endl - << "\t - focal (mm): " << mmFocals.at(i) << std::endl); + for (std::size_t i = 0; i < nbCameras; ++i) + { + ALICEVISION_LOG_INFO("Camera: " << inputPaths.at(i) << std::endl + << "\t - brand: " << brands.at(i) << std::endl + << "\t - model: " << models.at(i) << std::endl + << "\t - focal (mm): " << mmFocals.at(i) << std::endl); } } @@ -240,8 +247,7 @@ int aliceVision_main(int argc, char** argv) omp_set_num_threads(hwc.getMaxThreads()); // Initialize KeyframeSelector - keyframe::KeyframeSelector selector(inputPaths, maskPaths, sensorDbPath, outputFolder, outputSfMDataKeyframes, - outputSfMDataFrames); + keyframe::KeyframeSelector selector(inputPaths, maskPaths, sensorDbPath, outputFolder, outputSfMDataKeyframes, outputSfMDataFrames); // Set frame-related algorithm parameters selector.setMinFrameStep(minFrameStep); @@ -250,7 +256,8 @@ int aliceVision_main(int argc, char** argv) selector.setMaxOutFrames(maxNbOutFrames); selector.setMinBlockSize(minBlockSize); - if (flowVisualisationOnly) { + if (flowVisualisationOnly) + { bool exported = selector.exportFlowVisualisation(rescaledWidthFlow); if (exported) return EXIT_SUCCESS; @@ -258,9 +265,9 @@ int aliceVision_main(int argc, char** argv) return EXIT_FAILURE; } - if (skipSelection) { - selector.computeScores(rescaledWidthSharp, rescaledWidthFlow, sharpnessWindowSize, flowCellSize, - skipSharpnessComputation); + if (skipSelection) + { + selector.computeScores(rescaledWidthSharp, rescaledWidthFlow, sharpnessWindowSize, flowCellSize, skipSharpnessComputation); if (exportScores) selector.exportScoresToFile(csvFilename); // Frames have not been selected, ignore 'exportSelectedFrames' if (exportFlowVisualisation) @@ -271,8 +278,7 @@ int aliceVision_main(int argc, char** argv) // Process media paths with regular or smart method if (useSmartSelection) - selector.processSmart(pxDisplacement, rescaledWidthSharp, rescaledWidthFlow, sharpnessWindowSize, flowCellSize, - skipSharpnessComputation); + selector.processSmart(pxDisplacement, rescaledWidthSharp, rescaledWidthFlow, sharpnessWindowSize, flowCellSize, skipSharpnessComputation); else selector.processRegular(); diff --git a/src/software/utils/main_lightingEstimation.cpp b/src/software/utils/main_lightingEstimation.cpp index 3a97558197..be7e132495 100644 --- a/src/software/utils/main_lightingEstimation.cpp +++ b/src/software/utils/main_lightingEstimation.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include @@ -35,10 +35,10 @@ namespace fs = std::filesystem; enum class EAlbedoEstimation { - CONSTANT, - PICTURE, - MEDIAN_FILTER, - BLUR_FILTER + CONSTANT, + PICTURE, + MEDIAN_FILTER, + BLUR_FILTER }; /** @@ -47,11 +47,11 @@ enum class EAlbedoEstimation */ std::string EAlbedoEstimation_informations() { - return "Albedo estimation method used for light estimation:\n" - "* constant: constant color.\n" - "* picture: from original picture.\n" - "* median_filter: from original picture with median filter.\n" - "* blur_filter: from original picture with blur filter.\n"; + return "Albedo estimation method used for light estimation:\n" + "* constant: constant color.\n" + "* picture: from original picture.\n" + "* median_filter: from original picture with median filter.\n" + "* blur_filter: from original picture with blur filter.\n"; } /** @@ -61,14 +61,18 @@ std::string EAlbedoEstimation_informations() */ inline std::string EAlbedoEstimation_enumToString(EAlbedoEstimation albedoEstimation) { - switch(albedoEstimation) - { - case EAlbedoEstimation::CONSTANT: return "constant"; - case EAlbedoEstimation::PICTURE: return "picture"; - case EAlbedoEstimation::MEDIAN_FILTER: return "median_filter"; - case EAlbedoEstimation::BLUR_FILTER: return "blur_filter"; - } - throw std::out_of_range("Invalid albedoEstimation enum: " + std::to_string(int(albedoEstimation))); + switch (albedoEstimation) + { + case EAlbedoEstimation::CONSTANT: + return "constant"; + case EAlbedoEstimation::PICTURE: + return "picture"; + case EAlbedoEstimation::MEDIAN_FILTER: + return "median_filter"; + case EAlbedoEstimation::BLUR_FILTER: + return "blur_filter"; + } + throw std::out_of_range("Invalid albedoEstimation enum: " + std::to_string(int(albedoEstimation))); } /** @@ -78,21 +82,22 @@ inline std::string EAlbedoEstimation_enumToString(EAlbedoEstimation albedoEstima */ inline EAlbedoEstimation EAlbedoEstimation_stringToEnum(const std::string& albedoEstimation) { - std::string albedo = albedoEstimation; - std::transform(albedo.begin(), albedo.end(), albedo.begin(), ::tolower); //tolower - - if(albedo == "constant") return EAlbedoEstimation::CONSTANT; - if(albedo == "picture") return EAlbedoEstimation::PICTURE; - if(albedo == "median_filter") return EAlbedoEstimation::MEDIAN_FILTER; - if(albedo == "blur_filter") return EAlbedoEstimation::BLUR_FILTER; - - throw std::out_of_range("Invalid albedoEstimation: " + albedoEstimation); + std::string albedo = albedoEstimation; + std::transform(albedo.begin(), albedo.end(), albedo.begin(), ::tolower); // tolower + + if (albedo == "constant") + return EAlbedoEstimation::CONSTANT; + if (albedo == "picture") + return EAlbedoEstimation::PICTURE; + if (albedo == "median_filter") + return EAlbedoEstimation::MEDIAN_FILTER; + if (albedo == "blur_filter") + return EAlbedoEstimation::BLUR_FILTER; + + throw std::out_of_range("Invalid albedoEstimation: " + albedoEstimation); } -inline std::ostream& operator<<(std::ostream& os, EAlbedoEstimation v) -{ - return os << EAlbedoEstimation_enumToString(v); -} +inline std::ostream& operator<<(std::ostream& os, EAlbedoEstimation v) { return os << EAlbedoEstimation_enumToString(v); } inline std::istream& operator>>(std::istream& in, EAlbedoEstimation& v) { @@ -104,35 +109,36 @@ inline std::istream& operator>>(std::istream& in, EAlbedoEstimation& v) enum class ELightingEstimationMode { - GLOBAL = 1, - PER_IMAGE = 2 + GLOBAL = 1, + PER_IMAGE = 2 }; inline std::string ELightingEstimationMode_enumToString(ELightingEstimationMode v) { - switch(v) - { - case ELightingEstimationMode::GLOBAL: return "global"; - case ELightingEstimationMode::PER_IMAGE: return "per_image"; - } - throw std::out_of_range("Invalid LightEstimationMode enum: " + std::to_string(int(v))); + switch (v) + { + case ELightingEstimationMode::GLOBAL: + return "global"; + case ELightingEstimationMode::PER_IMAGE: + return "per_image"; + } + throw std::out_of_range("Invalid LightEstimationMode enum: " + std::to_string(int(v))); } inline ELightingEstimationMode ELightingEstimationMode_stringToEnum(const std::string& v) { - std::string vv = v; - std::transform(vv.begin(), vv.end(), vv.begin(), ::tolower); //tolower + std::string vv = v; + std::transform(vv.begin(), vv.end(), vv.begin(), ::tolower); // tolower - if(vv == "global") return ELightingEstimationMode::GLOBAL; - if(vv == "per_image") return ELightingEstimationMode::PER_IMAGE; + if (vv == "global") + return ELightingEstimationMode::GLOBAL; + if (vv == "per_image") + return ELightingEstimationMode::PER_IMAGE; - throw std::out_of_range("Invalid LightEstimationMode: " + v); + throw std::out_of_range("Invalid LightEstimationMode: " + v); } -inline std::ostream& operator<<(std::ostream& os, ELightingEstimationMode v) -{ - return os << ELightingEstimationMode_enumToString(v); -} +inline std::ostream& operator<<(std::ostream& os, ELightingEstimationMode v) { return os << ELightingEstimationMode_enumToString(v); } inline std::istream& operator>>(std::istream& in, ELightingEstimationMode& v) { @@ -142,39 +148,37 @@ inline std::istream& operator>>(std::istream& in, ELightingEstimationMode& v) return in; } -enum class ELightingColor { - Luminance = 1, - RGB = 3 +enum class ELightingColor +{ + Luminance = 1, + RGB = 3 }; inline std::string ELightingColor_enumToString(ELightingColor v) { - switch(v) - { - case ELightingColor::RGB: - return "rgb"; - case ELightingColor::Luminance: - return "luminance"; - } - throw std::out_of_range("Invalid LightingColor type Enum: " + std::to_string(int(v))); + switch (v) + { + case ELightingColor::RGB: + return "rgb"; + case ELightingColor::Luminance: + return "luminance"; + } + throw std::out_of_range("Invalid LightingColor type Enum: " + std::to_string(int(v))); } inline ELightingColor ELightingColor_stringToEnum(const std::string& v) { - std::string vv = v; - std::transform(vv.begin(), vv.end(), vv.begin(), ::tolower); //tolower - - if(vv == "rgb") - return ELightingColor::RGB; - if(vv == "luminance") - return ELightingColor::Luminance; - throw std::out_of_range("Invalid LightingColor type string " + v); + std::string vv = v; + std::transform(vv.begin(), vv.end(), vv.begin(), ::tolower); // tolower + + if (vv == "rgb") + return ELightingColor::RGB; + if (vv == "luminance") + return ELightingColor::Luminance; + throw std::out_of_range("Invalid LightingColor type string " + v); } -inline std::ostream& operator<<(std::ostream& os, ELightingColor v) -{ - return os << ELightingColor_enumToString(v); -} +inline std::ostream& operator<<(std::ostream& os, ELightingColor v) { return os << ELightingColor_enumToString(v); } inline std::istream& operator>>(std::istream& in, ELightingColor& v) { @@ -184,99 +188,109 @@ inline std::istream& operator>>(std::istream& in, ELightingColor& v) return in; } -void initAlbedo(image::Image& albedo, const image::Image& picture, EAlbedoEstimation albedoEstimationMethod, int albedoEstimationFilterSize, const std::string& outputFolder, IndexT viewId) +void initAlbedo(image::Image& albedo, + const image::Image& picture, + EAlbedoEstimation albedoEstimationMethod, + int albedoEstimationFilterSize, + const std::string& outputFolder, + IndexT viewId) { - switch(albedoEstimationMethod) - { - case EAlbedoEstimation::CONSTANT: - { - albedo.resize(picture.width(), picture.height()); - albedo.fill(image::RGBfColor(.5f,.5f,.5f)); - } - break; - case EAlbedoEstimation::PICTURE: + switch (albedoEstimationMethod) { - albedo = picture; + case EAlbedoEstimation::CONSTANT: + { + albedo.resize(picture.width(), picture.height()); + albedo.fill(image::RGBfColor(.5f, .5f, .5f)); + } + break; + case EAlbedoEstimation::PICTURE: + { + albedo = picture; + } + break; + case EAlbedoEstimation::MEDIAN_FILTER: + { + albedo.resize(picture.width(), picture.height()); + const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), + const_cast((void*)&picture(0, 0)(0))); + oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), albedo.data()); + oiio::ImageBufAlgo::median_filter(albedoBuf, pictureBuf, albedoEstimationFilterSize, albedoEstimationFilterSize); + image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, image::ImageWriteOptions()); + } + break; + case EAlbedoEstimation::BLUR_FILTER: + { + albedo.resize(picture.width(), picture.height()); + const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), + const_cast((void*)&picture(0, 0)(0))); + oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), albedo.data()); + oiio::ImageBuf K = oiio::ImageBufAlgo::make_kernel("gaussian", albedoEstimationFilterSize, albedoEstimationFilterSize); + oiio::ImageBufAlgo::convolve(albedoBuf, pictureBuf, K); + image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, image::ImageWriteOptions()); + } + break; } - break; - case EAlbedoEstimation::MEDIAN_FILTER: - { - albedo.resize(picture.width(), picture.height()); - const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), const_cast((void*)&picture(0,0)(0))); - oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), albedo.data()); - oiio::ImageBufAlgo::median_filter(albedoBuf, pictureBuf, albedoEstimationFilterSize, albedoEstimationFilterSize); - image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, - image::ImageWriteOptions()); - } - break; - case EAlbedoEstimation::BLUR_FILTER: - { - albedo.resize(picture.width(), picture.height()); - const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), const_cast((void*)&picture(0,0)(0))); - oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 3, oiio::TypeDesc::FLOAT), albedo.data()); - oiio::ImageBuf K = oiio::ImageBufAlgo::make_kernel("gaussian", albedoEstimationFilterSize, albedoEstimationFilterSize); - oiio::ImageBufAlgo::convolve(albedoBuf, pictureBuf, K); - image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, - image::ImageWriteOptions()); - } - break; - } } -void initAlbedo(image::Image& albedo, const image::Image& picture, EAlbedoEstimation albedoEstimationMethod, int albedoEstimationFilterSize, const std::string& outputFolder, IndexT viewId) +void initAlbedo(image::Image& albedo, + const image::Image& picture, + EAlbedoEstimation albedoEstimationMethod, + int albedoEstimationFilterSize, + const std::string& outputFolder, + IndexT viewId) { - switch(albedoEstimationMethod) - { - case EAlbedoEstimation::CONSTANT: + switch (albedoEstimationMethod) { - albedo.resize(picture.width(), picture.height()); - albedo.fill(.5f); + case EAlbedoEstimation::CONSTANT: + { + albedo.resize(picture.width(), picture.height()); + albedo.fill(.5f); + } + break; + case EAlbedoEstimation::PICTURE: + { + albedo = picture; + } + break; + case EAlbedoEstimation::MEDIAN_FILTER: + { + albedo.resize(picture.width(), picture.height()); + const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), + const_cast(picture.data())); + oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), albedo.data()); + oiio::ImageBufAlgo::median_filter(albedoBuf, pictureBuf, albedoEstimationFilterSize, albedoEstimationFilterSize); + image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, image::ImageWriteOptions()); + } + break; + case EAlbedoEstimation::BLUR_FILTER: + { + albedo.resize(picture.width(), picture.height()); + const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), + const_cast(picture.data())); + oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), albedo.data()); + oiio::ImageBuf K = oiio::ImageBufAlgo::make_kernel("gaussian", albedoEstimationFilterSize, albedoEstimationFilterSize); + oiio::ImageBufAlgo::convolve(albedoBuf, pictureBuf, K); + image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, image::ImageWriteOptions()); + } + break; } - break; - case EAlbedoEstimation::PICTURE: - { - albedo = picture; - } - break; - case EAlbedoEstimation::MEDIAN_FILTER: - { - albedo.resize(picture.width(), picture.height()); - const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), const_cast(picture.data())); - oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), albedo.data()); - oiio::ImageBufAlgo::median_filter(albedoBuf, pictureBuf, albedoEstimationFilterSize, albedoEstimationFilterSize); - image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, - image::ImageWriteOptions()); - } - break; - case EAlbedoEstimation::BLUR_FILTER: - { - albedo.resize(picture.width(), picture.height()); - const oiio::ImageBuf pictureBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), const_cast(picture.data())); - oiio::ImageBuf albedoBuf(oiio::ImageSpec(picture.width(), picture.height(), 1, oiio::TypeDesc::FLOAT), albedo.data()); - oiio::ImageBuf K = oiio::ImageBufAlgo::make_kernel("gaussian", albedoEstimationFilterSize, albedoEstimationFilterSize); - oiio::ImageBufAlgo::convolve(albedoBuf, pictureBuf, K); - image::writeImage((fs::path(outputFolder) / (std::to_string(viewId) + "_albedo.jpg")).string(), albedo, - image::ImageWriteOptions()); - } - break; - } } int main(int argc, char** argv) { - system::Timer timer; + system::Timer timer; - // command-line parameters - std::string sfmDataFilename; - std::string depthMapsFilterFolder; - std::string imagesFolder; - std::string outputFolder; + // command-line parameters + std::string sfmDataFilename; + std::string depthMapsFilterFolder; + std::string imagesFolder; + std::string outputFolder; - EAlbedoEstimation albedoEstimationMethod = EAlbedoEstimation::CONSTANT; - ELightingEstimationMode lightEstimationMode = ELightingEstimationMode::PER_IMAGE; + EAlbedoEstimation albedoEstimationMethod = EAlbedoEstimation::CONSTANT; + ELightingEstimationMode lightEstimationMode = ELightingEstimationMode::PER_IMAGE; - int albedoEstimationFilterSize = 3; - ELightingColor lightingColor = ELightingColor::RGB; + int albedoEstimationFilterSize = 3; + ELightingColor lightingColor = ELightingColor::RGB; // clang-format off po::options_description requiredParams("Required parameters"); @@ -303,84 +317,84 @@ int main(int argc, char** argv) "Albedo filter size for estimation method using filter."); // clang-format on - CmdLine cmdline("AliceVision lightingEstimation"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // read the input SfM scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); - return EXIT_FAILURE; - } - - // initialization - mvsUtils::MultiViewParams mp(sfmData, imagesFolder, "", depthMapsFilterFolder, false); - - lightingEstimation::LighthingEstimator estimator; - - for(const auto& viewPair : sfmData.getViews()) - { - const IndexT viewId = viewPair.first; - - const std::string picturePath = mp.getImagePath(mp.getIndexFromViewId(viewId)); - const std::string normalsPath = mvsUtils::getFileNameFromViewId(mp, viewId, mvsUtils::EFileType::normalMapFiltered); - - image::Image normals; - image::readImage(normalsPath, normals, image::EImageColorSpace::LINEAR); - - if(lightingColor == ELightingColor::Luminance) + CmdLine cmdline("AliceVision lightingEstimation"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - image::Image albedo, picture; - image::readImage(picturePath, picture, image::EImageColorSpace::LINEAR); - - initAlbedo(albedo, picture, albedoEstimationMethod, albedoEstimationFilterSize, outputFolder, viewId); - - estimator.addImage(albedo, picture, normals); + return EXIT_FAILURE; } - else if(lightingColor == ELightingColor::RGB) + + // read the input SfM scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - image::Image albedo, picture; - image::readImage(picturePath, picture, image::EImageColorSpace::LINEAR); + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read."); + return EXIT_FAILURE; + } - initAlbedo(albedo, picture, albedoEstimationMethod, albedoEstimationFilterSize, outputFolder, viewId); + // initialization + mvsUtils::MultiViewParams mp(sfmData, imagesFolder, "", depthMapsFilterFolder, false); - estimator.addImage(albedo, picture, normals); - } + lightingEstimation::LighthingEstimator estimator; - if(lightEstimationMode == ELightingEstimationMode::PER_IMAGE) - { - ALICEVISION_LOG_INFO("Solving view: " << viewId << " lighting estimation..."); - lightingEstimation::LightingVector shl; - estimator.estimateLigthing(shl); - estimator.clear(); // clear aggregate data - - std::ofstream file((fs::path(outputFolder) / (std::to_string(viewId) + ".shl")).string()); - if(file.is_open()) - file << shl; - } - else + for (const auto& viewPair : sfmData.getViews()) { - ALICEVISION_LOG_INFO("View: " << viewId << " added to the problem."); + const IndexT viewId = viewPair.first; + + const std::string picturePath = mp.getImagePath(mp.getIndexFromViewId(viewId)); + const std::string normalsPath = mvsUtils::getFileNameFromViewId(mp, viewId, mvsUtils::EFileType::normalMapFiltered); + + image::Image normals; + image::readImage(normalsPath, normals, image::EImageColorSpace::LINEAR); + + if (lightingColor == ELightingColor::Luminance) + { + image::Image albedo, picture; + image::readImage(picturePath, picture, image::EImageColorSpace::LINEAR); + + initAlbedo(albedo, picture, albedoEstimationMethod, albedoEstimationFilterSize, outputFolder, viewId); + + estimator.addImage(albedo, picture, normals); + } + else if (lightingColor == ELightingColor::RGB) + { + image::Image albedo, picture; + image::readImage(picturePath, picture, image::EImageColorSpace::LINEAR); + + initAlbedo(albedo, picture, albedoEstimationMethod, albedoEstimationFilterSize, outputFolder, viewId); + + estimator.addImage(albedo, picture, normals); + } + + if (lightEstimationMode == ELightingEstimationMode::PER_IMAGE) + { + ALICEVISION_LOG_INFO("Solving view: " << viewId << " lighting estimation..."); + lightingEstimation::LightingVector shl; + estimator.estimateLigthing(shl); + estimator.clear(); // clear aggregate data + + std::ofstream file((fs::path(outputFolder) / (std::to_string(viewId) + ".shl")).string()); + if (file.is_open()) + file << shl; + } + else + { + ALICEVISION_LOG_INFO("View: " << viewId << " added to the problem."); + } } - } - if(lightEstimationMode == ELightingEstimationMode::GLOBAL) - { - ALICEVISION_LOG_INFO("Solving global scene lighting estimation..."); - lightingEstimation::LightingVector shl; - estimator.estimateLigthing(shl); + if (lightEstimationMode == ELightingEstimationMode::GLOBAL) + { + ALICEVISION_LOG_INFO("Solving global scene lighting estimation..."); + lightingEstimation::LightingVector shl; + estimator.estimateLigthing(shl); - std::ofstream file((fs::path(outputFolder) / ("global.shl")).string()); - if(file.is_open()) - file << shl; - } + std::ofstream file((fs::path(outputFolder) / ("global.shl")).string()); + if (file.is_open()) + file << shl; + } - ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); - return EXIT_SUCCESS; + ALICEVISION_LOG_INFO("Task done in (s): " + std::to_string(timer.elapsed())); + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_mergeMeshes.cpp b/src/software/utils/main_mergeMeshes.cpp index 06aa17b6e3..3b73a41f24 100644 --- a/src/software/utils/main_mergeMeshes.cpp +++ b/src/software/utils/main_mergeMeshes.cpp @@ -63,13 +63,16 @@ std::string EOperationType_informations() */ std::string EOperationType_enumToString(EOperationType operationType) { - switch(operationType) - { - case EOperationType::BOOLEAN_UNION: return "boolean_union"; - case EOperationType::BOOLEAN_INTERSECTION: return "boolean_intersection"; - case EOperationType::BOOLEAN_DIFFERENCE: return "boolean_difference"; - } - throw std::out_of_range("Invalid operationType enum: " + std::to_string(int(operationType))); + switch (operationType) + { + case EOperationType::BOOLEAN_UNION: + return "boolean_union"; + case EOperationType::BOOLEAN_INTERSECTION: + return "boolean_intersection"; + case EOperationType::BOOLEAN_DIFFERENCE: + return "boolean_difference"; + } + throw std::out_of_range("Invalid operationType enum: " + std::to_string(int(operationType))); } /** @@ -79,13 +82,16 @@ std::string EOperationType_enumToString(EOperationType operationType) */ EOperationType EOperationType_stringToEnum(const std::string& operationType) { - std::string type = operationType; - std::transform(type.begin(), type.end(), type.begin(), ::tolower); // tolower - - if(type == "boolean_union") return EOperationType::BOOLEAN_UNION; - if(type == "boolean_intersection") return EOperationType::BOOLEAN_INTERSECTION; - if(type == "boolean_difference") return EOperationType::BOOLEAN_DIFFERENCE; - throw std::out_of_range("Invalid operationType: " + operationType); + std::string type = operationType; + std::transform(type.begin(), type.end(), type.begin(), ::tolower); // tolower + + if (type == "boolean_union") + return EOperationType::BOOLEAN_UNION; + if (type == "boolean_intersection") + return EOperationType::BOOLEAN_INTERSECTION; + if (type == "boolean_difference") + return EOperationType::BOOLEAN_DIFFERENCE; + throw std::out_of_range("Invalid operationType: " + operationType); } std::ostream& operator<<(std::ostream& os, const EOperationType operationType) @@ -164,21 +170,21 @@ int aliceVision_main(int argc, char** argv) } // check first mesh file path - if(!inputFirstMeshPath.empty() && !fs::exists(inputFirstMeshPath) && !fs::is_regular_file(inputFirstMeshPath)) + if (!inputFirstMeshPath.empty() && !fs::exists(inputFirstMeshPath) && !fs::is_regular_file(inputFirstMeshPath)) { ALICEVISION_LOG_ERROR("The first mesh file doesn't exist"); return EXIT_FAILURE; } // check second mesh file path - if(!inputSecondMeshPath.empty() && !fs::exists(inputSecondMeshPath) && !fs::is_regular_file(inputSecondMeshPath)) + if (!inputSecondMeshPath.empty() && !fs::exists(inputSecondMeshPath) && !fs::is_regular_file(inputSecondMeshPath)) { ALICEVISION_LOG_ERROR("The second mesh file doesn't exist"); return EXIT_FAILURE; } // check output file path - if(outputFilePath.empty()) + if (outputFilePath.empty()) { ALICEVISION_LOG_ERROR("Invalid output"); return EXIT_FAILURE; @@ -188,9 +194,9 @@ int aliceVision_main(int argc, char** argv) { const std::string outputFolderPart = fs::path(outputFilePath).parent_path().string(); - if(!outputFolderPart.empty() && !fs::exists(outputFolderPart)) + if (!outputFolderPart.empty() && !fs::exists(outputFolderPart)) { - if(!fs::create_directory(outputFolderPart)) + if (!fs::create_directory(outputFolderPart)) { ALICEVISION_LOG_ERROR("Cannot create output folder"); return EXIT_FAILURE; @@ -208,23 +214,23 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Geogram initialized."); GEO::Mesh inputFirstMesh, inputSecondMesh, outputMesh; - + // load first input mesh - if(!GEO::mesh_load(inputFirstMeshPath, inputFirstMesh)) + if (!GEO::mesh_load(inputFirstMeshPath, inputFirstMesh)) { ALICEVISION_LOG_ERROR("Failed to load mesh file: \"" << inputFirstMeshPath << "\"."); return EXIT_FAILURE; } // load second input mesh - if(!GEO::mesh_load(inputSecondMeshPath, inputSecondMesh)) + if (!GEO::mesh_load(inputSecondMeshPath, inputSecondMesh)) { ALICEVISION_LOG_ERROR("Failed to load mesh file: \"" << inputSecondMeshPath << "\"."); return EXIT_FAILURE; } - + // pre-process input meshes - if(preProcess) + if (preProcess) { ALICEVISION_LOG_INFO("Pre-process input meshes..."); fixMeshForBooleanOperations(inputFirstMesh); @@ -233,30 +239,30 @@ int aliceVision_main(int argc, char** argv) // merge mesh ALICEVISION_LOG_INFO("Merging meshes..."); - switch(operationType) + switch (operationType) { case EOperationType::BOOLEAN_UNION: { - ALICEVISION_LOG_INFO("BOOLEAN_UNION"); - GEO::mesh_union(outputMesh, inputFirstMesh, inputSecondMesh); + ALICEVISION_LOG_INFO("BOOLEAN_UNION"); + GEO::mesh_union(outputMesh, inputFirstMesh, inputSecondMesh); } break; case EOperationType::BOOLEAN_INTERSECTION: { - ALICEVISION_LOG_INFO("BOOLEAN_INTERSECTION"); - GEO::mesh_intersection(outputMesh, inputFirstMesh, inputSecondMesh); + ALICEVISION_LOG_INFO("BOOLEAN_INTERSECTION"); + GEO::mesh_intersection(outputMesh, inputFirstMesh, inputSecondMesh); } break; case EOperationType::BOOLEAN_DIFFERENCE: { - ALICEVISION_LOG_INFO("BOOLEAN_DIFFERENCE"); - GEO::mesh_difference(outputMesh, inputFirstMesh, inputSecondMesh); + ALICEVISION_LOG_INFO("BOOLEAN_DIFFERENCE"); + GEO::mesh_difference(outputMesh, inputFirstMesh, inputSecondMesh); } break; } // post-process final mesh - if(postProcess) + if (postProcess) { ALICEVISION_LOG_INFO("Post-process final mesh..."); fixMeshForBooleanOperations(outputMesh); @@ -264,7 +270,7 @@ int aliceVision_main(int argc, char** argv) // save output mesh ALICEVISION_LOG_INFO("Save mesh."); - if(!GEO::mesh_save(outputMesh, outputFilePath)) + if (!GEO::mesh_save(outputMesh, outputFilePath)) { ALICEVISION_LOG_ERROR("Failed to save mesh file: \"" << outputFilePath << "\"."); return EXIT_FAILURE; diff --git a/src/software/utils/main_qualityEvaluation.cpp b/src/software/utils/main_qualityEvaluation.cpp index 2625a6c20e..897c9da581 100644 --- a/src/software/utils/main_qualityEvaluation.cpp +++ b/src/software/utils/main_qualityEvaluation.cpp @@ -33,12 +33,12 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder; - std::string gtFilename; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; + std::string gtFilename; // clang-format off po::options_description requiredParams("Required parameters"); @@ -51,84 +51,83 @@ int aliceVision_main(int argc, char **argv) "Path to a ground truth reconstructed scene."); // clang-format on - CmdLine cmdline("AliceVision qualityEvaluation"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(outputFolder.empty()) - { - ALICEVISION_LOG_ERROR("Invalid output folder"); - return EXIT_FAILURE; - } - - if (!fs::exists(outputFolder)) - fs::create_directory(outputFolder); - - // load GT camera rotations & positions [R|C]: - std::mt19937 randomNumberGenerator; - sfmData::SfMData sfmData_gt; - - if(!sfmDataIO::load(sfmData_gt, gtFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< gtFilename << "' cannot be read"); - return EXIT_FAILURE; - } - ALICEVISION_LOG_INFO(sfmData_gt.getPoses().size() << " gt cameras have been found"); - - // load the camera that we have to evaluate - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS|sfmDataIO::INTRINSICS|sfmDataIO::EXTRINSICS))) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - // fill vectors of valid views for evaluation - std::vector vec_camPosGT, vec_C; - std::vector vec_camRotGT, vec_camRot; - for(const auto &iter : sfmData.getViews()) - { - const auto &view = iter.second; - // jump to next view if there is no correponding pose in reconstruction - if(sfmData.getPoses().find(view->getPoseId()) == sfmData.getPoses().end()) + CmdLine cmdline("AliceVision qualityEvaluation"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) { - ALICEVISION_LOG_INFO("no pose in input for view " << view->getPoseId()); - continue; + return EXIT_FAILURE; } - // jump to next view if there is no corresponding view in GT - if(sfmData_gt.getViews().find(view->getViewId()) == sfmData_gt.getViews().end()) + if (outputFolder.empty()) { - ALICEVISION_LOG_INFO("no view in GT for viewId " << view->getViewId()); - continue; + ALICEVISION_LOG_ERROR("Invalid output folder"); + return EXIT_FAILURE; } - const int idPoseGT = sfmData_gt.getViews().at(view->getViewId())->getPoseId(); - // gt - const geometry::Pose3& pose_gt = sfmData_gt.getAbsolutePose(idPoseGT).getTransform(); - vec_camPosGT.push_back(pose_gt.center()); - vec_camRotGT.push_back(pose_gt.rotation()); + if (!fs::exists(outputFolder)) + fs::create_directory(outputFolder); - // data to evaluate - const geometry::Pose3 pose_eval = sfmData.getPose(*view).getTransform(); - vec_C.push_back(pose_eval.center()); - vec_camRot.push_back(pose_eval.rotation()); - } + // load GT camera rotations & positions [R|C]: + std::mt19937 randomNumberGenerator; + sfmData::SfMData sfmData_gt; - // visual output of the camera location - plyHelper::exportToPly(vec_camPosGT, (fs::path(outputFolder) / "camGT.ply").string()); - plyHelper::exportToPly(vec_C, (fs::path(outputFolder) / "camComputed.ply").string()); + if (!sfmDataIO::load(sfmData_gt, gtFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << gtFilename << "' cannot be read"); + return EXIT_FAILURE; + } + ALICEVISION_LOG_INFO(sfmData_gt.getPoses().size() << " gt cameras have been found"); + + // load the camera that we have to evaluate + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS | sfmDataIO::EXTRINSICS))) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; + } - // evaluation - htmlDocument::htmlDocumentStream _htmlDocStream("aliceVision Quality evaluation."); - EvaluteToGT(vec_camPosGT, vec_C, vec_camRotGT, vec_camRot, outputFolder, randomNumberGenerator, &_htmlDocStream); + // fill vectors of valid views for evaluation + std::vector vec_camPosGT, vec_C; + std::vector vec_camRotGT, vec_camRot; + for (const auto& iter : sfmData.getViews()) + { + const auto& view = iter.second; + // jump to next view if there is no correponding pose in reconstruction + if (sfmData.getPoses().find(view->getPoseId()) == sfmData.getPoses().end()) + { + ALICEVISION_LOG_INFO("no pose in input for view " << view->getPoseId()); + continue; + } + + // jump to next view if there is no corresponding view in GT + if (sfmData_gt.getViews().find(view->getViewId()) == sfmData_gt.getViews().end()) + { + ALICEVISION_LOG_INFO("no view in GT for viewId " << view->getViewId()); + continue; + } + const int idPoseGT = sfmData_gt.getViews().at(view->getViewId())->getPoseId(); + + // gt + const geometry::Pose3& pose_gt = sfmData_gt.getAbsolutePose(idPoseGT).getTransform(); + vec_camPosGT.push_back(pose_gt.center()); + vec_camRotGT.push_back(pose_gt.rotation()); + + // data to evaluate + const geometry::Pose3 pose_eval = sfmData.getPose(*view).getTransform(); + vec_C.push_back(pose_eval.center()); + vec_camRot.push_back(pose_eval.rotation()); + } - std::ofstream htmlFileStream((fs::path(outputFolder) / "ExternalCalib_Report.html").string()); - htmlFileStream << _htmlDocStream.getDoc(); + // visual output of the camera location + plyHelper::exportToPly(vec_camPosGT, (fs::path(outputFolder) / "camGT.ply").string()); + plyHelper::exportToPly(vec_C, (fs::path(outputFolder) / "camComputed.ply").string()); - return EXIT_SUCCESS; -} + // evaluation + htmlDocument::htmlDocumentStream _htmlDocStream("aliceVision Quality evaluation."); + EvaluteToGT(vec_camPosGT, vec_C, vec_camRotGT, vec_camRot, outputFolder, randomNumberGenerator, &_htmlDocStream); + std::ofstream htmlFileStream((fs::path(outputFolder) / "ExternalCalib_Report.html").string()); + htmlFileStream << _htmlDocStream.getDoc(); + + return EXIT_SUCCESS; +} diff --git a/src/software/utils/main_rigTransform.cpp b/src/software/utils/main_rigTransform.cpp index 745f8dd18c..49ae04860c 100644 --- a/src/software/utils/main_rigTransform.cpp +++ b/src/software/utils/main_rigTransform.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -30,23 +30,23 @@ namespace po = boost::program_options; static std::vector ReadIntrinsicsFile(const std::string& fname) { - ALICEVISION_LOG_INFO("reading intrinsics: " << fname); + ALICEVISION_LOG_INFO("reading intrinsics: " << fname); - std::vector v(8); - std::ifstream ifs(fname); - if (!(ifs >> v[0] >> v[1] >> v[2] >> v[3] >> v[4] >> v[5] >> v[6] >> v[7])) - throw std::runtime_error("failed to read intrinsics file"); - return v; + std::vector v(8); + std::ifstream ifs(fname); + if (!(ifs >> v[0] >> v[1] >> v[2] >> v[3] >> v[4] >> v[5] >> v[6] >> v[7])) + throw std::runtime_error("failed to read intrinsics file"); + return v; } int aliceVision_main(int argc, char** argv) { - std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); - std::string exportFile; - std::string importFile; - std::string rigFile; - std::string calibFile; - std::vector extrinsics; // the rig subposes + std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel()); + std::string exportFile; + std::string importFile; + std::string rigFile; + std::string calibFile; + std::vector extrinsics; // the rig subposes // clang-format off po::options_description requiredParams("Required parameters"); @@ -62,53 +62,47 @@ int aliceVision_main(int argc, char** argv) "Rig calibration file that will be applied to input."); // clang-format on - CmdLine cmdline("This program is used to deduce the pose of the not localized cameras of the RIG.\n" - "Use if you have localized a single camera from an acquisition with a RIG of cameras.\n" - "AliceVision rigTransform"); - cmdline.add(requiredParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - // load rig calibration file - if(!rig::loadRigCalibration(rigFile, extrinsics)) - { - ALICEVISION_LOG_ERROR("Unable to open " << rigFile); - return EXIT_FAILURE; - } - assert(!extrinsics.empty()); - - // import sfm data - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, importFile, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '"<< importFile << "' cannot be read"); - return EXIT_FAILURE; - } - - // load intrinsics - auto v = ReadIntrinsicsFile(calibFile); - camera::Pinhole intrinsics = camera::Pinhole( - v[0], v[1], - v[2], v[2], - v[3], v[4], - std::make_shared(v[5], v[6], v[7])); - - // export to abc - sfmDataIO::AlembicExporter exporter(exportFile); - exporter.initAnimatedCamera("camera"); - - std::size_t idx = 0; - for (auto &p : sfmData.getPoses()) - { - const geometry::Pose3 rigPose = extrinsics[0].inverse() * p.second.getTransform(); - exporter.addCameraKeyframe(rigPose, &intrinsics, "", idx, idx); - ++idx; - } - exporter.addLandmarks(sfmData.getLandmarks()); - - return EXIT_SUCCESS; + CmdLine cmdline("This program is used to deduce the pose of the not localized cameras of the RIG.\n" + "Use if you have localized a single camera from an acquisition with a RIG of cameras.\n" + "AliceVision rigTransform"); + cmdline.add(requiredParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // load rig calibration file + if (!rig::loadRigCalibration(rigFile, extrinsics)) + { + ALICEVISION_LOG_ERROR("Unable to open " << rigFile); + return EXIT_FAILURE; + } + assert(!extrinsics.empty()); + + // import sfm data + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, importFile, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << importFile << "' cannot be read"); + return EXIT_FAILURE; + } + + // load intrinsics + auto v = ReadIntrinsicsFile(calibFile); + camera::Pinhole intrinsics = camera::Pinhole(v[0], v[1], v[2], v[2], v[3], v[4], std::make_shared(v[5], v[6], v[7])); + + // export to abc + sfmDataIO::AlembicExporter exporter(exportFile); + exporter.initAnimatedCamera("camera"); + + std::size_t idx = 0; + for (auto& p : sfmData.getPoses()) + { + const geometry::Pose3 rigPose = extrinsics[0].inverse() * p.second.getTransform(); + exporter.addCameraKeyframe(rigPose, &intrinsics, "", idx, idx); + ++idx; + } + exporter.addLandmarks(sfmData.getLandmarks()); + + return EXIT_SUCCESS; } - diff --git a/src/software/utils/main_sfmAlignment.cpp b/src/software/utils/main_sfmAlignment.cpp index bb5b4d8555..322aef7b6e 100644 --- a/src/software/utils/main_sfmAlignment.cpp +++ b/src/software/utils/main_sfmAlignment.cpp @@ -30,50 +30,60 @@ namespace po = boost::program_options; namespace { /** -* @brief Alignment method enum -*/ + * @brief Alignment method enum + */ enum class EAlignmentMethod : unsigned char { - FROM_CAMERAS_VIEWID = 0 - , FROM_CAMERAS_POSEID - , FROM_CAMERAS_FILEPATH - , FROM_CAMERAS_METADATA - , FROM_MARKERS + FROM_CAMERAS_VIEWID = 0, + FROM_CAMERAS_POSEID, + FROM_CAMERAS_FILEPATH, + FROM_CAMERAS_METADATA, + FROM_MARKERS }; /** -* @brief Convert an EAlignmentMethod enum to its corresponding string -* @param[in] alignmentMethod The given EAlignmentMethod enum -* @return string -*/ + * @brief Convert an EAlignmentMethod enum to its corresponding string + * @param[in] alignmentMethod The given EAlignmentMethod enum + * @return string + */ std::string EAlignmentMethod_enumToString(EAlignmentMethod alignmentMethod) { switch (alignmentMethod) { - case EAlignmentMethod::FROM_CAMERAS_VIEWID: return "from_cameras_viewid"; - case EAlignmentMethod::FROM_CAMERAS_POSEID: return "from_cameras_poseid"; - case EAlignmentMethod::FROM_CAMERAS_FILEPATH: return "from_cameras_filepath"; - case EAlignmentMethod::FROM_CAMERAS_METADATA: return "from_cameras_metadata"; - case EAlignmentMethod::FROM_MARKERS: return "from_markers"; + case EAlignmentMethod::FROM_CAMERAS_VIEWID: + return "from_cameras_viewid"; + case EAlignmentMethod::FROM_CAMERAS_POSEID: + return "from_cameras_poseid"; + case EAlignmentMethod::FROM_CAMERAS_FILEPATH: + return "from_cameras_filepath"; + case EAlignmentMethod::FROM_CAMERAS_METADATA: + return "from_cameras_metadata"; + case EAlignmentMethod::FROM_MARKERS: + return "from_markers"; } throw std::out_of_range("Invalid EAlignmentMethod enum"); } /** -* @brief Convert a string to its corresponding EAlignmentMethod enum -* @param[in] alignmentMethod The given string -* @return EAlignmentMethod enum -*/ + * @brief Convert a string to its corresponding EAlignmentMethod enum + * @param[in] alignmentMethod The given string + * @return EAlignmentMethod enum + */ EAlignmentMethod EAlignmentMethod_stringToEnum(const std::string& alignmentMethod) { std::string method = alignmentMethod; - std::transform(method.begin(), method.end(), method.begin(), ::tolower); //tolower + std::transform(method.begin(), method.end(), method.begin(), ::tolower); // tolower - if (method == "from_cameras_viewid") return EAlignmentMethod::FROM_CAMERAS_VIEWID; - if (method == "from_cameras_poseid") return EAlignmentMethod::FROM_CAMERAS_POSEID; - if (method == "from_cameras_filepath") return EAlignmentMethod::FROM_CAMERAS_FILEPATH; - if (method == "from_cameras_metadata") return EAlignmentMethod::FROM_CAMERAS_METADATA; - if (method == "from_markers") return EAlignmentMethod::FROM_MARKERS; + if (method == "from_cameras_viewid") + return EAlignmentMethod::FROM_CAMERAS_VIEWID; + if (method == "from_cameras_poseid") + return EAlignmentMethod::FROM_CAMERAS_POSEID; + if (method == "from_cameras_filepath") + return EAlignmentMethod::FROM_CAMERAS_FILEPATH; + if (method == "from_cameras_metadata") + return EAlignmentMethod::FROM_CAMERAS_METADATA; + if (method == "from_markers") + return EAlignmentMethod::FROM_MARKERS; throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); } @@ -85,26 +95,23 @@ inline std::istream& operator>>(std::istream& in, EAlignmentMethod& alignment) return in; } -inline std::ostream& operator<<(std::ostream& os, EAlignmentMethod e) -{ - return os << EAlignmentMethod_enumToString(e); -} +inline std::ostream& operator<<(std::ostream& os, EAlignmentMethod e) { return os << EAlignmentMethod_enumToString(e); } -} // namespace +} // namespace -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outSfMDataFilename; - std::string sfmDataReferenceFilename; - bool applyScale = true; - bool applyRotation = true; - bool applyTranslation = true; - EAlignmentMethod alignmentMethod = EAlignmentMethod::FROM_CAMERAS_VIEWID; - std::string fileMatchingPattern; - std::vector metadataMatchingList = {"Make", "Model", "Exif:BodySerialNumber" , "Exif:LensSerialNumber" }; - std::string outputViewsAndPosesFilepath; + // command-line parameters + std::string sfmDataFilename; + std::string outSfMDataFilename; + std::string sfmDataReferenceFilename; + bool applyScale = true; + bool applyRotation = true; + bool applyTranslation = true; + EAlignmentMethod alignmentMethod = EAlignmentMethod::FROM_CAMERAS_VIEWID; + std::string fileMatchingPattern; + std::vector metadataMatchingList = {"Make", "Model", "Exif:BodySerialNumber", "Exif:LensSerialNumber"}; + std::string outputViewsAndPosesFilepath; // clang-format off po::options_description requiredParams("Required parameters"); @@ -139,111 +146,111 @@ int aliceVision_main(int argc, char **argv) "Path of the output SfMData file."); // clang-format on - CmdLine cmdline("AliceVision sfmAlignment"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - - std::mt19937 randomNumberGenerator; - - // Load input scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - // Load reference scene - sfmData::SfMData sfmDataInRef; - if(!sfmDataIO::load(sfmDataInRef, sfmDataReferenceFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The reference SfMData file '" << sfmDataReferenceFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - ALICEVISION_LOG_INFO("Search similarity transformation."); - - double S; - Mat3 R; - Vec3 t; - bool hasValidSimilarity = false; - - switch(alignmentMethod) - { - case EAlignmentMethod::FROM_CAMERAS_VIEWID: + CmdLine cmdline("AliceVision sfmAlignment"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_viewId(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); - break; + return EXIT_FAILURE; } - case EAlignmentMethod::FROM_CAMERAS_POSEID: + + std::mt19937 randomNumberGenerator; + + // Load input scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_poseId(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); - break; + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; } - case EAlignmentMethod::FROM_CAMERAS_FILEPATH: + + // Load reference scene + sfmData::SfMData sfmDataInRef; + if (!sfmDataIO::load(sfmDataInRef, sfmDataReferenceFilename, sfmDataIO::ESfMData::ALL)) { - hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_imageFileMatching(sfmData, sfmDataInRef, fileMatchingPattern, randomNumberGenerator, &S, &R, &t); - break; + ALICEVISION_LOG_ERROR("The reference SfMData file '" << sfmDataReferenceFilename << "' cannot be read"); + return EXIT_FAILURE; } - case EAlignmentMethod::FROM_CAMERAS_METADATA: + + ALICEVISION_LOG_INFO("Search similarity transformation."); + + double S; + Mat3 R; + Vec3 t; + bool hasValidSimilarity = false; + + switch (alignmentMethod) { - hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_metadataMatching(sfmData, sfmDataInRef, metadataMatchingList, randomNumberGenerator, &S, &R, &t); - break; + case EAlignmentMethod::FROM_CAMERAS_VIEWID: + { + hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_viewId(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); + break; + } + case EAlignmentMethod::FROM_CAMERAS_POSEID: + { + hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_poseId(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); + break; + } + case EAlignmentMethod::FROM_CAMERAS_FILEPATH: + { + hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_imageFileMatching( + sfmData, sfmDataInRef, fileMatchingPattern, randomNumberGenerator, &S, &R, &t); + break; + } + case EAlignmentMethod::FROM_CAMERAS_METADATA: + { + hasValidSimilarity = sfm::computeSimilarityFromCommonCameras_metadataMatching( + sfmData, sfmDataInRef, metadataMatchingList, randomNumberGenerator, &S, &R, &t); + break; + } + case EAlignmentMethod::FROM_MARKERS: + { + hasValidSimilarity = sfm::computeSimilarityFromCommonMarkers(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); + break; + } } - case EAlignmentMethod::FROM_MARKERS: + + if (!hasValidSimilarity) { - hasValidSimilarity = sfm::computeSimilarityFromCommonMarkers(sfmData, sfmDataInRef, randomNumberGenerator, &S, &R, &t); - break; + std::stringstream ss; + ss << "Failed to find similarity between the 2 SfM scenes:"; + ss << "\t- " << sfmDataFilename << std::endl; + ss << "\t- " << sfmDataReferenceFilename << std::endl; + ALICEVISION_LOG_ERROR(ss.str()); + return EXIT_FAILURE; } - } - - if(!hasValidSimilarity) - { - std::stringstream ss; - ss << "Failed to find similarity between the 2 SfM scenes:"; - ss << "\t- " << sfmDataFilename << std::endl; - ss << "\t- " << sfmDataReferenceFilename << std::endl; - ALICEVISION_LOG_ERROR(ss.str()); - return EXIT_FAILURE; - } - - { - std::stringstream ss; - ss << "Transformation:" << std::endl; - ss << "\t- Scale: " << S << std::endl; - ss << "\t- Rotation:\n" << R << std::endl; - ss << "\t- Translate: " << t.transpose() << std::endl; - ALICEVISION_LOG_INFO(ss.str()); - } - - if (!applyScale) - S = 1; - if (!applyRotation) - R = Mat3::Identity(); - if (!applyTranslation) - t = Vec3::Zero(); - - sfm::applyTransform(sfmData, S, R, t); - - ALICEVISION_LOG_INFO("Save into '" << outSfMDataFilename << "'"); - - // Export the SfMData scene in the expected format - if(!sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outSfMDataFilename << "'"); - return EXIT_FAILURE; - } - - if(!outputViewsAndPosesFilepath.empty()) - { - sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); - } - - return EXIT_SUCCESS; + + { + std::stringstream ss; + ss << "Transformation:" << std::endl; + ss << "\t- Scale: " << S << std::endl; + ss << "\t- Rotation:\n" << R << std::endl; + ss << "\t- Translate: " << t.transpose() << std::endl; + ALICEVISION_LOG_INFO(ss.str()); + } + + if (!applyScale) + S = 1; + if (!applyRotation) + R = Mat3::Identity(); + if (!applyTranslation) + t = Vec3::Zero(); + + sfm::applyTransform(sfmData, S, R, t); + + ALICEVISION_LOG_INFO("Save into '" << outSfMDataFilename << "'"); + + // Export the SfMData scene in the expected format + if (!sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outSfMDataFilename << "'"); + return EXIT_FAILURE; + } + + if (!outputViewsAndPosesFilepath.empty()) + { + sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); + } + + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_sfmColorHarmonize.cpp b/src/software/utils/main_sfmColorHarmonize.cpp index 24d17203dd..bdf1f94acf 100644 --- a/src/software/utils/main_sfmColorHarmonize.cpp +++ b/src/software/utils/main_sfmColorHarmonize.cpp @@ -28,16 +28,16 @@ using namespace aliceVision; namespace po = boost::program_options; namespace fs = std::filesystem; -int aliceVision_main( int argc, char **argv ) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outputFolder ; - std::vector featuresFolders; - std::vector matchesFolders; - std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); - EHistogramSelectionMethod selectionMethod; - int imgRef; + // command-line parameters + std::string sfmDataFilename; + std::string outputFolder; + std::vector featuresFolders; + std::vector matchesFolders; + std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT); + EHistogramSelectionMethod selectionMethod; + int imgRef; // user optional parameters // clang-format off @@ -62,46 +62,41 @@ int aliceVision_main( int argc, char **argv ) feature::EImageDescriberType_informations().c_str()); // clang-format on - CmdLine cmdline("AliceVision sfmColorHarmonize"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if(sfmDataFilename.empty()) - { - ALICEVISION_LOG_ERROR("It is an invalid file input"); - return EXIT_FAILURE; - } + CmdLine cmdline("AliceVision sfmColorHarmonize"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (sfmDataFilename.empty()) + { + ALICEVISION_LOG_ERROR("It is an invalid file input"); + return EXIT_FAILURE; + } - const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); + const std::vector describerTypes = feature::EImageDescriberType_stringToEnums(describerTypesName); - if(!fs::exists(outputFolder)) - fs::create_directory(outputFolder); + if (!fs::exists(outputFolder)) + fs::create_directory(outputFolder); - // harmonization process + // harmonization process - aliceVision::system::Timer timer; + aliceVision::system::Timer timer; - ColorHarmonizationEngineGlobal colorHarmonizeEngine(sfmDataFilename, - featuresFolders, - matchesFolders, - outputFolder, - describerTypes, - selectionMethod, - imgRef); + ColorHarmonizationEngineGlobal colorHarmonizeEngine( + sfmDataFilename, featuresFolders, matchesFolders, outputFolder, describerTypes, selectionMethod, imgRef); - if(colorHarmonizeEngine.Process()) - { - ALICEVISION_LOG_INFO("Color harmonization took: " << timer.elapsed() << " s"); - return EXIT_SUCCESS; - } - else - { - ALICEVISION_LOG_ERROR("Something goes wrong during the color harmonization process."); - } + if (colorHarmonizeEngine.Process()) + { + ALICEVISION_LOG_INFO("Color harmonization took: " << timer.elapsed() << " s"); + return EXIT_SUCCESS; + } + else + { + ALICEVISION_LOG_ERROR("Something goes wrong during the color harmonization process."); + } - return EXIT_FAILURE; + return EXIT_FAILURE; } diff --git a/src/software/utils/main_sfmDistances.cpp b/src/software/utils/main_sfmDistances.cpp index 7c1032ae04..fdb1021d4b 100644 --- a/src/software/utils/main_sfmDistances.cpp +++ b/src/software/utils/main_sfmDistances.cpp @@ -20,7 +20,6 @@ #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 @@ -34,10 +33,10 @@ namespace fs = std::filesystem; /** * @brief Alignment method enum */ -enum class EObject: unsigned char +enum class EObject : unsigned char { - CAMERAS, - LANDMARKS + CAMERAS, + LANDMARKS }; /** @@ -47,12 +46,14 @@ enum class EObject: unsigned char */ std::string EObject_enumToString(EObject obj) { - switch(obj) - { - case EObject::CAMERAS: return "cameras"; - case EObject::LANDMARKS: return "landmarks"; - } - throw std::out_of_range("Invalid EAlignmentMethod enum"); + switch (obj) + { + case EObject::CAMERAS: + return "cameras"; + case EObject::LANDMARKS: + return "landmarks"; + } + throw std::out_of_range("Invalid EAlignmentMethod enum"); } /** @@ -62,15 +63,16 @@ std::string EObject_enumToString(EObject obj) */ EObject EObject_stringToEnum(const std::string& alignmentMethod) { - std::string method = alignmentMethod; - std::transform(method.begin(), method.end(), method.begin(), ::tolower); - - if(method == "landmarks") return EObject::LANDMARKS; - if(method == "cameras") return EObject::CAMERAS; - throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); + std::string method = alignmentMethod; + std::transform(method.begin(), method.end(), method.begin(), ::tolower); + + if (method == "landmarks") + return EObject::LANDMARKS; + if (method == "cameras") + return EObject::CAMERAS; + throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); } - inline std::istream& operator>>(std::istream& in, EObject& alignment) { std::string token; @@ -79,20 +81,19 @@ inline std::istream& operator>>(std::istream& in, EObject& alignment) return in; } -inline std::ostream& operator<<(std::ostream& os, EObject e) -{ - return os << EObject_enumToString(e); -} - +inline std::ostream& operator<<(std::ostream& os, EObject e) { return os << EObject_enumToString(e); } -void extractLandmarksPositions(std::vector>& outputPositions, const sfmData::SfMData& sfmData, const std::vector& search, const std::vector& landmarksDescriberTypes) +void extractLandmarksPositions(std::vector>& outputPositions, + const sfmData::SfMData& sfmData, + const std::vector& search, + const std::vector& landmarksDescriberTypes) { const std::set isCCTag = { #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - feature::EImageDescriberType::CCTAG3, - feature::EImageDescriberType::CCTAG4 + feature::EImageDescriberType::CCTAG3, + feature::EImageDescriberType::CCTAG4 #endif - }; + }; const std::set descTypes(landmarksDescriberTypes.begin(), landmarksDescriberTypes.end()); std::set searchIdx; for (const std::string& s : search) @@ -105,26 +106,27 @@ void extractLandmarksPositions(std::vector>& output if (descTypes.count(landmarkIt.second.descType)) { bool isMarker = isCCTag.count(landmarkIt.second.descType); - if (searchIdx.empty() || - (isMarker ? searchIdx.count(landmarkIt.second.rgb.r()) : searchIdx.count(landmarkIt.first)) - ) + if (searchIdx.empty() || (isMarker ? searchIdx.count(landmarkIt.second.rgb.r()) : searchIdx.count(landmarkIt.first))) { - outputPositions.push_back(std::make_pair(std::to_string(isMarker ? landmarkIt.second.rgb.r() : landmarkIt.first), landmarkIt.second.X)); + outputPositions.push_back( + std::make_pair(std::to_string(isMarker ? landmarkIt.second.rgb.r() : landmarkIt.first), landmarkIt.second.X)); } } } } -void extractCamerasPositions(std::vector>& outputPositions, const sfmData::SfMData& sfmData, const std::vector& search) +void extractCamerasPositions(std::vector>& outputPositions, + const sfmData::SfMData& sfmData, + const std::vector& search) { std::set searchSet(search.begin(), search.end()); - for (const auto& viewIt: sfmData.getViews()) + for (const auto& viewIt : sfmData.getViews()) { if (!sfmData.isPoseAndIntrinsicDefined(viewIt.second.get())) continue; const std::string viewIdStr = std::to_string(viewIt.second->getViewId()); - if(searchSet.count(viewIdStr)) + if (searchSet.count(viewIdStr)) { outputPositions.push_back(std::make_pair(viewIdStr, sfmData.getPose(*viewIt.second).getTransform().center())); continue; @@ -132,22 +134,22 @@ void extractCamerasPositions(std::vector>& outputPo std::string stem = fs::path(viewIt.second->getImage().getImagePath()).stem().string(); if (searchSet.empty() || searchSet.count(stem)) { - outputPositions.push_back(std::make_pair(viewIt.second->getImage().getImagePath(), sfmData.getPose(*viewIt.second).getTransform().center())); + outputPositions.push_back( + std::make_pair(viewIt.second->getImage().getImagePath(), sfmData.getPose(*viewIt.second).getTransform().center())); } } } - -int main(int argc, char **argv) +int main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - EObject objectType = EObject::LANDMARKS; + // command-line parameters + std::string sfmDataFilename; + EObject objectType = EObject::LANDMARKS; - // user optional parameters - std::string objectA; - std::string objectB; - std::string landmarksDescriberTypesName; + // user optional parameters + std::string objectA; + std::string objectB; + std::string landmarksDescriberTypesName; // clang-format off po::options_description requiredParams("Required parameters"); @@ -175,61 +177,61 @@ int main(int argc, char **argv) "Use all of them if empty\n" + feature::EImageDescriberType_informations()).c_str()); // clang-format on - CmdLine cmdline("AliceVision sfmDistances"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - std::vector landmarksDescriberTypes = feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName); - - // Load input scene - sfmData::SfMData sfmDataIn; - if(!sfmDataIO::load(sfmDataIn, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - std::vector> positionsA; - std::vector> positionsB; - - std::vector inputA; - std::vector inputB; - - if(!objectA.empty()) - boost::split(inputA, objectA, boost::algorithm::is_any_of(",")); - if (!objectB.empty()) - boost::split(inputB, objectB, boost::algorithm::is_any_of(",")); - - switch (objectType) - { - case EObject::CAMERAS: - { - std::cout << "== Cameras ==" << std::endl; - extractCamerasPositions(positionsA, sfmDataIn, inputA); - extractCamerasPositions(positionsB, sfmDataIn, inputB); - break; - } - case EObject::LANDMARKS: - { - std::cout << "== Landmarks ==" << std::endl; - extractLandmarksPositions(positionsA, sfmDataIn, inputA, landmarksDescriberTypes); - extractLandmarksPositions(positionsB, sfmDataIn, inputB, landmarksDescriberTypes); - break; - } - } - - for (const auto& a : positionsA) - { - for (const auto& b : positionsB) - { - if(a.first != b.first) - std::cout << a.first << " <=> " << b.first << ": " << (b.second - a.second).norm() << std::endl; - } - } - - return EXIT_SUCCESS; + CmdLine cmdline("AliceVision sfmDistances"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + std::vector landmarksDescriberTypes = feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName); + + // Load input scene + sfmData::SfMData sfmDataIn; + if (!sfmDataIO::load(sfmDataIn, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; + } + + std::vector> positionsA; + std::vector> positionsB; + + std::vector inputA; + std::vector inputB; + + if (!objectA.empty()) + boost::split(inputA, objectA, boost::algorithm::is_any_of(",")); + if (!objectB.empty()) + boost::split(inputB, objectB, boost::algorithm::is_any_of(",")); + + switch (objectType) + { + case EObject::CAMERAS: + { + std::cout << "== Cameras ==" << std::endl; + extractCamerasPositions(positionsA, sfmDataIn, inputA); + extractCamerasPositions(positionsB, sfmDataIn, inputB); + break; + } + case EObject::LANDMARKS: + { + std::cout << "== Landmarks ==" << std::endl; + extractLandmarksPositions(positionsA, sfmDataIn, inputA, landmarksDescriberTypes); + extractLandmarksPositions(positionsB, sfmDataIn, inputB, landmarksDescriberTypes); + break; + } + } + + for (const auto& a : positionsA) + { + for (const auto& b : positionsB) + { + if (a.first != b.first) + std::cout << a.first << " <=> " << b.first << ": " << (b.second - a.second).norm() << std::endl; + } + } + + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_sfmMerge.cpp b/src/software/utils/main_sfmMerge.cpp index 58575b7ca7..6ca83b9442 100644 --- a/src/software/utils/main_sfmMerge.cpp +++ b/src/software/utils/main_sfmMerge.cpp @@ -10,7 +10,6 @@ #include #include - #include #include @@ -25,8 +24,7 @@ using namespace aliceVision; namespace po = boost::program_options; - -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { // command-line parameters std::string sfmDataFilename1, sfmDataFilename2; @@ -64,10 +62,10 @@ int aliceVision_main(int argc, char **argv) ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename2 << "' cannot be read"); return EXIT_FAILURE; } - + { - auto & views1 = sfmData1.getViews(); - auto & views2 = sfmData2.getViews(); + auto& views1 = sfmData1.getViews(); + auto& views2 = sfmData2.getViews(); const size_t totalSize = views1.size() + views2.size(); views1.insert(views2.begin(), views2.end()); @@ -79,8 +77,8 @@ int aliceVision_main(int argc, char **argv) } { - auto & intrinsics1 = sfmData1.getIntrinsics(); - auto & intrinsics2 = sfmData2.getIntrinsics(); + auto& intrinsics1 = sfmData1.getIntrinsics(); + auto& intrinsics2 = sfmData2.getIntrinsics(); const size_t totalSize = intrinsics1.size() + intrinsics2.size(); intrinsics1.insert(intrinsics2.begin(), intrinsics2.end()); @@ -92,8 +90,8 @@ int aliceVision_main(int argc, char **argv) } { - auto & rigs1 = sfmData1.getRigs(); - auto & rigs2 = sfmData2.getRigs(); + auto& rigs1 = sfmData1.getRigs(); + auto& rigs2 = sfmData2.getRigs(); const size_t totalSize = rigs1.size() + rigs2.size(); rigs1.insert(rigs2.begin(), rigs2.end()); @@ -105,8 +103,8 @@ int aliceVision_main(int argc, char **argv) } { - auto & landmarks1 = sfmData1.getLandmarks(); - auto & landmarks2 = sfmData2.getLandmarks(); + auto& landmarks1 = sfmData1.getLandmarks(); + auto& landmarks2 = sfmData2.getLandmarks(); const size_t totalSize = landmarks1.size() + landmarks2.size(); landmarks1.insert(landmarks2.begin(), landmarks2.end()); diff --git a/src/software/utils/main_sfmRegression.cpp b/src/software/utils/main_sfmRegression.cpp index 11af86a409..f1d55329ea 100644 --- a/src/software/utils/main_sfmRegression.cpp +++ b/src/software/utils/main_sfmRegression.cpp @@ -4,7 +4,6 @@ // 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 @@ -28,8 +27,7 @@ using namespace aliceVision::sfm; namespace po = boost::program_options; - -void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::SfMData & returnSfmDataToEstimate) +void generateSampleSceneOnePlane(sfmData::SfMData& returnSfmDataGT, sfmData::SfMData& returnSfmDataToEstimate) { sfmData::SfMData sfmDataGT; sfmData::SfMData sfmDataEst; @@ -48,7 +46,7 @@ void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::Sf for (int i = 0; i < 120; i++) { Vec3 pos = direction * double(i) / 120.0; - Eigen::Matrix3d R = SO3::expm(axis * double(i) * M_PI / (8*120.0)); + Eigen::Matrix3d R = SO3::expm(axis * double(i) * M_PI / (8 * 120.0)); geometry::Pose3 poseGT(R, pos); sfmData::CameraPose cposeGT(poseGT); sfmDataGT.getPoses()[i] = cposeGT; @@ -58,16 +56,15 @@ void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::Sf Eigen::Vector3d tup = Vec3::Random() * (0.5); geometry::Pose3 poseEst(Rup * R, pos + tup); - sfmData::CameraPose cposeEst(poseEst, (i==0)); + sfmData::CameraPose cposeEst(poseEst, (i == 0)); sfmDataEst.getPoses()[i] = cposeEst; sfmDataEst.getViews().emplace(i, std::make_shared("", i, 0, i, 1920, 1080)); - } int tid = 0; - for (double y = -2.0; y < 2.0; y+=0.1) + for (double y = -2.0; y < 2.0; y += 0.1) { - for (double x = -2.0; x < 2.0; x+=0.1) + for (double x = -2.0; x < 2.0; x += 0.1) { sfmData::Landmark lGT; lGT.X = Vec3(x, y, 4.0); @@ -82,12 +79,12 @@ void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::Sf } } - for (double y = -2.0; y < 2.0; y+=0.1) + for (double y = -2.0; y < 2.0; y += 0.1) { - for (double x = -2.0; x < 2.0; x+=0.1) + for (double x = -2.0; x < 2.0; x += 0.1) { sfmData::Landmark lGT; - lGT.X = Vec3(x, y, 3.0 + sqrt(x*x + y*y)); + lGT.X = Vec3(x, y, 3.0 + sqrt(x * x + y * y)); lGT.descType = feature::EImageDescriberType::SIFT; sfmDataGT.getLandmarks()[tid] = lGT; @@ -98,16 +95,16 @@ void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::Sf tid++; } } - - //Compute observations - for (auto & pl : sfmDataGT.getLandmarks()) + + // Compute observations + for (auto& pl : sfmDataGT.getLandmarks()) { - sfmData::Landmark & lEst = sfmDataEst.getLandmarks()[pl.first]; - - for (auto & pp : sfmDataGT.getPoses()) + sfmData::Landmark& lEst = sfmDataEst.getLandmarks()[pl.first]; + + for (auto& pp : sfmDataGT.getPoses()) { sfmData::Observation obs(phPinholeGT->project(pp.second.getTransform(), pl.second.X.homogeneous(), true), pl.first, 1.0); - + if (pp.second.getTransform()(pl.second.X)(2) < 0.1) { continue; @@ -122,43 +119,49 @@ void generateSampleSceneOnePlane(sfmData::SfMData & returnSfmDataGT, sfmData::Sf returnSfmDataToEstimate = sfmDataEst; } -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - CmdLine cmdline("AliceVision sfmRegression"); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - { - srand(0); - sfmData::SfMData sfmDataGT; - sfmData::SfMData sfmDataEst; - generateSampleSceneOnePlane(sfmDataGT, sfmDataEst); - - BundleAdjustmentSymbolicCeres::CeresOptions options; - BundleAdjustment::ERefineOptions refineOptions = BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION |BundleAdjustment::REFINE_STRUCTURE | BundleAdjustment::REFINE_INTRINSICS_FOCAL | BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | BundleAdjustment::REFINE_INTRINSICS_DISTORTION; - options.summary = true; - //options.nbThreads = 1; - - BundleAdjustmentSymbolicCeres BA(options, 3); - const bool success = BA.adjust(sfmDataEst, refineOptions); - } - - { - srand(0); - sfmData::SfMData sfmDataGT; - sfmData::SfMData sfmDataEst; - generateSampleSceneOnePlane(sfmDataGT, sfmDataEst); + CmdLine cmdline("AliceVision sfmRegression"); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } - BundleAdjustmentCeres::CeresOptions options; - BundleAdjustment::ERefineOptions refineOptions = BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION |BundleAdjustment::REFINE_STRUCTURE | BundleAdjustment::REFINE_INTRINSICS_FOCAL | BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | BundleAdjustment::REFINE_INTRINSICS_DISTORTION; - options.summary = true; - //options.nbThreads = 1; + { + srand(0); + sfmData::SfMData sfmDataGT; + sfmData::SfMData sfmDataEst; + generateSampleSceneOnePlane(sfmDataGT, sfmDataEst); + + BundleAdjustmentSymbolicCeres::CeresOptions options; + BundleAdjustment::ERefineOptions refineOptions = BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION | + BundleAdjustment::REFINE_STRUCTURE | BundleAdjustment::REFINE_INTRINSICS_FOCAL | + BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | + BundleAdjustment::REFINE_INTRINSICS_DISTORTION; + options.summary = true; + // options.nbThreads = 1; + + BundleAdjustmentSymbolicCeres BA(options, 3); + const bool success = BA.adjust(sfmDataEst, refineOptions); + } - BundleAdjustmentCeres BA(options, 3); - const bool success = BA.adjust(sfmDataEst, refineOptions); - } + { + srand(0); + sfmData::SfMData sfmDataGT; + sfmData::SfMData sfmDataEst; + generateSampleSceneOnePlane(sfmDataGT, sfmDataEst); + + BundleAdjustmentCeres::CeresOptions options; + BundleAdjustment::ERefineOptions refineOptions = BundleAdjustment::REFINE_ROTATION | BundleAdjustment::REFINE_TRANSLATION | + BundleAdjustment::REFINE_STRUCTURE | BundleAdjustment::REFINE_INTRINSICS_FOCAL | + BundleAdjustment::REFINE_INTRINSICS_OPTICALOFFSET_ALWAYS | + BundleAdjustment::REFINE_INTRINSICS_DISTORTION; + options.summary = true; + // options.nbThreads = 1; + + BundleAdjustmentCeres BA(options, 3); + const bool success = BA.adjust(sfmDataEst, refineOptions); + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_sfmSplitReconstructed.cpp b/src/software/utils/main_sfmSplitReconstructed.cpp index 8f9d8170ec..2f0365cea6 100644 --- a/src/software/utils/main_sfmSplitReconstructed.cpp +++ b/src/software/utils/main_sfmSplitReconstructed.cpp @@ -46,23 +46,23 @@ int aliceVision_main(int argc, char** argv) CmdLine cmdline("AliceVision sfmSplitReconstructed"); cmdline.add(requiredParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } // Load input scene sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); return EXIT_FAILURE; } - //Create reconstructed only sfmData + // Create reconstructed only sfmData { sfmData::SfMData outReconstructed = sfmData; - auto & views = outReconstructed.getViews(); + auto& views = outReconstructed.getViews(); auto it = views.begin(); while (it != views.end()) @@ -77,20 +77,20 @@ int aliceVision_main(int argc, char** argv) } // Export the SfMData scene in the expected format - if(!sfmDataIO::save(outReconstructed, outRSfMDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::save(outReconstructed, outRSfMDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outRSfMDataFilename << "'"); return EXIT_FAILURE; } } - //Create non reconstructed only sfmData + // Create non reconstructed only sfmData { sfmData::SfMData outNonReconstructed = sfmData; outNonReconstructed.getConstraints2D().clear(); outNonReconstructed.getRotationPriors().clear(); outNonReconstructed.getLandmarks().clear(); - auto & views = outNonReconstructed.getViews(); + auto& views = outNonReconstructed.getViews(); auto it = views.begin(); while (it != views.end()) @@ -104,9 +104,8 @@ int aliceVision_main(int argc, char** argv) it = views.erase(it); } - // Export the SfMData scene in the expected format - if(!sfmDataIO::save(outNonReconstructed, outNRSfMDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::save(outNonReconstructed, outNRSfMDataFilename, sfmDataIO::ESfMData::ALL)) { ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outNRSfMDataFilename << "'"); return EXIT_FAILURE; diff --git a/src/software/utils/main_sfmToRig.cpp b/src/software/utils/main_sfmToRig.cpp index 35b89639ad..1ccc63d325 100644 --- a/src/software/utils/main_sfmToRig.cpp +++ b/src/software/utils/main_sfmToRig.cpp @@ -10,7 +10,6 @@ #include #include - #include #include @@ -25,8 +24,7 @@ using namespace aliceVision; namespace po = boost::program_options; - -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { // command-line parameters std::string sfmDataFilename; @@ -73,9 +71,9 @@ int aliceVision_main(int argc, char **argv) size_t indexRig = 0; int index = 0; std::map mapPoseToSubPose; - for (const auto & pp : sfmData.getPoses()) + for (const auto& pp : sfmData.getPoses()) { - const auto & pose = pp.second; + const auto& pose = pp.second; sfmData::RigSubPose subPose(pose.getTransform(), sfmData::ERigSubPoseStatus::CONSTANT); rig.setSubPose(index, subPose); @@ -91,7 +89,7 @@ int aliceVision_main(int argc, char **argv) sfmData.getRigs().emplace(indexRig, rig); // Update - for (const auto & pv : sfmData.getViews()) + for (const auto& pv : sfmData.getViews()) { std::shared_ptr view = pv.second; if (!sfmData.isPoseAndIntrinsicDefined(view.get())) @@ -101,12 +99,12 @@ int aliceVision_main(int argc, char **argv) const IndexT poseId = view->getPoseId(); const int subPoseId = mapPoseToSubPose[poseId]; - + // New commmon pose id is the same than the rig id for convenience view->setPoseId(indexRig); view->setRigAndSubPoseId(indexRig, subPoseId); view->setIndependantPose(false); - + // Update intrinsicId size_t intrinsicId = view->getIntrinsicId(); stl::hash_combine(intrinsicId, indexRig); @@ -116,7 +114,7 @@ int aliceVision_main(int argc, char **argv) // Update intrinsics sfmData::Intrinsics intrinsics = sfmData.getIntrinsics(); sfmData.getIntrinsics().clear(); - for (const auto & pi : intrinsics) + for (const auto& pi : intrinsics) { size_t intrinsicId = pi.first; stl::hash_combine(intrinsicId, indexRig); diff --git a/src/software/utils/main_sfmTransfer.cpp b/src/software/utils/main_sfmTransfer.cpp index 08a93a870d..eb4bf8e985 100644 --- a/src/software/utils/main_sfmTransfer.cpp +++ b/src/software/utils/main_sfmTransfer.cpp @@ -28,49 +28,56 @@ using namespace aliceVision::sfm; namespace po = boost::program_options; - /** -* @brief Matching Views method enum -*/ + * @brief Matching Views method enum + */ enum class EMatchingMethod : unsigned char { - FROM_VIEWID = 0 - , FROM_FILEPATH - , FROM_METADATA - , FROM_INTRINSICID + FROM_VIEWID = 0, + FROM_FILEPATH, + FROM_METADATA, + FROM_INTRINSICID }; /** -* @brief Convert an EMatchingMethod enum to its corresponding string -* @param[in] matchingMethod The given EMatchingMethod enum -* @return string -*/ + * @brief Convert an EMatchingMethod enum to its corresponding string + * @param[in] matchingMethod The given EMatchingMethod enum + * @return string + */ std::string EMatchingMethod_enumToString(EMatchingMethod alignmentMethod) { switch (alignmentMethod) { - case EMatchingMethod::FROM_VIEWID: return "from_viewid"; - case EMatchingMethod::FROM_FILEPATH: return "from_filepath"; - case EMatchingMethod::FROM_METADATA: return "from_metadata"; - case EMatchingMethod::FROM_INTRINSICID: return "from_intrinsicid"; + case EMatchingMethod::FROM_VIEWID: + return "from_viewid"; + case EMatchingMethod::FROM_FILEPATH: + return "from_filepath"; + case EMatchingMethod::FROM_METADATA: + return "from_metadata"; + case EMatchingMethod::FROM_INTRINSICID: + return "from_intrinsicid"; } throw std::out_of_range("Invalid EMatchingMethod enum"); } /** -* @brief Convert a string to its corresponding EMatchingMethod enum -* @param[in] matchingMethod The given string -* @return EMatchingMethod enum -*/ + * @brief Convert a string to its corresponding EMatchingMethod enum + * @param[in] matchingMethod The given string + * @return EMatchingMethod enum + */ EMatchingMethod EMatchingMethod_stringToEnum(const std::string& alignmentMethod) { std::string method = alignmentMethod; - std::transform(method.begin(), method.end(), method.begin(), ::tolower); //tolower - - if (method == "from_viewid") return EMatchingMethod::FROM_VIEWID; - if (method == "from_filepath") return EMatchingMethod::FROM_FILEPATH; - if (method == "from_metadata") return EMatchingMethod::FROM_METADATA; - if (method == "from_intrinsicid") return EMatchingMethod::FROM_INTRINSICID; + std::transform(method.begin(), method.end(), method.begin(), ::tolower); // tolower + + if (method == "from_viewid") + return EMatchingMethod::FROM_VIEWID; + if (method == "from_filepath") + return EMatchingMethod::FROM_FILEPATH; + if (method == "from_metadata") + return EMatchingMethod::FROM_METADATA; + if (method == "from_intrinsicid") + return EMatchingMethod::FROM_INTRINSICID; throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); } @@ -82,13 +89,9 @@ inline std::istream& operator>>(std::istream& in, EMatchingMethod& alignment) return in; } -inline std::ostream& operator<<(std::ostream& os, EMatchingMethod e) -{ - return os << EMatchingMethod_enumToString(e); -} - +inline std::ostream& operator<<(std::ostream& os, EMatchingMethod e) { return os << EMatchingMethod_enumToString(e); } -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { // command-line parameters std::string sfmDataFilename; @@ -99,7 +102,7 @@ int aliceVision_main(int argc, char **argv) bool transferLandmarks = true; EMatchingMethod matchingMethod = EMatchingMethod::FROM_VIEWID; std::string fileMatchingPattern; - std::vector metadataMatchingList = { "Make", "Model", "Exif:BodySerialNumber" , "Exif:LensSerialNumber" }; + std::vector metadataMatchingList = {"Make", "Model", "Exif:BodySerialNumber", "Exif:LensSerialNumber"}; std::string outputViewsAndPosesFilepath; // clang-format off @@ -182,14 +185,14 @@ int aliceVision_main(int argc, char **argv) sfm::matchViewsByMetadataMatching(sfmData, sfmDataRef, metadataMatchingList, commonViewIds); break; } - case EMatchingMethod::FROM_INTRINSICID: + case EMatchingMethod::FROM_INTRINSICID: { break; } } ALICEVISION_LOG_DEBUG("Found " << commonViewIds.size() << " common views."); - if (matchingMethod == EMatchingMethod::FROM_INTRINSICID) + if (matchingMethod == EMatchingMethod::FROM_INTRINSICID) { for (auto intrinsic : sfmData.getIntrinsics()) { @@ -214,9 +217,9 @@ int aliceVision_main(int argc, char **argv) ALICEVISION_LOG_ERROR("Failed to find matching Views between the 2 SfmData."); return EXIT_FAILURE; } - for (const auto& matchingViews: commonViewIds) + for (const auto& matchingViews : commonViewIds) { - if(sfmDataRef.isPoseAndIntrinsicDefined(matchingViews.second)) + if (sfmDataRef.isPoseAndIntrinsicDefined(matchingViews.second)) { // Missing pose in sfmData and valid pose in sfmDataRef, // so we can transfer the pose. @@ -226,11 +229,15 @@ int aliceVision_main(int argc, char **argv) if (transferPoses) { - ALICEVISION_LOG_TRACE("Transfer pose (pose id: " << viewA.getPoseId() << " <- " << viewB.getPoseId() << ", " << viewA.getImage().getImagePath() << " <- " << viewB.getImage().getImagePath() << ")."); + ALICEVISION_LOG_TRACE("Transfer pose (pose id: " << viewA.getPoseId() << " <- " << viewB.getPoseId() << ", " + << viewA.getImage().getImagePath() << " <- " << viewB.getImage().getImagePath() + << ")."); if (viewA.isPartOfRig() && viewB.isPartOfRig()) { - ALICEVISION_LOG_TRACE("Transfer rig (rig id: " << viewA.getRigId() << " <- " << viewB.getRigId() << ", " << viewA.getImage().getImagePath() << " <- " << viewB.getImage().getImagePath() << ")."); + ALICEVISION_LOG_TRACE("Transfer rig (rig id: " << viewA.getRigId() << " <- " << viewB.getRigId() << ", " + << viewA.getImage().getImagePath() << " <- " << viewB.getImage().getImagePath() + << ")."); if (!viewB.isPoseIndependant()) { @@ -277,20 +284,22 @@ int aliceVision_main(int argc, char **argv) } if (transferIntrinsics) { - ALICEVISION_LOG_TRACE("Transfer intrinsics (intrinsic id: " << viewA.getIntrinsicId() << " <- " << viewB.getIntrinsicId() << ", " << viewA.getImage().getImagePath() << " <- " << viewB.getImage().getImagePath() << ")."); + ALICEVISION_LOG_TRACE("Transfer intrinsics (intrinsic id: " << viewA.getIntrinsicId() << " <- " << viewB.getIntrinsicId() << ", " + << viewA.getImage().getImagePath() << " <- " + << viewB.getImage().getImagePath() << ")."); sfmData.getIntrinsicPtr(viewA.getIntrinsicId())->assign(*sfmDataRef.getIntrinsicPtr(viewB.getIntrinsicId())); } if (transferLandmarks) { aliceVision::sfmData::Landmarks refLandmarks = sfmDataRef.getLandmarks(); - if(!refLandmarks.empty()) + if (!refLandmarks.empty()) { ALICEVISION_LOG_TRACE("Transfer landmarks"); std::map commonViewsMap; // Create map of common views - for(auto viewPair: commonViewIds) + for (auto viewPair : commonViewIds) { commonViewsMap.emplace(viewPair.second, viewPair.first); } @@ -307,18 +316,18 @@ int aliceVision_main(int argc, char **argv) // For all observations of the ref landmark : for (const auto& obsIt : landIt.second.getObservations()) { - const IndexT viewId = obsIt.first; - // If the observation view has a correspondance in the other sfmData, we copy it : - if (commonViewsMap.find(viewId) != commonViewsMap.end() ) - { - newLandmark.getObservations().emplace(commonViewsMap.at(viewId), landIt.second.getObservations().at(viewId)); - } + const IndexT viewId = obsIt.first; + // If the observation view has a correspondance in the other sfmData, we copy it : + if (commonViewsMap.find(viewId) != commonViewsMap.end()) + { + newLandmark.getObservations().emplace(commonViewsMap.at(viewId), landIt.second.getObservations().at(viewId)); + } } // If the landmark has at least one observation in the new scene, we copy it : - if(newLandmark.getObservations().size() > 0) + if (newLandmark.getObservations().size() > 0) { - newLandmarks.emplace(landIt.first,newLandmark); + newLandmarks.emplace(landIt.first, newLandmark); } } sfmData.getLandmarks() = newLandmarks; @@ -355,10 +364,9 @@ int aliceVision_main(int argc, char **argv) return EXIT_FAILURE; } - if(!outputViewsAndPosesFilepath.empty()) + if (!outputViewsAndPosesFilepath.empty()) { - sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, - sfmDataIO::ESfMData(sfmDataIO::ALL)); + sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL)); } return EXIT_SUCCESS; diff --git a/src/software/utils/main_sfmTransform.cpp b/src/software/utils/main_sfmTransform.cpp index 2aadb8fd61..c3927ad733 100644 --- a/src/software/utils/main_sfmTransform.cpp +++ b/src/software/utils/main_sfmTransform.cpp @@ -19,7 +19,6 @@ #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 @@ -56,21 +55,32 @@ enum class EAlignmentMethod : unsigned char */ std::string EAlignmentMethod_enumToString(EAlignmentMethod alignmentMethod) { - switch(alignmentMethod) - { - case EAlignmentMethod::TRANSFORMATION: return "transformation"; - case EAlignmentMethod::MANUAL: return "manual"; - case EAlignmentMethod::AUTO: return "auto"; - case EAlignmentMethod::AUTO_FROM_CAMERAS: return "auto_from_cameras"; - case EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS: return "auto_from_cameras_x_axis"; - case EAlignmentMethod::AUTO_FROM_LANDMARKS: return "auto_from_landmarks"; - case EAlignmentMethod::FROM_SINGLE_CAMERA: return "from_single_camera"; - case EAlignmentMethod::FROM_CENTER_CAMERA: return "from_center_camera"; - case EAlignmentMethod::FROM_MARKERS: return "from_markers"; - case EAlignmentMethod::FROM_GPS: return "from_gps"; - case EAlignmentMethod::ALIGN_GROUND: return "align_ground"; - } - throw std::out_of_range("Invalid EAlignmentMethod enum"); + switch (alignmentMethod) + { + case EAlignmentMethod::TRANSFORMATION: + return "transformation"; + case EAlignmentMethod::MANUAL: + return "manual"; + case EAlignmentMethod::AUTO: + return "auto"; + case EAlignmentMethod::AUTO_FROM_CAMERAS: + return "auto_from_cameras"; + case EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS: + return "auto_from_cameras_x_axis"; + case EAlignmentMethod::AUTO_FROM_LANDMARKS: + return "auto_from_landmarks"; + case EAlignmentMethod::FROM_SINGLE_CAMERA: + return "from_single_camera"; + case EAlignmentMethod::FROM_CENTER_CAMERA: + return "from_center_camera"; + case EAlignmentMethod::FROM_MARKERS: + return "from_markers"; + case EAlignmentMethod::FROM_GPS: + return "from_gps"; + case EAlignmentMethod::ALIGN_GROUND: + return "align_ground"; + } + throw std::out_of_range("Invalid EAlignmentMethod enum"); } /** @@ -80,24 +90,34 @@ std::string EAlignmentMethod_enumToString(EAlignmentMethod alignmentMethod) */ EAlignmentMethod EAlignmentMethod_stringToEnum(const std::string& alignmentMethod) { - std::string method = alignmentMethod; - std::transform(method.begin(), method.end(), method.begin(), ::tolower); //tolower - - if (method == "transformation") return EAlignmentMethod::TRANSFORMATION; - if (method == "manual") return EAlignmentMethod::MANUAL; - if (method == "auto") return EAlignmentMethod::AUTO; - if (method == "auto_from_cameras") return EAlignmentMethod::AUTO_FROM_CAMERAS; - if (method == "auto_from_cameras_x_axis") return EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS; - if (method == "auto_from_landmarks") return EAlignmentMethod::AUTO_FROM_LANDMARKS; - if (method == "from_single_camera") return EAlignmentMethod::FROM_SINGLE_CAMERA; - if (method == "from_center_camera") return EAlignmentMethod::FROM_CENTER_CAMERA; - if (method == "from_markers") return EAlignmentMethod::FROM_MARKERS; - if (method == "from_gps") return EAlignmentMethod::FROM_GPS; - if (method == "align_ground") return EAlignmentMethod::ALIGN_GROUND; - throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); + std::string method = alignmentMethod; + std::transform(method.begin(), method.end(), method.begin(), ::tolower); // tolower + + if (method == "transformation") + return EAlignmentMethod::TRANSFORMATION; + if (method == "manual") + return EAlignmentMethod::MANUAL; + if (method == "auto") + return EAlignmentMethod::AUTO; + if (method == "auto_from_cameras") + return EAlignmentMethod::AUTO_FROM_CAMERAS; + if (method == "auto_from_cameras_x_axis") + return EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS; + if (method == "auto_from_landmarks") + return EAlignmentMethod::AUTO_FROM_LANDMARKS; + if (method == "from_single_camera") + return EAlignmentMethod::FROM_SINGLE_CAMERA; + if (method == "from_center_camera") + return EAlignmentMethod::FROM_CENTER_CAMERA; + if (method == "from_markers") + return EAlignmentMethod::FROM_MARKERS; + if (method == "from_gps") + return EAlignmentMethod::FROM_GPS; + if (method == "align_ground") + return EAlignmentMethod::ALIGN_GROUND; + throw std::out_of_range("Invalid SfM alignment method : " + alignmentMethod); } - inline std::istream& operator>>(std::istream& in, EAlignmentMethod& alignment) { std::string token; @@ -106,33 +126,28 @@ inline std::istream& operator>>(std::istream& in, EAlignmentMethod& alignment) return in; } -inline std::ostream& operator<<(std::ostream& os, EAlignmentMethod e) -{ - return os << EAlignmentMethod_enumToString(e); -} - - +inline std::ostream& operator<<(std::ostream& os, EAlignmentMethod e) { return os << EAlignmentMethod_enumToString(e); } static bool parseAlignScale(const std::string& alignScale, double& S, Mat3& R, Vec3& t) { - double rx, ry, rz, rr; + double rx, ry, rz, rr; - { - char delim[4]; - std::istringstream iss(alignScale); - if (!(iss >> rx >> delim[0] >> ry >> delim[1] >> rz >> delim[2] >> rr >> delim[3] >> S)) - return false; - if (delim[0] != ',' || delim[1] != ',' || delim[2] != ';' || delim[3] != ';') - return false; - } + { + char delim[4]; + std::istringstream iss(alignScale); + if (!(iss >> rx >> delim[0] >> ry >> delim[1] >> rz >> delim[2] >> rr >> delim[3] >> S)) + return false; + if (delim[0] != ',' || delim[1] != ',' || delim[2] != ';' || delim[3] != ';') + return false; + } - auto q = Eigen::Quaterniond::FromTwoVectors(Vec3(rx, ry, rz), Vec3::UnitY()); - auto r = Eigen::AngleAxisd(rr*M_PI/180, Vec3::UnitY()); + auto q = Eigen::Quaterniond::FromTwoVectors(Vec3(rx, ry, rz), Vec3::UnitY()); + auto r = Eigen::AngleAxisd(rr * M_PI / 180, Vec3::UnitY()); - R = r * q.toRotationMatrix(); - t = Vec3::Zero(); + R = r * q.toRotationMatrix(); + t = Vec3::Zero(); - return true; + return true; } static void parseManualTransform(const std::string& manualTransform, double& S, Mat3& R, Vec3& t) @@ -153,10 +168,10 @@ static void parseManualTransform(const std::string& manualTransform, double& S, } // Assignments - t << data[0], data[1], data[2]; // Assign Translation - S = data[6]; // Assign Scale - - Vec3 eulerAngles(data[3], data[4], data[5]); // Temporary eulerAngles vector + t << data[0], data[1], data[2]; // Assign Translation + S = data[6]; // Assign Scale + + Vec3 eulerAngles(data[3], data[4], data[5]); // Temporary eulerAngles vector // Compute the rotation matrix from quaternion made with Euler angles in that order: ZXY (same as Qt algorithm) Mat3 rotateMat = Mat3::Identity(); @@ -186,19 +201,19 @@ static void parseManualTransform(const std::string& manualTransform, double& S, Eigen::Quaterniond quaternion(w, x, y, z); rotateMat = quaternion.matrix(); } - R = rotateMat; // Assign Rotation + R = rotateMat; // Assign Rotation } -} // namespace +} // namespace -IndexT getReferenceViewId(const sfmData::SfMData & sfmData, const std::string & transform) +IndexT getReferenceViewId(const sfmData::SfMData& sfmData, const std::string& transform) { - IndexT refViewId; + IndexT refViewId; try { refViewId = sfm::getViewIdFromExpression(sfmData, transform); if (!sfmData.isPoseAndIntrinsicDefined(refViewId)) - { + { return UndefinedIndexT; } } @@ -207,17 +222,18 @@ IndexT getReferenceViewId(const sfmData::SfMData & sfmData, const std::string & refViewId = UndefinedIndexT; } - //Default to select the view given timestamp + // Default to select the view given timestamp if (refViewId == UndefinedIndexT) { // Sort views with poses per timestamps std::vector> sorted_views; - for (auto v : sfmData.getViews()) { + for (auto v : sfmData.getViews()) + { if (!sfmData.isPoseAndIntrinsicDefined(v.first)) - { + { continue; } - + int64_t t = v.second->getImage().getMetadataDateTimestamp(); sorted_views.push_back(std::make_pair(t, v.first)); } @@ -228,8 +244,7 @@ IndexT getReferenceViewId(const sfmData::SfMData & sfmData, const std::string & return UndefinedIndexT; } - - // Get the view which was taken at the middle of the sequence + // Get the view which was taken at the middle of the sequence int median = sorted_views.size() / 2; refViewId = sorted_views[sorted_views.size() - 1].second; } @@ -237,24 +252,24 @@ IndexT getReferenceViewId(const sfmData::SfMData & sfmData, const std::string & return refViewId; } -int aliceVision_main(int argc, char **argv) +int aliceVision_main(int argc, char** argv) { - // command-line parameters - std::string sfmDataFilename; - std::string outSfMDataFilename; - EAlignmentMethod alignmentMethod = EAlignmentMethod::AUTO_FROM_CAMERAS; - - // user optional parameters - std::string transform; - std::string landmarksDescriberTypesName; - double userScale = 1; - bool applyScale = true; - bool applyRotation = true; - bool applyTranslation = true; - std::vector markers; - std::string outputViewsAndPosesFilepath; - - std::string manualTransform; + // command-line parameters + std::string sfmDataFilename; + std::string outSfMDataFilename; + EAlignmentMethod alignmentMethod = EAlignmentMethod::AUTO_FROM_CAMERAS; + + // user optional parameters + std::string transform; + std::string landmarksDescriberTypesName; + double userScale = 1; + bool applyScale = true; + bool applyRotation = true; + bool applyTranslation = true; + std::vector markers; + std::string outputViewsAndPosesFilepath; + + std::string manualTransform; // clang-format off po::options_description requiredParams("Required parameters"); @@ -302,247 +317,241 @@ int aliceVision_main(int argc, char **argv) "Path of the output SfMData file."); // clang-format on - CmdLine cmdline("AliceVision sfmTransform"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if (alignmentMethod == EAlignmentMethod::FROM_MARKERS && markers.empty()) - { - ALICEVISION_LOG_ERROR("Missing --markers option"); - return EXIT_FAILURE; - } - - // Load input scene - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); - return EXIT_FAILURE; - } - - //Check that at least one view has a defined pose - int count = 0; - for (const auto p : sfmData.getViews()) - { - if(sfmData.isPoseAndIntrinsicDefined(p.first)) + CmdLine cmdline("AliceVision sfmTransform"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - count++; - } - } - - if (count == 0) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' has no valid views with estimated poses"); - return EXIT_FAILURE; - } - - double S = 1.0; - Mat3 R = Mat3::Identity(); - Vec3 t = Vec3::Zero(); - - switch(alignmentMethod) - { - case EAlignmentMethod::TRANSFORMATION: - { - if(transform.empty()) - { - ALICEVISION_LOG_WARNING("No transformation option set, so the transform will be identity."); - } - else - { - if(!parseAlignScale(transform, S, R, t)) - { - ALICEVISION_LOG_ERROR("Failed to parse align/scale argument"); - return EXIT_FAILURE; - } - } + return EXIT_FAILURE; } - break; - case EAlignmentMethod::MANUAL: + if (alignmentMethod == EAlignmentMethod::FROM_MARKERS && markers.empty()) { - if (manualTransform.empty()) - ALICEVISION_LOG_WARNING("No manualTransform option set, so the transform will be identity."); - else - parseManualTransform(manualTransform, S, R, t); + ALICEVISION_LOG_ERROR("Missing --markers option"); + return EXIT_FAILURE; } - break; - case EAlignmentMethod::AUTO_FROM_CAMERAS: - sfm::computeNewCoordinateSystemFromCameras(sfmData, S, R, t); - break; - - case EAlignmentMethod::AUTO: + // Load input scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - sfm::computeNewCoordinateSystemAuto(sfmData, S, R, t); + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); + return EXIT_FAILURE; } - break; - case EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS: + // Check that at least one view has a defined pose + int count = 0; + for (const auto p : sfmData.getViews()) { - // Align with x axis - sfm::computeNewCoordinateSystemFromCamerasXAxis(sfmData, S, R, t); + if (sfmData.isPoseAndIntrinsicDefined(p.first)) + { + count++; + } + } - const IndexT refViewId = getReferenceViewId(sfmData, transform); + if (count == 0) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' has no valid views with estimated poses"); + return EXIT_FAILURE; + } - const Eigen::Matrix3d ref_R_world = sfmData.getPose(sfmData.getView(refViewId)).getTransform().rotation(); + double S = 1.0; + Mat3 R = Mat3::Identity(); + Vec3 t = Vec3::Zero(); - // Apply x axis alignment before doing the y alignment - const Eigen::Matrix3d refcam_R_updatedWorld = ref_R_world * R.transpose(); + switch (alignmentMethod) + { + case EAlignmentMethod::TRANSFORMATION: + { + if (transform.empty()) + { + ALICEVISION_LOG_WARNING("No transformation option set, so the transform will be identity."); + } + else + { + if (!parseAlignScale(transform, S, R, t)) + { + ALICEVISION_LOG_ERROR("Failed to parse align/scale argument"); + return EXIT_FAILURE; + } + } + } + break; - Eigen::Matrix3d zeroX_R_world; - sfm::getRotationNullifyX(zeroX_R_world, refcam_R_updatedWorld); - R = zeroX_R_world * R; - } - break; + case EAlignmentMethod::MANUAL: + { + if (manualTransform.empty()) + ALICEVISION_LOG_WARNING("No manualTransform option set, so the transform will be identity."); + else + parseManualTransform(manualTransform, S, R, t); + } + break; - case EAlignmentMethod::AUTO_FROM_LANDMARKS: - sfm::computeNewCoordinateSystemFromLandmarks(sfmData, feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName), S, R, t); - break; + case EAlignmentMethod::AUTO_FROM_CAMERAS: + sfm::computeNewCoordinateSystemFromCameras(sfmData, S, R, t); + break; - case EAlignmentMethod::FROM_SINGLE_CAMERA: - if(transform.empty()) + case EAlignmentMethod::AUTO: { - ALICEVISION_LOG_WARNING("No transformation option set, so the transform will be identity."); + sfm::computeNewCoordinateSystemAuto(sfmData, S, R, t); } - else + break; + + case EAlignmentMethod::AUTO_FROM_CAMERAS_X_AXIS: { - const IndexT viewId = sfm::getViewIdFromExpression(sfmData, transform); - sfm::computeNewCoordinateSystemFromSingleCamera(sfmData, viewId, S, R, t); - } - break; + // Align with x axis + sfm::computeNewCoordinateSystemFromCamerasXAxis(sfmData, S, R, t); - case EAlignmentMethod::FROM_CENTER_CAMERA: - { - const IndexT centerViewId = sfm::getCenterCameraView(sfmData); - sfm::computeNewCoordinateSystemFromSingleCamera(sfmData, centerViewId, S, R, t); + const IndexT refViewId = getReferenceViewId(sfmData, transform); + + const Eigen::Matrix3d ref_R_world = sfmData.getPose(sfmData.getView(refViewId)).getTransform().rotation(); + + // Apply x axis alignment before doing the y alignment + const Eigen::Matrix3d refcam_R_updatedWorld = ref_R_world * R.transpose(); + + Eigen::Matrix3d zeroX_R_world; + sfm::getRotationNullifyX(zeroX_R_world, refcam_R_updatedWorld); + R = zeroX_R_world * R; + } break; - } - case EAlignmentMethod::FROM_MARKERS: - { - std::vector markersDescTypes = { + case EAlignmentMethod::AUTO_FROM_LANDMARKS: + sfm::computeNewCoordinateSystemFromLandmarks(sfmData, feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName), S, R, t); + break; + + case EAlignmentMethod::FROM_SINGLE_CAMERA: + if (transform.empty()) + { + ALICEVISION_LOG_WARNING("No transformation option set, so the transform will be identity."); + } + else + { + const IndexT viewId = sfm::getViewIdFromExpression(sfmData, transform); + sfm::computeNewCoordinateSystemFromSingleCamera(sfmData, viewId, S, R, t); + } + break; + + case EAlignmentMethod::FROM_CENTER_CAMERA: + { + const IndexT centerViewId = sfm::getCenterCameraView(sfmData); + sfm::computeNewCoordinateSystemFromSingleCamera(sfmData, centerViewId, S, R, t); + break; + } + + case EAlignmentMethod::FROM_MARKERS: + { + std::vector markersDescTypes = { #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) - feature::EImageDescriberType::CCTAG3, feature::EImageDescriberType::CCTAG4, + feature::EImageDescriberType::CCTAG3, + feature::EImageDescriberType::CCTAG4, #endif #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_APRILTAG) - feature::EImageDescriberType::APRILTAG16H5, + feature::EImageDescriberType::APRILTAG16H5, #endif - }; - std::set usedDescTypes = sfmData.getLandmarkDescTypes(); - - std::vector usedMarkersDescTypes; - std::set_intersection( - usedDescTypes.begin(), usedDescTypes.end(), - markersDescTypes.begin(), markersDescTypes.end(), - std::back_inserter(usedMarkersDescTypes) - ); - std::vector inDescTypes = feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName); - - std::vector vDescTypes; - std::set_intersection( - usedMarkersDescTypes.begin(), usedMarkersDescTypes.end(), - inDescTypes.begin(), inDescTypes.end(), - std::back_inserter(vDescTypes) - ); - if (vDescTypes.size() != 1) + }; + std::set usedDescTypes = sfmData.getLandmarkDescTypes(); + + std::vector usedMarkersDescTypes; + std::set_intersection( + usedDescTypes.begin(), usedDescTypes.end(), markersDescTypes.begin(), markersDescTypes.end(), std::back_inserter(usedMarkersDescTypes)); + std::vector inDescTypes = feature::EImageDescriberType_stringToEnums(landmarksDescriberTypesName); + + std::vector vDescTypes; + std::set_intersection( + usedMarkersDescTypes.begin(), usedMarkersDescTypes.end(), inDescTypes.begin(), inDescTypes.end(), std::back_inserter(vDescTypes)); + if (vDescTypes.size() != 1) + { + ALICEVISION_LOG_ERROR("Alignment from markers: Invalid number of image describer types: " << vDescTypes.size()); + for (auto d : vDescTypes) + { + ALICEVISION_LOG_ERROR(" - " << feature::EImageDescriberType_enumToString(d)); + } + return EXIT_FAILURE; + } + const bool success = sfm::computeNewCoordinateSystemFromSpecificMarkers(sfmData, vDescTypes.front(), markers, applyScale, S, R, t); + if (!success) + { + ALICEVISION_LOG_ERROR("Failed to find a valid transformation for these " << markers.size() << " markers."); + return EXIT_FAILURE; + } + break; + } + case EAlignmentMethod::FROM_GPS: { - ALICEVISION_LOG_ERROR("Alignment from markers: Invalid number of image describer types: " << vDescTypes.size()); - for (auto d : vDescTypes) + std::mt19937 randomNumberGenerator; + if (!sfm::computeNewCoordinateSystemFromGpsData(sfmData, randomNumberGenerator, S, R, t)) { - ALICEVISION_LOG_ERROR(" - " << feature::EImageDescriberType_enumToString(d)); + ALICEVISION_LOG_ERROR("Failed to find a valid transformation from the GPS metadata."); + return EXIT_FAILURE; } - return EXIT_FAILURE; + break; } - const bool success = sfm::computeNewCoordinateSystemFromSpecificMarkers(sfmData, vDescTypes.front(), markers, applyScale, S, R, t); - if (!success) + case EAlignmentMethod::ALIGN_GROUND: { - ALICEVISION_LOG_ERROR("Failed to find a valid transformation for these " << markers.size() << " markers."); - return EXIT_FAILURE; + sfm::computeNewCoordinateSystemGroundAuto(sfmData, t); + break; } - break; } - case EAlignmentMethod::FROM_GPS: - { - std::mt19937 randomNumberGenerator; - if(!sfm::computeNewCoordinateSystemFromGpsData(sfmData, randomNumberGenerator, S, R, t)) - { - ALICEVISION_LOG_ERROR("Failed to find a valid transformation from the GPS metadata."); - return EXIT_FAILURE; - } - break; - } - case EAlignmentMethod::ALIGN_GROUND: - { - sfm::computeNewCoordinateSystemGroundAuto(sfmData, t); - break; - } - } - - if(!applyRotation) - { - // remove rotation from translation - t = R.transpose() * t; - // remove rotation - R = Mat3::Identity(); - } - if(applyScale) - { - // apply user scale - S *= userScale; - t *= userScale; - } - else - { - // remove scale from translation - if(std::abs(S) > 0.00001) - { - t /= S; - } - // reset scale to 1 - S = 1.0; - } - if (!applyTranslation) - { - // remove translation - t = Vec3::Zero(); - } - - { - ALICEVISION_LOG_INFO(std::setprecision(17) - << "Transformation:" << std::endl - << "\t- Scale: " << S << std::endl - << "\t- Rotation:\n" << R << std::endl - << "\t- Translate: " << t.transpose()); - } - - sfm::applyTransform(sfmData, S, R, t); - - // In AUTO mode, ground detection and alignment is performed as a post-process - if (alignmentMethod == EAlignmentMethod::AUTO && applyTranslation) - { - sfm::computeNewCoordinateSystemGroundAuto(sfmData, t); - sfm::applyTransform(sfmData, 1.0, Eigen::Matrix3d::Identity(), t); - } - - ALICEVISION_LOG_INFO("Save into '" << outSfMDataFilename << "'"); - - // Export the SfMData scene in the expected format - if(!sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outSfMDataFilename << "'"); - return EXIT_FAILURE; - } - - if(!outputViewsAndPosesFilepath.empty()) - { - sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); - } - - return EXIT_SUCCESS; + + if (!applyRotation) + { + // remove rotation from translation + t = R.transpose() * t; + // remove rotation + R = Mat3::Identity(); + } + if (applyScale) + { + // apply user scale + S *= userScale; + t *= userScale; + } + else + { + // remove scale from translation + if (std::abs(S) > 0.00001) + { + t /= S; + } + // reset scale to 1 + S = 1.0; + } + if (!applyTranslation) + { + // remove translation + t = Vec3::Zero(); + } + + { + ALICEVISION_LOG_INFO(std::setprecision(17) << "Transformation:" << std::endl + << "\t- Scale: " << S << std::endl + << "\t- Rotation:\n" + << R << std::endl + << "\t- Translate: " << t.transpose()); + } + + sfm::applyTransform(sfmData, S, R, t); + + // In AUTO mode, ground detection and alignment is performed as a post-process + if (alignmentMethod == EAlignmentMethod::AUTO && applyTranslation) + { + sfm::computeNewCoordinateSystemGroundAuto(sfmData, t); + sfm::applyTransform(sfmData, 1.0, Eigen::Matrix3d::Identity(), t); + } + + ALICEVISION_LOG_INFO("Save into '" << outSfMDataFilename << "'"); + + // Export the SfMData scene in the expected format + if (!sfmDataIO::save(sfmData, outSfMDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("An error occurred while trying to save '" << outSfMDataFilename << "'"); + return EXIT_FAILURE; + } + + if (!outputViewsAndPosesFilepath.empty()) + { + sfmDataIO::save(sfmData, outputViewsAndPosesFilepath, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::EXTRINSICS | sfmDataIO::INTRINSICS)); + } + + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_split360Images.cpp b/src/software/utils/main_split360Images.cpp index 3d4d19cbef..c2357fb61f 100644 --- a/src/software/utils/main_split360Images.cpp +++ b/src/software/utils/main_split360Images.cpp @@ -50,33 +50,22 @@ namespace oiio = OIIO; */ class PinholeCameraR { -public: - + public: PinholeCameraR(int focal, int width, int height, const Mat3& R) - : _R(R) + : _R(R) { - _K << focal, 0, width/2.0, - 0, focal, height/2.0, - 0, 0, 1; + _K << focal, 0, width / 2.0, 0, focal, height / 2.0, 0, 0, 1; } - Vec3 getLocalRay(double x, double y) const - { - return (_K.inverse() * Vec3(x, y, 1.0)).normalized(); - } - - Vec3 getRay(double x, double y) const - { - return _R * getLocalRay(x, y); - } + Vec3 getLocalRay(double x, double y) const { return (_K.inverse() * Vec3(x, y, 1.0)).normalized(); } -private: + Vec3 getRay(double x, double y) const { return _R * getLocalRay(x, y); } + private: /// Rotation matrix Mat3 _R; /// Intrinsic matrix Mat3 _K; - }; /** @@ -88,7 +77,7 @@ class PinholeCameraR double focalFromPinholeHeight(int height, double thetaMax = degreeToRadian(60.0)) { float f = 1.f; - while (thetaMax < atan2(height / (2 * f) , 1)) + while (thetaMax < atan2(height / (2 * f), 1)) { ++f; } @@ -96,8 +85,11 @@ double focalFromPinholeHeight(int height, double thetaMax = degreeToRadian(60.0) } bool splitDualFisheye(sfmData::SfMData& outSfmData, - const std::string& imagePath, const std::string& outputFolder, const std::string& extension, - const std::string& offsetPresetX, const std::string& offsetPresetY) + const std::string& imagePath, + const std::string& outputFolder, + const std::string& extension, + const std::string& offsetPresetX, + const std::string& offsetPresetY) { // Load source image from disk image::Image imageSource; @@ -108,15 +100,9 @@ bool splitDualFisheye(sfmData::SfMData& outSfmData, // Retrieve useful dimensions for cropping bool vertical = (imageSource.height() > imageSource.width()); - const int outSide = vertical - ? std::min(imageSource.height() / 2, imageSource.width()) - : std::min(imageSource.height(), imageSource.width() / 2); - const int offset_x = vertical - ? (imageSource.width() - outSide) - : ((imageSource.width() / 2) - outSide); - const int offset_y = vertical - ? ((imageSource.height() / 2) - outSide) - : (imageSource.height() - outSide); + const int outSide = vertical ? std::min(imageSource.height() / 2, imageSource.width()) : std::min(imageSource.height(), imageSource.width() / 2); + const int offset_x = vertical ? (imageSource.width() - outSide) : ((imageSource.width() / 2) - outSide); + const int offset_y = vertical ? ((imageSource.height() / 2) - outSide) : (imageSource.height() - outSide); // Make sure rig folder exists std::string rigFolder = outputFolder + "/rig"; @@ -155,27 +141,24 @@ bool splitDualFisheye(sfmData::SfMData& outSfmData, // Save new image on disk fs::path path(imagePath); - std::string filename = extension.empty() ? - path.filename().string() : path.stem().string() + "." + extension; - image::writeImage(subFolder + std::string("/") + filename, - imageOut, image::ImageWriteOptions(), metadataSource); - - // Initialize view and add it to SfMData - #pragma omp critical (split360Images_addView) + std::string filename = extension.empty() ? path.filename().string() : path.stem().string() + "." + extension; + image::writeImage(subFolder + std::string("/") + filename, imageOut, image::ImageWriteOptions(), metadataSource); + +// Initialize view and add it to SfMData +#pragma omp critical(split360Images_addView) { auto& views = outSfmData.getViews(); IndexT viewId = views.size(); auto view = std::make_shared( - /* image path */ subFolder + std::string("/") + filename, - /* viewId */ viewId, - /* intrinsicId */ 0, - /* poseId */ UndefinedIndexT, - /* width */ outSide, - /* height */ outSide, - /* rigId */ 0, - /* subPoseId */ i, - /* metadata */ image::getMapFromMetadata(metadataSource) - ); + /* image path */ subFolder + std::string("/") + filename, + /* viewId */ viewId, + /* intrinsicId */ 0, + /* poseId */ UndefinedIndexT, + /* width */ outSide, + /* height */ outSide, + /* rigId */ 0, + /* subPoseId */ i, + /* metadata */ image::getMapFromMetadata(metadataSource)); views.emplace(viewId, view); } } @@ -186,8 +169,12 @@ bool splitDualFisheye(sfmData::SfMData& outSfmData, } bool splitEquirectangular(sfmData::SfMData& outSfmData, - const std::string& imagePath, const std::string& outputFolder, const std::string& extension, - std::size_t nbSplits, std::size_t splitResolution, double fovDegree) + const std::string& imagePath, + const std::string& outputFolder, + const std::string& extension, + std::size_t nbSplits, + std::size_t splitResolution, + double fovDegree) { // Load source image from disk image::Image imageSource; @@ -205,7 +192,7 @@ bool splitEquirectangular(sfmData::SfMData& outSfmData, const double focal_px = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; - for(std::size_t i = 0; i < nbSplits; ++i) + for (std::size_t i = 0; i < nbSplits; ++i) { cameras.emplace_back(focal_px, splitResolution, splitResolution, RotationAroundY(angle)); angle += alpha; @@ -219,19 +206,19 @@ bool splitEquirectangular(sfmData::SfMData& outSfmData, fs::create_directory(rigFolder); size_t index = 0; - for(const PinholeCameraR& camera : cameras) + for (const PinholeCameraR& camera : cameras) { imaOut.fill(image::BLACK); // Backward mapping: // - Find for each pixels of the pinhole image where it comes from the panoramic image - for(int j = 0; j < splitResolution; ++j) + for (int j = 0; j < splitResolution; ++j) { - for(int i = 0; i < splitResolution; ++i) + for (int i = 0; i < splitResolution; ++i) { const Vec3 ray = camera.getRay(i, j); const Vec2 x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); - imaOut(j,i) = sampler(imageSource, x(1), x(0)); + imaOut(j, i) = sampler(imageSource, x(1), x(0)); } } @@ -244,9 +231,9 @@ bool splitEquirectangular(sfmData::SfMData& outSfmData, outMetadataSpec.extra_attribs = image::readImageMetadata(imagePath); // Override make and model in order to force camera model in SfM - outMetadataSpec.attribute("Make", "Custom"); + outMetadataSpec.attribute("Make", "Custom"); outMetadataSpec.attribute("Model", "Pinhole"); - const float focal_mm = focal_px * (36.0 / splitResolution); // muliplied by sensorWidth (36mm by default) + const float focal_mm = focal_px * (36.0 / splitResolution); // muliplied by sensorWidth (36mm by default) outMetadataSpec.attribute("Exif:FocalLength", focal_mm); // Make sure sub-folder exists for complete rig structure @@ -255,30 +242,27 @@ bool splitEquirectangular(sfmData::SfMData& outSfmData, // Save new image on disk fs::path path(imagePath); - std::string filename = extension.empty() ? - path.filename().string() : path.stem().string() + "." + extension; - image::writeImage(subFolder + std::string("/") + filename, - imaOut, image::ImageWriteOptions(), outMetadataSpec.extra_attribs); - - // Initialize view and add it to SfMData - #pragma omp critical (split360Images_addView) + std::string filename = extension.empty() ? path.filename().string() : path.stem().string() + "." + extension; + image::writeImage(subFolder + std::string("/") + filename, imaOut, image::ImageWriteOptions(), outMetadataSpec.extra_attribs); + +// Initialize view and add it to SfMData +#pragma omp critical(split360Images_addView) { auto& views = outSfmData.getViews(); IndexT viewId = views.size(); auto view = std::make_shared( - /* image path */ subFolder + std::string("/") + filename, - /* viewId */ viewId, - /* intrinsicId */ 0, - /* poseId */ UndefinedIndexT, - /* width */ splitResolution, - /* height */ splitResolution, - /* rigId */ 0, - /* subPoseId */ index, - /* metadata */ image::getMapFromMetadata(outMetadataSpec.extra_attribs) - ); + /* image path */ subFolder + std::string("/") + filename, + /* viewId */ viewId, + /* intrinsicId */ 0, + /* poseId */ UndefinedIndexT, + /* width */ splitResolution, + /* height */ splitResolution, + /* rigId */ 0, + /* subPoseId */ index, + /* metadata */ image::getMapFromMetadata(outMetadataSpec.extra_attribs)); views.emplace(viewId, view); } - + // Increment index ++index; } @@ -286,9 +270,11 @@ bool splitEquirectangular(sfmData::SfMData& outSfmData, return true; } - -bool splitEquirectangularPreview(const std::string& imagePath, const std::string& outputFolder, - std::size_t nbSplits, std::size_t splitResolution, double fovDegree) +bool splitEquirectangularPreview(const std::string& imagePath, + const std::string& outputFolder, + std::size_t nbSplits, + std::size_t splitResolution, + double fovDegree) { // Load source image from disk image::Image imageSource; @@ -306,7 +292,7 @@ bool splitEquirectangularPreview(const std::string& imagePath, const std::string const double focal_px = (splitResolution / 2.0) / tan(fov / 2.0); double angle = 0.0; - for(std::size_t i = 0; i < nbSplits; ++i) + for (std::size_t i = 0; i < nbSplits; ++i) { cameras.emplace_back(focal_px, splitResolution, splitResolution, RotationAroundY(angle)); angle += alpha; @@ -315,8 +301,8 @@ bool splitEquirectangularPreview(const std::string& imagePath, const std::string svg::svgDrawer svgStream(inWidth, inHeight); svgStream.drawRectangle(0, 0, inWidth, inHeight, svg::svgStyle().fill("black")); svgStream.drawImage(imagePath, inWidth, inHeight, 0, 0, 0.7f); - svgStream.drawLine(0,0,inWidth, inHeight, svg::svgStyle().stroke("white")); - svgStream.drawLine(inWidth,0, 0, inHeight, svg::svgStyle().stroke("white")); + svgStream.drawLine(0, 0, inWidth, inHeight, svg::svgStyle().stroke("white")); + svgStream.drawLine(inWidth, 0, 0, inHeight, svg::svgStyle().stroke("white")); // For each cam, reproject the image borders onto the panoramic image @@ -327,30 +313,30 @@ bool splitEquirectangularPreview(const std::string& imagePath, const std::string Vec3 ray; // Vertical rectilinear image border - for (double j = 0; j <= splitResolution; j += splitResolution/static_cast(step)) + for (double j = 0; j <= splitResolution; j += splitResolution / static_cast(step)) { - Vec2 pt(0.,j); + Vec2 pt(0., j); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); + Vec2 x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); pt[0] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); + x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("magenta").stroke("white", 4)); } // Horizontal rectilinear image border - for (double j = 0; j <= splitResolution; j += splitResolution/static_cast(step)) + for (double j = 0; j <= splitResolution; j += splitResolution / static_cast(step)) { - Vec2 pt(j,0.); + Vec2 pt(j, 0.); ray = camera.getRay(pt(0), pt(1)); - Vec2 x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); + Vec2 x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); pt[1] = splitResolution; ray = camera.getRay(pt(0), pt(1)); - x = SphericalMapping::toEquirectangular( ray, inWidth, inHeight); + x = SphericalMapping::toEquirectangular(ray, inWidth, inHeight); svgStream.drawCircle(x(0), x(1), 8, svg::svgStyle().fill("lime").stroke("white", 4)); } } @@ -364,19 +350,19 @@ bool splitEquirectangularPreview(const std::string& imagePath, const std::string int aliceVision_main(int argc, char** argv) { // command-line parameters - std::string inputPath; // media file path list or SfMData file - std::string outputFolder; // output folder for splited images - std::string outSfmDataFilepath; // output SfMData file - std::string splitMode; // split mode (dualfisheye, equirectangular) - std::string dualFisheyeOffsetPresetX; // dual-fisheye offset preset on X axis - std::string dualFisheyeOffsetPresetY; // dual-fisheye offset preset on Y axis - std::string dualFisheyeCameraModel; // camera model (fisheye4 or equidistant_r3) - std::size_t equirectangularNbSplits; // nb splits for equirectangular image - std::size_t equirectangularSplitResolution; // split resolution for equirectangular image + std::string inputPath; // media file path list or SfMData file + std::string outputFolder; // output folder for splited images + std::string outSfmDataFilepath; // output SfMData file + std::string splitMode; // split mode (dualfisheye, equirectangular) + std::string dualFisheyeOffsetPresetX; // dual-fisheye offset preset on X axis + std::string dualFisheyeOffsetPresetY; // dual-fisheye offset preset on Y axis + std::string dualFisheyeCameraModel; // camera model (fisheye4 or equidistant_r3) + std::size_t equirectangularNbSplits; // nb splits for equirectangular image + std::size_t equirectangularSplitResolution; // split resolution for equirectangular image bool equirectangularPreviewMode = false; - double fov = 110.0; // Field of View in degree + double fov = 110.0; // Field of View in degree int nbThreads = 3; - std::string extension; // extension of output images + std::string extension; // extension of output images // clang-format off po::options_description requiredParams("Required parameters"); @@ -437,8 +423,7 @@ int aliceVision_main(int argc, char** argv) // splitMode to lower std::transform(splitMode.begin(), splitMode.end(), splitMode.begin(), ::tolower); - if (splitMode != "equirectangular" && - splitMode != "dualfisheye") + if (splitMode != "equirectangular" && splitMode != "dualfisheye") { ALICEVISION_LOG_ERROR("Invalid split mode : " << splitMode); return EXIT_FAILURE; @@ -450,9 +435,7 @@ int aliceVision_main(int argc, char** argv) // dualFisheyeOffsetPresetX to lower std::transform(dualFisheyeOffsetPresetX.begin(), dualFisheyeOffsetPresetX.end(), dualFisheyeOffsetPresetX.begin(), ::tolower); - if (dualFisheyeOffsetPresetX != "left" && - dualFisheyeOffsetPresetX != "right" && - dualFisheyeOffsetPresetX != "center") + if (dualFisheyeOffsetPresetX != "left" && dualFisheyeOffsetPresetX != "right" && dualFisheyeOffsetPresetX != "center") { ALICEVISION_LOG_ERROR("Invalid dual-fisheye X offset preset : " << dualFisheyeOffsetPresetX); return EXIT_FAILURE; @@ -461,9 +444,7 @@ int aliceVision_main(int argc, char** argv) // dualFisheyeOffsetPresetY to lower std::transform(dualFisheyeOffsetPresetY.begin(), dualFisheyeOffsetPresetY.end(), dualFisheyeOffsetPresetY.begin(), ::tolower); - if (dualFisheyeOffsetPresetY != "top" && - dualFisheyeOffsetPresetY != "bottom" && - dualFisheyeOffsetPresetY != "center") + if (dualFisheyeOffsetPresetY != "top" && dualFisheyeOffsetPresetY != "bottom" && dualFisheyeOffsetPresetY != "center") { ALICEVISION_LOG_ERROR("Invalid dual-fisheye Y offset preset : " << dualFisheyeOffsetPresetY); return EXIT_FAILURE; @@ -472,8 +453,7 @@ int aliceVision_main(int argc, char** argv) // Check dual-fisheye camera model { - if (dualFisheyeCameraModel != "fisheye4" && - dualFisheyeCameraModel != "equidistant_r3") + if (dualFisheyeCameraModel != "fisheye4" && dualFisheyeCameraModel != "equidistant_r3") { ALICEVISION_LOG_ERROR("Invalid dual-fisheye camera model : " << dualFisheyeCameraModel); return EXIT_FAILURE; @@ -529,7 +509,6 @@ int aliceVision_main(int argc, char** argv) } } - // Output SfMData is constituted of: // - a rig // - an intrinsic @@ -539,9 +518,8 @@ int aliceVision_main(int argc, char** argv) // - all views have the same intrinsic sfmData::SfMData outSfmData; - - // Split images to create views - #pragma omp parallel for num_threads(nbThreads) +// Split images to create views +#pragma omp parallel for num_threads(nbThreads) for (int i = 0; i < imagePaths.size(); ++i) { const std::string& imagePath = imagePaths[i]; @@ -549,26 +527,19 @@ int aliceVision_main(int argc, char** argv) if (splitMode == "equirectangular") { - if(equirectangularPreviewMode) + if (equirectangularPreviewMode) { - hasCorrectPath = - splitEquirectangularPreview(imagePath, outputFolder, - equirectangularNbSplits, equirectangularSplitResolution, fov); + hasCorrectPath = splitEquirectangularPreview(imagePath, outputFolder, equirectangularNbSplits, equirectangularSplitResolution, fov); } else { hasCorrectPath = - splitEquirectangular(outSfmData, - imagePath, outputFolder, extension, - equirectangularNbSplits, equirectangularSplitResolution, fov); + splitEquirectangular(outSfmData, imagePath, outputFolder, extension, equirectangularNbSplits, equirectangularSplitResolution, fov); } } - else if(splitMode == "dualfisheye") + else if (splitMode == "dualfisheye") { - hasCorrectPath = - splitDualFisheye(outSfmData, - imagePath, outputFolder, extension, - dualFisheyeOffsetPresetX, dualFisheyeOffsetPresetY); + hasCorrectPath = splitDualFisheye(outSfmData, imagePath, outputFolder, extension, dualFisheyeOffsetPresetX, dualFisheyeOffsetPresetY); } if (!hasCorrectPath) @@ -577,7 +548,6 @@ int aliceVision_main(int argc, char** argv) } } - // Rig { // Initialize with number of sub-poses @@ -599,7 +569,6 @@ int aliceVision_main(int argc, char** argv) rigs[0] = rig; } - // Intrinsic { // Initialize with dimensions, focal and camera model @@ -646,9 +615,8 @@ int aliceVision_main(int argc, char** argv) intrinsics.emplace(0, intrinsic); } - // Save sfmData with modified path to images - if(!sfmDataIO::save(outSfmData, outSfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) + if (!sfmDataIO::save(outSfmData, outSfmDataFilepath, sfmDataIO::ESfMData(sfmDataIO::ALL))) { ALICEVISION_LOG_ERROR("The output SfMData file '" << outSfmDataFilepath << "' cannot be written."); return EXIT_FAILURE; diff --git a/src/software/utils/main_voctreeCreation.cpp b/src/software/utils/main_voctreeCreation.cpp index 79e270be26..441ce472a8 100644 --- a/src/software/utils/main_voctreeCreation.cpp +++ b/src/software/utils/main_voctreeCreation.cpp @@ -33,7 +33,7 @@ static const int DIMENSION = 128; using namespace aliceVision; -//using namespace boost::accumulators; +// using namespace boost::accumulators; namespace po = boost::program_options; typedef aliceVision::feature::Descriptor DescriptorFloat; @@ -44,15 +44,15 @@ typedef aliceVision::feature::Descriptor DescriptorUCh */ int aliceVision_main(int argc, char** argv) { - int tbVerbosity = 2; - std::string weightName; - std::string treeName; - std::string sfmDataFilename; - std::vector featuresFolders; - std::uint32_t K = 10; - std::uint32_t restart = 5; - std::uint32_t LEVELS = 6; - bool sanityCheck = true; + int tbVerbosity = 2; + std::string weightName; + std::string treeName; + std::string sfmDataFilename; + std::vector featuresFolders; + std::uint32_t K = 10; + std::uint32_t restart = 5; + std::uint32_t LEVELS = 6; + bool sanityCheck = true; // clang-format off po::options_description requiredParams("Required parameters"); @@ -79,158 +79,156 @@ int aliceVision_main(int argc, char** argv) "The sanity check is a query to the database with the same documents/images useed to train the vocabulary tree."); // clang-format on - CmdLine cmdline("This program is used to load the sift descriptors from a SfMData file and create a vocabulary tree.\n" - "It takes as input either a list.txt file containing a simple list of images (bundler format and older AliceVision version format)\n" - "or a sfm_data file (JSON) containing the list of images. In both cases it is assumed that the .desc to load are in the same folder as the input file.\n" - "AliceVision voctreeCreation"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - // load SfMData - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - std::vector descriptors; - - std::vector descRead; - ALICEVISION_COUT("Reading descriptors from " << sfmDataFilename); - auto detect_start = std::chrono::steady_clock::now(); - size_t numTotDescriptors = aliceVision::voctree::readDescFromFiles(sfmData, featuresFolders, descriptors, descRead); - auto detect_end = std::chrono::steady_clock::now(); - auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - if(descriptors.empty()) - { - ALICEVISION_CERR("No descriptors loaded!!"); - return EXIT_FAILURE; - } - - ALICEVISION_COUT("Done! " << descRead.size() << " sets of descriptors read for a total of " << numTotDescriptors << " features"); - ALICEVISION_COUT("Reading took " << detect_elapsed.count() << " sec"); - - // Create tree - aliceVision::voctree::TreeBuilder builder(DescriptorFloat(0)); - builder.setVerbose(tbVerbosity); - builder.kmeans().setRestarts(restart); - ALICEVISION_COUT("Building a tree of L=" << LEVELS << " levels with a branching factor of k=" << K); - detect_start = std::chrono::steady_clock::now(); - builder.build(descriptors, K, LEVELS); - detect_end = std::chrono::steady_clock::now(); - detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("Tree created in " << ((float) detect_elapsed.count()) / 1000 << " sec"); - ALICEVISION_COUT(builder.tree().centers().size() << " centers"); - ALICEVISION_COUT("Saving vocabulary tree as " << treeName); - builder.tree().save(treeName); - - aliceVision::voctree::SparseHistogramPerImage allSparseHistograms; - // temporary vector used to save all the visual word for each image before adding them to documents - std::vector imgVisualWords; - ALICEVISION_COUT("Quantizing the features"); - size_t offset = 0; ///< this is used to align to the features of a given image in 'feature' - detect_start = std::chrono::steady_clock::now(); - // pass each feature through the vocabulary tree to get the associated visual word - // for each read images, recover the number of features in it from descRead and loop over the features - for(size_t i = 0; i < descRead.size(); ++i) - { - // for each image: - // clear the temporary vector used to save all the visual word and allocate the proper size - imgVisualWords.clear(); - // allocate as many visual words as the number of the features in the image - imgVisualWords.resize(descRead[i], 0); - - #pragma omp parallel for - for(ptrdiff_t j = 0; j < static_cast(descRead[i]); ++j) + CmdLine cmdline( + "This program is used to load the sift descriptors from a SfMData file and create a vocabulary tree.\n" + "It takes as input either a list.txt file containing a simple list of images (bundler format and older AliceVision version format)\n" + "or a sfm_data file (JSON) containing the list of images. In both cases it is assumed that the .desc to load are in the same folder as the " + "input file.\n" + "AliceVision voctreeCreation"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) { - // store the visual word associated to the feature in the temporary list - imgVisualWords[j] = builder.tree().quantize(descriptors[ j + offset ]); + return EXIT_FAILURE; } - aliceVision::voctree::SparseHistogram histo; - aliceVision::voctree::computeSparseHistogram(imgVisualWords, histo); - // add the vector to the documents - allSparseHistograms[i] = histo; - - // update the offset - offset += descRead[i]; - } - detect_end = std::chrono::steady_clock::now(); - detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("Feature quantization took " << detect_elapsed.count() << " sec"); - - - ALICEVISION_COUT("Creating the database..."); - // Add each object (document) to the database - aliceVision::voctree::Database db(builder.tree().words()); - ALICEVISION_COUT("\tfound " << allSparseHistograms.size() << " documents"); - for(const auto &doc : allSparseHistograms) - { - db.insert(doc.first, doc.second); - } - ALICEVISION_COUT("Database created!"); - - // Compute and save the word weights - ALICEVISION_COUT("Computing weights..."); - detect_start = std::chrono::steady_clock::now(); - db.computeTfIdfWeights(); - detect_end = std::chrono::steady_clock::now(); - detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("Computing weights done in " << detect_elapsed.count() << " sec"); - ALICEVISION_COUT("Saving weights as " << weightName); - db.saveWeights(weightName); - - - if(sanityCheck) - { - // Now query each document (sanity check) - std::vector matches; - size_t wrong = 0; // count the wrong matches - double recval = 0.0; - ALICEVISION_COUT("Sanity check: querying the database with the same documents"); - // for each document - for(const auto &doc : allSparseHistograms) + + // load SfMData + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) { - detect_start = std::chrono::steady_clock::now(); - // retrieve the best 4 matches - db.find(doc.second, 4, matches); - detect_end = std::chrono::steady_clock::now(); - detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - ALICEVISION_COUT("query document " << doc.first - << " took " << detect_elapsed.count() - << " ms and has " << matches.size() - << " matches\tBest " << matches[0].id - << " with score " << matches[0].score << std::endl); - // for each found match print the score, ideally the first one should be the document itself - for(size_t j = 0; j < matches.size(); ++j) - { - ALICEVISION_COUT("\t match " << matches[j].id << " with score " << matches[j].score); - if(matches[j].id / 4 == (doc.first) / 4) recval += 1; - } - - // if the first one is not the document itself notify and increment the counter - if(doc.first != matches[0].id) - { - ++wrong; - ALICEVISION_COUT("##### wrong match for document " << doc.first); - } + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; + } + + std::vector descriptors; + std::vector descRead; + ALICEVISION_COUT("Reading descriptors from " << sfmDataFilename); + auto detect_start = std::chrono::steady_clock::now(); + size_t numTotDescriptors = + aliceVision::voctree::readDescFromFiles(sfmData, featuresFolders, descriptors, descRead); + auto detect_end = std::chrono::steady_clock::now(); + auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + if (descriptors.empty()) + { + ALICEVISION_CERR("No descriptors loaded!!"); + return EXIT_FAILURE; } - if(wrong) + ALICEVISION_COUT("Done! " << descRead.size() << " sets of descriptors read for a total of " << numTotDescriptors << " features"); + ALICEVISION_COUT("Reading took " << detect_elapsed.count() << " sec"); + + // Create tree + aliceVision::voctree::TreeBuilder builder(DescriptorFloat(0)); + builder.setVerbose(tbVerbosity); + builder.kmeans().setRestarts(restart); + ALICEVISION_COUT("Building a tree of L=" << LEVELS << " levels with a branching factor of k=" << K); + detect_start = std::chrono::steady_clock::now(); + builder.build(descriptors, K, LEVELS); + detect_end = std::chrono::steady_clock::now(); + detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("Tree created in " << ((float)detect_elapsed.count()) / 1000 << " sec"); + ALICEVISION_COUT(builder.tree().centers().size() << " centers"); + ALICEVISION_COUT("Saving vocabulary tree as " << treeName); + builder.tree().save(treeName); + + aliceVision::voctree::SparseHistogramPerImage allSparseHistograms; + // temporary vector used to save all the visual word for each image before adding them to documents + std::vector imgVisualWords; + ALICEVISION_COUT("Quantizing the features"); + size_t offset = 0; ///< this is used to align to the features of a given image in 'feature' + detect_start = std::chrono::steady_clock::now(); + // pass each feature through the vocabulary tree to get the associated visual word + // for each read images, recover the number of features in it from descRead and loop over the features + for (size_t i = 0; i < descRead.size(); ++i) + { + // for each image: + // clear the temporary vector used to save all the visual word and allocate the proper size + imgVisualWords.clear(); + // allocate as many visual words as the number of the features in the image + imgVisualWords.resize(descRead[i], 0); + +#pragma omp parallel for + for (ptrdiff_t j = 0; j < static_cast(descRead[i]); ++j) + { + // store the visual word associated to the feature in the temporary list + imgVisualWords[j] = builder.tree().quantize(descriptors[j + offset]); + } + aliceVision::voctree::SparseHistogram histo; + aliceVision::voctree::computeSparseHistogram(imgVisualWords, histo); + // add the vector to the documents + allSparseHistograms[i] = histo; + + // update the offset + offset += descRead[i]; + } + detect_end = std::chrono::steady_clock::now(); + detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("Feature quantization took " << detect_elapsed.count() << " sec"); + + ALICEVISION_COUT("Creating the database..."); + // Add each object (document) to the database + aliceVision::voctree::Database db(builder.tree().words()); + ALICEVISION_COUT("\tfound " << allSparseHistograms.size() << " documents"); + for (const auto& doc : allSparseHistograms) { - ALICEVISION_COUT("there are " << wrong << " wrong matches"); + db.insert(doc.first, doc.second); } - else + ALICEVISION_COUT("Database created!"); + + // Compute and save the word weights + ALICEVISION_COUT("Computing weights..."); + detect_start = std::chrono::steady_clock::now(); + db.computeTfIdfWeights(); + detect_end = std::chrono::steady_clock::now(); + detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("Computing weights done in " << detect_elapsed.count() << " sec"); + ALICEVISION_COUT("Saving weights as " << weightName); + db.saveWeights(weightName); + + if (sanityCheck) { - ALICEVISION_COUT("Yay! no wrong matches!"); + // Now query each document (sanity check) + std::vector matches; + size_t wrong = 0; // count the wrong matches + double recval = 0.0; + ALICEVISION_COUT("Sanity check: querying the database with the same documents"); + // for each document + for (const auto& doc : allSparseHistograms) + { + detect_start = std::chrono::steady_clock::now(); + // retrieve the best 4 matches + db.find(doc.second, 4, matches); + detect_end = std::chrono::steady_clock::now(); + detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + ALICEVISION_COUT("query document " << doc.first << " took " << detect_elapsed.count() << " ms and has " << matches.size() + << " matches\tBest " << matches[0].id << " with score " << matches[0].score << std::endl); + // for each found match print the score, ideally the first one should be the document itself + for (size_t j = 0; j < matches.size(); ++j) + { + ALICEVISION_COUT("\t match " << matches[j].id << " with score " << matches[j].score); + if (matches[j].id / 4 == (doc.first) / 4) + recval += 1; + } + + // if the first one is not the document itself notify and increment the counter + if (doc.first != matches[0].id) + { + ++wrong; + ALICEVISION_COUT("##### wrong match for document " << doc.first); + } + } + + if (wrong) + { + ALICEVISION_COUT("there are " << wrong << " wrong matches"); + } + else + { + ALICEVISION_COUT("Yay! no wrong matches!"); + } + ALICEVISION_COUT("recval: " << recval / (double)(allSparseHistograms.size())); } - ALICEVISION_COUT("recval: " << recval / (double) (allSparseHistograms.size())); - } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/src/software/utils/main_voctreeQueryUtility.cpp b/src/software/utils/main_voctreeQueryUtility.cpp index 41108aac86..64f15245fb 100644 --- a/src/software/utils/main_voctreeQueryUtility.cpp +++ b/src/software/utils/main_voctreeQueryUtility.cpp @@ -51,13 +51,13 @@ typedef aliceVision::feature::Descriptor DescriptorUCh bool saveSparseHistogramPerImage(const std::string& filename, const aliceVision::voctree::SparseHistogramPerImage& docs) { std::ofstream fileout(filename); - if(!fileout.is_open()) + if (!fileout.is_open()) return false; - for(const auto& d : docs) + for (const auto& d : docs) { fileout << "d{" << d.first << "} = ["; - for(const auto& i : d.second) + for (const auto& i : d.second) fileout << i.first << ", "; fileout << "]\n"; } @@ -67,23 +67,23 @@ bool saveSparseHistogramPerImage(const std::string& filename, const aliceVision: } static const std::string programDescription = - "This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary " - "tree.\n " - "The database is then queried optionally with another set of images in order to retrieve for each image the set of " - "most similar images in the dataset\n" - "If another set of images is not provided, the program will perform a sanity check of the database by querying the " - "database using the same images used to build it\n" - "It takes as input either a list.txt file containing the a simple list of images (bundler format and older " - "AliceVision version format)\n" - "or a sfm_data file (JSON) containing the list of images. In both cases it is assumed that the .desc to load are " - "in the same folder as the input file\n" - "For the vocabulary tree, it takes as input the input.tree (and the input.weight) file generated by createVoctree\n" - "As a further output option (--outdir), it is possible to specify a folder in which it will create, for each query " - "image (be it a query image of querylist or an image of keylist)\n" - "it creates a folder with the same name of the image, inside which it creates a list of symbolic links to all the " - "similar images found. The symbolic link naming convention\n" - "is matchNumber.filename, where matchNumber is the relevant position of the image in the list of matches ([0-r]) " - "and filename is its image file (eg image.jpg)\n"; + "This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary " + "tree.\n " + "The database is then queried optionally with another set of images in order to retrieve for each image the set of " + "most similar images in the dataset\n" + "If another set of images is not provided, the program will perform a sanity check of the database by querying the " + "database using the same images used to build it\n" + "It takes as input either a list.txt file containing the a simple list of images (bundler format and older " + "AliceVision version format)\n" + "or a sfm_data file (JSON) containing the list of images. In both cases it is assumed that the .desc to load are " + "in the same folder as the input file\n" + "For the vocabulary tree, it takes as input the input.tree (and the input.weight) file generated by createVoctree\n" + "As a further output option (--outdir), it is possible to specify a folder in which it will create, for each query " + "image (be it a query image of querylist or an image of keylist)\n" + "it creates a folder with the same name of the image, inside which it creates a list of symbolic links to all the " + "similar images found. The symbolic link naming convention\n" + "is matchNumber.filename, where matchNumber is the relevant position of the image in the list of matches ([0-r]) " + "and filename is its image file (eg image.jpg)\n"; /* * This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary @@ -167,24 +167,24 @@ int aliceVision_main(int argc, char** argv) cmdline.add(requiredParams); cmdline.add(optionalParams); - if(!cmdline.execute(argc, argv)) + if (!cmdline.execute(argc, argv)) { return EXIT_FAILURE; } - if(!weightsName.empty()) + if (!weightsName.empty()) { withWeights = true; } - if(!outfile.empty()) + if (!outfile.empty()) { withOutput = true; } - if(!querySfmDataFilename.empty()) + if (!querySfmDataFilename.empty()) { withQuery = true; } - if(!outDir.empty()) + if (!outDir.empty()) { withOutDir = true; } @@ -193,8 +193,7 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Loading vocabulary tree\n"); aliceVision::voctree::VocabularyTree tree(treeName); - ALICEVISION_LOG_INFO("tree loaded with\n\t" << tree.levels() << " levels\n\t" << tree.splits() - << " branching factor"); + ALICEVISION_LOG_INFO("tree loaded with\n\t" << tree.levels() << " levels\n\t" << tree.splits() << " branching factor"); // create the database @@ -202,7 +201,7 @@ int aliceVision_main(int argc, char** argv) // Add each object (document) to the database aliceVision::voctree::Database db(tree.words()); - if(withWeights) + if (withWeights) { ALICEVISION_LOG_INFO("Loading weights..."); db.loadWeights(weightsName); @@ -212,10 +211,10 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("No weights specified, skipping..."); } - if(withOutDir) + if (withOutDir) { // load the json for the dataset used to build the database - if(sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + if (sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_INFO("SfMData loaded from " << sfmDataFilename << " containing: "); ALICEVISION_LOG_INFO("\tnumber of views: " << sfmData.getViews().size()); @@ -227,11 +226,10 @@ int aliceVision_main(int argc, char** argv) } // load the json for the dataset used to query the database - if(withQuery) + if (withQuery) { querySfmData = new aliceVision::sfmData::SfMData(); - if(sfmDataIO::load(*querySfmData, querySfmDataFilename, - sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) + if (sfmDataIO::load(*querySfmData, querySfmDataFilename, sfmDataIO::ESfMData(sfmDataIO::VIEWS | sfmDataIO::INTRINSICS))) { ALICEVISION_LOG_INFO("SfMData loaded from " << querySfmDataFilename << " containing: "); ALICEVISION_LOG_INFO("\tnumber of views: " << querySfmData->getViews().size()); @@ -249,7 +247,7 @@ int aliceVision_main(int argc, char** argv) } // create recursively the provided out dir - if(!fs::exists(fs::path(outDir))) + if (!fs::exists(fs::path(outDir))) { // ALICEVISION_COUT("creating folder" << outDir); fs::create_directories(fs::path(outDir)); @@ -260,27 +258,26 @@ int aliceVision_main(int argc, char** argv) ALICEVISION_LOG_INFO("Reading descriptors from " << sfmDataFilename); auto detect_start = std::chrono::steady_clock::now(); - std::size_t numTotFeatures = - aliceVision::voctree::populateDatabase(sfmData, featuresFolders, tree, db, Nmax); + std::size_t numTotFeatures = aliceVision::voctree::populateDatabase(sfmData, featuresFolders, tree, db, Nmax); auto detect_end = std::chrono::steady_clock::now(); auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - if(numTotFeatures == 0) + if (numTotFeatures == 0) { ALICEVISION_LOG_ERROR("No descriptors loaded!!"); return EXIT_FAILURE; } - ALICEVISION_LOG_INFO("Done! " << db.getSparseHistogramPerImage().size() - << " sets of descriptors read for a total of " << numTotFeatures << " features"); + ALICEVISION_LOG_INFO("Done! " << db.getSparseHistogramPerImage().size() << " sets of descriptors read for a total of " << numTotFeatures + << " features"); ALICEVISION_LOG_INFO("Reading took " << detect_elapsed.count() << " sec"); - if(!documentMapFile.empty()) + if (!documentMapFile.empty()) { saveSparseHistogramPerImage(documentMapFile, db.getSparseHistogramPerImage()); } - if(!withWeights) + if (!withWeights) { // If we don't have an input weight file, we compute weights based on the // current database. @@ -292,13 +289,13 @@ int aliceVision_main(int argc, char** argv) std::map allDocMatches; std::size_t wrong = 0; - if(numImageQuery == 0) + if (numImageQuery == 0) { // if 0 retrieve the score for all the documents of the database numImageQuery = db.size(); } std::ofstream fileout; - if(withOutput) + if (withOutput) { fileout.open(outfile, std::ofstream::out); } @@ -306,7 +303,7 @@ int aliceVision_main(int argc, char** argv) std::map histograms; // if the query list is not provided - if(!withQuery) + if (!withQuery) { // do a sanity check ALICEVISION_LOG_INFO("Sanity check: querying the database with the same documents"); @@ -316,22 +313,21 @@ int aliceVision_main(int argc, char** argv) { // otherwise query the database with the provided query list ALICEVISION_LOG_INFO("Querying the database with the documents in " << querySfmDataFilename); - voctree::queryDatabase(*querySfmData, featuresFolders, tree, db, numImageQuery, allDocMatches, - histograms, distance, Nmax); + voctree::queryDatabase(*querySfmData, featuresFolders, tree, db, numImageQuery, allDocMatches, histograms, distance, Nmax); } // Load the corresponding RegionsPerView // Get imageDescriberMethodType EImageDescriberType describerType = EImageDescriberType_stringToEnum(describerMethod); - if((describerType != EImageDescriberType::SIFT) && (describerType != EImageDescriberType::SIFT_FLOAT)) + if ((describerType != EImageDescriberType::SIFT) && (describerType != EImageDescriberType::SIFT_FLOAT)) { ALICEVISION_LOG_ERROR("Invalid describer method." << std::endl); return EXIT_FAILURE; } feature::RegionsPerView regionsPerView; - if(!aliceVision::sfm::loadRegionsPerView(regionsPerView, sfmData, featuresFolders, {describerType})) + if (!aliceVision::sfm::loadRegionsPerView(regionsPerView, sfmData, featuresFolders, {describerType})) { ALICEVISION_LOG_ERROR("Invalid regions." << std::endl); return EXIT_FAILURE; @@ -339,16 +335,16 @@ int aliceVision_main(int argc, char** argv) aliceVision::matching::PairwiseSimpleMatches allMatches; - for(auto docMatches : allDocMatches) + for (auto docMatches : allDocMatches) { const aliceVision::voctree::DocMatches& matches = docMatches.second; fs::path dirname; ALICEVISION_LOG_INFO("Camera: " << docMatches.first); - ALICEVISION_LOG_INFO("query document " << docMatches.first << " has " << matches.size() << " matches\tBest " - << matches[0].id << " with score " << matches[0].score); - if(withOutput) + ALICEVISION_LOG_INFO("query document " << docMatches.first << " has " << matches.size() << " matches\tBest " << matches[0].id + << " with score " << matches[0].score); + if (withOutput) { - if(!matlabOutput) + if (!matlabOutput) { fileout << "Camera: " << docMatches.first << std::endl; } @@ -358,19 +354,19 @@ int aliceVision_main(int argc, char** argv) fileout << matches; } } - if(withOutDir) + if (withOutDir) { // create a new folder inside outDir with the same name as the query image // the query image can be either from the dataset or from the query list if provided // to put a symlink to the query image too - fs::path absoluteFilename; //< the abs path to the image - fs::path sylinkName; //< the name used for the symbolic link + fs::path absoluteFilename; //< the abs path to the image + fs::path sylinkName; //< the name used for the symbolic link // get the dirname from the filename aliceVision::sfmData::Views::const_iterator it = querySfmData->getViews().find(docMatches.first); - if(it == querySfmData->getViews().end()) + if (it == querySfmData->getViews().end()) { // this is very wrong ALICEVISION_LOG_ERROR("Could not find the image file for the document " << docMatches.first << "!"); @@ -385,7 +381,7 @@ int aliceVision_main(int argc, char** argv) // Perform features matching const aliceVision::voctree::SparseHistogram& currentHistogram = histograms.at(docMatches.first); - for(const auto comparedPicture : matches) + for (const auto comparedPicture : matches) { aliceVision::voctree::SparseHistogram comparedHistogram = histograms.at(comparedPicture.id); aliceVision::Pair indexImagePair = aliceVision::Pair(docMatches.first, comparedPicture.id); @@ -401,27 +397,27 @@ int aliceVision_main(int argc, char** argv) aliceVision::matching::IndMatches featureMatches; - for(const auto& currentLeaf : currentHistogram) + for (const auto& currentLeaf : currentHistogram) { - if((currentLeaf.second.size() == 1)) + if ((currentLeaf.second.size() == 1)) { auto leafRightIt = comparedHistogram.find(currentLeaf.first); - if(leafRightIt == comparedHistogram.end()) + if (leafRightIt == comparedHistogram.end()) continue; - if(leafRightIt->second.size() != 1) + if (leafRightIt->second.size() != 1) continue; const Regions& siftRegionsLeft = regionsPerView.getRegions(docMatches.first, describerType); const Regions& siftRegionsRight = regionsPerView.getRegions(comparedPicture.id, describerType); - double dist = siftRegionsLeft.SquaredDescriptorDistance( - currentLeaf.second[0], &siftRegionsRight, leafRightIt->second[0]); - aliceVision::matching::IndMatch currentMatch = - aliceVision::matching::IndMatch(currentLeaf.second[0], leafRightIt->second[0] + double dist = siftRegionsLeft.SquaredDescriptorDistance(currentLeaf.second[0], &siftRegionsRight, leafRightIt->second[0]); + aliceVision::matching::IndMatch currentMatch = aliceVision::matching::IndMatch(currentLeaf.second[0], + leafRightIt->second[0] #ifdef ALICEVISION_DEBUG_MATCHING - , dist + , + dist #endif - ); + ); featureMatches.push_back(currentMatch); // TODO: distance computation @@ -435,27 +431,27 @@ int aliceVision_main(int argc, char** argv) } // now parse all the returned matches - for(std::size_t j = 0; j < matches.size(); ++j) + for (std::size_t j = 0; j < matches.size(); ++j) { ALICEVISION_LOG_INFO("\t match " << matches[j].id << " with score " << matches[j].score); // ALICEVISION_CERR("" << i->first << " " << matches[j].id << " " << matches[j].score); - if(withOutput && !matlabOutput) + if (withOutput && !matlabOutput) fileout << docMatches.first << " " << matches[j].id << " " << matches[j].score << std::endl; - if(withOutDir) + if (withOutDir) { // create a new symbolic link inside the current folder pointing to // the relevant matching image - fs::path absoluteFilename; //< the abs path to the image - fs::path sylinkName; //< the name used for the symbolic link + fs::path absoluteFilename; //< the abs path to the image + fs::path sylinkName; //< the name used for the symbolic link // get the dirname from the filename aliceVision::sfmData::Views::const_iterator it = sfmData.getViews().find(matches[j].id); - if(it != sfmData.getViews().end()) + if (it != sfmData.getViews().end()) { absoluteFilename = it->second->getImage().getImagePath(); - sylinkName = fs::path(utils::toStringZeroPadded(j, 4) + "." + std::to_string(matches[j].score) + - "." + absoluteFilename.filename().string()); + sylinkName = + fs::path(utils::toStringZeroPadded(j, 4) + "." + std::to_string(matches[j].score) + "." + absoluteFilename.filename().string()); } else { @@ -467,10 +463,10 @@ int aliceVision_main(int argc, char** argv) } } - if(!withQuery) + if (!withQuery) { // only for the sanity check, check if the best matching image is the document itself - if(docMatches.first != matches[0].id) + if (docMatches.first != matches[0].id) { ++wrong; ALICEVISION_LOG_INFO("##### wrong match for document " << docMatches.first); @@ -482,36 +478,36 @@ int aliceVision_main(int argc, char** argv) std::cout << " ---------------------------- \n" << std::endl; std::cout << "Matching distances - Histogram: \n" << std::endl; std::map stats; - for(const auto& imgMatches : allMatches) + for (const auto& imgMatches : allMatches) { - if(imgMatches.first.first == imgMatches.first.second) + if (imgMatches.first.first == imgMatches.first.second) // Ignore auto-match continue; - for(const aliceVision::matching::IndMatch& featMatches : imgMatches.second) + for (const aliceVision::matching::IndMatch& featMatches : imgMatches.second) { int d = std::floor(featMatches._distance / 1000.0); - if(stats.find(d) != stats.end()) + if (stats.find(d) != stats.end()) stats[d] += 1; else stats[d] = 1; } } - for(const auto& stat : stats) + for (const auto& stat : stats) { std::cout << stat.first << "\t" << stat.second << std::endl; } #endif - if(!withQuery) + if (!withQuery) { - if(wrong) + if (wrong) ALICEVISION_LOG_INFO("there are " << wrong << " wrong matches"); else ALICEVISION_LOG_INFO("no wrong matches!"); } - if(withOutput) + if (withOutput) { fileout.close(); } diff --git a/src/software/utils/main_voctreeStatistics.cpp b/src/software/utils/main_voctreeStatistics.cpp index 155745d304..5671d740f2 100644 --- a/src/software/utils/main_voctreeStatistics.cpp +++ b/src/software/utils/main_voctreeStatistics.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -42,8 +42,7 @@ namespace po = boost::program_options; typedef aliceVision::feature::Descriptor DescriptorFloat; typedef aliceVision::feature::Descriptor DescriptorUChar; -static const std::string programDescription = - "This program is used to generate some statistics.\n "; +static const std::string programDescription = "This program is used to generate some statistics.\n "; /* * This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary tree @@ -51,13 +50,13 @@ static const std::string programDescription = */ int aliceVision_main(int argc, char** argv) { - std::string weightsName; // the filename for the voctree weights - bool withWeights = false; // flag for the optional weights file - std::string treeName; // the filename of the voctree - std::string sfmDataFilename; // the file containing the list of features to use to build the database - std::vector featuresFolders; - std::string querySfmDataFilename = ""; // the file containing the list of features to use as query - std::string distance; + std::string weightsName; // the filename for the voctree weights + bool withWeights = false; // flag for the optional weights file + std::string treeName; // the filename of the voctree + std::string sfmDataFilename; // the file containing the list of features to use to build the database + std::vector featuresFolders; + std::string querySfmDataFilename = ""; // the file containing the list of features to use as query + std::string distance; // clang-format off po::options_description requiredParams("Required parameters"); @@ -85,95 +84,95 @@ int aliceVision_main(int argc, char** argv) " - inversedWeightedCommonPoints: strongCommonPoints with inverted weights."); // clang-format on - CmdLine cmdline("This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary tree.\n" - "The database is then queried with the same images in order to retrieve for each image the set of most similar images in the dataset.\n" - "AliceVision voctreeStatistics"); - cmdline.add(requiredParams); - cmdline.add(optionalParams); - if (!cmdline.execute(argc, argv)) - { - return EXIT_FAILURE; - } - - if (weightsName.size() > 0) - { - withWeights = true; - } - - // load vocabulary tree - ALICEVISION_LOG_INFO("Loading vocabulary tree\n"); - aliceVision::voctree::VocabularyTree tree(treeName); - ALICEVISION_LOG_INFO("tree loaded with\n\t" - << tree.levels() << " levels\n\t" - << tree.splits() << " branching factor"); - - // load SfMData - sfmData::SfMData sfmData; - if(!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - // create the database - ALICEVISION_LOG_INFO("Creating the database..."); - - // add each object (document) to the database - aliceVision::voctree::Database db(tree.words()); - - if(withWeights) - { - ALICEVISION_LOG_INFO("Loading weights..."); - db.loadWeights(weightsName); - } - else - { - ALICEVISION_LOG_INFO("No weights specified, skipping..."); - } - - // read the descriptors and populate the database - ALICEVISION_LOG_INFO("Reading descriptors from " << sfmDataFilename); - auto detect_start = std::chrono::steady_clock::now(); - size_t numTotFeatures = aliceVision::voctree::populateDatabase(sfmData, featuresFolders, tree, db); - auto detect_end = std::chrono::steady_clock::now(); - auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); - - if(numTotFeatures == 0) - { - ALICEVISION_LOG_INFO("No descriptors loaded!!"); - return EXIT_FAILURE; - } - - ALICEVISION_LOG_INFO("Done! " << db.getSparseHistogramPerImage().size() << " sets of descriptors read for a total of " << numTotFeatures << " features"); - ALICEVISION_LOG_INFO("Reading took " << detect_elapsed.count() << " sec"); - - if(!withWeights) - { - // compute and save the word weights - ALICEVISION_LOG_INFO("Computing weights..."); - db.computeTfIdfWeights(); - } - - // query documents for Statistics - std::map globalHisto; - - ALICEVISION_LOG_INFO("Getting some stats for " << querySfmDataFilename); - - sfmData::SfMData querySfmData; - if(!sfmDataIO::load(querySfmData, querySfmDataFilename, sfmDataIO::ESfMData::ALL)) - { - ALICEVISION_LOG_ERROR("The input SfMData file '" + querySfmDataFilename + "' cannot be read."); - return EXIT_FAILURE; - } - - aliceVision::voctree::voctreeStatistics(querySfmData, featuresFolders, tree, db, distance, globalHisto); - - std::cout << "-----------------" << std::endl; - - for(const auto &itHisto : globalHisto) - std::cout << itHisto.first << ": " << itHisto.second << ", "; - - std::cout << std::endl; - - return EXIT_SUCCESS; + CmdLine cmdline( + "This program is used to create a database with a provided dataset of image descriptors using a trained vocabulary tree.\n" + "The database is then queried with the same images in order to retrieve for each image the set of most similar images in the dataset.\n" + "AliceVision voctreeStatistics"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + if (weightsName.size() > 0) + { + withWeights = true; + } + + // load vocabulary tree + ALICEVISION_LOG_INFO("Loading vocabulary tree\n"); + aliceVision::voctree::VocabularyTree tree(treeName); + ALICEVISION_LOG_INFO("tree loaded with\n\t" << tree.levels() << " levels\n\t" << tree.splits() << " branching factor"); + + // load SfMData + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + sfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; + } + + // create the database + ALICEVISION_LOG_INFO("Creating the database..."); + + // add each object (document) to the database + aliceVision::voctree::Database db(tree.words()); + + if (withWeights) + { + ALICEVISION_LOG_INFO("Loading weights..."); + db.loadWeights(weightsName); + } + else + { + ALICEVISION_LOG_INFO("No weights specified, skipping..."); + } + + // read the descriptors and populate the database + ALICEVISION_LOG_INFO("Reading descriptors from " << sfmDataFilename); + auto detect_start = std::chrono::steady_clock::now(); + size_t numTotFeatures = aliceVision::voctree::populateDatabase(sfmData, featuresFolders, tree, db); + auto detect_end = std::chrono::steady_clock::now(); + auto detect_elapsed = std::chrono::duration_cast(detect_end - detect_start); + + if (numTotFeatures == 0) + { + ALICEVISION_LOG_INFO("No descriptors loaded!!"); + return EXIT_FAILURE; + } + + ALICEVISION_LOG_INFO("Done! " << db.getSparseHistogramPerImage().size() << " sets of descriptors read for a total of " << numTotFeatures + << " features"); + ALICEVISION_LOG_INFO("Reading took " << detect_elapsed.count() << " sec"); + + if (!withWeights) + { + // compute and save the word weights + ALICEVISION_LOG_INFO("Computing weights..."); + db.computeTfIdfWeights(); + } + + // query documents for Statistics + std::map globalHisto; + + ALICEVISION_LOG_INFO("Getting some stats for " << querySfmDataFilename); + + sfmData::SfMData querySfmData; + if (!sfmDataIO::load(querySfmData, querySfmDataFilename, sfmDataIO::ESfMData::ALL)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" + querySfmDataFilename + "' cannot be read."); + return EXIT_FAILURE; + } + + aliceVision::voctree::voctreeStatistics(querySfmData, featuresFolders, tree, db, distance, globalHisto); + + std::cout << "-----------------" << std::endl; + + for (const auto& itHisto : globalHisto) + std::cout << itHisto.first << ": " << itHisto.second << ", "; + + std::cout << std::endl; + + return EXIT_SUCCESS; } diff --git a/src/software/utils/precisionEvaluationToGt.hpp b/src/software/utils/precisionEvaluationToGt.hpp index 8ae448f097..872ced00f5 100644 --- a/src/software/utils/precisionEvaluationToGt.hpp +++ b/src/software/utils/precisionEvaluationToGt.hpp @@ -20,315 +20,312 @@ namespace fs = std::filesystem; -namespace aliceVision -{ +namespace aliceVision { /// Compute a 5DOF rigid transform between the two camera trajectories -inline bool computeSimilarity( - const std::vector & vec_camPosGT, - const std::vector & vec_camPosComputed, - std::mt19937 &randomNumberGenerator, - std::vector & vec_camPosComputed_T, - double *Sout, Mat3 * Rout, Vec3 * tout) +inline bool computeSimilarity(const std::vector& vec_camPosGT, + const std::vector& vec_camPosComputed, + std::mt19937& randomNumberGenerator, + std::vector& vec_camPosComputed_T, + double* Sout, + Mat3* Rout, + Vec3* tout) { - if (vec_camPosGT.size() != vec_camPosComputed.size()) { - std::cerr << "Cannot perform registration, vector sizes are different" << std::endl; - return false; - } - - // Move input point in appropriate container - Mat x1(3, vec_camPosGT.size()); - Mat x2(3, vec_camPosGT.size()); - for (size_t i = 0; i < vec_camPosGT.size(); ++i) { - x1.col(i) = vec_camPosComputed[i]; - x2.col(i) = vec_camPosGT[i]; - } - // Compute rigid transformation p'i = S R pi + t - - double S; - Vec3 t; - Mat3 R; - std::vector inliers; - if(!aliceVision::geometry::ACRansac_FindRTS(x1, x2, randomNumberGenerator, S, t, R, inliers, true)) - return false; - - vec_camPosComputed_T.resize(vec_camPosGT.size()); - // std::vector vec_residualErrors(vec_camPosGT.size()); - for(size_t i = 0; i < vec_camPosGT.size(); ++i) - { - Vec3 newPos = S * R * (vec_camPosComputed[i]) + t; - vec_camPosComputed_T[i] = newPos; - // const double dResidual = (newPos - vec_camPosGT[i]).norm(); - // vec_residualErrors[i] = dResidual; - } - - *Sout = S; - *Rout = R; - *tout = t; - return true; + if (vec_camPosGT.size() != vec_camPosComputed.size()) + { + std::cerr << "Cannot perform registration, vector sizes are different" << std::endl; + return false; + } + + // Move input point in appropriate container + Mat x1(3, vec_camPosGT.size()); + Mat x2(3, vec_camPosGT.size()); + for (size_t i = 0; i < vec_camPosGT.size(); ++i) + { + x1.col(i) = vec_camPosComputed[i]; + x2.col(i) = vec_camPosGT[i]; + } + // Compute rigid transformation p'i = S R pi + t + + double S; + Vec3 t; + Mat3 R; + std::vector inliers; + if (!aliceVision::geometry::ACRansac_FindRTS(x1, x2, randomNumberGenerator, S, t, R, inliers, true)) + return false; + + vec_camPosComputed_T.resize(vec_camPosGT.size()); + // std::vector vec_residualErrors(vec_camPosGT.size()); + for (size_t i = 0; i < vec_camPosGT.size(); ++i) + { + Vec3 newPos = S * R * (vec_camPosComputed[i]) + t; + vec_camPosComputed_T[i] = newPos; + // const double dResidual = (newPos - vec_camPosGT[i]).norm(); + // vec_residualErrors[i] = dResidual; + } + + *Sout = S; + *Rout = R; + *tout = t; + return true; } /// Export to PLY two camera trajectories -inline bool exportToPly(const std::vector & vec_camPosGT, - const std::vector & vec_camPosComputed, - const std::string & sFileName) +inline bool exportToPly(const std::vector& vec_camPosGT, const std::vector& vec_camPosComputed, const std::string& sFileName) { - std::ofstream outfile; - outfile.open(sFileName, std::ios_base::out); - - outfile << "ply" - << '\n' << "format ascii 1.0" - << '\n' << "element vertex " << vec_camPosGT.size()+vec_camPosComputed.size() - << '\n' << "property float x" - << '\n' << "property float y" - << '\n' << "property float z" - << '\n' << "property uchar red" - << '\n' << "property uchar green" - << '\n' << "property uchar blue" - << '\n' << "end_header" << std::endl; - - for (size_t i=0; i < vec_camPosGT.size(); ++i) { - outfile << vec_camPosGT[i].transpose() - << " 0 255 0" << "\n"; - } - - for (size_t i=0; i < vec_camPosComputed.size(); ++i) { - outfile << vec_camPosComputed[i].transpose() - << " 255 255 0" << "\n"; - } - outfile.flush(); - bool bOk = outfile.good(); - outfile.close(); - return bOk; + std::ofstream outfile; + outfile.open(sFileName, std::ios_base::out); + + outfile << "ply" << '\n' + << "format ascii 1.0" << '\n' + << "element vertex " << vec_camPosGT.size() + vec_camPosComputed.size() << '\n' + << "property float x" << '\n' + << "property float y" << '\n' + << "property float z" << '\n' + << "property uchar red" << '\n' + << "property uchar green" << '\n' + << "property uchar blue" << '\n' + << "end_header" << std::endl; + + for (size_t i = 0; i < vec_camPosGT.size(); ++i) + { + outfile << vec_camPosGT[i].transpose() << " 0 255 0" + << "\n"; + } + + for (size_t i = 0; i < vec_camPosComputed.size(); ++i) + { + outfile << vec_camPosComputed[i].transpose() << " 255 255 0" + << "\n"; + } + outfile.flush(); + bool bOk = outfile.good(); + outfile.close(); + return bOk; } /// Compare two camera path (translation and rotation residual after a 5DOF rigid registration) /// Export computed statistics to a HTLM stream -inline void EvaluteToGT( - const std::vector & vec_camCenterGT, - const std::vector & vec_camCenterComputed, - const std::vector & vec_camRotGT, - const std::vector & vec_camRotComputed, - const std::string & sOutPath, - std::mt19937 &randomNumberGenerator, - htmlDocument::htmlDocumentStream * htmlDocStream - ) +inline void EvaluteToGT(const std::vector& vec_camCenterGT, + const std::vector& vec_camCenterComputed, + const std::vector& vec_camRotGT, + const std::vector& vec_camRotComputed, + const std::string& sOutPath, + std::mt19937& randomNumberGenerator, + htmlDocument::htmlDocumentStream* htmlDocStream) { - const std::size_t numCameras = vec_camCenterGT.size(); - assert(numCameras>0); - assert(numCameras == vec_camCenterComputed.size()); - assert(numCameras == vec_camRotGT.size()); - assert(numCameras == vec_camRotComputed.size()); - - // Compute global 3D similarity between the camera position - std::vector vec_camPosComputed_T; - Mat3 R; - Vec3 t; - double scale; - - computeSimilarity(vec_camCenterGT, vec_camCenterComputed, randomNumberGenerator, vec_camPosComputed_T, &scale, &R, &t); - - std::cout << "\nEstimated similarity transformation between the sequences\n"; - std::cout << "R\n" << R << std::endl; - std::cout << "t\n" << t << std::endl; - std::cout << "scale\n" << scale << std::endl; - - // Compute statistics and export them - // -a. distance between camera center - // -b. angle between rotation matrix - - // -a. distance between camera center - double trajectoryLength = 0; - std::vector vec_baselineErrors; - for(std::size_t i = 0; i < numCameras; ++i) - { - const double dResidual = (vec_camCenterGT[i] - vec_camPosComputed_T[i]).norm(); - vec_baselineErrors.push_back(dResidual); - if(i > 0 && i < numCameras-2) - trajectoryLength += (vec_camCenterGT[i] - vec_camCenterGT[i+1]).norm(); - } - - std::cout << std::endl << "\nTrajectory length: " << trajectoryLength ; - - // -b. angle between rotation matrix - std::vector vec_angularErrors; - { - std::vector::const_iterator iter1 = vec_camRotGT.begin(); - for (std::vector::const_iterator iter2 = vec_camRotComputed.begin(); - iter2 != vec_camRotComputed.end(); ++iter2, ++iter1) { - const Mat3 R1 = *iter1; //GT - const Mat3 R2T = *iter2 * R.transpose(); // Computed - - const double angularErrorDegree = radianToDegree(getRotationMagnitude(R1 * R2T.transpose())); - vec_angularErrors.push_back(angularErrorDegree); - } - } - - // Display residual errors : -// std::cout << "\nBaseline residuals (in GT unit)\n"; -// copy(vec_baselineErrors.begin(), vec_baselineErrors.end(), std::ostream_iterator(std::cout, " , ")); -// std::cout << "\nAngular residuals (Degree) \n"; -// copy(vec_angularErrors.begin(), vec_angularErrors.end(), std::ostream_iterator(std::cout, " , ")); - - BoxStats statsBaseline(vec_baselineErrors.begin(), vec_baselineErrors.end()); - std::cout << std::endl << "\nBaseline error statistics:\n" << statsBaseline; - - BoxStats statsAngular(vec_angularErrors.begin(), vec_angularErrors.end()); - std::cout << std::endl << "\nAngular error statistics:\n" << statsAngular; - - // Export camera position (viewable) - exportToPly(vec_camCenterGT, vec_camPosComputed_T, (fs::path(sOutPath) / "camera_Registered.ply").string()); - - exportToPly(vec_camCenterGT, vec_camCenterComputed, (fs::path(sOutPath) / "camera_original.ply").string()); - - //-- Export residual to the HTML report - { - using namespace htmlDocument; - const std::string sNewLine = "
"; - const std::string sFullLine = "
"; - - htmlDocStream->pushInfo(sFullLine); - htmlDocStream->pushInfo(htmlMarkup("h1", "Compare GT camera position and looking direction.")); - htmlDocStream->pushInfo(" Display per camera after a 3D similarity estimation:
"); - htmlDocStream->pushInfo("
  • Baseline_Residual -> localization error of camera center to GT (in GT unit),
  • "); - htmlDocStream->pushInfo("
  • Angular_residuals -> direction error as an angular degree error.
"); - - htmlDocStream->pushInfo(htmlMarkup("h2","Baseline errors")); - std::ostringstream os; - os << "Baseline_Residual=["; - std::copy(vec_baselineErrors.begin(), vec_baselineErrors.end(), std::ostream_iterator(os, " ")); - os <<"];"; - htmlDocStream->pushInfo(sFullLine); - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "min = " << statsBaseline.min; - htmlDocStream->pushInfo(sFullLine); - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "max = " << statsBaseline.max; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "mean = " << statsBaseline.mean; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "median = " << statsBaseline.median; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - const double maxRange = *std::max_element(vec_baselineErrors.begin(), vec_baselineErrors.end()); - utils::Histogram baselineHistogram(0.0, maxRange, 50); - baselineHistogram.Add(vec_baselineErrors.begin(), vec_baselineErrors.end()); - - svg::svgHisto svg_BaselineHistogram; - svg_BaselineHistogram.draw(baselineHistogram.GetHist(), std::pair(0.f, maxRange), - (fs::path(sOutPath) / "baseline_histogram.svg").string(), - 600, 200); - - os.str(""); - os << sNewLine << "Baseline errors histogram" << sNewLine; - os << "\n"; - htmlDocStream->pushInfo(os.str()); - + const std::size_t numCameras = vec_camCenterGT.size(); + assert(numCameras > 0); + assert(numCameras == vec_camCenterComputed.size()); + assert(numCameras == vec_camRotGT.size()); + assert(numCameras == vec_camRotComputed.size()); + + // Compute global 3D similarity between the camera position + std::vector vec_camPosComputed_T; + Mat3 R; + Vec3 t; + double scale; + + computeSimilarity(vec_camCenterGT, vec_camCenterComputed, randomNumberGenerator, vec_camPosComputed_T, &scale, &R, &t); + + std::cout << "\nEstimated similarity transformation between the sequences\n"; + std::cout << "R\n" << R << std::endl; + std::cout << "t\n" << t << std::endl; + std::cout << "scale\n" << scale << std::endl; + + // Compute statistics and export them + // -a. distance between camera center + // -b. angle between rotation matrix + + // -a. distance between camera center + double trajectoryLength = 0; + std::vector vec_baselineErrors; + for (std::size_t i = 0; i < numCameras; ++i) { - std::vector xvalues(vec_baselineErrors.size()); - std::iota(xvalues.begin(), xvalues.end(), 0); - std::pair< std::pair, std::pair > range = - autoJSXGraphViewport(xvalues, vec_baselineErrors); - range.first.first = 0; - range.first.second = xvalues.size()+1; - htmlDocument::JSXGraphWrapper jsxGraph; - jsxGraph.init("baselineErrors",1000,300); - jsxGraph.addXYChart(xvalues, vec_baselineErrors, "line,point"); - jsxGraph.UnsuspendUpdate(); - jsxGraph.setViewport(range); - jsxGraph.close(); - htmlDocStream->pushInfo(jsxGraph.toStr()); - + const double dResidual = (vec_camCenterGT[i] - vec_camPosComputed_T[i]).norm(); + vec_baselineErrors.push_back(dResidual); + if (i > 0 && i < numCameras - 2) + trajectoryLength += (vec_camCenterGT[i] - vec_camCenterGT[i + 1]).norm(); } - htmlDocStream->pushInfo(sFullLine); - - htmlDocStream->pushInfo(htmlMarkup("h2","Angular errors")); - os.str(""); - os << "Angular_residuals=["; - std::copy(vec_angularErrors.begin(), vec_angularErrors.end(), std::ostream_iterator(os, " ")); - os <<"];"; - htmlDocStream->pushInfo("
"); - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "min = " << statsAngular.min; - htmlDocStream->pushInfo(sFullLine); - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "max = " << statsAngular.max; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "mean = " << statsAngular.mean; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - os.str(""); - os << "median = " << statsAngular.median; - htmlDocStream->pushInfo( htmlDocument::htmlMarkup("pre", os.str())); - - const double maxRangeAngular = *std::max_element(vec_angularErrors.begin(), vec_angularErrors.end()); - utils::Histogram angularHistogram(0.0, maxRangeAngular, 50); - angularHistogram.Add(vec_angularErrors.begin(), vec_angularErrors.end()); - - svg::svgHisto svg_AngularHistogram; - svg_AngularHistogram.draw(angularHistogram.GetHist(), std::pair(0.f, maxRangeAngular), - (fs::path(sOutPath) / "angular_histogram.svg").string(), - 600, 200); - - os.str(""); - os << sNewLine << "Angular errors histogram" << sNewLine; - os << "\n"; - htmlDocStream->pushInfo(os.str()); - + + std::cout << std::endl << "\nTrajectory length: " << trajectoryLength; + + // -b. angle between rotation matrix + std::vector vec_angularErrors; { - std::vector xvalues(vec_angularErrors.size()); - std::iota(xvalues.begin(), xvalues.end(), 0); - std::pair< std::pair, std::pair > range = - autoJSXGraphViewport(xvalues, vec_angularErrors); - range.first.first = 0; - range.first.second = xvalues.size()+1; - htmlDocument::JSXGraphWrapper jsxGraph; - jsxGraph.init("AngularErrors", 1000, 300); - jsxGraph.addXYChart(xvalues, vec_angularErrors, "line,point"); - jsxGraph.UnsuspendUpdate(); - jsxGraph.setViewport(range); - jsxGraph.close(); - htmlDocStream->pushInfo(jsxGraph.toStr()); + std::vector::const_iterator iter1 = vec_camRotGT.begin(); + for (std::vector::const_iterator iter2 = vec_camRotComputed.begin(); iter2 != vec_camRotComputed.end(); ++iter2, ++iter1) + { + const Mat3 R1 = *iter1; // GT + const Mat3 R2T = *iter2 * R.transpose(); // Computed + + const double angularErrorDegree = radianToDegree(getRotationMagnitude(R1 * R2T.transpose())); + vec_angularErrors.push_back(angularErrorDegree); + } + } + // Display residual errors : + // std::cout << "\nBaseline residuals (in GT unit)\n"; + // copy(vec_baselineErrors.begin(), vec_baselineErrors.end(), std::ostream_iterator(std::cout, " , ")); + // std::cout << "\nAngular residuals (Degree) \n"; + // copy(vec_angularErrors.begin(), vec_angularErrors.end(), std::ostream_iterator(std::cout, " , ")); + + BoxStats statsBaseline(vec_baselineErrors.begin(), vec_baselineErrors.end()); + std::cout << std::endl << "\nBaseline error statistics:\n" << statsBaseline; + + BoxStats statsAngular(vec_angularErrors.begin(), vec_angularErrors.end()); + std::cout << std::endl << "\nAngular error statistics:\n" << statsAngular; + + // Export camera position (viewable) + exportToPly(vec_camCenterGT, vec_camPosComputed_T, (fs::path(sOutPath) / "camera_Registered.ply").string()); + + exportToPly(vec_camCenterGT, vec_camCenterComputed, (fs::path(sOutPath) / "camera_original.ply").string()); + + //-- Export residual to the HTML report + { + using namespace htmlDocument; + const std::string sNewLine = "
"; + const std::string sFullLine = "
"; + + htmlDocStream->pushInfo(sFullLine); + htmlDocStream->pushInfo(htmlMarkup("h1", "Compare GT camera position and looking direction.")); + htmlDocStream->pushInfo(" Display per camera after a 3D similarity estimation:
"); + htmlDocStream->pushInfo("
  • Baseline_Residual -> localization error of camera center to GT (in GT unit),
  • "); + htmlDocStream->pushInfo("
  • Angular_residuals -> direction error as an angular degree error.
"); + + htmlDocStream->pushInfo(htmlMarkup("h2", "Baseline errors")); + std::ostringstream os; + os << "Baseline_Residual=["; + std::copy(vec_baselineErrors.begin(), vec_baselineErrors.end(), std::ostream_iterator(os, " ")); + os << "];"; + htmlDocStream->pushInfo(sFullLine); + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "min = " << statsBaseline.min; + htmlDocStream->pushInfo(sFullLine); + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "max = " << statsBaseline.max; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "mean = " << statsBaseline.mean; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "median = " << statsBaseline.median; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + const double maxRange = *std::max_element(vec_baselineErrors.begin(), vec_baselineErrors.end()); + utils::Histogram baselineHistogram(0.0, maxRange, 50); + baselineHistogram.Add(vec_baselineErrors.begin(), vec_baselineErrors.end()); + + svg::svgHisto svg_BaselineHistogram; + svg_BaselineHistogram.draw( + baselineHistogram.GetHist(), std::pair(0.f, maxRange), (fs::path(sOutPath) / "baseline_histogram.svg").string(), 600, 200); + + os.str(""); + os << sNewLine << "Baseline errors histogram" << sNewLine; + os << "\n"; + htmlDocStream->pushInfo(os.str()); + + { + std::vector xvalues(vec_baselineErrors.size()); + std::iota(xvalues.begin(), xvalues.end(), 0); + std::pair, std::pair> range = autoJSXGraphViewport(xvalues, vec_baselineErrors); + range.first.first = 0; + range.first.second = xvalues.size() + 1; + htmlDocument::JSXGraphWrapper jsxGraph; + jsxGraph.init("baselineErrors", 1000, 300); + jsxGraph.addXYChart(xvalues, vec_baselineErrors, "line,point"); + jsxGraph.UnsuspendUpdate(); + jsxGraph.setViewport(range); + jsxGraph.close(); + htmlDocStream->pushInfo(jsxGraph.toStr()); + } + htmlDocStream->pushInfo(sFullLine); + + htmlDocStream->pushInfo(htmlMarkup("h2", "Angular errors")); + os.str(""); + os << "Angular_residuals=["; + std::copy(vec_angularErrors.begin(), vec_angularErrors.end(), std::ostream_iterator(os, " ")); + os << "];"; + htmlDocStream->pushInfo("
"); + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "min = " << statsAngular.min; + htmlDocStream->pushInfo(sFullLine); + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "max = " << statsAngular.max; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "mean = " << statsAngular.mean; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + os.str(""); + os << "median = " << statsAngular.median; + htmlDocStream->pushInfo(htmlDocument::htmlMarkup("pre", os.str())); + + const double maxRangeAngular = *std::max_element(vec_angularErrors.begin(), vec_angularErrors.end()); + utils::Histogram angularHistogram(0.0, maxRangeAngular, 50); + angularHistogram.Add(vec_angularErrors.begin(), vec_angularErrors.end()); + + svg::svgHisto svg_AngularHistogram; + svg_AngularHistogram.draw(angularHistogram.GetHist(), + std::pair(0.f, maxRangeAngular), + (fs::path(sOutPath) / "angular_histogram.svg").string(), + 600, + 200); + + os.str(""); + os << sNewLine << "Angular errors histogram" << sNewLine; + os << "\n"; + htmlDocStream->pushInfo(os.str()); + + { + std::vector xvalues(vec_angularErrors.size()); + std::iota(xvalues.begin(), xvalues.end(), 0); + std::pair, std::pair> range = autoJSXGraphViewport(xvalues, vec_angularErrors); + range.first.first = 0; + range.first.second = xvalues.size() + 1; + htmlDocument::JSXGraphWrapper jsxGraph; + jsxGraph.init("AngularErrors", 1000, 300); + jsxGraph.addXYChart(xvalues, vec_angularErrors, "line,point"); + jsxGraph.UnsuspendUpdate(); + jsxGraph.setViewport(range); + jsxGraph.close(); + htmlDocStream->pushInfo(jsxGraph.toStr()); + } + htmlDocStream->pushInfo(sFullLine); } - htmlDocStream->pushInfo(sFullLine); - } } // Find a file in a list and return the index, or -1 if nothing found. // Handle relative/absolute paths inline int findIdGT(const std::string& file, const std::vector& filelist) { - int result = -1; - std::string file_relative = fs::path(file).filename().string(); - for(unsigned int i = 0; i < filelist.size(); ++i) - { - if(file_relative.compare(fs::path(filelist[i]).stem().string()) == 0) + int result = -1; + std::string file_relative = fs::path(file).filename().string(); + for (unsigned int i = 0; i < filelist.size(); ++i) { - result = i; - break; + if (file_relative.compare(fs::path(filelist[i]).stem().string()) == 0) + { + result = i; + break; + } } - } - return result; + return result; } -} //namespace aliceVision +} // namespace aliceVision diff --git a/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.cpp b/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.cpp index e7e158a9e8..07f5b0958b 100644 --- a/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.cpp +++ b/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.cpp @@ -16,7 +16,7 @@ #include #include #include -//load features per view +// load features per view #include // feature matches #include @@ -55,525 +55,510 @@ typedef std::vector featsT; EHistogramSelectionMethod EHistogramSelectionMethod_stringToEnum(const std::string& histogramSelectionMethod) { - if(histogramSelectionMethod == "full_frame") return EHistogramSelectionMethod::eHistogramHarmonizeFullFrame; - if(histogramSelectionMethod == "matched_points") return EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints; - if(histogramSelectionMethod == "VLD_segments") return EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment; - - throw std::invalid_argument("Invalid selection method: " + histogramSelectionMethod); + if (histogramSelectionMethod == "full_frame") + return EHistogramSelectionMethod::eHistogramHarmonizeFullFrame; + if (histogramSelectionMethod == "matched_points") + return EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints; + if (histogramSelectionMethod == "VLD_segments") + return EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment; + + throw std::invalid_argument("Invalid selection method: " + histogramSelectionMethod); } std::string EHistogramSelectionMethod_enumToString(const EHistogramSelectionMethod histogramSelectionMethod) { - if(histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeFullFrame) return "full_frame"; - if(histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints) return "matched_points"; - if(histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment) return "VLD_segments"; - - throw std::invalid_argument("Unrecognized EHistogramSelectionMethod: " + std::to_string(int(histogramSelectionMethod))); + if (histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeFullFrame) + return "full_frame"; + if (histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints) + return "matched_points"; + if (histogramSelectionMethod == EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment) + return "VLD_segments"; + + throw std::invalid_argument("Unrecognized EHistogramSelectionMethod: " + std::to_string(int(histogramSelectionMethod))); } -std::ostream& operator<<(std::ostream& os, EHistogramSelectionMethod p) -{ - return os << EHistogramSelectionMethod_enumToString(p); -} +std::ostream& operator<<(std::ostream& os, EHistogramSelectionMethod p) { return os << EHistogramSelectionMethod_enumToString(p); } std::istream& operator>>(std::istream& in, EHistogramSelectionMethod& p) { - std::string token; - in >> token; - p = EHistogramSelectionMethod_stringToEnum(token); - return in; + std::string token; + in >> token; + p = EHistogramSelectionMethod_stringToEnum(token); + return in; } -ColorHarmonizationEngineGlobal::ColorHarmonizationEngineGlobal( - const std::string& sfmDataFilename, - const std::vector& featuresFolders, - const std::vector& matchesFolders, - const std::string& outputDirectory, - const std::vector& descTypes, - EHistogramSelectionMethod selectionMethod, - int imgRef) - : _sfmDataFilename(sfmDataFilename) - , _featuresFolders(featuresFolders) - , _matchesFolders(matchesFolders) - , _outputDirectory(outputDirectory) - , _descTypes(descTypes) - , _selectionMethod(selectionMethod) - , _imgRef(imgRef) +ColorHarmonizationEngineGlobal::ColorHarmonizationEngineGlobal(const std::string& sfmDataFilename, + const std::vector& featuresFolders, + const std::vector& matchesFolders, + const std::string& outputDirectory, + const std::vector& descTypes, + EHistogramSelectionMethod selectionMethod, + int imgRef) + : _sfmDataFilename(sfmDataFilename), + _featuresFolders(featuresFolders), + _matchesFolders(matchesFolders), + _outputDirectory(outputDirectory), + _descTypes(descTypes), + _selectionMethod(selectionMethod), + _imgRef(imgRef) { - if(!fs::exists(outputDirectory)) - fs::create_directory(outputDirectory); - + if (!fs::exists(outputDirectory)) + fs::create_directory(outputDirectory); } -ColorHarmonizationEngineGlobal::~ColorHarmonizationEngineGlobal() -{} +ColorHarmonizationEngineGlobal::~ColorHarmonizationEngineGlobal() {} inline void pauseProcess() { - unsigned char i; - std::cout << "\nPause : type key and press enter: "; - std::cin >> i; + unsigned char i; + std::cout << "\nPause : type key and press enter: "; + std::cin >> i; } - bool ColorHarmonizationEngineGlobal::Process() { - const std::string vec_harmonizeMethod[ 1 ] = { "quantifiedGainCompensation" }; - const int harmonizeMethod = 0; - - //------------------- - // Load data - //------------------- - - if( !ReadInputData() ) - return false; - if( _pairwiseMatches.empty() ) - { - std::cout << std::endl << "Matches file is empty" << std::endl; - return false; - } - - //-- Remove EG with poor support: - for (matching::PairwiseMatches::iterator matchesPerViewIt = _pairwiseMatches.begin(); - matchesPerViewIt != _pairwiseMatches.end(); - ) - { - if (matchesPerViewIt->second.getNbAllMatches() < 120) + const std::string vec_harmonizeMethod[1] = {"quantifiedGainCompensation"}; + const int harmonizeMethod = 0; + + //------------------- + // Load data + //------------------- + + if (!ReadInputData()) + return false; + if (_pairwiseMatches.empty()) { - matchesPerViewIt = _pairwiseMatches.erase(matchesPerViewIt); + std::cout << std::endl << "Matches file is empty" << std::endl; + return false; } - else + + //-- Remove EG with poor support: + for (matching::PairwiseMatches::iterator matchesPerViewIt = _pairwiseMatches.begin(); matchesPerViewIt != _pairwiseMatches.end();) { - ++matchesPerViewIt; + if (matchesPerViewIt->second.getNbAllMatches() < 120) + { + matchesPerViewIt = _pairwiseMatches.erase(matchesPerViewIt); + } + else + { + ++matchesPerViewIt; + } } - } - { - graph::indexedGraph putativeGraph(matching::getImagePairs(_pairwiseMatches)); + { + graph::indexedGraph putativeGraph(matching::getImagePairs(_pairwiseMatches)); - // Save the graph before cleaning: - graph::exportToGraphvizData( - (fs::path(_outputDirectory) / "input_graph_poor_supportRemoved").string(), - putativeGraph.g); - } - - //------------------- - // Keep the largest CC in the image graph - //------------------- - if (!CleanGraph()) - { - std::cout << std::endl << "There is no largest CC in the graph" << std::endl; - return false; - } - - //------------------- - // Compute remaining camera node Id - //------------------- - - std::map map_cameraNodeToCameraIndex; // graph node Id to 0->Ncam - std::map map_cameraIndexTocameraNode; // 0->Ncam correspondance to graph node Id - std::set set_indeximage; - - for (size_t i = 0; i < _pairwiseMatches.size(); ++i) - { - matching::PairwiseMatches::const_iterator iter = _pairwiseMatches.begin(); - std::advance(iter, i); - - const size_t I = iter->first.first; - const size_t J = iter->first.second; - set_indeximage.insert(I); - set_indeximage.insert(J); - } - - for (std::set::const_iterator iterSet = set_indeximage.begin(); - iterSet != set_indeximage.end(); ++iterSet) - { - map_cameraIndexTocameraNode[std::distance(set_indeximage.begin(), iterSet)] = *iterSet; - map_cameraNodeToCameraIndex[*iterSet] = std::distance(set_indeximage.begin(), iterSet); - } - - std::cout << "\nRemaining cameras after CC filter : \n" - << map_cameraIndexTocameraNode.size() << " from a total of " << _fileNames.size() << std::endl; - - size_t bin = 256; - double minvalue = 0.0; - double maxvalue = 255.0; - - // For each edge computes the selection masks and histograms (for the RGB channels) - std::vector map_relativeHistograms[3]; - map_relativeHistograms[0].resize(_pairwiseMatches.size()); - map_relativeHistograms[1].resize(_pairwiseMatches.size()); - map_relativeHistograms[2].resize(_pairwiseMatches.size()); - - for (size_t i = 0; i < _pairwiseMatches.size(); ++i) - { - matching::PairwiseMatches::const_iterator iter = _pairwiseMatches.begin(); - std::advance(iter, i); - - const size_t viewI = iter->first.first; - const size_t viewJ = iter->first.second; - - // - const MatchesPerDescType& matchesPerDesc = iter->second; - - //-- Edges names: - std::pair< std::string, std::string > p_imaNames; - p_imaNames = make_pair( _fileNames[ viewI ], _fileNames[ viewJ ] ); - std::cout << "Current edge : " - << fs::path(p_imaNames.first).filename().string() << "\t" - << fs::path(p_imaNames.second).filename().string() << std::endl; - - //-- Compute the masks from the data selection: - Image< unsigned char > maskI ( _imageSize[ viewI ].first, _imageSize[ viewI ].second ); - Image< unsigned char > maskJ ( _imageSize[ viewJ ].first, _imageSize[ viewJ ].second ); - - switch(_selectionMethod) + // Save the graph before cleaning: + graph::exportToGraphvizData((fs::path(_outputDirectory) / "input_graph_poor_supportRemoved").string(), putativeGraph.g); + } + + //------------------- + // Keep the largest CC in the image graph + //------------------- + if (!CleanGraph()) { - case EHistogramSelectionMethod::eHistogramHarmonizeFullFrame: - { - colorHarmonization::CommonDataByPair_fullFrame dataSelector( - p_imaNames.first, - p_imaNames.second); - dataSelector.computeMask( maskI, maskJ ); - } - break; - case EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints: - { - int circleSize = 10; - colorHarmonization::CommonDataByPair_matchedPoints dataSelector( - p_imaNames.first, - p_imaNames.second, - matchesPerDesc, - _regionsPerView.getRegionsPerDesc(viewI), - _regionsPerView.getRegionsPerDesc(viewJ), - circleSize); - dataSelector.computeMask( maskI, maskJ ); - } - break; - case EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment: - { - maskI.fill(0); - maskJ.fill(0); - - for(const auto& matchesIt: matchesPerDesc) - { - const feature::EImageDescriberType descType = matchesIt.first; - const IndMatches& matches = matchesIt.second; - colorHarmonization::CommonDataByPair_vldSegment dataSelector( - p_imaNames.first, - p_imaNames.second, - matches, - _regionsPerView.getRegions(viewI, descType).Features(), - _regionsPerView.getRegions(viewJ, descType).Features()); - - dataSelector.computeMask( maskI, maskJ ); - } - } - break; - default: - std::cout << "Selection method unsupported" << std::endl; + std::cout << std::endl << "There is no largest CC in the graph" << std::endl; return false; } - //-- Export the masks - bool bExportMask = false; - if (bExportMask) - { - std::string sEdge = _fileNames[ viewI ] + "_" + _fileNames[ viewJ ]; - sEdge = (fs::path(_outputDirectory) / sEdge ).string(); + //------------------- + // Compute remaining camera node Id + //------------------- - if( !fs::exists(sEdge) ) - fs::create_directory(sEdge); + std::map map_cameraNodeToCameraIndex; // graph node Id to 0->Ncam + std::map map_cameraIndexTocameraNode; // 0->Ncam correspondance to graph node Id + std::set set_indeximage; - std::string out_filename_I = "00_mask_I.png"; - out_filename_I = (fs::path(sEdge) / out_filename_I).string(); + for (size_t i = 0; i < _pairwiseMatches.size(); ++i) + { + matching::PairwiseMatches::const_iterator iter = _pairwiseMatches.begin(); + std::advance(iter, i); - std::string out_filename_J = "00_mask_J.png"; - out_filename_J = (fs::path(sEdge) / out_filename_J).string(); - writeImage(out_filename_I, maskI, image::ImageWriteOptions()); - writeImage(out_filename_J, maskJ, image::ImageWriteOptions()); + const size_t I = iter->first.first; + const size_t J = iter->first.second; + set_indeximage.insert(I); + set_indeximage.insert(J); } - //-- Compute the histograms - Image< RGBColor > imageI, imageJ; - readImage(p_imaNames.first, imageI, image::EImageColorSpace::LINEAR); - readImage(p_imaNames.second, imageJ, image::EImageColorSpace::LINEAR); - - utils::Histogram< double > histoI( minvalue, maxvalue, bin); - utils::Histogram< double > histoJ( minvalue, maxvalue, bin); - - int channelIndex = 0; // RED channel - colorHarmonization::CommonDataByPair::computeHisto( histoI, maskI, channelIndex, imageI ); - colorHarmonization::CommonDataByPair::computeHisto( histoJ, maskJ, channelIndex, imageJ ); - relativeColorHistogramEdge & edgeR = map_relativeHistograms[channelIndex][i]; - edgeR = relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], - histoI.GetHist(), histoJ.GetHist()); - - histoI = histoJ = utils::Histogram(minvalue, maxvalue, bin); - channelIndex = 1; // GREEN channel - colorHarmonization::CommonDataByPair::computeHisto( histoI, maskI, channelIndex, imageI ); - colorHarmonization::CommonDataByPair::computeHisto( histoJ, maskJ, channelIndex, imageJ ); - relativeColorHistogramEdge & edgeG = map_relativeHistograms[channelIndex][i]; - edgeG = relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], - histoI.GetHist(), histoJ.GetHist()); - - histoI = histoJ = utils::Histogram(minvalue, maxvalue, bin); - channelIndex = 2; // BLUE channel - colorHarmonization::CommonDataByPair::computeHisto( histoI, maskI, channelIndex, imageI ); - colorHarmonization::CommonDataByPair::computeHisto( histoJ, maskJ, channelIndex, imageJ ); - relativeColorHistogramEdge & edgeB = map_relativeHistograms[channelIndex][i]; - edgeB = relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], - histoI.GetHist(), histoJ.GetHist()); - } - - std::cout << "\n -- \n SOLVE for color consistency with linear programming\n --" << std::endl; - //-- Solve for the gains and offsets: - std::vector vec_indexToFix; - vec_indexToFix.push_back(map_cameraNodeToCameraIndex[_imgRef]); - - using namespace aliceVision::linearProgramming; - - std::vector vec_solution_r(_fileNames.size() * 2 + 1); - std::vector vec_solution_g(_fileNames.size() * 2 + 1); - std::vector vec_solution_b(_fileNames.size() * 2 + 1); - - aliceVision::system::Timer timer; - - #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_MOSEK) - typedef MOSEKSolver SOLVER_LP_T; - #else - typedef OSI_CISolverWrapper SOLVER_LP_T; - #endif - // Red channel - { - SOLVER_LP_T lpSolver(vec_solution_r.size()); - - GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[0], vec_indexToFix); - LPConstraintsSparse constraint; - cstBuilder.Build(constraint); - lpSolver.setup(constraint); - lpSolver.solve(); - lpSolver.getSolution(vec_solution_r); - } - // Green channel - { - SOLVER_LP_T lpSolver(vec_solution_g.size()); - - GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[1], vec_indexToFix); - LPConstraintsSparse constraint; - cstBuilder.Build(constraint); - lpSolver.setup(constraint); - lpSolver.solve(); - lpSolver.getSolution(vec_solution_g); - } - // Blue channel - { - SOLVER_LP_T lpSolver(vec_solution_b.size()); - - GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[2], vec_indexToFix); - LPConstraintsSparse constraint; - cstBuilder.Build(constraint); - lpSolver.setup(constraint); - lpSolver.solve(); - lpSolver.getSolution(vec_solution_b); - } - - std::cout << std::endl - << " ColorHarmonization solving on a graph with: " << _pairwiseMatches.size() << " edges took (s): " - << timer.elapsed() << std::endl - << "LInfinity fitting error: \n" - << "- for the red channel is: " << vec_solution_r.back() << " gray level(s)" <(std::cout, " ")); - - std::cout << "\n\nFound solution_g:\n"; - std::copy(vec_solution_g.begin(), vec_solution_g.end(), std::ostream_iterator(std::cout, " ")); - - std::cout << "\n\nFound solution_b:\n"; - std::copy(vec_solution_b.begin(), vec_solution_b.end(), std::ostream_iterator(std::cout, " ")); - std::cout << std::endl; - - std::cout << "\n\nThere is :\n" << set_indeximage.size() << " images to transform." << std::endl; - - //-> convert solution to gain offset and creation of the LUT per image - auto progressDisplay = system::createConsoleProgressDisplay(set_indeximage.size(), std::cout); - for (std::set::const_iterator iterSet = set_indeximage.begin(); - iterSet != set_indeximage.end(); ++iterSet, ++progressDisplay) - { - const size_t imaNum = *iterSet; - typedef Eigen::Matrix Vec256; - std::vector< Vec256 > vec_map_lut(3); - - const size_t nodeIndex = std::distance(set_indeximage.begin(), iterSet); - - const double g_r = vec_solution_r[nodeIndex*2]; - const double offset_r = vec_solution_r[nodeIndex*2+1]; - const double g_g = vec_solution_g[nodeIndex*2]; - const double offset_g = vec_solution_g[nodeIndex*2+1]; - const double g_b = vec_solution_b[nodeIndex*2]; - const double offset_b = vec_solution_b[nodeIndex*2+1]; - - for( size_t k = 0; k < 256; ++k) + for (std::set::const_iterator iterSet = set_indeximage.begin(); iterSet != set_indeximage.end(); ++iterSet) { - vec_map_lut[0][k] = clamp( k * g_r + offset_r, 0., 255. ); - vec_map_lut[1][k] = clamp( k * g_g + offset_g, 0., 255. ); - vec_map_lut[2][k] = clamp( k * g_b + offset_b, 0., 255. ); + map_cameraIndexTocameraNode[std::distance(set_indeximage.begin(), iterSet)] = *iterSet; + map_cameraNodeToCameraIndex[*iterSet] = std::distance(set_indeximage.begin(), iterSet); } - Image< RGBColor > image_c; - readImage( _fileNames[ imaNum ], image_c , image::EImageColorSpace::LINEAR); + std::cout << "\nRemaining cameras after CC filter : \n" + << map_cameraIndexTocameraNode.size() << " from a total of " << _fileNames.size() << std::endl; - #pragma omp parallel for - for( int j = 0; j < image_c.height(); ++j ) + size_t bin = 256; + double minvalue = 0.0; + double maxvalue = 255.0; + + // For each edge computes the selection masks and histograms (for the RGB channels) + std::vector map_relativeHistograms[3]; + map_relativeHistograms[0].resize(_pairwiseMatches.size()); + map_relativeHistograms[1].resize(_pairwiseMatches.size()); + map_relativeHistograms[2].resize(_pairwiseMatches.size()); + + for (size_t i = 0; i < _pairwiseMatches.size(); ++i) { - for( int i = 0; i < image_c.width(); ++i ) - { - image_c(j, i)[0] = clamp(vec_map_lut[0][image_c(j, i)[0]], 0., 255.); - image_c(j, i)[1] = clamp(vec_map_lut[1][image_c(j, i)[1]], 0., 255.); - image_c(j, i)[2] = clamp(vec_map_lut[2][image_c(j, i)[2]], 0., 255.); - } + matching::PairwiseMatches::const_iterator iter = _pairwiseMatches.begin(); + std::advance(iter, i); + + const size_t viewI = iter->first.first; + const size_t viewJ = iter->first.second; + + // + const MatchesPerDescType& matchesPerDesc = iter->second; + + //-- Edges names: + std::pair p_imaNames; + p_imaNames = make_pair(_fileNames[viewI], _fileNames[viewJ]); + std::cout << "Current edge : " << fs::path(p_imaNames.first).filename().string() << "\t" << fs::path(p_imaNames.second).filename().string() + << std::endl; + + //-- Compute the masks from the data selection: + Image maskI(_imageSize[viewI].first, _imageSize[viewI].second); + Image maskJ(_imageSize[viewJ].first, _imageSize[viewJ].second); + + switch (_selectionMethod) + { + case EHistogramSelectionMethod::eHistogramHarmonizeFullFrame: + { + colorHarmonization::CommonDataByPair_fullFrame dataSelector(p_imaNames.first, p_imaNames.second); + dataSelector.computeMask(maskI, maskJ); + } + break; + case EHistogramSelectionMethod::eHistogramHarmonizeMatchedPoints: + { + int circleSize = 10; + colorHarmonization::CommonDataByPair_matchedPoints dataSelector(p_imaNames.first, + p_imaNames.second, + matchesPerDesc, + _regionsPerView.getRegionsPerDesc(viewI), + _regionsPerView.getRegionsPerDesc(viewJ), + circleSize); + dataSelector.computeMask(maskI, maskJ); + } + break; + case EHistogramSelectionMethod::eHistogramHarmonizeVLDSegment: + { + maskI.fill(0); + maskJ.fill(0); + + for (const auto& matchesIt : matchesPerDesc) + { + const feature::EImageDescriberType descType = matchesIt.first; + const IndMatches& matches = matchesIt.second; + colorHarmonization::CommonDataByPair_vldSegment dataSelector(p_imaNames.first, + p_imaNames.second, + matches, + _regionsPerView.getRegions(viewI, descType).Features(), + _regionsPerView.getRegions(viewJ, descType).Features()); + + dataSelector.computeMask(maskI, maskJ); + } + } + break; + default: + std::cout << "Selection method unsupported" << std::endl; + return false; + } + + //-- Export the masks + bool bExportMask = false; + if (bExportMask) + { + std::string sEdge = _fileNames[viewI] + "_" + _fileNames[viewJ]; + sEdge = (fs::path(_outputDirectory) / sEdge).string(); + + if (!fs::exists(sEdge)) + fs::create_directory(sEdge); + + std::string out_filename_I = "00_mask_I.png"; + out_filename_I = (fs::path(sEdge) / out_filename_I).string(); + + std::string out_filename_J = "00_mask_J.png"; + out_filename_J = (fs::path(sEdge) / out_filename_J).string(); + writeImage(out_filename_I, maskI, image::ImageWriteOptions()); + writeImage(out_filename_J, maskJ, image::ImageWriteOptions()); + } + + //-- Compute the histograms + Image imageI, imageJ; + readImage(p_imaNames.first, imageI, image::EImageColorSpace::LINEAR); + readImage(p_imaNames.second, imageJ, image::EImageColorSpace::LINEAR); + + utils::Histogram histoI(minvalue, maxvalue, bin); + utils::Histogram histoJ(minvalue, maxvalue, bin); + + int channelIndex = 0; // RED channel + colorHarmonization::CommonDataByPair::computeHisto(histoI, maskI, channelIndex, imageI); + colorHarmonization::CommonDataByPair::computeHisto(histoJ, maskJ, channelIndex, imageJ); + relativeColorHistogramEdge& edgeR = map_relativeHistograms[channelIndex][i]; + edgeR = + relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], histoI.GetHist(), histoJ.GetHist()); + + histoI = histoJ = utils::Histogram(minvalue, maxvalue, bin); + channelIndex = 1; // GREEN channel + colorHarmonization::CommonDataByPair::computeHisto(histoI, maskI, channelIndex, imageI); + colorHarmonization::CommonDataByPair::computeHisto(histoJ, maskJ, channelIndex, imageJ); + relativeColorHistogramEdge& edgeG = map_relativeHistograms[channelIndex][i]; + edgeG = + relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], histoI.GetHist(), histoJ.GetHist()); + + histoI = histoJ = utils::Histogram(minvalue, maxvalue, bin); + channelIndex = 2; // BLUE channel + colorHarmonization::CommonDataByPair::computeHisto(histoI, maskI, channelIndex, imageI); + colorHarmonization::CommonDataByPair::computeHisto(histoJ, maskJ, channelIndex, imageJ); + relativeColorHistogramEdge& edgeB = map_relativeHistograms[channelIndex][i]; + edgeB = + relativeColorHistogramEdge(map_cameraNodeToCameraIndex[viewI], map_cameraNodeToCameraIndex[viewJ], histoI.GetHist(), histoJ.GetHist()); } - const std::string out_folder = (fs::path(_outputDirectory) / (EHistogramSelectionMethod_enumToString(_selectionMethod) + "_" + vec_harmonizeMethod[ harmonizeMethod ])).string(); - if(!fs::exists(out_folder)) - fs::create_directory(out_folder); - const std::string out_filename = (fs::path(out_folder) / fs::path(_fileNames[ imaNum ]).filename() ).string(); + std::cout << "\n -- \n SOLVE for color consistency with linear programming\n --" << std::endl; + //-- Solve for the gains and offsets: + std::vector vec_indexToFix; + vec_indexToFix.push_back(map_cameraNodeToCameraIndex[_imgRef]); + + using namespace aliceVision::linearProgramming; + + std::vector vec_solution_r(_fileNames.size() * 2 + 1); + std::vector vec_solution_g(_fileNames.size() * 2 + 1); + std::vector vec_solution_b(_fileNames.size() * 2 + 1); + + aliceVision::system::Timer timer; + +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_MOSEK) + typedef MOSEKSolver SOLVER_LP_T; +#else + typedef OSI_CISolverWrapper SOLVER_LP_T; +#endif + // Red channel + { + SOLVER_LP_T lpSolver(vec_solution_r.size()); + + GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[0], vec_indexToFix); + LPConstraintsSparse constraint; + cstBuilder.Build(constraint); + lpSolver.setup(constraint); + lpSolver.solve(); + lpSolver.getSolution(vec_solution_r); + } + // Green channel + { + SOLVER_LP_T lpSolver(vec_solution_g.size()); + + GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[1], vec_indexToFix); + LPConstraintsSparse constraint; + cstBuilder.Build(constraint); + lpSolver.setup(constraint); + lpSolver.solve(); + lpSolver.getSolution(vec_solution_g); + } + // Blue channel + { + SOLVER_LP_T lpSolver(vec_solution_b.size()); + + GainOffsetConstraintBuilder cstBuilder(map_relativeHistograms[2], vec_indexToFix); + LPConstraintsSparse constraint; + cstBuilder.Build(constraint); + lpSolver.setup(constraint); + lpSolver.solve(); + lpSolver.getSolution(vec_solution_b); + } + + std::cout << std::endl + << " ColorHarmonization solving on a graph with: " << _pairwiseMatches.size() << " edges took (s): " << timer.elapsed() << std::endl + << "LInfinity fitting error: \n" + << "- for the red channel is: " << vec_solution_r.back() << " gray level(s)" << std::endl + << "- for the green channel is: " << vec_solution_g.back() << " gray level(s)" << std::endl + << "- for the blue channel is: " << vec_solution_b.back() << " gray level(s)" << std::endl; + + std::cout << "\n\nFound solution_r:\n"; + std::copy(vec_solution_r.begin(), vec_solution_r.end(), std::ostream_iterator(std::cout, " ")); - writeImage(out_filename, image_c , image::ImageWriteOptions()); - } - return true; + std::cout << "\n\nFound solution_g:\n"; + std::copy(vec_solution_g.begin(), vec_solution_g.end(), std::ostream_iterator(std::cout, " ")); + + std::cout << "\n\nFound solution_b:\n"; + std::copy(vec_solution_b.begin(), vec_solution_b.end(), std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + + std::cout << "\n\nThere is :\n" << set_indeximage.size() << " images to transform." << std::endl; + + //-> convert solution to gain offset and creation of the LUT per image + auto progressDisplay = system::createConsoleProgressDisplay(set_indeximage.size(), std::cout); + for (std::set::const_iterator iterSet = set_indeximage.begin(); iterSet != set_indeximage.end(); ++iterSet, ++progressDisplay) + { + const size_t imaNum = *iterSet; + typedef Eigen::Matrix Vec256; + std::vector vec_map_lut(3); + + const size_t nodeIndex = std::distance(set_indeximage.begin(), iterSet); + + const double g_r = vec_solution_r[nodeIndex * 2]; + const double offset_r = vec_solution_r[nodeIndex * 2 + 1]; + const double g_g = vec_solution_g[nodeIndex * 2]; + const double offset_g = vec_solution_g[nodeIndex * 2 + 1]; + const double g_b = vec_solution_b[nodeIndex * 2]; + const double offset_b = vec_solution_b[nodeIndex * 2 + 1]; + + for (size_t k = 0; k < 256; ++k) + { + vec_map_lut[0][k] = clamp(k * g_r + offset_r, 0., 255.); + vec_map_lut[1][k] = clamp(k * g_g + offset_g, 0., 255.); + vec_map_lut[2][k] = clamp(k * g_b + offset_b, 0., 255.); + } + + Image image_c; + readImage(_fileNames[imaNum], image_c, image::EImageColorSpace::LINEAR); + +#pragma omp parallel for + for (int j = 0; j < image_c.height(); ++j) + { + for (int i = 0; i < image_c.width(); ++i) + { + image_c(j, i)[0] = clamp(vec_map_lut[0][image_c(j, i)[0]], 0., 255.); + image_c(j, i)[1] = clamp(vec_map_lut[1][image_c(j, i)[1]], 0., 255.); + image_c(j, i)[2] = clamp(vec_map_lut[2][image_c(j, i)[2]], 0., 255.); + } + } + + const std::string out_folder = + (fs::path(_outputDirectory) / (EHistogramSelectionMethod_enumToString(_selectionMethod) + "_" + vec_harmonizeMethod[harmonizeMethod])) + .string(); + if (!fs::exists(out_folder)) + fs::create_directory(out_folder); + const std::string out_filename = (fs::path(out_folder) / fs::path(_fileNames[imaNum]).filename()).string(); + + writeImage(out_filename, image_c, image::ImageWriteOptions()); + } + return true; } bool ColorHarmonizationEngineGlobal::ReadInputData() { - if(!fs::is_directory( _outputDirectory)) - { - std::cerr << "The output folder is not a valid folder" << std::endl; - return false; - } - - if(!fs::is_regular_file(_sfmDataFilename )) - { - std::cerr << "Invalid input sfm_data file: " << _sfmDataFilename << std::endl; - return false; - } - - // a. Read input scenes views - SfMData sfmData; - if(!sfmDataIO::load(sfmData, _sfmDataFilename, sfmDataIO::ESfMData::VIEWS)) - { - std::cerr << "The input file \""<< _sfmDataFilename << "\" cannot be read" << std::endl; - return false; - } - - // Read images names - for(Views::const_iterator iter = sfmData.getViews().begin(); - iter != sfmData.getViews().end(); ++iter) - { - const View* v = iter->second.get(); - _fileNames.push_back(v->getImage().getImagePath()); - _imageSize.push_back(std::make_pair( v->getImage().getWidth(), v->getImage().getHeight() )); - } - - // b. Read matches - if(!sfm::loadPairwiseMatches(_pairwiseMatches, sfmData, _matchesFolders, _descTypes)) - { - std::cerr << "Can't load matches files" << std::endl; - return false; - } - - // Read features: - if(!sfm::loadRegionsPerView(_regionsPerView, sfmData, _featuresFolders, _descTypes)) - { - std::cerr << "Can't load feature files" << std::endl; - return false; - } - - graph::indexedGraph putativeGraph(getImagePairs(_pairwiseMatches)); - - // Save the graph before cleaning: - graph::exportToGraphvizData((fs::path(_outputDirectory) / "initialGraph" ).string(),putativeGraph.g ); - - return true; + if (!fs::is_directory(_outputDirectory)) + { + std::cerr << "The output folder is not a valid folder" << std::endl; + return false; + } + + if (!fs::is_regular_file(_sfmDataFilename)) + { + std::cerr << "Invalid input sfm_data file: " << _sfmDataFilename << std::endl; + return false; + } + + // a. Read input scenes views + SfMData sfmData; + if (!sfmDataIO::load(sfmData, _sfmDataFilename, sfmDataIO::ESfMData::VIEWS)) + { + std::cerr << "The input file \"" << _sfmDataFilename << "\" cannot be read" << std::endl; + return false; + } + + // Read images names + for (Views::const_iterator iter = sfmData.getViews().begin(); iter != sfmData.getViews().end(); ++iter) + { + const View* v = iter->second.get(); + _fileNames.push_back(v->getImage().getImagePath()); + _imageSize.push_back(std::make_pair(v->getImage().getWidth(), v->getImage().getHeight())); + } + + // b. Read matches + if (!sfm::loadPairwiseMatches(_pairwiseMatches, sfmData, _matchesFolders, _descTypes)) + { + std::cerr << "Can't load matches files" << std::endl; + return false; + } + + // Read features: + if (!sfm::loadRegionsPerView(_regionsPerView, sfmData, _featuresFolders, _descTypes)) + { + std::cerr << "Can't load feature files" << std::endl; + return false; + } + + graph::indexedGraph putativeGraph(getImagePairs(_pairwiseMatches)); + + // Save the graph before cleaning: + graph::exportToGraphvizData((fs::path(_outputDirectory) / "initialGraph").string(), putativeGraph.g); + + return true; } bool ColorHarmonizationEngineGlobal::CleanGraph() { - // Create a graph from pairwise correspondences: - // - keep the largest connected component. - - graph::indexedGraph putativeGraph(getImagePairs(_pairwiseMatches)); - - // Save the graph before cleaning: - graph::exportToGraphvizData((fs::path(_outputDirectory) / "initialGraph").string(), putativeGraph.g); - - const int connectedComponentCount = lemon::countConnectedComponents(putativeGraph.g); - std::cout << "\n" - << "ColorHarmonizationEngineGlobal::CleanGraph() :: => connected Component cardinal: " - << connectedComponentCount << std::endl; - - if (connectedComponentCount > 1) // If more than one CC, keep the largest - { - // Search the largest CC index - const std::map > map_subgraphs = - aliceVision::graph::exportGraphToMapSubgraphs(putativeGraph.g); - size_t count = std::numeric_limits::min(); - std::map >::const_iterator iterLargestCC = map_subgraphs.end(); - for(std::map >::const_iterator iter = map_subgraphs.begin(); - iter != map_subgraphs.end(); ++iter) - { - if (iter->second.size() > count) { - count = iter->second.size(); - iterLargestCC = iter; - } - std::cout << "Connected component of size : " << iter->second.size() << std::endl; - } + // Create a graph from pairwise correspondences: + // - keep the largest connected component. + + graph::indexedGraph putativeGraph(getImagePairs(_pairwiseMatches)); - //-- Remove all nodes that are not listed in the largest CC - for(std::map >::const_iterator iter = map_subgraphs.begin(); - iter != map_subgraphs.end(); ++iter) + // Save the graph before cleaning: + graph::exportToGraphvizData((fs::path(_outputDirectory) / "initialGraph").string(), putativeGraph.g); + + const int connectedComponentCount = lemon::countConnectedComponents(putativeGraph.g); + std::cout << "\n" + << "ColorHarmonizationEngineGlobal::CleanGraph() :: => connected Component cardinal: " << connectedComponentCount << std::endl; + + if (connectedComponentCount > 1) // If more than one CC, keep the largest { - if (iter == iterLargestCC) // Skip this CC since it's the one we want to keep - continue; - - const std::set & ccSet = iter->second; - for (std::set::const_iterator iter2 = ccSet.begin(); - iter2 != ccSet.end(); ++iter2) - { - // Remove all outgoing edges - for (lemon::ListGraph::OutArcIt e(putativeGraph.g, *iter2); e!=INVALID; ++e) + // Search the largest CC index + const std::map> map_subgraphs = + aliceVision::graph::exportGraphToMapSubgraphs(putativeGraph.g); + size_t count = std::numeric_limits::min(); + std::map>::const_iterator iterLargestCC = map_subgraphs.end(); + for (std::map>::const_iterator iter = map_subgraphs.begin(); iter != map_subgraphs.end(); ++iter) + { + if (iter->second.size() > count) + { + count = iter->second.size(); + iterLargestCC = iter; + } + std::cout << "Connected component of size : " << iter->second.size() << std::endl; + } + + //-- Remove all nodes that are not listed in the largest CC + for (std::map>::const_iterator iter = map_subgraphs.begin(); iter != map_subgraphs.end(); ++iter) { - putativeGraph.g.erase(e); - const IndexT Idu = (*putativeGraph.map_nodeMapIndex)[putativeGraph.g.target(e)]; - const IndexT Idv = (*putativeGraph.map_nodeMapIndex)[putativeGraph.g.source(e)]; - matching::PairwiseMatches::iterator iterM = _pairwiseMatches.find(std::make_pair(Idu,Idv)); - if( iterM != _pairwiseMatches.end()) - { - _pairwiseMatches.erase(iterM); - } - else // Try to find the opposite directed edge - { - iterM = _pairwiseMatches.find(std::make_pair(Idv,Idu)); - if( iterM != _pairwiseMatches.end()) - _pairwiseMatches.erase(iterM); - } + if (iter == iterLargestCC) // Skip this CC since it's the one we want to keep + continue; + + const std::set& ccSet = iter->second; + for (std::set::const_iterator iter2 = ccSet.begin(); iter2 != ccSet.end(); ++iter2) + { + // Remove all outgoing edges + for (lemon::ListGraph::OutArcIt e(putativeGraph.g, *iter2); e != INVALID; ++e) + { + putativeGraph.g.erase(e); + const IndexT Idu = (*putativeGraph.map_nodeMapIndex)[putativeGraph.g.target(e)]; + const IndexT Idv = (*putativeGraph.map_nodeMapIndex)[putativeGraph.g.source(e)]; + matching::PairwiseMatches::iterator iterM = _pairwiseMatches.find(std::make_pair(Idu, Idv)); + if (iterM != _pairwiseMatches.end()) + { + _pairwiseMatches.erase(iterM); + } + else // Try to find the opposite directed edge + { + iterM = _pairwiseMatches.find(std::make_pair(Idv, Idu)); + if (iterM != _pairwiseMatches.end()) + _pairwiseMatches.erase(iterM); + } + } + } } - } } - } - // Save the graph after cleaning: - graph::exportToGraphvizData((fs::path(_outputDirectory) / "cleanedGraph").string(), putativeGraph.g); + // Save the graph after cleaning: + graph::exportToGraphvizData((fs::path(_outputDirectory) / "cleanedGraph").string(), putativeGraph.g); - std::cout << "\n" - << "Cardinal of nodes: " << lemon::countNodes(putativeGraph.g) << "\n" - << "Cardinal of edges: " << lemon::countEdges(putativeGraph.g) << std::endl - << std::endl; + std::cout << "\n" + << "Cardinal of nodes: " << lemon::countNodes(putativeGraph.g) << "\n" + << "Cardinal of edges: " << lemon::countEdges(putativeGraph.g) << std::endl + << std::endl; - return true; + return true; } -} // namespace aliceVision +} // namespace aliceVision diff --git a/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.hpp b/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.hpp index 272ca5cda1..235adee715 100644 --- a/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.hpp +++ b/src/software/utils/sfmColorHarmonize/colorHarmonizeEngineGlobal.hpp @@ -25,10 +25,10 @@ enum class EHistogramSelectionMethod inline std::string EHistogramSelectionMethod_description() { - return "Histogram selection method: \n" - "* full_frame \n" - "* matched_points \n" - "* VLD_segments\n"; + return "Histogram selection method: \n" + "* full_frame \n" + "* matched_points \n" + "* VLD_segments\n"; } EHistogramSelectionMethod EEHistogramSelectionMethod_stringToEnum(const std::string& histogramSelectionMethod); @@ -46,50 +46,48 @@ std::istream& operator>>(std::istream& in, EHistogramSelectionMethod& p); */ class ColorHarmonizationEngineGlobal { -public: - ColorHarmonizationEngineGlobal( - const std::string& sfmDataFilename, - const std::vector& featuresFolders, - const std::vector& matchesFolders, - const std::string& outputDirectory, - const std::vector& descTypes, - EHistogramSelectionMethod selectionMethod, - int imgRef = 0); - - ~ColorHarmonizationEngineGlobal(); - - virtual bool Process(); - -private: - - EHistogramSelectionMethod _selectionMethod; - int _imgRef; - - // Input data - - feature::RegionsPerView _regionsPerView; - /// considered images - std::vector _fileNames; - /// size of each image - std::vector> _imageSize; - /// pairwise geometric matches - aliceVision::matching::PairwiseMatches _pairwiseMatches; - /// describer type use for color harmonizations - std::vector _descTypes; - /// path to the Sfm Scene - std::string _sfmDataFilename; - /// path to matches - std::vector _matchesFolders; - /// path to features - std::vector _featuresFolders; - /// output path where outputs will be stored - std::string _outputDirectory; - - /// Clean graph - bool CleanGraph(); - - /// Read input data (point correspondences) - bool ReadInputData(); + public: + ColorHarmonizationEngineGlobal(const std::string& sfmDataFilename, + const std::vector& featuresFolders, + const std::vector& matchesFolders, + const std::string& outputDirectory, + const std::vector& descTypes, + EHistogramSelectionMethod selectionMethod, + int imgRef = 0); + + ~ColorHarmonizationEngineGlobal(); + + virtual bool Process(); + + private: + EHistogramSelectionMethod _selectionMethod; + int _imgRef; + + // Input data + + feature::RegionsPerView _regionsPerView; + /// considered images + std::vector _fileNames; + /// size of each image + std::vector> _imageSize; + /// pairwise geometric matches + aliceVision::matching::PairwiseMatches _pairwiseMatches; + /// describer type use for color harmonizations + std::vector _descTypes; + /// path to the Sfm Scene + std::string _sfmDataFilename; + /// path to matches + std::vector _matchesFolders; + /// path to features + std::vector _featuresFolders; + /// output path where outputs will be stored + std::string _outputDirectory; + + /// Clean graph + bool CleanGraph(); + + /// Read input data (point correspondences) + bool ReadInputData(); }; -} // namespace aliceVision +} // namespace aliceVision diff --git a/src/software/utils/sfmHelper/sfmIOHelper.hpp b/src/software/utils/sfmHelper/sfmIOHelper.hpp index 42bd2f39ae..481efd87b0 100644 --- a/src/software/utils/sfmHelper/sfmIOHelper.hpp +++ b/src/software/utils/sfmHelper/sfmIOHelper.hpp @@ -17,39 +17,48 @@ #include #include #include -#include // std::lexicographical_compare -#include // std::tolower +#include // std::lexicographical_compare +#include // std::tolower -namespace aliceVision{ -namespace SfMIO{ +namespace aliceVision { +namespace SfMIO { struct CameraInfo { - std::string m_sImageName; - size_t m_intrinsicId; + std::string m_sImageName; + size_t m_intrinsicId; }; struct IntrinsicCameraInfo { - size_t m_w, m_h; - float m_focal; - Mat3 m_K; - double m_k1; - double m_k2; - double m_k3; - bool m_bKnownIntrinsic; // true if 11 or 6, else false - std::string m_sCameraMaker, m_sCameraModel; - - IntrinsicCameraInfo(): m_w(0), m_h(0), m_K(Mat3::Zero()), m_k1(0), m_k2(0), m_k3(0), m_bKnownIntrinsic(false), m_sCameraModel(""), m_sCameraMaker("") - { } - - /// Functor used to tell if two IntrinsicCameraInfo share the same optical properties - friend bool operator== (IntrinsicCameraInfo const &ci1, IntrinsicCameraInfo const &ci2) - { - // Two camera share optical properties if they share the same K matrix (and the same camera name) - bool bequal = ci1.m_K == ci2.m_K && ci1.m_sCameraMaker == ci2.m_sCameraMaker && ci1.m_sCameraModel == ci2.m_sCameraModel; - return bequal; - } + size_t m_w, m_h; + float m_focal; + Mat3 m_K; + double m_k1; + double m_k2; + double m_k3; + bool m_bKnownIntrinsic; // true if 11 or 6, else false + std::string m_sCameraMaker, m_sCameraModel; + + IntrinsicCameraInfo() + : m_w(0), + m_h(0), + m_K(Mat3::Zero()), + m_k1(0), + m_k2(0), + m_k3(0), + m_bKnownIntrinsic(false), + m_sCameraModel(""), + m_sCameraMaker("") + {} + + /// Functor used to tell if two IntrinsicCameraInfo share the same optical properties + friend bool operator==(IntrinsicCameraInfo const& ci1, IntrinsicCameraInfo const& ci2) + { + // Two camera share optical properties if they share the same K matrix (and the same camera name) + bool bequal = ci1.m_K == ci2.m_K && ci1.m_sCameraMaker == ci2.m_sCameraMaker && ci1.m_sCameraModel == ci2.m_sCameraModel; + return bequal; + } }; // Load an image file list @@ -59,160 +68,163 @@ struct IntrinsicCameraInfo // - a camera with exif data found in the database // - a camera with exif data not found in the database // - a camera with known intrinsic -inline bool loadImageList( std::vector & vec_camImageName, - std::vector & vec_focalGroup, - const std::string & sFileName, - bool bVerbose = true ) +inline bool loadImageList(std::vector& vec_camImageName, + std::vector& vec_focalGroup, + const std::string& sFileName, + bool bVerbose = true) { - std::ifstream in(sFileName); - if(!in.is_open()) - { - std::cerr << std::endl - << "Impossible to read the specified file." << std::endl; - } - std::string sValue; - std::vector vec_str; - while(getline( in, sValue ) ) - { - vec_str.clear(); - boost::trim(sValue); - boost::split(vec_str, sValue, boost::is_any_of(";")); - if (vec_str.size() == 1) - { - std::cerr << "Invalid input file" << std::endl; - in.close(); - return false; - } - std::stringstream oss; - oss.clear(); oss.str(vec_str[1]); - size_t width, height; - oss >> width; - oss.clear(); oss.str(vec_str[2]); - oss >> height; - - IntrinsicCameraInfo intrinsicCamInfo; - intrinsicCamInfo.m_w = width; - intrinsicCamInfo.m_h = height; - - switch ( vec_str.size() ) - { - case 3 : // a camera without exif data - { - intrinsicCamInfo.m_focal = -1; - intrinsicCamInfo.m_bKnownIntrinsic = false; - intrinsicCamInfo.m_sCameraMaker = ""; - intrinsicCamInfo.m_sCameraModel = ""; - } - break; - case 5 : // a camera with exif data not found in the database - { - intrinsicCamInfo.m_focal = -1; - intrinsicCamInfo.m_bKnownIntrinsic = false; - intrinsicCamInfo.m_sCameraMaker = vec_str[3]; - intrinsicCamInfo.m_sCameraModel = vec_str[4]; - } - break; - case 6 : // a camera with exif data found in the database - { - oss.clear(); oss.str(vec_str[3]); - float focal; - oss >> focal; - intrinsicCamInfo.m_focal = focal; - intrinsicCamInfo.m_bKnownIntrinsic = true; - intrinsicCamInfo.m_sCameraMaker = vec_str[4]; - intrinsicCamInfo.m_sCameraModel = vec_str[5]; - - Mat3 K; - K << focal, 0, float(width) / 2.f, - 0, focal, float(height) / 2.f, - 0, 0, 1; - intrinsicCamInfo.m_K = K; - } - break; - case 12 : // a camera with known intrinsic - { - intrinsicCamInfo.m_bKnownIntrinsic = true; - intrinsicCamInfo.m_sCameraMaker = intrinsicCamInfo.m_sCameraModel = ""; - - Mat3 K = Mat3::Identity(); - - oss.clear(); oss.str(vec_str[3]); - oss >> K(0,0); - oss.clear(); oss.str(vec_str[4]); - oss >> K(0,1); - oss.clear(); oss.str(vec_str[5]); - oss >> K(0,2); - oss.clear(); oss.str(vec_str[6]); - oss >> K(1,0); - oss.clear(); oss.str(vec_str[7]); - oss >> K(1,1); - oss.clear(); oss.str(vec_str[8]); - oss >> K(1,2); - oss.clear(); oss.str(vec_str[9]); - oss >> K(2,0); - oss.clear(); oss.str(vec_str[10]); - oss >> K(2,1); - oss.clear(); oss.str(vec_str[11]); - oss >> K(2,2); - - intrinsicCamInfo.m_K = K; - intrinsicCamInfo.m_focal = static_cast(K(0,0)); // unknown sensor size; - } - break; - default : - { - std::cerr << "Invalid image list line: wrong number of arguments" << std::endl; - in.close(); - return false; - } - } - - std::vector::const_iterator iterIntrinsicGroup = find(vec_focalGroup.begin(), vec_focalGroup.end(), intrinsicCamInfo); - size_t id = -1; - if ( iterIntrinsicGroup == vec_focalGroup.end()) + std::ifstream in(sFileName); + if (!in.is_open()) { - vec_focalGroup.push_back(intrinsicCamInfo); - id = vec_focalGroup.size()-1; + std::cerr << std::endl << "Impossible to read the specified file." << std::endl; } - else + std::string sValue; + std::vector vec_str; + while (getline(in, sValue)) { - id = std::distance( std::vector::const_iterator(vec_focalGroup.begin()), iterIntrinsicGroup); + vec_str.clear(); + boost::trim(sValue); + boost::split(vec_str, sValue, boost::is_any_of(";")); + if (vec_str.size() == 1) + { + std::cerr << "Invalid input file" << std::endl; + in.close(); + return false; + } + std::stringstream oss; + oss.clear(); + oss.str(vec_str[1]); + size_t width, height; + oss >> width; + oss.clear(); + oss.str(vec_str[2]); + oss >> height; + + IntrinsicCameraInfo intrinsicCamInfo; + intrinsicCamInfo.m_w = width; + intrinsicCamInfo.m_h = height; + + switch (vec_str.size()) + { + case 3: // a camera without exif data + { + intrinsicCamInfo.m_focal = -1; + intrinsicCamInfo.m_bKnownIntrinsic = false; + intrinsicCamInfo.m_sCameraMaker = ""; + intrinsicCamInfo.m_sCameraModel = ""; + } + break; + case 5: // a camera with exif data not found in the database + { + intrinsicCamInfo.m_focal = -1; + intrinsicCamInfo.m_bKnownIntrinsic = false; + intrinsicCamInfo.m_sCameraMaker = vec_str[3]; + intrinsicCamInfo.m_sCameraModel = vec_str[4]; + } + break; + case 6: // a camera with exif data found in the database + { + oss.clear(); + oss.str(vec_str[3]); + float focal; + oss >> focal; + intrinsicCamInfo.m_focal = focal; + intrinsicCamInfo.m_bKnownIntrinsic = true; + intrinsicCamInfo.m_sCameraMaker = vec_str[4]; + intrinsicCamInfo.m_sCameraModel = vec_str[5]; + + Mat3 K; + K << focal, 0, float(width) / 2.f, 0, focal, float(height) / 2.f, 0, 0, 1; + intrinsicCamInfo.m_K = K; + } + break; + case 12: // a camera with known intrinsic + { + intrinsicCamInfo.m_bKnownIntrinsic = true; + intrinsicCamInfo.m_sCameraMaker = intrinsicCamInfo.m_sCameraModel = ""; + + Mat3 K = Mat3::Identity(); + + oss.clear(); + oss.str(vec_str[3]); + oss >> K(0, 0); + oss.clear(); + oss.str(vec_str[4]); + oss >> K(0, 1); + oss.clear(); + oss.str(vec_str[5]); + oss >> K(0, 2); + oss.clear(); + oss.str(vec_str[6]); + oss >> K(1, 0); + oss.clear(); + oss.str(vec_str[7]); + oss >> K(1, 1); + oss.clear(); + oss.str(vec_str[8]); + oss >> K(1, 2); + oss.clear(); + oss.str(vec_str[9]); + oss >> K(2, 0); + oss.clear(); + oss.str(vec_str[10]); + oss >> K(2, 1); + oss.clear(); + oss.str(vec_str[11]); + oss >> K(2, 2); + + intrinsicCamInfo.m_K = K; + intrinsicCamInfo.m_focal = static_cast(K(0, 0)); // unknown sensor size; + } + break; + default: + { + std::cerr << "Invalid image list line: wrong number of arguments" << std::endl; + in.close(); + return false; + } + } + + std::vector::const_iterator iterIntrinsicGroup = find(vec_focalGroup.begin(), vec_focalGroup.end(), intrinsicCamInfo); + size_t id = -1; + if (iterIntrinsicGroup == vec_focalGroup.end()) + { + vec_focalGroup.push_back(intrinsicCamInfo); + id = vec_focalGroup.size() - 1; + } + else + { + id = std::distance(std::vector::const_iterator(vec_focalGroup.begin()), iterIntrinsicGroup); + } + + CameraInfo camInfo; + camInfo.m_sImageName = vec_str[0]; + camInfo.m_intrinsicId = id; + vec_camImageName.push_back(camInfo); + + vec_str.clear(); } - - CameraInfo camInfo; - camInfo.m_sImageName = vec_str[0]; - camInfo.m_intrinsicId = id; - vec_camImageName.push_back(camInfo); - - vec_str.clear(); - } - in.close(); - return !(vec_camImageName.empty()); + in.close(); + return !(vec_camImageName.empty()); } //-- Load an image list file but only return camera image names -inline bool loadImageList( std::vector & vec_camImageName, - const std::string & sListFileName, - bool bVerbose = true ) +inline bool loadImageList(std::vector& vec_camImageName, const std::string& sListFileName, bool bVerbose = true) { - vec_camImageName.clear(); - std::vector vec_camImageIntrinsicInfo; - std::vector vec_focalGroup; - if (loadImageList( vec_camImageIntrinsicInfo, - vec_focalGroup, - sListFileName, - bVerbose) ) - { - for ( std::vector::const_iterator - iter_camInfo = vec_camImageIntrinsicInfo.begin(); - iter_camInfo != vec_camImageIntrinsicInfo.end(); - iter_camInfo++ ) + vec_camImageName.clear(); + std::vector vec_camImageIntrinsicInfo; + std::vector vec_focalGroup; + if (loadImageList(vec_camImageIntrinsicInfo, vec_focalGroup, sListFileName, bVerbose)) { - vec_camImageName.push_back( iter_camInfo->m_sImageName ); + for (std::vector::const_iterator iter_camInfo = vec_camImageIntrinsicInfo.begin(); + iter_camInfo != vec_camImageIntrinsicInfo.end(); + iter_camInfo++) + { + vec_camImageName.push_back(iter_camInfo->m_sImageName); + } } - } - return (!vec_camImageName.empty()); + return (!vec_camImageName.empty()); } -} // namespace SfMIO -} // namespace aliceVision +} // namespace SfMIO +} // namespace aliceVision diff --git a/src/software/utils/sfmHelper/sfmIOHelper_test.cpp b/src/software/utils/sfmHelper/sfmIOHelper_test.cpp index 2bd7f01a78..b2b3005e77 100644 --- a/src/software/utils/sfmHelper/sfmIOHelper_test.cpp +++ b/src/software/utils/sfmHelper/sfmIOHelper_test.cpp @@ -11,153 +11,153 @@ using namespace aliceVision; using namespace aliceVision::SfMIO; -TEST(SfMIOHelper, EmptyFile) { - std::ostringstream os; - os.str(""); - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_FALSE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(0, vec_intrinsicGroups.size()); +TEST(SfMIOHelper, EmptyFile) +{ + std::ostringstream os; + os.str(""); + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_FALSE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(0, vec_intrinsicGroups.size()); } -TEST(SfMIOHelper, UniqueIntrinsicGroup) { - - std::ostringstream os; - os //ImaName;W;H;FocalPix;KMatrix - << "0.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1"; - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_TRUE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(1, vec_intrinsicGroups.size()); +TEST(SfMIOHelper, UniqueIntrinsicGroup) +{ + std::ostringstream os; + os // ImaName;W;H;FocalPix;KMatrix + << "0.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1"; + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_TRUE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(1, vec_intrinsicGroups.size()); } -TEST(SfMIOHelper, SameCameraDifferentFocal) { - - std::ostringstream os; - os //ImaName;W;H;FocalPix;KMatrix - << "DSC00402.JPG;4912;3264;3344;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" <<'\n' - << "DSC00403.JPG;4912;3264;6644;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA"; - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_TRUE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(2, vec_intrinsicGroups.size()); +TEST(SfMIOHelper, SameCameraDifferentFocal) +{ + std::ostringstream os; + os // ImaName;W;H;FocalPix;KMatrix + << "DSC00402.JPG;4912;3264;3344;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" << '\n' + << "DSC00403.JPG;4912;3264;6644;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA"; + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_TRUE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(2, vec_intrinsicGroups.size()); } -TEST(SfMIOHelper, ManyCameraDifferentFocal) { - - std::ostringstream os; - os //ImaName;W;H;FocalPix;CamMaker;CamName - << "DSC00402.JPG;4912;3264;3344.34;SONY;NEX-3N" <<'\n' - << "100_7100.JPG;2832;2128;2881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" <<'\n' - << "DSC00403.JPG;4912;3264;3344.34;SONY;NEX-3N" <<'\n' // same group as DSC00402 - << "100_7101.JPG;2832;2128;4881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" <<'\n' - << "IMG_3266.JPG;5472;3648;4377.6;Canon;Canon EOS 70D" << '\n' - << "100_7102.JPG;2832;2128;6881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" <<'\n' - << "IMG_3267.JPG;5472;3648;6677.6;Canon;Canon EOS 70D" << '\n' - << "IMG_3210.JPG;5616;3744;13260;Canon;Canon EOS 5D Mark II" << '\n' - << "IMG_3211.JPG;5616;3744;10260;Canon;Canon EOS 5D Mark II" << '\n' - << "IMG_3212.JPG;5616;3744;13260;Canon;Canon EOS 5D Mark II" << '\n' // same group as IMG_3210 - << "IMG_3212.JPG;5616;3744;Xylus;Junior"; // not known camera - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_TRUE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(9, vec_intrinsicGroups.size()); - // Check intrinsic group Ids correctness - const size_t intrinsicGTIDs [] = {0,1,0,2,3,4,5,6,7,6,8}; - for (size_t i =0; i < 9; ++i) { - EXPECT_EQ(intrinsicGTIDs[i], vec_camImageNames[i].m_intrinsicId); - } +TEST(SfMIOHelper, ManyCameraDifferentFocal) +{ + std::ostringstream os; + os // ImaName;W;H;FocalPix;CamMaker;CamName + << "DSC00402.JPG;4912;3264;3344.34;SONY;NEX-3N" << '\n' + << "100_7100.JPG;2832;2128;2881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" << '\n' + << "DSC00403.JPG;4912;3264;3344.34;SONY;NEX-3N" << '\n' // same group as DSC00402 + << "100_7101.JPG;2832;2128;4881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" << '\n' + << "IMG_3266.JPG;5472;3648;4377.6;Canon;Canon EOS 70D" << '\n' + << "100_7102.JPG;2832;2128;6881.25;EASTMAN KODAK COMPANY;KODAK Z612 ZOOM DIGITAL CAMERA" << '\n' + << "IMG_3267.JPG;5472;3648;6677.6;Canon;Canon EOS 70D" << '\n' + << "IMG_3210.JPG;5616;3744;13260;Canon;Canon EOS 5D Mark II" << '\n' + << "IMG_3211.JPG;5616;3744;10260;Canon;Canon EOS 5D Mark II" << '\n' + << "IMG_3212.JPG;5616;3744;13260;Canon;Canon EOS 5D Mark II" << '\n' // same group as IMG_3210 + << "IMG_3212.JPG;5616;3744;Xylus;Junior"; // not known camera + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_TRUE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(9, vec_intrinsicGroups.size()); + // Check intrinsic group Ids correctness + const size_t intrinsicGTIDs[] = {0, 1, 0, 2, 3, 4, 5, 6, 7, 6, 8}; + for (size_t i = 0; i < 9; ++i) + { + EXPECT_EQ(intrinsicGTIDs[i], vec_camImageNames[i].m_intrinsicId); + } } -TEST(SfMIOHelper, KnowAndUnknowCamera) { - - std::ostringstream os; - os //ImaName;W;H;FocalPix;CamMaker;CamName - << "DSC00402.JPG;4912;3264;3344.34;SONY;NEX-3N" <<'\n' - << "0.jpg;4912;3264;3344.34;0;2456;0;3344.34;1632;0;0;1"; - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_TRUE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(2, vec_intrinsicGroups.size()); +TEST(SfMIOHelper, KnowAndUnknowCamera) +{ + std::ostringstream os; + os // ImaName;W;H;FocalPix;CamMaker;CamName + << "DSC00402.JPG;4912;3264;3344.34;SONY;NEX-3N" << '\n' + << "0.jpg;4912;3264;3344.34;0;2456;0;3344.34;1632;0;0;1"; + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_TRUE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(2, vec_intrinsicGroups.size()); } -TEST(SfMIOHelper, ThreeIntrinsicGroup_KMatrix) { - - std::ostringstream os; - os //ImaName;W;H;FocalPix;KMatrix - << "0.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' - << "1.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' - << "2.jpg;2592;1936;2059.94;0;1274.91;0;2059.94;967.70;0;0;1" << '\n' - << "3.jpg;2592;1936;2044.66;0;1253.00;0;2044.66;981.52;0;0;1" << '\n' - << "4.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' - << "5.jpg;2592;1936;2059.94;0;1274.91;0;2059.94;967.70;0;0;1" << '\n' - << "6.jpg;2592;1936;2044.66;0;1253.00;0;2044.66;981.52;0;0;1"; - - const std::string sListsFile = "./lists.txt"; - std::ofstream file(sListsFile); - file << os.str(); - file.close(); - - // Read data from the lists.txt file - std::vector vec_camImageNames; - std::vector vec_intrinsicGroups; - - EXPECT_TRUE( - aliceVision::SfMIO::loadImageList( vec_camImageNames, vec_intrinsicGroups, sListsFile)); - - EXPECT_EQ(3, vec_intrinsicGroups.size()); +TEST(SfMIOHelper, ThreeIntrinsicGroup_KMatrix) +{ + std::ostringstream os; + os // ImaName;W;H;FocalPix;KMatrix + << "0.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' + << "1.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' + << "2.jpg;2592;1936;2059.94;0;1274.91;0;2059.94;967.70;0;0;1" << '\n' + << "3.jpg;2592;1936;2044.66;0;1253.00;0;2044.66;981.52;0;0;1" << '\n' + << "4.jpg;2592;1936;2052.91;0;1278.59;0;2052.91;958.71;0;0;1" << '\n' + << "5.jpg;2592;1936;2059.94;0;1274.91;0;2059.94;967.70;0;0;1" << '\n' + << "6.jpg;2592;1936;2044.66;0;1253.00;0;2044.66;981.52;0;0;1"; + + const std::string sListsFile = "./lists.txt"; + std::ofstream file(sListsFile); + file << os.str(); + file.close(); + + // Read data from the lists.txt file + std::vector vec_camImageNames; + std::vector vec_intrinsicGroups; + + EXPECT_TRUE(aliceVision::SfMIO::loadImageList(vec_camImageNames, vec_intrinsicGroups, sListsFile)); + + EXPECT_EQ(3, vec_intrinsicGroups.size()); } /* ************************************************************************* */ -int main() { TestResult tr; return TestRegistry::runAllTests(tr);} +int main() +{ + TestResult tr; + return TestRegistry::runAllTests(tr); +} /* ************************************************************************* */ diff --git a/src/software/utils/sfmHelper/sfmPlyHelper.hpp b/src/software/utils/sfmHelper/sfmPlyHelper.hpp index 6894ea6eb2..f1fd057f2b 100644 --- a/src/software/utils/sfmHelper/sfmPlyHelper.hpp +++ b/src/software/utils/sfmHelper/sfmPlyHelper.hpp @@ -13,81 +13,81 @@ #include #include -namespace aliceVision{ -namespace plyHelper{ +namespace aliceVision { +namespace plyHelper { /// Export 3D point vector to PLY format -inline bool exportToPly(const std::vector & vec_points, - const std::string & sFileName) +inline bool exportToPly(const std::vector& vec_points, const std::string& sFileName) { - std::ofstream outfile; - outfile.open(sFileName, std::ios_base::out); - - if(!outfile.is_open()) - throw std::runtime_error("Unable to create file "+sFileName); + std::ofstream outfile; + outfile.open(sFileName, std::ios_base::out); - outfile << "ply" - << std::endl << "format ascii 1.0" - << std::endl << "element vertex " << vec_points.size() - << std::endl << "property float x" - << std::endl << "property float y" - << std::endl << "property float z" - << std::endl << "property uchar red" - << std::endl << "property uchar green" - << std::endl << "property uchar blue" - << std::endl << "end_header" << std::endl; + if (!outfile.is_open()) + throw std::runtime_error("Unable to create file " + sFileName); - for (size_t i=0; i < vec_points.size(); ++i) - { - outfile << vec_points[i].transpose() - << " 255 255 255" << "\n"; - } - bool bOk = outfile.good(); - outfile.close(); - return bOk; + outfile << "ply" << std::endl + << "format ascii 1.0" << std::endl + << "element vertex " << vec_points.size() << std::endl + << "property float x" << std::endl + << "property float y" << std::endl + << "property float z" << std::endl + << "property uchar red" << std::endl + << "property uchar green" << std::endl + << "property uchar blue" << std::endl + << "end_header" << std::endl; + + for (size_t i = 0; i < vec_points.size(); ++i) + { + outfile << vec_points[i].transpose() << " 255 255 255" + << "\n"; + } + bool bOk = outfile.good(); + outfile.close(); + return bOk; } /// Export 3D point vector and camera position to PLY format -inline bool exportToPly(const std::vector & vec_points, - const std::vector & vec_camPos, - const std::string & sFileName, - const std::vector * vec_coloredPoints = NULL) +inline bool exportToPly(const std::vector& vec_points, + const std::vector& vec_camPos, + const std::string& sFileName, + const std::vector* vec_coloredPoints = NULL) { - std::ofstream outfile; - outfile.open(sFileName, std::ios_base::out); - - if(!outfile.is_open()) - throw std::runtime_error("Unable to create file "+sFileName); + std::ofstream outfile; + outfile.open(sFileName, std::ios_base::out); + + if (!outfile.is_open()) + throw std::runtime_error("Unable to create file " + sFileName); - outfile << "ply" - << '\n' << "format ascii 1.0" - << '\n' << "element vertex " << vec_points.size()+vec_camPos.size() - << '\n' << "property float x" - << '\n' << "property float y" - << '\n' << "property float z" - << '\n' << "property uchar red" - << '\n' << "property uchar green" - << '\n' << "property uchar blue" - << '\n' << "end_header" << std::endl; + outfile << "ply" << '\n' + << "format ascii 1.0" << '\n' + << "element vertex " << vec_points.size() + vec_camPos.size() << '\n' + << "property float x" << '\n' + << "property float y" << '\n' + << "property float z" << '\n' + << "property uchar red" << '\n' + << "property uchar green" << '\n' + << "property uchar blue" << '\n' + << "end_header" << std::endl; - for (size_t i=0; i < vec_points.size(); ++i) { - if (vec_coloredPoints == NULL) - outfile << vec_points[i].transpose() - << " 255 255 255" << "\n"; - else - outfile << vec_points[i].transpose() - << " " << (*vec_coloredPoints)[i].transpose() << "\n"; - } + for (size_t i = 0; i < vec_points.size(); ++i) + { + if (vec_coloredPoints == NULL) + outfile << vec_points[i].transpose() << " 255 255 255" + << "\n"; + else + outfile << vec_points[i].transpose() << " " << (*vec_coloredPoints)[i].transpose() << "\n"; + } - for (size_t i=0; i < vec_camPos.size(); ++i) { - outfile << vec_camPos[i].transpose() - << " 0 255 0" << "\n"; - } - outfile.flush(); - bool bOk = outfile.good(); - outfile.close(); - return bOk; + for (size_t i = 0; i < vec_camPos.size(); ++i) + { + outfile << vec_camPos[i].transpose() << " 0 255 0" + << "\n"; + } + outfile.flush(); + bool bOk = outfile.good(); + outfile.close(); + return bOk; } -} // namespace plyHelper -} // namespace aliceVision +} // namespace plyHelper +} // namespace aliceVision From 0fb0a02b75142bcf3b23009428028e2198917275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Candice=20Bent=C3=A9jac?= Date: Fri, 12 Jan 2024 10:33:04 +0100 Subject: [PATCH 2/3] [aliceVision] Reformat src/aliceVision with latest clang-format rules --- src/aliceVision/camera/DistortionBrown.cpp | 53 +++++++------- src/aliceVision/camera/DistortionFisheye1.cpp | 18 +++-- src/aliceVision/camera/Equidistant.cpp | 7 +- src/aliceVision/camera/Equidistant.hpp | 4 +- src/aliceVision/camera/Pinhole.hpp | 4 +- src/aliceVision/depthMap/volumeIO.cpp | 16 ++--- src/aliceVision/fuseCut/Fuser.cpp | 4 +- src/aliceVision/geometry/Intersection.cpp | 11 ++- src/aliceVision/geometry/Intersection.hpp | 5 +- src/aliceVision/image/dcp.cpp | 1 - src/aliceVision/image/io.cpp | 6 +- .../lInfinityCV/resection_kernel.hpp | 10 +-- .../lInfinityCV/triplet_tijsAndXis_kernel.hpp | 10 +-- .../localization/VoctreeLocalizer.cpp | 1 - src/aliceVision/localization/optimization.cpp | 3 +- src/aliceVision/matching/io.cpp | 3 +- src/aliceVision/mvsUtils/MultiViewParams.cpp | 8 +-- src/aliceVision/segmentation/segmentation.cpp | 70 +++++++++---------- src/aliceVision/segmentation/segmentation.hpp | 8 +-- .../StructureEstimationFromKnownPoses.cpp | 9 ++- src/aliceVision/sfmData/HashMapPtr.hpp | 15 ++-- src/aliceVision/sfmData/Landmark.hpp | 39 +++-------- src/aliceVision/sfmData/Observation.cpp | 6 +- src/aliceVision/sfmData/Observation.hpp | 56 ++++----------- src/aliceVision/sfmData/SfMData.hpp | 10 +-- src/aliceVision/track/track_test.cpp | 8 +-- src/aliceVision/utils/filesIO.hpp | 23 +++--- src/aliceVision/voctree/SimpleKmeans.hpp | 30 +++----- src/aliceVision/voctree/VocabularyTree.hpp | 3 +- src/aliceVision/voctree/descriptorLoader.cpp | 2 +- 30 files changed, 175 insertions(+), 268 deletions(-) diff --git a/src/aliceVision/camera/DistortionBrown.cpp b/src/aliceVision/camera/DistortionBrown.cpp index 116dff5292..1e319a04fb 100644 --- a/src/aliceVision/camera/DistortionBrown.cpp +++ b/src/aliceVision/camera/DistortionBrown.cpp @@ -11,12 +11,12 @@ namespace aliceVision { namespace camera { -Vec2 DistortionBrown::addDistortion(const Vec2& p) const -{ +Vec2 DistortionBrown::addDistortion(const Vec2& p) const +{ const double k1 = _distortionParams[0]; const double k2 = _distortionParams[1]; const double k3 = _distortionParams[2]; - const double t1 = _distortionParams[3]; + const double t1 = _distortionParams[3]; const double t2 = _distortionParams[4]; double px = p(0); @@ -29,21 +29,21 @@ Vec2 DistortionBrown::addDistortion(const Vec2& p) const const double k_diff = (k1 * r2 + k2 * r4 + k3 * r6); const double t_x = t2 * (r2 + 2 * px * px) + 2 * t1 * px * py; const double t_y = t1 * (r2 + 2 * py * py) + 2 * t2 * px * py; - + Vec2 result; - + result(0) = px + px * k_diff + t_x; result(1) = py + py * k_diff + t_y; - return result; + return result; } -Eigen::Matrix2d DistortionBrown::getDerivativeAddDistoWrtPt(const Vec2& p) const +Eigen::Matrix2d DistortionBrown::getDerivativeAddDistoWrtPt(const Vec2& p) const { const double k1 = _distortionParams[0]; const double k2 = _distortionParams[1]; const double k3 = _distortionParams[2]; - const double t1 = _distortionParams[3]; + const double t1 = _distortionParams[3]; const double t2 = _distortionParams[4]; double px = p(0); @@ -54,11 +54,10 @@ Eigen::Matrix2d DistortionBrown::getDerivativeAddDistoWrtPt(const Vec2& p) const const double r6 = r4 * r2; Eigen::Matrix2d ret; - ret(0, 0) = k1*r2 + k2*r4 + k3*r6 + 2*px*px*(k1 + 2*k2*r2 + 3*k3*r4) + 6*px*t2 + 2*py*t1 + 1; - ret(0, 1) = 2*px*py*(k1 + 2*k2*r2 + 3*k3*r4) + 2*px*t1 + 2*py*t2; - ret(1, 0) = 2*px*py*(k1 + 2*k2*r2 + 3*k3*r4) + 2*px*t1 + 2*py*t2; - ret(1, 1) = k1*r2 + k2*r4 + k3*r6 + 2*px*t2 + 2*py*py*(k1 + 2*k2*r2 + 3*k3*r4) + 6*py*t1 + 1; - + ret(0, 0) = k1 * r2 + k2 * r4 + k3 * r6 + 2 * px * px * (k1 + 2 * k2 * r2 + 3 * k3 * r4) + 6 * px * t2 + 2 * py * t1 + 1; + ret(0, 1) = 2 * px * py * (k1 + 2 * k2 * r2 + 3 * k3 * r4) + 2 * px * t1 + 2 * py * t2; + ret(1, 0) = 2 * px * py * (k1 + 2 * k2 * r2 + 3 * k3 * r4) + 2 * px * t1 + 2 * py * t2; + ret(1, 1) = k1 * r2 + k2 * r4 + k3 * r6 + 2 * px * t2 + 2 * py * py * (k1 + 2 * k2 * r2 + 3 * k3 * r4) + 6 * py * t1 + 1; return ret; } @@ -68,7 +67,7 @@ Eigen::MatrixXd DistortionBrown::getDerivativeAddDistoWrtDisto(const Vec2& p) co const double k1 = _distortionParams[0]; const double k2 = _distortionParams[1]; const double k3 = _distortionParams[2]; - const double t1 = _distortionParams[3]; + const double t1 = _distortionParams[3]; const double t2 = _distortionParams[4]; double px = p(0); @@ -80,18 +79,17 @@ Eigen::MatrixXd DistortionBrown::getDerivativeAddDistoWrtDisto(const Vec2& p) co Eigen::Matrix ret = Eigen::Matrix::Zero(); - ret(0, 0) = px*r2; - ret(0, 1) = px*r4; - ret(0, 2) = px*r6; - ret(0, 3) = 2*px*py; - ret(0, 4) = 3*px*px + py*py; - + ret(0, 0) = px * r2; + ret(0, 1) = px * r4; + ret(0, 2) = px * r6; + ret(0, 3) = 2 * px * py; + ret(0, 4) = 3 * px * px + py * py; - ret(1, 0) = py*r2; - ret(1, 1) = py*r4; - ret(1, 2) = py*r6; - ret(1, 3) = px*px + 3*py*py; - ret(1, 4) = 2*px*py; + ret(1, 0) = py * r2; + ret(1, 1) = py * r4; + ret(1, 2) = py * r6; + ret(1, 3) = px * px + 3 * py * py; + ret(1, 4) = 2 * px * py; return ret; } @@ -101,20 +99,19 @@ Vec2 DistortionBrown::removeDistortion(const Vec2& p) const double epsilon = 1e-8; Vec2 undistorted_value = p; - Vec2 diff = addDistortion(undistorted_value) - p; int iter = 0; while (diff.norm() > epsilon) { undistorted_value = undistorted_value - getDerivativeAddDistoWrtPt(undistorted_value).inverse() * diff; - + diff = addDistortion(undistorted_value) - p; iter++; if (iter > 10) { break; - } + } } return undistorted_value; diff --git a/src/aliceVision/camera/DistortionFisheye1.cpp b/src/aliceVision/camera/DistortionFisheye1.cpp index b69a0cf511..4e42b7cdec 100644 --- a/src/aliceVision/camera/DistortionFisheye1.cpp +++ b/src/aliceVision/camera/DistortionFisheye1.cpp @@ -18,7 +18,7 @@ Vec2 DistortionFisheye1::addDistortion(const Vec2& p) const { return p; } - + const double coef = (std::atan(2.0 * r * std::tan(0.5 * k1)) / k1) / r; return p * coef; @@ -48,7 +48,7 @@ Eigen::Matrix2d DistortionFisheye1::getDerivativeAddDistoWrtPt(const Vec2& p) co double d_part1_d_r = 2.0 * std::tan(0.5 * k1); double d_coef_d_part2 = 1.0 / part3; - double d_coef_d_part3 = - part2 / (part3 * part3); + double d_coef_d_part3 = -part2 / (part3 * part3); double d_coef_d_r = d_coef_d_part2 * d_part2_d_part1 * d_part1_d_r + d_coef_d_part3 * d_part3_d_r; Eigen::Matrix d_coef_d_p = d_coef_d_r * d_r_d_p; @@ -72,7 +72,7 @@ Eigen::MatrixXd DistortionFisheye1::getDerivativeAddDistoWrtDisto(const Vec2& p) { return p; } - + const double coef = (std::atan(2.0 * r * std::tan(0.5 * k1)) / k1) / r; return p * coef; @@ -88,7 +88,7 @@ Eigen::MatrixXd DistortionFisheye1::getDerivativeAddDistoWrtDisto(const Vec2& p) double d_part3_d_params = r; double d_part2_d_part1 = 1.0 / (part1 * part1 + 1.0); double d_coef_d_part2 = 1.0 / part3; - double d_coef_d_part3 = - part2 / (part3 * part3); + double d_coef_d_part3 = -part2 / (part3 * part3); double d_coef_d_params = d_coef_d_part3 * d_part3_d_params + d_coef_d_part2 * d_part2_d_part1 * d_part1_d_params; @@ -126,7 +126,6 @@ Eigen::Matrix2d DistortionFisheye1::getDerivativeRemoveDistoWrtPt(const Vec2& p) return Jinv.inverse(); } - Eigen::MatrixXd DistortionFisheye1::getDerivativeRemoveDistoWrtDisto(const Vec2& p) const { const double& k1 = _distortionParams.at(0); @@ -140,7 +139,6 @@ Eigen::MatrixXd DistortionFisheye1::getDerivativeRemoveDistoWrtDisto(const Vec2& const Vec2 p_undist = removeDistortion(p); const double r = sqrt(p_undist(0) * p_undist(0) + p_undist(1) * p_undist(1)); - const double part1 = 2.0 * r * std::tan(0.5 * k1); const double part2 = std::atan(part1); const double part3 = k1 * r; @@ -151,13 +149,13 @@ Eigen::MatrixXd DistortionFisheye1::getDerivativeRemoveDistoWrtDisto(const Vec2& double d_part3_d_params = r; double d_part2_d_part1 = 1.0 / (part1 * part1 + 1.0); double d_coef_d_part2 = (part2 * part2) / part3; - double d_coef_d_part3 = - part2 / (part3 * part3); + double d_coef_d_part3 = -part2 / (part3 * part3); double d_coef_d_params = d_coef_d_part3 * d_part3_d_params + d_coef_d_part2 * d_part2_d_part1 * d_part1_d_params; - //p'/coef + // p'/coef Eigen::Matrix ret; - ret(0, 0) = - p(0) * d_coef_d_params / (coef * coef); - ret(1, 0) = - p(1) * d_coef_d_params / (coef * coef); + ret(0, 0) = -p(0) * d_coef_d_params / (coef * coef); + ret(1, 0) = -p(1) * d_coef_d_params / (coef * coef); return ret; } diff --git a/src/aliceVision/camera/Equidistant.cpp b/src/aliceVision/camera/Equidistant.cpp index 96dca6495f..6bf171e7b9 100644 --- a/src/aliceVision/camera/Equidistant.cpp +++ b/src/aliceVision/camera/Equidistant.cpp @@ -289,7 +289,6 @@ Eigen::Matrix Equidistant::getDerivativeProjectWrtDisto(const Eige Eigen::Matrix4d T = pose; const Vec4 X = T * pt; // apply pose - /* Compute angle with optical center */ const double len2d = sqrt(X(0) * X(0) + X(1) * X(1)); const double angle_Z = std::atan2(len2d, X(2)); @@ -481,7 +480,6 @@ EINTRINSIC Equidistant::getType() const return EINTRINSIC::EQUIDISTANT_CAMERA; } - double Equidistant::getHorizontalFov() const { const double rsensor = std::min(sensorWidth(), sensorHeight()); @@ -492,10 +490,7 @@ double Equidistant::getHorizontalFov() const return fov; } -double Equidistant::getVerticalFov() const -{ - return getHorizontalFov(); -} +double Equidistant::getVerticalFov() const { return getHorizontalFov(); } } // namespace camera } // namespace aliceVision diff --git a/src/aliceVision/camera/Equidistant.hpp b/src/aliceVision/camera/Equidistant.hpp index beefe4f751..680dd05fe4 100644 --- a/src/aliceVision/camera/Equidistant.hpp +++ b/src/aliceVision/camera/Equidistant.hpp @@ -131,13 +131,13 @@ class Equidistant : public IntrinsicScaleOffsetDisto /** * @Brief get horizontal fov in radians * @return horizontal fov in radians - */ + */ double getHorizontalFov() const override; /** * @Brief get vertical fov in radians * @return vertical fov in radians - */ + */ double getVerticalFov() const override; protected: diff --git a/src/aliceVision/camera/Pinhole.hpp b/src/aliceVision/camera/Pinhole.hpp index 4bc00f8fef..ab0a909767 100644 --- a/src/aliceVision/camera/Pinhole.hpp +++ b/src/aliceVision/camera/Pinhole.hpp @@ -105,13 +105,13 @@ class Pinhole : public IntrinsicScaleOffsetDisto /** * @Brief get horizontal fov in radians * @return horizontal fov in radians - */ + */ double getHorizontalFov() const override; /** * @Brief get vertical fov in radians * @return vertical fov in radians - */ + */ double getVerticalFov() const override; }; diff --git a/src/aliceVision/depthMap/volumeIO.cpp b/src/aliceVision/depthMap/volumeIO.cpp index c3fdb7b4d8..b1f68e0163 100644 --- a/src/aliceVision/depthMap/volumeIO.cpp +++ b/src/aliceVision/depthMap/volumeIO.cpp @@ -182,8 +182,8 @@ void exportSimilarityVolume(const CudaHostMemoryHeap& in_volumeSim_hmh, if (simValue > maxValue) continue; const rgb c = getRGBFromJetColorMap(simValue / maxValue); - pointCloud.getLandmarks()[landmarkId] = sfmData::Landmark( - Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); + pointCloud.getLandmarks()[landmarkId] = + sfmData::Landmark(Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); ++landmarkId; } @@ -234,8 +234,8 @@ void exportSimilarityVolumeCross(const CudaHostMemoryHeap& in_volumeSim continue; const rgb c = getRGBFromJetColorMap(simValue / maxValue); - pointCloud.getLandmarks()[landmarkId] = sfmData::Landmark( - Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); + pointCloud.getLandmarks()[landmarkId] = + sfmData::Landmark(Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); ++landmarkId; } @@ -292,8 +292,8 @@ void exportSimilarityVolumeCross(const CudaHostMemoryHeap& in_vol const Point3d p = mp.CArr[camIndex] + (mp.iCamArr[camIndex] * pix).normalize() * depth; const rgb c = getRGBFromJetColorMap(simValue / maxValue); - pointCloud.getLandmarks()[landmarkId] = sfmData::Landmark( - Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); + pointCloud.getLandmarks()[landmarkId] = + sfmData::Landmark(Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); ++landmarkId; } @@ -490,8 +490,8 @@ void exportColorVolume(const CudaHostMemoryHeap& in_volumeSim_hmh, float4 colorValue = *get3DBufferAt_h(in_volumeSim_hmh.getBuffer(), spitch, pitch, vx, vy, vz); const rgb c = float4_to_rgb(colorValue); // TODO: convert Lab color into sRGB color - pointCloud.getLandmarks()[landmarkId] = sfmData::Landmark( - Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); + pointCloud.getLandmarks()[landmarkId] = + sfmData::Landmark(Vec3(p.x, p.y, p.z), feature::EImageDescriberType::UNKNOWN, image::RGBColor(c.r, c.g, c.b)); ++landmarkId; } diff --git a/src/aliceVision/fuseCut/Fuser.cpp b/src/aliceVision/fuseCut/Fuser.cpp index d85004937e..17329ff1f2 100644 --- a/src/aliceVision/fuseCut/Fuser.cpp +++ b/src/aliceVision/fuseCut/Fuser.cpp @@ -526,8 +526,8 @@ bool checkLandmarkMinObservationAngle(const sfmData::SfMData& sfmData, const sfm const geometry::Pose3 poseJ = sfmData.getPose(viewJ).getTransform(); const camera::IntrinsicBase* intrinsicPtrJ = sfmData.getIntrinsicPtr(viewJ.getIntrinsicId()); - const double angle = - camera::angleBetweenRays(poseI, intrinsicPtrI, poseJ, intrinsicPtrJ, observationPairI.second.getCoordinates(), observationPairJ.second.getCoordinates()); + const double angle = camera::angleBetweenRays( + poseI, intrinsicPtrI, poseJ, intrinsicPtrJ, observationPairI.second.getCoordinates(), observationPairJ.second.getCoordinates()); // check angle between two observation if (angle < minObservationAngle) diff --git a/src/aliceVision/geometry/Intersection.cpp b/src/aliceVision/geometry/Intersection.cpp index db178d0fb7..9525e913b7 100644 --- a/src/aliceVision/geometry/Intersection.cpp +++ b/src/aliceVision/geometry/Intersection.cpp @@ -9,7 +9,7 @@ namespace aliceVision { namespace geometry { -bool rayIntersectUnitSphere(Vec3 & coordinates, const Vec3 & start, const Vec3 & direction) +bool rayIntersectUnitSphere(Vec3& coordinates, const Vec3& start, const Vec3& direction) { /* "Which point on the sphere relates to this point ?" @@ -22,16 +22,16 @@ bool rayIntersectUnitSphere(Vec3 & coordinates, const Vec3 & start, const Vec3 & + (lambda * directionz)^2 + startz^2 + 2.0 * lambda * directionoriginz * directionz = 1 - (lambda^2) * (directionx^2 + directiony^2 + directionz^2) - + lambda * (2.0 * directionoriginx * directionx + 2.0 * directionoriginy * directiony + 2.0 * directionoriginz * directionz) + (lambda^2) * (directionx^2 + directiony^2 + directionz^2) + + lambda * (2.0 * directionoriginx * directionx + 2.0 * directionoriginy * directiony + 2.0 * directionoriginz * directionz) + (startx^2 + startx^2 + startx^2) - 1 = 0 */ double a = direction.dot(direction); double b = direction.dot(start) * 2.0; double c = start.dot(start) - 1.0; - double det = b*b - 4.0*a*c; - + double det = b * b - 4.0 * a * c; + if (det < 0) { return false; @@ -48,4 +48,3 @@ bool rayIntersectUnitSphere(Vec3 & coordinates, const Vec3 & start, const Vec3 & } // namespace geometry } // namespace aliceVision - diff --git a/src/aliceVision/geometry/Intersection.hpp b/src/aliceVision/geometry/Intersection.hpp index b191ebda93..f2a3f67947 100644 --- a/src/aliceVision/geometry/Intersection.hpp +++ b/src/aliceVision/geometry/Intersection.hpp @@ -17,9 +17,8 @@ namespace geometry { * @param start the starting point of the ray * @param direction the direction of the ray * @return true if an intersection is found -*/ -bool rayIntersectUnitSphere(Vec3 & coordinates, const Vec3 & start, const Vec3 & direction); + */ +bool rayIntersectUnitSphere(Vec3& coordinates, const Vec3& start, const Vec3& direction); } // namespace geometry } // namespace aliceVision - diff --git a/src/aliceVision/image/dcp.cpp b/src/aliceVision/image/dcp.cpp index 50ed3c2ff6..78f8db6ced 100644 --- a/src/aliceVision/image/dcp.cpp +++ b/src/aliceVision/image/dcp.cpp @@ -4,7 +4,6 @@ #include #include - #include #include diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index 48e098ea85..50f084af0d 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -1014,7 +1014,7 @@ void writeImage(const std::string& path, { const fs::path bPath = fs::path(path); const std::string extension = boost::to_lower_copy(bPath.extension().string()); - const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + extension; + const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + extension; const bool isEXR = (extension == ".exr"); // const bool isTIF = (extension == ".tif"); const bool isJPG = (extension == ".jpg"); @@ -1185,9 +1185,9 @@ void writeImageNoFloat(const std::string& path, { const fs::path bPath = fs::path(path); const std::string extension = boost::to_lower_copy(bPath.extension().string()); - const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + extension; + const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + extension; const bool isEXR = (extension == ".exr"); - //const bool isTIF = (extension == ".tif"); + // const bool isTIF = (extension == ".tif"); const bool isJPG = (extension == ".jpg"); const bool isPNG = (extension == ".png"); diff --git a/src/aliceVision/linearProgramming/lInfinityCV/resection_kernel.hpp b/src/aliceVision/linearProgramming/lInfinityCV/resection_kernel.hpp index dbaa42322a..47d71b65ad 100644 --- a/src/aliceVision/linearProgramming/lInfinityCV/resection_kernel.hpp +++ b/src/aliceVision/linearProgramming/lInfinityCV/resection_kernel.hpp @@ -30,19 +30,13 @@ struct l1SixPointResectionSolver : public robustEstimation::ISolver - #include #include #include diff --git a/src/aliceVision/localization/optimization.cpp b/src/aliceVision/localization/optimization.cpp index 1d405447ff..4c0f7669d4 100644 --- a/src/aliceVision/localization/optimization.cpp +++ b/src/aliceVision/localization/optimization.cpp @@ -152,7 +152,8 @@ bool refineSequence(std::vector& vec_localizationResult, { // this is weird but it could happen when two features are really close to each other (?) ALICEVISION_LOG_DEBUG("Point 3D " << match.landmarkId << " has multiple features " - << "in the same view " << viewID << ", current size of obs: " << landmark.getObservations().size()); + << "in the same view " << viewID + << ", current size of obs: " << landmark.getObservations().size()); ALICEVISION_LOG_DEBUG("its associated features are: "); for (std::size_t i = 0; i < matches.size(); ++i) { diff --git a/src/aliceVision/matching/io.cpp b/src/aliceVision/matching/io.cpp index de1d6ccec6..b39bdb318b 100644 --- a/src/aliceVision/matching/io.cpp +++ b/src/aliceVision/matching/io.cpp @@ -280,7 +280,8 @@ class MatchExporter void saveTxt(const std::string& filepath, const PairwiseMatches::const_iterator& matchBegin, const PairwiseMatches::const_iterator& matchEnd) { const fs::path bPath = fs::path(filepath); - const std::string tmpPath = (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + bPath.extension().string(); + const std::string tmpPath = + (bPath.parent_path() / bPath.stem()).string() + "." + utils::generateUniqueFilename() + bPath.extension().string(); // write temporary file { diff --git a/src/aliceVision/mvsUtils/MultiViewParams.cpp b/src/aliceVision/mvsUtils/MultiViewParams.cpp index 244424fa58..e2d5592585 100644 --- a/src/aliceVision/mvsUtils/MultiViewParams.cpp +++ b/src/aliceVision/mvsUtils/MultiViewParams.cpp @@ -545,8 +545,8 @@ StaticVector MultiViewParams::findNearestCamsFromLandmarks(int rc, int nbNe const geometry::Pose3 otherPose = _sfmData.getPose(otherView).getTransform(); const camera::IntrinsicBase* otherIntrinsicPtr = _sfmData.getIntrinsicPtr(otherView.getIntrinsicId()); - const double angle = - camera::angleBetweenRays(pose, intrinsicPtr, otherPose, otherIntrinsicPtr, viewObsIt->second.getCoordinates(), observationPair.second.getCoordinates()); + const double angle = camera::angleBetweenRays( + pose, intrinsicPtr, otherPose, otherIntrinsicPtr, viewObsIt->second.getCoordinates(), observationPair.second.getCoordinates()); if (angle < _minViewAngle || angle > _maxViewAngle) continue; @@ -634,8 +634,8 @@ std::vector MultiViewParams::findTileNearestCams(int rc, int nbNearestCams, const geometry::Pose3 otherPose = sfmData.getPose(otherView).getTransform(); const camera::IntrinsicBase* otherIntrinsicPtr = sfmData.getIntrinsicPtr(otherView.getIntrinsicId()); - const double angle = - camera::angleBetweenRays(pose, intrinsicPtr, otherPose, otherIntrinsicPtr, viewObsIt->second.getCoordinates(), observationPair.second.getCoordinates()); + const double angle = camera::angleBetweenRays( + pose, intrinsicPtr, otherPose, otherIntrinsicPtr, viewObsIt->second.getCoordinates(), observationPair.second.getCoordinates()); tcScore[tc] += plateauFunction(1, 10, 50, 150, angle); } diff --git a/src/aliceVision/segmentation/segmentation.cpp b/src/aliceVision/segmentation/segmentation.cpp index 07e42da978..e8a54afa74 100644 --- a/src/aliceVision/segmentation/segmentation.cpp +++ b/src/aliceVision/segmentation/segmentation.cpp @@ -53,36 +53,36 @@ bool Segmentation::initialize() // this is false if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) is false if (_parameters.useGpu) { - //Disable for compilation purpose if needed - #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) - OrtCUDAProviderOptionsV2* cuda_options = nullptr; - api.CreateCUDAProviderOptions(&cuda_options); - api.SessionOptionsAppendExecutionProvider_CUDA_V2(static_cast(ortSessionOptions), cuda_options); - api.ReleaseCUDAProviderOptions(cuda_options); - - #if defined(_WIN32) || defined(_WIN64) - std::wstring modelWeights(_parameters.modelWeights.begin(), _parameters.modelWeights.end()); - _ortSession = std::make_unique(*_ortEnvironment, modelWeights.c_str(), ortSessionOptions); - #else - _ortSession = std::make_unique(*_ortEnvironment, _parameters.modelWeights.c_str(), ortSessionOptions); - #endif - - Ort::MemoryInfo memInfoCuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemType::OrtMemTypeDefault); - Ort::Allocator cudaAllocator(*_ortSession, memInfoCuda); - - _output.resize(_parameters.classes.size() * _parameters.modelHeight * _parameters.modelWidth); - _cudaInput = cudaAllocator.Alloc(_output.size() * sizeof(float)); - _cudaOutput = cudaAllocator.Alloc(_output.size() * sizeof(float)); - #endif +// Disable for compilation purpose if needed +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) + OrtCUDAProviderOptionsV2* cuda_options = nullptr; + api.CreateCUDAProviderOptions(&cuda_options); + api.SessionOptionsAppendExecutionProvider_CUDA_V2(static_cast(ortSessionOptions), cuda_options); + api.ReleaseCUDAProviderOptions(cuda_options); + + #if defined(_WIN32) || defined(_WIN64) + std::wstring modelWeights(_parameters.modelWeights.begin(), _parameters.modelWeights.end()); + _ortSession = std::make_unique(*_ortEnvironment, modelWeights.c_str(), ortSessionOptions); + #else + _ortSession = std::make_unique(*_ortEnvironment, _parameters.modelWeights.c_str(), ortSessionOptions); + #endif + + Ort::MemoryInfo memInfoCuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemType::OrtMemTypeDefault); + Ort::Allocator cudaAllocator(*_ortSession, memInfoCuda); + + _output.resize(_parameters.classes.size() * _parameters.modelHeight * _parameters.modelWidth); + _cudaInput = cudaAllocator.Alloc(_output.size() * sizeof(float)); + _cudaOutput = cudaAllocator.Alloc(_output.size() * sizeof(float)); +#endif } else { - #if defined(_WIN32) || defined(_WIN64) - std::wstring modelWeights(_parameters.modelWeights.begin(), _parameters.modelWeights.end()); - _ortSession = std::make_unique(*_ortEnvironment, modelWeights.c_str(), ortSessionOptions); - #else - _ortSession = std::make_unique(*_ortEnvironment, _parameters.modelWeights.c_str(), ortSessionOptions); - #endif +#if defined(_WIN32) || defined(_WIN64) + std::wstring modelWeights(_parameters.modelWeights.begin(), _parameters.modelWeights.end()); + _ortSession = std::make_unique(*_ortEnvironment, modelWeights.c_str(), ortSessionOptions); +#else + _ortSession = std::make_unique(*_ortEnvironment, _parameters.modelWeights.c_str(), ortSessionOptions); +#endif } return true; @@ -90,12 +90,12 @@ bool Segmentation::initialize() bool Segmentation::terminate() { - #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) - Ort::MemoryInfo mem_info_cuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemType::OrtMemTypeDefault); - Ort::Allocator cudaAllocator(*_ortSession, mem_info_cuda); - cudaAllocator.Free(_cudaInput); - cudaAllocator.Free(_cudaOutput); - #endif +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) + Ort::MemoryInfo mem_info_cuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemType::OrtMemTypeDefault); + Ort::Allocator cudaAllocator(*_ortSession, mem_info_cuda); + cudaAllocator.Free(_cudaInput); + cudaAllocator.Free(_cudaOutput); +#endif return true; } @@ -314,7 +314,7 @@ bool Segmentation::processTile(image::Image& labels, const image::I bool Segmentation::processTileGPU(image::Image& labels, const image::Image::Base& source) { ALICEVISION_LOG_TRACE("Process tile using gpu"); - #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CUDA) +#if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CUDA) Ort::MemoryInfo mem_info_cuda("Cuda", OrtAllocatorType::OrtArenaAllocator, 0, OrtMemType::OrtMemTypeDefault); Ort::Allocator cudaAllocator(*_ortSession, mem_info_cuda); @@ -351,7 +351,7 @@ bool Segmentation::processTileGPU(image::Image& labels, const image return false; } - #endif +#endif return true; } diff --git a/src/aliceVision/segmentation/segmentation.hpp b/src/aliceVision/segmentation/segmentation.hpp index c7ef4e049f..bb90ae8fda 100644 --- a/src/aliceVision/segmentation/segmentation.hpp +++ b/src/aliceVision/segmentation/segmentation.hpp @@ -46,10 +46,10 @@ class Segmentation Segmentation(const Parameters& parameters) : _parameters(parameters) { - //Disable gpu if disabled on compilation side - #if !ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) - _parameters.useGpu = false; - #endif +// Disable gpu if disabled on compilation side +#if !ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ONNX_GPU) + _parameters.useGpu = false; +#endif if (!initialize()) { diff --git a/src/aliceVision/sfm/pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp b/src/aliceVision/sfm/pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp index 95767f5c7d..aa05084791 100644 --- a/src/aliceVision/sfm/pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp +++ b/src/aliceVision/sfm/pipeline/structureFromKnownPoses/StructureEstimationFromKnownPoses.cpp @@ -239,9 +239,12 @@ void StructureEstimationFromKnownPoses::filter(const SfMData& sfmData, const Pai std::advance(iterJ, 1); std::advance(iterK, 2); - _tripletMatches[std::make_pair(I, J)][subTrack.descType].emplace_back(iterI->second.featureId, iterJ->second.featureId); - _tripletMatches[std::make_pair(J, K)][subTrack.descType].emplace_back(iterJ->second.featureId, iterK->second.featureId); - _tripletMatches[std::make_pair(I, K)][subTrack.descType].emplace_back(iterI->second.featureId, iterK->second.featureId); + _tripletMatches[std::make_pair(I, J)][subTrack.descType].emplace_back(iterI->second.featureId, + iterJ->second.featureId); + _tripletMatches[std::make_pair(J, K)][subTrack.descType].emplace_back(iterJ->second.featureId, + iterK->second.featureId); + _tripletMatches[std::make_pair(I, K)][subTrack.descType].emplace_back(iterI->second.featureId, + iterK->second.featureId); } } } diff --git a/src/aliceVision/sfmData/HashMapPtr.hpp b/src/aliceVision/sfmData/HashMapPtr.hpp index bfb02e8f72..29165e6659 100644 --- a/src/aliceVision/sfmData/HashMapPtr.hpp +++ b/src/aliceVision/sfmData/HashMapPtr.hpp @@ -11,7 +11,7 @@ namespace aliceVision { namespace sfmData { template -auto cloneT(T * ptr, int val) -> decltype(ptr->clone()) +auto cloneT(T* ptr, int val) -> decltype(ptr->clone()) { if (ptr) { @@ -22,7 +22,7 @@ auto cloneT(T * ptr, int val) -> decltype(ptr->clone()) } template -T * cloneT(T * ptr, long val) +T* cloneT(T* ptr, long val) { if (ptr) { @@ -36,14 +36,13 @@ template class HashMapPtr : public HashMap> { public: - HashMapPtr() : HashMap>() - { - - } + HashMapPtr() + : HashMap>() + {} - HashMapPtr(const HashMapPtr & other) + HashMapPtr(const HashMapPtr& other) { - for (const auto & pair : other) + for (const auto& pair : other) { this->insert({pair.first, std::shared_ptr(cloneT(pair.second.get(), 0))}); } diff --git a/src/aliceVision/sfmData/Landmark.hpp b/src/aliceVision/sfmData/Landmark.hpp index 428930a921..f0ed9e31aa 100644 --- a/src/aliceVision/sfmData/Landmark.hpp +++ b/src/aliceVision/sfmData/Landmark.hpp @@ -22,17 +22,12 @@ namespace sfmData { */ class Landmark { -public: - Landmark() - { - - } + public: + Landmark() {} explicit Landmark(feature::EImageDescriberType descType) : descType(descType) - { - - } + {} Landmark(const Vec3& pos3d, feature::EImageDescriberType descType = feature::EImageDescriberType::UNINITIALIZED, @@ -40,8 +35,7 @@ class Landmark : X(pos3d), descType(descType), rgb(color) - { - } + {} Vec3 X; feature::EImageDescriberType descType = feature::EImageDescriberType::UNINITIALIZED; @@ -50,28 +44,17 @@ class Landmark bool operator==(const Landmark& other) const { - return AreVecNearEqual(X, other.X, 1e-3) && - AreVecNearEqual(rgb, other.rgb, 1e-3) && - _observations == other._observations && - descType == other.descType; + return AreVecNearEqual(X, other.X, 1e-3) && AreVecNearEqual(rgb, other.rgb, 1e-3) && _observations == other._observations && + descType == other.descType; } - inline bool operator!=(const Landmark& other) const - { - return !(*this == other); - } - - const Observations & getObservations() const - { - return _observations; - } + inline bool operator!=(const Landmark& other) const { return !(*this == other); } - Observations & getObservations() - { - return _observations; - } + const Observations& getObservations() const { return _observations; } + + Observations& getObservations() { return _observations; } -private: + private: Observations _observations; }; diff --git a/src/aliceVision/sfmData/Observation.cpp b/src/aliceVision/sfmData/Observation.cpp index 0ce5b99edc..238606c9da 100644 --- a/src/aliceVision/sfmData/Observation.cpp +++ b/src/aliceVision/sfmData/Observation.cpp @@ -9,9 +9,9 @@ namespace aliceVision { namespace sfmData { -bool Observation::operator==(const Observation& other) const -{ - return AreVecNearEqual(_coordinates, other._coordinates, 1e-6) && _idFeature == other._idFeature; +bool Observation::operator==(const Observation& other) const +{ + return AreVecNearEqual(_coordinates, other._coordinates, 1e-6) && _idFeature == other._idFeature; } } // namespace sfmData diff --git a/src/aliceVision/sfmData/Observation.hpp b/src/aliceVision/sfmData/Observation.hpp index 575ea9577b..12a24a2a7c 100644 --- a/src/aliceVision/sfmData/Observation.hpp +++ b/src/aliceVision/sfmData/Observation.hpp @@ -20,44 +20,26 @@ namespace sfmData { */ class Observation { -public: - Observation() - { - } + public: + Observation() {} Observation(const Vec2& p, IndexT idFeat, double scale_) : _coordinates(p), _idFeature(idFeat), _scale(scale_) - { - } + {} bool operator==(const Observation& other) const; - const Vec2 & getCoordinates() const - { - return _coordinates; - } + const Vec2& getCoordinates() const { return _coordinates; } - Vec2 & getCoordinates() - { - return _coordinates; - } + Vec2& getCoordinates() { return _coordinates; } - double getX() const - { - return _coordinates.x(); - } + double getX() const { return _coordinates.x(); } - double getY() const - { - return _coordinates.y(); - } + double getY() const { return _coordinates.y(); } - void setCoordinates(const Vec2 & coordinates) - { - _coordinates = coordinates; - } + void setCoordinates(const Vec2& coordinates) { _coordinates = coordinates; } void setCoordinates(double x, double y) { @@ -65,27 +47,15 @@ class Observation _coordinates(1) = y; } - IndexT getFeatureId() const - { - return _idFeature; - } + IndexT getFeatureId() const { return _idFeature; } - void setFeatureId(IndexT featureId) - { - _idFeature = featureId; - } + void setFeatureId(IndexT featureId) { _idFeature = featureId; } - double getScale() const - { - return _scale; - } + double getScale() const { return _scale; } - void setScale(double scale) - { - _scale = scale; - } + void setScale(double scale) { _scale = scale; } -private: + private: Vec2 _coordinates; IndexT _idFeature = UndefinedIndexT; double _scale = 0.0; diff --git a/src/aliceVision/sfmData/SfMData.hpp b/src/aliceVision/sfmData/SfMData.hpp index 10700968d2..bb588a7397 100644 --- a/src/aliceVision/sfmData/SfMData.hpp +++ b/src/aliceVision/sfmData/SfMData.hpp @@ -207,8 +207,8 @@ class SfMData * @brief Return a shared pointer to an intrinsic if available or nullptr otherwise. * @param[in] v */ - std::shared_ptr getIntrinsicsharedPtr(const View & v) - { + std::shared_ptr getIntrinsicsharedPtr(const View& v) + { IndexT intrinsicId = v.getIntrinsicId(); if (_intrinsics.count(intrinsicId)) @@ -231,8 +231,8 @@ class SfMData * @brief Return a shared pointer to an intrinsic if available or nullptr otherwise. * @param[in] v */ - const std::shared_ptr getIntrinsicsharedPtr(const View & v) const - { + const std::shared_ptr getIntrinsicsharedPtr(const View& v) const + { IndexT intrinsicId = v.getIntrinsicId(); if (_intrinsics.count(intrinsicId)) @@ -516,7 +516,7 @@ class SfMData /** * @Brief For all required items, update the * state with respect to the associated lock - */ + */ void resetParameterStates(); private: diff --git a/src/aliceVision/track/track_test.cpp b/src/aliceVision/track/track_test.cpp index 1300731971..a49167cf15 100644 --- a/src/aliceVision/track/track_test.cpp +++ b/src/aliceVision/track/track_test.cpp @@ -157,13 +157,7 @@ BOOST_AUTO_TEST_CASE(Track_Conflict) // 0, {(0,0) (1,0) (2,0)} // 1, {(0,1) (1,1) (2,6)} const std::pair GT_Tracks[] = { - std::make_pair(0, 0), - std::make_pair(1, 0), - std::make_pair(2, 0), - std::make_pair(0, 1), - std::make_pair(1, 1), - std::make_pair(2, 6) - }; + std::make_pair(0, 0), std::make_pair(1, 0), std::make_pair(2, 0), std::make_pair(0, 1), std::make_pair(1, 1), std::make_pair(2, 6)}; BOOST_CHECK_EQUAL(2, map_tracks.size()); std::size_t cpt = 0, i = 0; diff --git a/src/aliceVision/utils/filesIO.hpp b/src/aliceVision/utils/filesIO.hpp index b0230739c6..134302104d 100644 --- a/src/aliceVision/utils/filesIO.hpp +++ b/src/aliceVision/utils/filesIO.hpp @@ -15,7 +15,6 @@ #include #include - namespace aliceVision { namespace utils { @@ -27,8 +26,7 @@ namespace fs = std::filesystem; * @param[in] the predicate * @return the paths list to the corresponding files if they validate the predicate, otherwise it returns an empty list. */ -inline std::vector getFilesPathsFromFolder(const std::string& folder, - const std::function& predicate) +inline std::vector getFilesPathsFromFolder(const std::string& folder, const std::function& predicate) { // Get all files paths in folder std::vector paths; @@ -90,7 +88,6 @@ inline std::string generateUniqueFilename(const int length = 16) return filename; } - /** * @brief Returns the last time a file was modified (based on OIIO's implementation). * @param[in] path The path to get the last write time from @@ -98,15 +95,15 @@ inline std::string generateUniqueFilename(const int length = 16) */ inline std::time_t getLastWriteTime(const std::string& path) { - #ifdef _WIN32 - struct __stat64 st; - std::wstring_convert, wchar_t> conv; - std::wstring str = conv.from_bytes(path.data(), path.data() + path.size()); - auto r = _wstat64(std::filesystem::path(str).c_str(), &st); - #else - struct stat st; - auto r = stat(std::filesystem::path(path).c_str(), &st); - #endif +#ifdef _WIN32 + struct __stat64 st; + std::wstring_convert, wchar_t> conv; + std::wstring str = conv.from_bytes(path.data(), path.data() + path.size()); + auto r = _wstat64(std::filesystem::path(str).c_str(), &st); +#else + struct stat st; + auto r = stat(std::filesystem::path(path).c_str(), &st); +#endif if (r == 0) // success { diff --git a/src/aliceVision/voctree/SimpleKmeans.hpp b/src/aliceVision/voctree/SimpleKmeans.hpp index 9147c826d2..3c36df70ed 100644 --- a/src/aliceVision/voctree/SimpleKmeans.hpp +++ b/src/aliceVision/voctree/SimpleKmeans.hpp @@ -33,11 +33,7 @@ namespace voctree { struct InitRandom { template - void operator()(const std::vector& features, - size_t k, - std::vector& centers, - Distance distance, - const int verbose = 0) + void operator()(const std::vector& features, size_t k, std::vector& centers, Distance distance, const int verbose = 0) { ALICEVISION_LOG_DEBUG("#\t\tRandom initialization"); // Construct a random permutation of the features using a Fisher-Yates shuffle @@ -64,11 +60,7 @@ struct InitRandom struct InitKmeanspp { template - void operator()(const std::vector& features, - size_t k, - std::vector& centers, - Distance distance, - const int verbose = 0) + void operator()(const std::vector& features, size_t k, std::vector& centers, Distance distance, const int verbose = 0) { typedef typename Distance::result_type squared_distance_type; @@ -219,11 +211,7 @@ struct InitKmeanspp struct InitGiven { template - void operator()(const std::vector& features, - std::size_t k, - std::vector& centers, - Distance distance, - const int verbose = 0) + void operator()(const std::vector& features, std::size_t k, std::vector& centers, Distance distance, const int verbose = 0) { // Do nothing! } @@ -301,8 +289,7 @@ class SimpleKmeans { public: typedef typename Distance::result_type squared_distance_type; - typedef boost::function&, std::size_t, std::vector&, Distance, const int verbose)> - Initializer; + typedef boost::function&, std::size_t, std::vector&, Distance, const int verbose)> Initializer; /** * @brief Constructor @@ -383,11 +370,10 @@ SimpleKmeans::SimpleKmeans(const Feature& zero, Distance d, c {} template -typename SimpleKmeans::squared_distance_type SimpleKmeans::cluster( - const std::vector& features, - size_t k, - std::vector& centers, - std::vector& membership) const +typename SimpleKmeans::squared_distance_type SimpleKmeans::cluster(const std::vector& features, + size_t k, + std::vector& centers, + std::vector& membership) const { std::vector feature_ptrs; feature_ptrs.reserve(features.size()); diff --git a/src/aliceVision/voctree/VocabularyTree.hpp b/src/aliceVision/voctree/VocabularyTree.hpp index c623c59029..96abbc0904 100644 --- a/src/aliceVision/voctree/VocabularyTree.hpp +++ b/src/aliceVision/voctree/VocabularyTree.hpp @@ -98,8 +98,7 @@ inline IVocabularyTree::~IVocabularyTree() {} * typedef specifying the type of the returned distance. For the purposes of VocabularyTree, this need not even be * a metric; distances simply need to be comparable. */ -template class Distance = L2> // TODO: rename Feature into Descriptor +template class Distance = L2> // TODO: rename Feature into Descriptor class VocabularyTree : public IVocabularyTree { public: diff --git a/src/aliceVision/voctree/descriptorLoader.cpp b/src/aliceVision/voctree/descriptorLoader.cpp index 22cadd2a74..712b82ec75 100644 --- a/src/aliceVision/voctree/descriptorLoader.cpp +++ b/src/aliceVision/voctree/descriptorLoader.cpp @@ -91,7 +91,7 @@ void getListOfDescriptorFiles(const sfmData::SfMData& sfmData, { // generate the equivalent .desc file path const std::string filepath = fs::path(fs::path(featureFolder) / (std::to_string(view.first) + "." + - feature::EImageDescriberType_enumToString(descType) + ".desc")) + feature::EImageDescriberType_enumToString(descType) + ".desc")) .string(); if (fs::exists(filepath)) From 341aa19a68b84e4f6a7697d599d6b361300d8425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Candice=20Bent=C3=A9jac?= Date: Fri, 12 Jan 2024 10:34:33 +0100 Subject: [PATCH 3/3] Add latest reformatting commits to `.git-blame-ignore-revs` --- .git-blame-ignore-revs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 74c87a0008..facad6658c 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,7 @@ +# [aliceVision] Reformat src/aliceVision with latest clang-format rules +0fb0a02b75142bcf3b23009428028e2198917275 +# [software] Reformat src/software with latest clang-format rules +ea46ddca23332c115790c02105372bda37d943e3 # Apply `clang-format` on modified files 807034b1eb101b19b3bc9b9078a1635460591959 # Estimator states in SfMData: Apply clang-format across all files