diff --git a/meshroom/aliceVision/TracksShapesInjecting.py b/meshroom/aliceVision/TracksShapesInjecting.py new file mode 100644 index 0000000000..12b7904b95 --- /dev/null +++ b/meshroom/aliceVision/TracksShapesInjecting.py @@ -0,0 +1,53 @@ +__version__ = "1.0" + +from meshroom.core import desc +from meshroom.core.utils import DESCRIBER_TYPES, VERBOSE_LEVEL + + +class TracksShapesInjecting(desc.AVCommandLineNode): + commandLine = "aliceVision_tracksShapesInjecting {allParams}" + size = desc.DynamicNodeSize("input") + + category = "Utils" + documentation = """ +This node creates tracks from keyable points drawn inside meshroom +""" + + inputs = [ + desc.File( + name="input", + label="SfMData", + description="Input SfMData file.", + value="", + exposed=True, + ), + desc.File( + name="shapesFile", + label="Shapes File", + description="Shapes File used to load shapes from meshroom.", + value="", + exposed=True, + ), + desc.BoolParam( + name="markAsSpecial", + label="Is survey", + description="Consider those tracks as 'special' : non removable and very precise.", + value=True, + ), + desc.ChoiceParam( + name="verboseLevel", + label="Verbose Level", + description="Verbosity level (fatal, error, warning, info, debug, trace).", + values=VERBOSE_LEVEL, + value="info", + ), + ] + + outputs = [ + desc.File( + name="output", + label="Tracks", + description="Path to the output tracks file.", + value="{nodeCacheFolder}/tracksFile.json", + ), + ] diff --git a/src/aliceVision/feature/imageDescriberCommon.cpp b/src/aliceVision/feature/imageDescriberCommon.cpp index d6248993de..89ccf796ad 100644 --- a/src/aliceVision/feature/imageDescriberCommon.cpp +++ b/src/aliceVision/feature/imageDescriberCommon.cpp @@ -39,6 +39,7 @@ std::string EImageDescriberType_informations() #endif "* akaze_ocv: OpenCV implementation of A-KAZE describer.\n" #endif + "* manual: Manually selected points.\n " ""; } @@ -62,6 +63,8 @@ std::string EImageDescriberType_enumToString(EImageDescriberType imageDescriberT return "akaze_liop"; case EImageDescriberType::AKAZE_MLDB: return "akaze_mldb"; + case EImageDescriberType::MANUAL: + return "manual"; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) case EImageDescriberType::CCTAG3: @@ -113,6 +116,8 @@ EImageDescriberType EImageDescriberType_stringToEnum(const std::string& imageDes return EImageDescriberType::AKAZE_LIOP; if (type == "akaze_mldb") return EImageDescriberType::AKAZE_MLDB; + if (type == "manual") + return EImageDescriberType::MANUAL; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) if (type == "cctag3") diff --git a/src/aliceVision/feature/imageDescriberCommon.hpp b/src/aliceVision/feature/imageDescriberCommon.hpp index bf9c629810..a0ea58db36 100644 --- a/src/aliceVision/feature/imageDescriberCommon.hpp +++ b/src/aliceVision/feature/imageDescriberCommon.hpp @@ -48,6 +48,8 @@ enum class EImageDescriberType : unsigned char , APRILTAG16H5 = 50 #endif + , + MANUAL }; /** @@ -131,7 +133,8 @@ inline float getStrongSupportCoeff(EImageDescriberType imageDescriberType) case EImageDescriberType::AKAZE_OCV: return 0.14f; #endif // ALICEVISION_HAVE_OPENCV - + case EImageDescriberType::MANUAL: + return 1.0f; case EImageDescriberType::UNKNOWN: return 1.0f; case EImageDescriberType::UNINITIALIZED: diff --git a/src/aliceVision/matching/svgVisualization.cpp b/src/aliceVision/matching/svgVisualization.cpp index d4b9a7d053..73c24ea291 100644 --- a/src/aliceVision/matching/svgVisualization.cpp +++ b/src/aliceVision/matching/svgVisualization.cpp @@ -36,6 +36,8 @@ std::string describerTypeColor(feature::EImageDescriberType descType) return "purple"; case feature::EImageDescriberType::AKAZE_MLDB: return "purple"; + case feature::EImageDescriberType::MANUAL: + return "white"; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_CCTAG) case feature::EImageDescriberType::CCTAG3: return "blue"; diff --git a/src/software/utils/CMakeLists.txt b/src/software/utils/CMakeLists.txt index 4eed8fc848..d26c544da1 100644 --- a/src/software/utils/CMakeLists.txt +++ b/src/software/utils/CMakeLists.txt @@ -71,7 +71,7 @@ if (ALICEVISION_BUILD_SFM) ${Boost_LIBRARIES} ) - # Track builder + # Track Simulating alicevision_add_software(aliceVision_tracksSimulating SOURCE main_tracksSimulating.cpp FOLDER ${FOLDER_SOFTWARE_UTILS} @@ -85,6 +85,18 @@ if (ALICEVISION_BUILD_SFM) Boost::json ) + # Track Shapes injecting + alicevision_add_software(aliceVision_tracksShapesInjecting + SOURCE main_tracksShapesInjecting.cpp + FOLDER ${FOLDER_SOFTWARE_UTILS} + LINKS aliceVision_system + aliceVision_cmdline + aliceVision_sfmData + aliceVision_sfmDataIO + aliceVision_track + Boost::program_options + Boost::json + ) # Track from depthmap alicevision_add_software(aliceVision_depthmapTracksInjecting diff --git a/src/software/utils/main_tracksShapesInjecting.cpp b/src/software/utils/main_tracksShapesInjecting.cpp new file mode 100644 index 0000000000..8c675561a0 --- /dev/null +++ b/src/software/utils/main_tracksShapesInjecting.cpp @@ -0,0 +1,153 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +// These constants define the current software version. +// They must be updated when the command line is changed. +#define ALICEVISION_SOFTWARE_VERSION_MAJOR 1 +#define ALICEVISION_SOFTWARE_VERSION_MINOR 0 + +using namespace aliceVision; + +namespace po = boost::program_options; + +bool readShapes(const std::string & filename, track::TracksMap & map) +{ + std::ifstream shapesFile(filename); + if (shapesFile.is_open() == false) + { + return false; + } + + // Parse json + std::stringstream buffer; + buffer << shapesFile.rdbuf(); + + IndexT count = 0; + + try + { + boost::json::value jv = boost::json::parse(buffer.str()); + + // Iterate through the array + for (const auto & item : jv.as_array()) + { + boost::json::object obj = item.as_object(); + std::string type = boost::json::value_to(obj.at("type")); + + boost::to_lower(type); + + if (type != "point2d") + { + ALICEVISION_LOG_TRACE("Unknown type " << type); + continue; + } + + track::Track & track = map[count]; + track.descType = feature::EImageDescriberType::MANUAL; + + boost::json::object obs = obj.at("observations").as_object(); + for (const auto& [strViewId, value] : obs) + { + IndexT viewId = std::stoul(std::string(strViewId)); + + boost::json::object coords = value.as_object(); + double x = boost::json::value_to(coords.at("x")); + double y = boost::json::value_to(coords.at("y")); + + track::TrackItem & item = track.featPerView[viewId]; + item.featureId = viewId; + item.scale = 1.0; + item.depth = -1.0; + item.coords.x() = x; + item.coords.y() = y; + } + + count++; + } + } + catch (const std::exception & e) + { + std::cout << e.what() << std::endl; + return false; + } + + return true; +} + +int aliceVision_main(int argc, char** argv) +{ + // command-line parameters + std::string outputTracksFileName; + std::string sfmDataFileName; + std::string shapesFilename; + bool markAsSpecial = false; + + // clang-format off + po::options_description requiredParams("Required parameters"); + requiredParams.add_options() + ("input,i", po::value(&sfmDataFileName)->required(), + "SfmData used for sanity check.") + ("shapesFile", po::value(&shapesFilename)->required(), + "Shapes to inject.") + ("outputTracksFileName,o", po::value(&outputTracksFileName)->required(), + "Output Tracks file."); + + po::options_description optionalParams("Optional parameters"); + optionalParams.add_options() + ("markAsSpecial", po::value(&markAsSpecial)->default_value(markAsSpecial), + "Consider these tracks as robust, precise measurements."); + // clang-format on + + CmdLine cmdline("AliceVision tracksShapesInjecting"); + cmdline.add(requiredParams); + cmdline.add(optionalParams); + if (!cmdline.execute(argc, argv)) + { + return EXIT_FAILURE; + } + + // Load input scene + sfmData::SfMData sfmData; + if (!sfmDataIO::load(sfmData, sfmDataFileName, sfmDataIO::ESfMData::VIEWS)) + { + ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFileName << "' cannot be read"); + return EXIT_FAILURE; + } + + track::TracksMap mapTracks; + if (!readShapes(shapesFilename, mapTracks)) + { + ALICEVISION_LOG_ERROR("Can't read shapes to '" << shapesFilename << "'"); + return EXIT_FAILURE; + } + + if (!saveTracks(mapTracks, outputTracksFileName)) + { + ALICEVISION_LOG_ERROR("Can't save tracks to '" << outputTracksFileName << "'"); + return EXIT_FAILURE; + } + + + return EXIT_SUCCESS; +}