Skip to content

Commit 755cf9b

Browse files
authored
Merge pull request #1864 from alicevision/dev/mergeTracks
Tracks updates
2 parents 2741284 + 1721b29 commit 755cf9b

12 files changed

Lines changed: 200 additions & 23 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
__version__ = "3.0"
2+
3+
from meshroom.core import desc
4+
from meshroom.core.utils import DESCRIBER_TYPES, VERBOSE_LEVEL
5+
6+
import os.path
7+
8+
9+
class TracksMerging(desc.Node):
10+
category = 'Utils'
11+
documentation = '''
12+
Merges multiple track files into one
13+
'''
14+
15+
inputs = [
16+
desc.ListAttribute(
17+
elementDesc=desc.File(
18+
name="input",
19+
label="Input Track File",
20+
description="A track file.",
21+
value="",
22+
),
23+
name="inputs",
24+
label="Inputs",
25+
description="Set of track files (at least 1 is required).",
26+
exposed=True,
27+
),
28+
desc.ChoiceParam(
29+
name="verboseLevel",
30+
label="Verbose Level",
31+
description="Verbosity level (fatal, error, warning, info, debug, trace).",
32+
values=VERBOSE_LEVEL,
33+
value="info",
34+
)
35+
]
36+
37+
outputs = [
38+
desc.File(
39+
name="output",
40+
label="Output tracks file",
41+
description="Path to the output track file",
42+
value="{nodeCacheFolder}/tracks.json",
43+
)
44+
]
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()

src/aliceVision/aliceVision.i

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
%import <aliceVision/sfmDataIO/SfMDataIO.i>
1818
%import <aliceVision/sfmData/SfMData.i>
1919
%import <aliceVision/stl/Stl.i>
20+
%import <aliceVision/track/track.i>
2021

