Skip to content

Commit

Permalink
[pipeline] added software triangulate
Browse files Browse the repository at this point in the history
  • Loading branch information
mh0g committed Dec 22, 2022
1 parent 830d5e0 commit ffb4764
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/software/pipeline/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ if(ALICEVISION_BUILD_SFM)
Boost::filesystem
)

#Triangulate
alicevision_add_software(aliceVision_triangulate
SOURCE main_triangulate.cpp
FOLDER ${FOLDER_SOFTWARE_PIPELINE}
LINKS aliceVision_system
aliceVision_image
aliceVision_feature
aliceVision_sfm
aliceVision_sfmData
aliceVision_sfmDataIO
Boost::program_options
Boost::filesystem
)

# Global SfM
alicevision_add_software(aliceVision_globalSfM
SOURCE main_globalSfM.cpp
Expand Down Expand Up @@ -476,4 +490,4 @@ alicevision_add_software(aliceVision_LdrToHdrMerge
Boost::filesystem
)

endif()
endif()
272 changes: 272 additions & 0 deletions src/software/pipeline/main_triangulate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// This file is part of the AliceVision project.
// Copyright (c) 2022 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include <aliceVision/sfmData/SfMData.hpp>
#include <aliceVision/sfmDataIO/sfmDataIO.hpp>
#include <aliceVision/sfm/sfm.hpp>
#include <aliceVision/sfm/pipeline/regionsIO.hpp>
#include <aliceVision/feature/imageDescriberCommon.hpp>
#include <aliceVision/system/Timer.hpp>
#include <aliceVision/system/Logger.hpp>
#include <aliceVision/system/main.hpp>
#include <aliceVision/system/cmdline.hpp>
#include <aliceVision/types.hpp>
#include <aliceVision/config.hpp>
#include <aliceVision/track/TracksBuilder.hpp>
#include <aliceVision/sfm/BundleAdjustment.hpp>

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

#include <cstdlib>

// 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 = boost::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
* @param[in] name the image name to find (uid or filename or path)
* @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)
{
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.getImagePath()).filename().string() ||
name == v.getImagePath())
{
out_viewId = v.getViewId();
break;
}
}

if (out_viewId == UndefinedIndexT)
ALICEVISION_LOG_ERROR("Can't find the given initial pair view: " << name);

return out_viewId != UndefinedIndexT;
}


int aliceVision_main(int argc, char** argv)
{
// command-line parameters
std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel());
std::string sfmDataFilename;
std::vector<std::string> featuresFolders;
std::vector<std::string> matchesFolders;
std::string outputSfM;

// user optional parameters
std::string extraInfoFolder;
std::string describerTypesName = feature::EImageDescriberType_enumToString(feature::EImageDescriberType::SIFT);
std::pair<std::string, std::string> initialPairString("", "");

sfm::ReconstructionEngine_sequentialSfM::Params sfmParams;
int maxNbMatches = 0;
int minNbMatches = 0;
bool useOnlyMatchesFromInputFolder = false;
bool computeStructureColor = true;

int randomSeed = std::mt19937::default_seed;

po::options_description allParams(
"Triangulation\n"
"Perform the triangulation from the incremental SfM step\n"
"AliceVision Triangulation");

po::options_description requiredParams("Required parameters");
requiredParams.add_options()
("input,i", po::value<std::string>(&sfmDataFilename)->required(),
"SfMData file, must contain the camera calibration.")
("output,o", po::value<std::string>(&outputSfM)->required(),
"Path to the output SfMData file.")
("featuresFolders,f", po::value<std::vector<std::string>>(&featuresFolders)->multitoken(),
"Path to folder(s) containing the extracted features.")
("matchesFolders,m", po::value<std::vector<std::string>>(&matchesFolders)->multitoken(),
"Path to folder(s) in which computed matches are stored.");

po::options_description optionalParams("Optional parameters");
optionalParams.add_options()

