Skip to content

Deep merging of multiple tracks sets without using original matches #1865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 4 additions & 28 deletions meshroom/aliceVision/TracksMerging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import os.path


class TracksMerging(desc.Node):
class TracksMerging(desc.AVCommandLineNode):
commandLine = 'aliceVision_tracksMerging {allParams}'

category = 'Utils'
documentation = '''
Merges multiple track files into one
Expand Down Expand Up @@ -42,30 +44,4 @@ class TracksMerging(desc.Node):
value="{nodeCacheFolder}/tracks.json",
)
]

def processChunk(self, chunk):
from pyalicevision import track

chunk.logManager.start(chunk.node.verboseLevel.value)

trackOutput = track.TracksMap()

#Loop over inputs
pos = 0
for input in chunk.node.inputs:

chunk.logger.info(f"Processing input file {input.value}")
trackInput = track.TracksMap()
if not track.loadTracks(trackInput, input.value):
chunk.logger.error("Cannot open input")
chunk.logManager.end()
raise RuntimeError()

for key, value in trackInput.items():
trackOutput[pos] = value
pos = pos + 1

chunk.logger.info(f"Save output to file {chunk.node.output.value}")
track.saveTracks(trackOutput, chunk.node.output.value)

chunk.logManager.end()

2 changes: 2 additions & 0 deletions src/aliceVision/track/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(tracks_files_headers
TracksHandler.hpp
tracksUtils.hpp
trackIO.hpp
TracksMerger.hpp
)

# Sources
Expand All @@ -13,6 +14,7 @@ set(tracks_files_sources
TracksHandler.cpp
tracksUtils.cpp
trackIO.cpp
TracksMerger.cpp
)

alicevision_add_library(aliceVision_track
Expand Down
69 changes: 69 additions & 0 deletions src/aliceVision/track/TracksMerger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 <aliceVision/track/TracksMerger.hpp>

namespace aliceVision
{
namespace track
{

bool TracksMerger::addTrackMap(const track::TracksMap & inputTracks)
{
for (const auto & [idTrack, track]: inputTracks)
{
IndexT foundTrack = UndefinedIndexT;
size_t newSize = track.featPerView.size();

for (const auto & [idView, feat]: track.featPerView)
{
TuplePoint tp = std::make_tuple(track.descType, idView, feat.featureId);
auto it = _existingTracks.find(tp);
if (it != _existingTracks.end())
{
foundTrack = it->second;
break;
}
}

if (foundTrack == UndefinedIndexT)
{
//Simply add track
foundTrack = _lastIndex;
_lastIndex++;
}

//New or old, assign the descType to make sure
auto & outputTrack = _tracks[foundTrack];
outputTrack.descType = track.descType;

//Previous Size is either 0 if new track, or the size of the matching track
size_t oldSize = outputTrack.featPerView.size();

//Append all features from existing track
for (const auto & [idView, feat]: track.featPerView)
{
TuplePoint tp = std::make_tuple(track.descType, idView, feat.featureId);
_existingTracks[tp] = foundTrack;

// Replace only if the new tracks is longer than the old one.
if (outputTrack.featPerView.find(idView) != outputTrack.featPerView.end())
{
if (newSize < oldSize)
{
continue;
}
}

outputTrack.featPerView[idView] = feat;
}
}

return true;
}

}
}
48 changes: 48 additions & 0 deletions src/aliceVision/track/TracksMerger.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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/.

#pragma once

#include <aliceVision/track/Track.hpp>
#include <aliceVision/track/trackIO.hpp>


namespace aliceVision
{
namespace track
{

class TracksMerger
{
public:
//viewId, featureId is a unique identifier for an observation
using TuplePoint = std::tuple<feature::EImageDescriberType, IndexT, std::size_t>;
public:

/**
* @brief add a new set of tracks to the pool of merged tracks
* @param inputTracks the input track to append to the merged tracks
* @return true if everything went ok
*/
bool addTrackMap(const track::TracksMap & inputTracks);

/**
* @brief get the tracks output
* @return a tracksMap const ref
*/
const track::TracksMap & getOutputTracks()
{
return _tracks;
}

private:
track::TracksMap _tracks;
std::map<TuplePoint, IndexT> _existingTracks;
size_t _lastIndex = 0;
};

}
}
45 changes: 45 additions & 0 deletions src/aliceVision/track/track_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include "aliceVision/track/TracksBuilder.hpp"
#include "aliceVision/track/TracksMerger.hpp"
#include "aliceVision/track/tracksUtils.hpp"
#include "aliceVision/matching/IndMatch.hpp"

Expand Down Expand Up @@ -221,3 +222,47 @@ BOOST_AUTO_TEST_CASE(Track_GetCommonTracksInImages)
BOOST_CHECK_EQUAL(base.size(), set_visibleTracks.size());
}
}

BOOST_AUTO_TEST_CASE(Track_Merger)
{
TracksMerger merger;

TracksMap map1, map2;

map1[0].descType = EImageDescriberType::UNKNOWN;
map1[0].featPerView[0].featureId = 100;
map1[0].featPerView[1].featureId = 101;
map1[0].featPerView[2].featureId = 102;
map1[0].featPerView[3].featureId = 103;

map1[1].descType = EImageDescriberType::SIFT;
map1[1].featPerView[0].featureId = 200;
map1[1].featPerView[1].featureId = 201;
map1[1].featPerView[2].featureId = 202;
map1[1].featPerView[3].featureId = 203;

map2[0].descType = EImageDescriberType::UNKNOWN;
map2[0].featPerView[0].featureId = 100;
map2[0].featPerView[4].featureId = 104;
map2[0].featPerView[5].featureId = 105;
map2[0].featPerView[6].featureId = 106;

map2[1].descType = EImageDescriberType::UNKNOWN;
map2[1].featPerView[0].featureId = 200;
map2[1].featPerView[1].featureId = 204;
map2[1].featPerView[2].featureId = 205;
map2[1].featPerView[3].featureId = 206;

merger.addTrackMap(map1);
merger.addTrackMap(map2);

const auto & result = merger.getOutputTracks();

BOOST_CHECK_EQUAL(result.size(), 3);

size_t max = result.at(0).featPerView.size();
max = std::max(max, result.at(1).featPerView.size());
max = std::max(max, result.at(2).featPerView.size());

BOOST_CHECK_EQUAL(max, 7);
}
10 changes: 10 additions & 0 deletions src/software/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,16 @@ if (ALICEVISION_BUILD_SFM)
Boost::program_options
)

# Tracks Merge
alicevision_add_software(aliceVision_tracksMerging
SOURCE main_tracksMerging.cpp
FOLDER ${FOLDER_SOFTWARE_UTILS}
LINKS aliceVision_system
aliceVision_cmdline
aliceVision_track
Boost::program_options
)

# SfM Pose Injecting
alicevision_add_software(aliceVision_sfmPoseInjecting
SOURCE main_sfmPoseInjecting.cpp
Expand Down
91 changes: 91 additions & 0 deletions src/software/utils/main_tracksMerging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// 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 <aliceVision/cmdline/cmdline.hpp>
#include <aliceVision/system/main.hpp>
#include <boost/program_options.hpp>

#include <aliceVision/track/Track.hpp>
#include <aliceVision/track/trackIO.hpp>
#include <aliceVision/track/TracksMerger.cpp>

#include <string>
#include <sstream>
#include <random>

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

int aliceVision_main(int argc, char** argv)
{
// command-line parameters
std::vector<std::string> tracksFilenames;
std::string outFilename;

// clang-format off
po::options_description requiredParams("Required parameters");
requiredParams.add_options()
("inputs,i", po::value<std::vector<std::string>>(&tracksFilenames)->multitoken(),
"Path to sfmDatas to merge.")
("output,o", po::value<std::string>(&outFilename)->required(),
"Output SfMData scene.");
// clang-format on

CmdLine cmdline("AliceVision sfmMerge");
cmdline.add(requiredParams);
if (!cmdline.execute(argc, argv))
{
return EXIT_FAILURE;
}

if (tracksFilenames.empty())
{
ALICEVISION_LOG_ERROR("At least one tracks input should be given.");
return EXIT_FAILURE;
}

//viewId, featureId is a unique identifier for an observation
using TuplePoint = std::tuple<feature::EImageDescriberType, IndexT, std::size_t>;

track::TracksMerger merger;

for (const auto & path : tracksFilenames)
{
track::TracksMap mapTracks;

ALICEVISION_LOG_INFO("Loading " << path);
if (!track::loadTracks(mapTracks, path))
{
continue;
}

ALICEVISION_LOG_INFO("File has " << mapTracks.size() << " tracks.");

if (!merger.addTrackMap(mapTracks))
{
ALICEVISION_LOG_ERROR("addTrackMap failed, abort");
return EXIT_FAILURE;
}
}

const auto & resultTracks = merger.getOutputTracks();

ALICEVISION_LOG_INFO("Saving to " << outFilename);
ALICEVISION_LOG_INFO("File has " << resultTracks.size() << " tracks.");
if (!track::saveTracks(resultTracks, outFilename))
{
ALICEVISION_LOG_ERROR("Failed to save file");
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}