Skip to content

Commit dcefe1a

Browse files
author
Fabien Servant
committed
Deep tracks merger
1 parent 8dda3b9 commit dcefe1a

File tree

7 files changed

+269
-28
lines changed

7 files changed

+269
-28
lines changed

meshroom/aliceVision/TracksMerging.py

+4-28
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import os.path
77

88

9-
class TracksMerging(desc.Node):
9+
class TracksMerging(desc.AVCommandLineNode):
10+
commandLine = 'aliceVision_tracksMerging {allParams}'
11+
1012
category = 'Utils'
1113
documentation = '''
1214
Merges multiple track files into one
@@ -42,30 +44,4 @@ class TracksMerging(desc.Node):
4244
value="{nodeCacheFolder}/tracks.json",
4345
)
4446
]
45-
46-
def processChunk(self, chunk):
47-
from pyalicevision import track
48-
49-
chunk.logManager.start(chunk.node.verboseLevel.value)
50-
51-
trackOutput = track.TracksMap()
52-
53-
#Loop over inputs
54-
pos = 0
55-
for input in chunk.node.inputs:
56-
57-
chunk.logger.info(f"Processing input file {input.value}")
58-
trackInput = track.TracksMap()
59-
if not track.loadTracks(trackInput, input.value):
60-
chunk.logger.error("Cannot open input")
61-
chunk.logManager.end()
62-
raise RuntimeError()
63-
64-
for key, value in trackInput.items():
65-
trackOutput[pos] = value
66-
pos = pos + 1
67-
68-
chunk.logger.info(f"Save output to file {chunk.node.output.value}")
69-
track.saveTracks(trackOutput, chunk.node.output.value)
70-
71-
chunk.logManager.end()
47+

src/aliceVision/track/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(tracks_files_headers
55
TracksHandler.hpp
66
tracksUtils.hpp
77
trackIO.hpp
8+
TracksMerger.hpp
89
)
910

1011
# Sources
@@ -13,6 +14,7 @@ set(tracks_files_sources
1314
TracksHandler.cpp
1415
tracksUtils.cpp
1516
trackIO.cpp
17+
TracksMerger.cpp
1618
)
1719

1820
alicevision_add_library(aliceVision_track
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#include <aliceVision/track/TracksMerger.hpp>
8+
9+
namespace aliceVision
10+
{
11+
namespace track
12+
{
13+
14+
bool TracksMerger::addTrackMap(const track::TracksMap & inputTracks)
15+
{
16+
for (const auto & [idTrack, track]: inputTracks)
17+
{
18+
IndexT foundTrack = UndefinedIndexT;
19+
size_t newSize = track.featPerView.size();
20+
21+
for (const auto & [idView, feat]: track.featPerView)
22+
{
23+
TuplePoint tp = std::make_tuple(track.descType, idView, feat.featureId);
24+
auto it = _existingTracks.find(tp);
25+
if (it != _existingTracks.end())
26+
{
27+
foundTrack = it->second;
28+
break;
29+
}
30+
}
31+
32+
if (foundTrack == UndefinedIndexT)
33+
{
34+
//Simply add track
35+
foundTrack = _lastIndex;
36+
_lastIndex++;
37+
}
38+
39+
//New or old, assign the descType to make sure
40+
auto & outputTrack = _tracks[foundTrack];
41+
outputTrack.descType = track.descType;
42+
43+
//Previous Size is either 0 if new track, or the size of the matching track
44+
size_t oldSize = outputTrack.featPerView.size();
45+
46+
//Append all features from existing track
47+
for (const auto & [idView, feat]: track.featPerView)
48+
{
49+
TuplePoint tp = std::make_tuple(track.descType, idView, feat.featureId);
50+
_existingTracks[tp] = foundTrack;
51+
52+
// Replace only if the new tracks is longer than the old one.
53+
if (outputTrack.featPerView.find(idView) != outputTrack.featPerView.end())
54+
{
55+
if (newSize < oldSize)
56+
{
57+
continue;
58+
}
59+
}
60+
61+
outputTrack.featPerView[idView] = feat;
62+
}
63+
}
64+
65+
return true;
66+
}
67+
68+
}
69+
}
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#pragma once
8+
9+
#include <aliceVision/track/Track.hpp>
10+
#include <aliceVision/track/trackIO.hpp>
11+
12+
13+
namespace aliceVision
14+
{
15+
namespace track
16+
{
17+
18+
class TracksMerger
19+
{
20+
public:
21+
//viewId, featureId is a unique identifier for an observation
22+
using TuplePoint = std::tuple<feature::EImageDescriberType, IndexT, std::size_t>;
23+
public:
24+
25+
/**
26+
* @brief add a new set of tracks to the pool of merged tracks
27+
* @param inputTracks the input track to append to the merged tracks
28+
* @return true if everything went ok
29+
*/
30+
bool addTrackMap(const track::TracksMap & inputTracks);
31+
32+
/**
33+
* @brief get the tracks output
34+
* @return a tracksMap const ref
35+
*/
36+
const track::TracksMap & getOutputTracks()
37+
{
38+
return _tracks;
39+
}
40+
41+
private:
42+
track::TracksMap _tracks;
43+
std::map<TuplePoint, IndexT> _existingTracks;
44+
size_t _lastIndex = 0;
45+
};
46+
47+
}
48+
}

src/aliceVision/track/track_test.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// You can obtain one at https://mozilla.org/MPL/2.0/.
77

88
#include "aliceVision/track/TracksBuilder.hpp"
9+
#include "aliceVision/track/TracksMerger.hpp"
910
#include "aliceVision/track/tracksUtils.hpp"
1011
#include "aliceVision/matching/IndMatch.hpp"
1112

@@ -221,3 +222,47 @@ BOOST_AUTO_TEST_CASE(Track_GetCommonTracksInImages)
221222
BOOST_CHECK_EQUAL(base.size(), set_visibleTracks.size());
222223
}
223224
}
225+
226+
BOOST_AUTO_TEST_CASE(Track_Merger)
227+
{
228+
TracksMerger merger;
229+
230+
TracksMap map1, map2;
231+
232+
map1[0].descType = EImageDescriberType::UNKNOWN;
233+
map1[0].featPerView[0].featureId = 100;
234+
map1[0].featPerView[1].featureId = 101;
235+
map1[0].featPerView[2].featureId = 102;
236+
map1[0].featPerView[3].featureId = 103;
237+
238+
map1[1].descType = EImageDescriberType::SIFT;
239+
map1[1].featPerView[0].featureId = 200;
240+
map1[1].featPerView[1].featureId = 201;
241+
map1[1].featPerView[2].featureId = 202;
242+
map1[1].featPerView[3].featureId = 203;
243+
244+
map2[0].descType = EImageDescriberType::UNKNOWN;
245+
map2[0].featPerView[0].featureId = 100;
246+
map2[0].featPerView[4].featureId = 104;
247+
map2[0].featPerView[5].featureId = 105;
248+
map2[0].featPerView[6].featureId = 106;
249+
250+
map2[1].descType = EImageDescriberType::UNKNOWN;
251+
map2[1].featPerView[0].featureId = 200;
252+
map2[1].featPerView[1].featureId = 204;
253+
map2[1].featPerView[2].featureId = 205;
254+
map2[1].featPerView[3].featureId = 206;
255+
256+
merger.addTrackMap(map1);
257+
merger.addTrackMap(map2);
258+
259+
const auto & result = merger.getOutputTracks();
260+
261+
BOOST_CHECK_EQUAL(result.size(), 3);
262+
263+
size_t max = result.at(0).featPerView.size();
264+
max = std::max(max, result.at(1).featPerView.size());
265+
max = std::max(max, result.at(2).featPerView.size());
266+
267+
BOOST_CHECK_EQUAL(max, 7);
268+
}

src/software/utils/CMakeLists.txt

+10
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,16 @@ if (ALICEVISION_BUILD_SFM)
282282
Boost::program_options
283283
)
284284

285+
# Tracks Merge
286+
alicevision_add_software(aliceVision_tracksMerging
287+
SOURCE main_tracksMerging.cpp
288+
FOLDER ${FOLDER_SOFTWARE_UTILS}
289+
LINKS aliceVision_system
290+
aliceVision_cmdline
291+
aliceVision_track
292+
Boost::program_options
293+
)
294+
285295
# SfM Pose Injecting
286296
alicevision_add_software(aliceVision_sfmPoseInjecting
287297
SOURCE main_sfmPoseInjecting.cpp
+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#include <aliceVision/cmdline/cmdline.hpp>
8+
#include <aliceVision/system/main.hpp>
9+
#include <boost/program_options.hpp>
10+
11+
#include <aliceVision/track/Track.hpp>
12+
#include <aliceVision/track/trackIO.hpp>
13+
#include <aliceVision/track/TracksMerger.cpp>
14+
15+
#include <string>
16+
#include <sstream>
17+
#include <random>
18+
19+
// These constants define the current software version.
20+
// They must be updated when the command line is changed.
21+
#define ALICEVISION_SOFTWARE_VERSION_MAJOR 1
22+
#define ALICEVISION_SOFTWARE_VERSION_MINOR 0
23+
24+
using namespace aliceVision;
25+
26+
namespace po = boost::program_options;
27+
28+
int aliceVision_main(int argc, char** argv)
29+
{
30+
// command-line parameters
31+
std::vector<std::string> tracksFilenames;
32+
std::string outFilename;
33+
34+
// clang-format off
35+
po::options_description requiredParams("Required parameters");
36+
requiredParams.add_options()
37+
("inputs,i", po::value<std::vector<std::string>>(&tracksFilenames)->multitoken(),
38+
"Path to sfmDatas to merge.")
39+
("output,o", po::value<std::string>(&outFilename)->required(),
40+
"Output SfMData scene.");
41+
// clang-format on
42+
43+
CmdLine cmdline("AliceVision sfmMerge");
44+
cmdline.add(requiredParams);
45+
if (!cmdline.execute(argc, argv))
46+
{
47+
return EXIT_FAILURE;
48+
}
49+
50+
if (tracksFilenames.empty())
51+
{
52+
ALICEVISION_LOG_ERROR("At least one tracks input should be given.");
53+
return EXIT_FAILURE;
54+
}
55+
56+
//viewId, featureId is a unique identifier for an observation
57+
using TuplePoint = std::tuple<feature::EImageDescriberType, IndexT, std::size_t>;
58+
59+
track::TracksMerger merger;
60+
61+
for (const auto & path : tracksFilenames)
62+
{
63+
track::TracksMap mapTracks;
64+
65+
ALICEVISION_LOG_INFO("Loading " << path);
66+
if (!track::loadTracks(mapTracks, path))
67+
{
68+
continue;
69+
}
70+
71+
ALICEVISION_LOG_INFO("File has " << mapTracks.size() << " tracks.");
72+
73+
if (!merger.addTrackMap(mapTracks))
74+
{
75+
ALICEVISION_LOG_ERROR("addTrackMap failed, abort");
76+
return EXIT_FAILURE;
77+
}
78+
}
79+
80+
const auto & resultTracks = merger.getOutputTracks();
81+
82+
ALICEVISION_LOG_INFO("Saving to " << outFilename);
83+
ALICEVISION_LOG_INFO("File has " << resultTracks.size() << " tracks.");
84+
if (!track::saveTracks(resultTracks, outFilename))
85+
{
86+
ALICEVISION_LOG_ERROR("Failed to save file");
87+
return EXIT_FAILURE;
88+
}
89+
90+
return EXIT_SUCCESS;
91+
}

0 commit comments

Comments
 (0)