2122
%{
2223
#include <aliceVision/version.hpp>

src/aliceVision/track/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,18 @@ alicevision_add_library(aliceVision_track
2828

2929
# Unit tests
3030
alicevision_add_test(track_test.cpp NAME "track" LINKS aliceVision_track)
31+
32+
# SWIG Binding
33+
if (ALICEVISION_BUILD_SWIG_BINDING)
34+
alicevision_swig_add_library(track
35+
SOURCES track.i
36+
PUBLIC_LINKS
37+
aliceVision_track
38+
${Python3_LIBRARIES}
39+
PRIVATE_INCLUDE_DIRS
40+
../include
41+
${ALICEVISION_ROOT}/include
42+
${Python3_INCLUDE_DIRS}
43+
${Python3_NumPy_INCLUDE_DIRS}
44+
)
45+
endif()

src/aliceVision/track/Track.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
#include <set>
2020
#include <map>
2121
#include <memory>
22+
#include <unordered_map>
2223
#include <aliceVision/numeric/numeric.hpp>
2324

2425
namespace aliceVision {
2526
namespace track {
2627

27-
using namespace aliceVision::matching;
2828

2929
using FeatureId = std::pair<feature::EImageDescriberType, std::size_t>;
3030

@@ -70,7 +70,7 @@ struct TrackItem
7070
struct Track
7171
{
7272
/// Data structure to store a track: collection of {ViewId, FeatureId}
73-
using TrackInfoPerView = stl::flat_map<std::size_t, TrackItem>;
73+
using TrackInfoPerView = std::unordered_map<std::size_t, TrackItem>;
7474

7575
Track() {}
7676

@@ -81,7 +81,7 @@ struct Track
8181
};
8282

8383
/// A track is a collection of {trackId, Track}
84-
using TracksMap = stl::flat_map<std::size_t, Track>;
84+
using TracksMap = std::unordered_map<std::size_t, Track>;
8585
using TrackIdSet = std::vector<std::size_t>;
8686

8787
/**

src/aliceVision/track/TracksBuilder.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ TracksBuilder::TracksBuilder() { _d.reset(new TracksBuilderData()); }
4242

4343
TracksBuilder::~TracksBuilder() = default;
4444

45-
void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
45+
void TracksBuilder::build(const matching::PairwiseMatches& pairwiseMatches)
4646
{
4747
typedef std::set<IndexedFeaturePair> SetIndexedPair;
4848

@@ -54,14 +54,14 @@ void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
5454
{
5555
const std::size_t& I = matchesPerDescIt.first.first;
5656
const std::size_t& J = matchesPerDescIt.first.second;
57-
const MatchesPerDescType& matchesPerDesc = matchesPerDescIt.second;
57+
const matching::MatchesPerDescType& matchesPerDesc = matchesPerDescIt.second;
5858

5959
for (const auto& matchesIt : matchesPerDesc)
6060
{
6161
const feature::EImageDescriberType descType = matchesIt.first;
62-
const IndMatches& matches = matchesIt.second;
62+
const matching::IndMatches& matches = matchesIt.second;
6363
// we have correspondences between I and J image index.
64-
for (const IndMatch& m : matches)
64+
for (const matching::IndMatch& m : matches)
6565
{
6666
IndexedFeaturePair pairI(I, KeypointId(descType, m._i));
6767
IndexedFeaturePair pairJ(J, KeypointId(descType, m._j));
@@ -97,14 +97,14 @@ void TracksBuilder::build(const PairwiseMatches& pairwiseMatches)
9797
{
9898
const std::size_t& I = matchesPerDescIt.first.first;
9999
const std::size_t& J = matchesPerDescIt.first.second;
100-
const MatchesPerDescType& matchesPerDesc = matchesPerDescIt.second;
100+
const matching::MatchesPerDescType& matchesPerDesc = matchesPerDescIt.second;
101101

102102
for (const auto& matchesIt : matchesPerDesc)
103103
{
104104
const feature::EImageDescriberType descType = matchesIt.first;
105-
const IndMatches& matches = matchesIt.second;
105+
const matching::IndMatches& matches = matchesIt.second;
106106
// we have correspondences between I and J image index.
107-
for (const IndMatch& m : matches)
107+
for (const matching::IndMatch& m : matches)
108108
{
109109
IndexedFeaturePair pairI(I, KeypointId(descType, m._i));
110110
IndexedFeaturePair pairJ(J, KeypointId(descType, m._j));

src/aliceVision/track/TracksBuilder.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class TracksBuilder
5353
* @param[in] pairwiseMatches PairWise matches
5454
* @param[in] featuresPerView all features (used for matching) in pairwiseMatches
5555
*/
56-
void build(const PairwiseMatches& pairwiseMatches);
56+
void build(const matching::PairwiseMatches& pairwiseMatches);
5757

5858
/**
5959
* @brief Remove bad tracks (too short or track with ids collision)

src/aliceVision/track/TracksHandler.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,12 @@ namespace track {
1616

1717
bool TracksHandler::load(const std::string& pathJson, const std::set<IndexT>& viewIds)
1818
{
19-
std::ifstream tracksFile(pathJson);
20-
if (tracksFile.is_open() == false)
19+
_mapTracks.clear();
20+
if (!loadTracks(_mapTracks, pathJson))
2121
{
2222
return false;
2323
}
2424

25-
std::stringstream buffer;
26-
buffer << tracksFile.rdbuf();
27-
28-
// Parse json
29-
boost::json::value jv = boost::json::parse(buffer.str());
30-
_mapTracks = track::TracksMap(track::flat_map_value_to<track::Track>(jv));
31-
3225
// Compute tracks per view
3326
_mapTracksPerView.clear();
3427
for (const auto& viewId : viewIds)

src/aliceVision/track/track.i

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
%module (module="pyalicevision") track
8+
9+
%include <std_unordered_map.i>
10+
%include <std_string.i>
11+
12+
%include <aliceVision/track/Track.hpp>
13+
%include <aliceVision/track/trackIO.hpp>
14+
15+
namespace std
16+
{
17+
typedef long unsigned int size_t;
18+
}
19+
20+
%{
21+
#include <aliceVision/track/Track.hpp>
22+
#include <aliceVision/track/trackIO.hpp>
23+
24+
using namespace aliceVision;
25+
%}
26+
27+
%template(TrackInfoPerView) std::unordered_map<std::size_t, aliceVision::track::TrackItem>;
28+
%template(TracksMap) std::unordered_map<std::size_t, aliceVision::track::Track>;
29+

src/aliceVision/track/trackIO.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "trackIO.hpp"
88
#include <aliceVision/dataio/json.hpp>
9+
#include <fstream>
10+
911
namespace aliceVision {
1012
namespace track {
1113

@@ -41,10 +43,44 @@ aliceVision::track::Track tag_invoke(boost::json::value_to_tag<aliceVision::trac
4143

4244
aliceVision::track::Track ret;
4345
ret.descType = feature::EImageDescriberType_stringToEnum(boost::json::value_to<std::string>(obj.at("descType")));
44-
ret.featPerView = flat_map_value_to<track::TrackItem>(obj.at("featPerView"));
46+
ret.featPerView = unordered_map_value_to<track::TrackItem>(obj.at("featPerView"));
4547

4648
return ret;
4749
}
4850

51+
bool loadTracks(TracksMap& mapTracks, const std::string& filename)
52+
{
53+
std::ifstream tracksFile(filename);
54+
if (tracksFile.is_open() == false)
55+
{
56+
return false;
57+
}
58+
59+
std::stringstream buffer;
60+
buffer << tracksFile.rdbuf();
61+
62+
// Parse json
63+
boost::json::value jv = boost::json::parse(buffer.str());
64+
mapTracks = track::TracksMap(unordered_map_value_to<track::Track>(jv));
65+
66+
return true;
67+
}
68+
69+
bool saveTracks(const TracksMap& mapTracks, const std::string& filename)
70+
{
71+
std::ofstream tracksFile(filename);
72+
if (tracksFile.is_open() == false)
73+
{
74+
return false;
75+
}
76+
77+
boost::json::value jv = boost::json::value_from(mapTracks);
78+
79+
tracksFile << boost::json::serialize(jv);
80+
tracksFile.close();
81+
82+
return true;
83+
}
84+
4985
} // namespace track
5086
} // namespace aliceVision

src/aliceVision/track/trackIO.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ stl::flat_map<size_t, T> flat_map_value_to(const boost::json::value& jv)
2929
return ret;
3030
}
3131

32+
template<class T>
33+
std::unordered_map<size_t, T> unordered_map_value_to(const boost::json::value& jv)
34+
{
35+
std::unordered_map<size_t, T> ret;
36+
37+
const boost::json::array obj = jv.as_array();
38+
39+
for (const auto& item : obj)
40+
{
41+
const boost::json::array inner = item.as_array();
42+
ret.insert({boost::json::value_to<std::size_t>(inner[0]), boost::json::value_to<T>(inner[1])});
43+
}
44+
45+
return ret;
46+
}
47+
3248
/**
3349
* @brief Serialize track to JSON object.
3450
*/
@@ -39,5 +55,21 @@ void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, alic
3955
*/
4056
aliceVision::track::Track tag_invoke(boost::json::value_to_tag<aliceVision::track::Track>, boost::json::value const& jv);
4157

58+
/**
59+
* @brief load tracks from file
60+
* @param mapTracks the result container
61+
* @param filename the input file path
62+
* @return false if the load failed
63+
*/
64+
bool loadTracks(TracksMap& mapTracks, const std::string& filename);
65+
66+
/**
67+
* @brief save tracks to file
68+
* @param mapTracks the input container
69+
* @param filename the input file path
70+
* @return false if the save failed
71+
*/
72+
bool saveTracks(const TracksMap& mapTracks, const std::string& filename);
73+
4274
} // namespace track
4375
} // namespace aliceVision

0 commit comments

Comments
 (0)