("extraInfoFolder", po::value<std::string>(&extraInfoFolder)->default_value(extraInfoFolder),
"Folder for intermediate reconstruction files and additional reconstruction information files.")
("describerTypes,d", po::value<std::string>(&describerTypesName)->default_value(describerTypesName),
feature::EImageDescriberType_informations().c_str())
("interFileExtension", po::value<std::string>(&sfmParams.sfmStepFileExtension)->default_value(sfmParams.sfmStepFileExtension),
"Extension of the intermediate file export.")
("maxNumberOfMatches", po::value<int>(&maxNbMatches)->default_value(maxNbMatches),
"Maximum number of matches per image pair (and per feature type). "
"This can be useful to have a quick reconstruction overview. 0 means no limit.")
("minNumberOfMatches", po::value<int>(&minNbMatches)->default_value(minNbMatches),
"Minimum number of matches per image pair (and per feature type). "
"This can be useful to have a meaningful reconstruction with accurate keypoints. 0 means no limit.")
("minAngleForTriangulation", po::value<double>(&sfmParams.minAngleForTriangulation)->default_value(sfmParams.minAngleForTriangulation),
"Minimum angle for triangulation.")
("minAngleForLandmark", po::value<double>(&sfmParams.minAngleForLandmark)->default_value(sfmParams.minAngleForLandmark),
"Minimum angle for landmark.")
("minNumberOfObservationsForTriangulation", po::value<std::size_t>(&sfmParams.minNbObservationsForTriangulation)->default_value(sfmParams.minNbObservationsForTriangulation),
"Minimum number of observations to triangulate a point.\n"
"Set it to 3 (or more) reduces drastically the noise in the point cloud, but the number of final poses is a little bit reduced (from 1.5% to 11% on the tested datasets).\n"
"Note: set it to 0 or 1 to use the old triangulation algorithm (using 2 views only) during resection.")
("useRigConstraint", po::value<bool>(&sfmParams.rig.useRigConstraint)->default_value(sfmParams.rig.useRigConstraint),
"Enable/Disable rig constraint.\n")
("rigMinNbCamerasForCalibration", po::value<int>(&sfmParams.rig.minNbCamerasForCalibration)->default_value(sfmParams.rig.minNbCamerasForCalibration),
"Minimal number of cameras to start the calibration of the rig.\n")
("observationConstraint", po::value<EFeatureConstraint>(&sfmParams.featureConstraint)->default_value(sfmParams.featureConstraint),
"Use of an observation constraint : basic, scale the observation or use of the covariance.\n")
("computeStructureColor", po::value<bool>(&computeStructureColor)->default_value(computeStructureColor),
"Compute each 3D point color.\n")
("randomSeed", po::value<int>(&randomSeed)->default_value(randomSeed),
"This seed value will generate a sequence using a linear random generator. Set -1 to use a random seed.")
;

po::options_description logParams("Log parameters");
logParams.add_options()
("verboseLevel,v", po::value<std::string>(&verboseLevel)->default_value(verboseLevel),
"verbosity level (fatal, error, warning, info, debug, trace).");

allParams.add(requiredParams).add(optionalParams).add(logParams);

po::variables_map vm;
try
{
po::store(po::parse_command_line(argc, argv, allParams), vm);

if (vm.count("help") || (argc == 1))
{
ALICEVISION_COUT(allParams);
return EXIT_SUCCESS;
}
po::notify(vm);
}
catch (boost::program_options::required_option& e)
{
ALICEVISION_CERR("ERROR: " << e.what());
ALICEVISION_COUT("Usage:\n\n" << allParams);
return EXIT_FAILURE;
}
catch (boost::program_options::error& e)
{
ALICEVISION_CERR("ERROR: " << e.what());
ALICEVISION_COUT("Usage:\n\n" << allParams);
return EXIT_FAILURE;
}

ALICEVISION_COUT("Program called with the following parameters:");
ALICEVISION_COUT(vm);

// set verbose level
system::Logger::get()->setLogLevel(verboseLevel);

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;
}

// get imageDescriber type
const std::vector<feature::EImageDescriberType> 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);

// triangulate
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;
}

//instantiate an sfmEngine for triangulation only
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);

//run the triangulation
std::set<IndexT> reconstructedViews = sfmData.getValidViews();
sfmEngine.triangulate({}, reconstructedViews);

// 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();

ALICEVISION_LOG_INFO("Triangulation took (s): " + std::to_string(timer.elapsed()));

// 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);

ALICEVISION_LOG_INFO("Triangulation Done" << std::endl
<< "\t- # landmarks: " << sfmEngine.getSfMData().getLandmarks().size());

return EXIT_SUCCESS;
}

0 comments on commit ffb4764

Please sign in to comment.