Skip to content

Commit 1447840

Browse files
Merge pull request #1961 from alicevision/dev/localbundle
Local bundle adjustment's covisibility estimation optimization
2 parents 5ae27df + 054157f commit 1447840

File tree

8 files changed

+245
-84
lines changed

8 files changed

+245
-84
lines changed

src/aliceVision/sfm/pipeline/expanding/ConnexityGraph.cpp

Lines changed: 139 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
// You can obtain one at https://mozilla.org/MPL/2.0/.
66

77
#include "ConnexityGraph.hpp"
8-
//#include <aliceVision/stl/Counter.hpp>
8+
#include <aliceVision/stl/stl.hpp>
99
#include <lemon/bfs.h>
1010

1111
namespace aliceVision {
1212
namespace sfm {
1313

14+
1415
struct viewIdScored
1516
{
1617
viewIdScored() = default;
@@ -33,77 +34,166 @@ struct viewIdScored
3334
size_t card = 0;
3435
};
3536

36-
bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<IndexT> & viewsOfInterest)
37+
bool ConnexityGraph::updateCocardinalities(const sfmData::SfMData & sfmData,
38+
const track::TracksPerView& tracksPerViews)
3739
{
38-
lemon::ListGraph graph;
39-
std::map<IndexT, lemon::ListGraph::Node> _nodePerViewId;
40-
std::map<lemon::ListGraph::Node, IndexT> viewIdPerNode;
41-
4240
//Create a list of reconstructed views
43-
std::vector<IndexT> views;
44-
for (const auto & pv : sfmData.getViews())
41+
std::set<IndexT> setViews;
42+
for (const auto & [viewId, view] : sfmData.getViews())
4543
{
46-
if (sfmData.isPoseAndIntrinsicDefined(pv.first))
44+
if (sfmData.isPoseAndIntrinsicDefined(viewId))
4745
{
48-
views.push_back(pv.first);
49-
50-
lemon::ListGraph::Node newNode = graph.addNode();
51-
_nodePerViewId[pv.first] = newNode;
52-
viewIdPerNode[newNode] = pv.first;
46+
setViews.insert(viewId);
5347
}
5448
}
5549

50+
//Get incoming views
51+
std::set<IndexT> newViews;
52+
std::set_difference(setViews.begin(), setViews.end(),
53+
_previousViews.begin(), _previousViews.end(),
54+
std::inserter(newViews, newViews.begin()));
55+
56+
ALICEVISION_LOG_INFO("Update cocardinalities with " << newViews.size() << " new views");
5657

57-
//Build a list of landmarks index per view
58-
std::map<IndexT, std::set<IndexT>> landmarksPerView;
59-
for (const auto & pl : sfmData.getLandmarks())
58+
//Retrieve landmarks
59+
const sfmData::Landmarks & landmarks = sfmData.getLandmarks();
60+
61+
//Get All landmark ids (keys)
62+
std::set<IndexT> landmarkIds;
63+
std::transform(landmarks.begin(), landmarks.end(),
64+
std::inserter(landmarkIds, landmarkIds.begin()),
65+
stl::RetrieveKey());
66+
67+
//For all incoming views only
68+
for (const IndexT newView: newViews)
6069
{
61-
for (const auto & po : pl.second.getObservations())
70+
const track::TrackIdSet & trackIds = tracksPerViews.at(newView);
71+
72+
//Build a list of landmarks observed by this view
73+
std::vector<IndexT> newViewLandmarks;
74+
newViewLandmarks.reserve(trackIds.size());
75+
std::set_intersection(trackIds.begin(), trackIds.end(),
76+
landmarkIds.begin(), landmarkIds.end(),
77+
std::back_inserter(newViewLandmarks));
78+
79+
//Loop over all landmarks
80+
std::map<IndexT, size_t> commonLandmarksCountPerView;
81+
for (const IndexT & idLandmark : newViewLandmarks)
82+
{
83+
//Loop over all view observing the landmark
84+
const sfmData::Landmark & landmark = landmarks.at(idLandmark);
85+
for (const auto & [idView, _] : landmark.getObservations())
86+
{
87+
if (idView == newView)
88+
{
89+
continue;
90+
}
91+
92+
//Update the coobservation counter
93+
auto it = commonLandmarksCountPerView.find(idView);
94+
if (it != commonLandmarksCountPerView.end())
95+
{
96+
it->second++;
97+
}
98+
else
99+
{
100+
commonLandmarksCountPerView[idView] = 1;
101+
}
102+
}
103+
}
104+
105+
//Update the cocardinalities sparse matrix
106+
for (const auto [otherView, card]: commonLandmarksCountPerView)
62107
{
63-
landmarksPerView[po.first].insert(pl.first);
108+
Pair p;
109+
p.first = std::min(newView, otherView);
110+
p.second = std::max(newView, otherView);
111+
_cocardinalities[p] = card;
64112
}
65113
}
114+
66115

67-
//For all possible unique pairs
68-
std::map<IndexT, std::vector<viewIdScored>> covisibility;
69-
for (int idref = 0; idref < views.size(); idref++)
116+
117+
//Get Removed views
118+
std::set<IndexT> removedViews;
119+
std::set_difference(_previousViews.begin(), _previousViews.end(),
120+
setViews.begin(), setViews.end(),
121+
std::inserter(removedViews, removedViews.begin()));
122+
123+
ALICEVISION_LOG_INFO("Update cocardinalities with " << removedViews.size() << " removed views");
124+
125+
for (const IndexT removedView : removedViews)
70126
{
71-
IndexT viewRef = views[idref];
127+
std::erase_if(_cocardinalities,
128+
[removedView](const auto & item)
129+
{
130+
const auto & pair = item.first;
131+
return ((pair.first == removedView) || (pair.second == removedView));
132+
}
133+
);
134+
}
72135

73-
const auto & ptref = landmarksPerView[viewRef];
74-
75-
for (int idcur = idref + 1; idcur < views.size(); idcur++)
76-
{
77-
IndexT viewCur = views[idcur];
136+
//Copy for next iteration
137+
_previousViews = setViews;
138+
139+
return true;
140+
}
78141

79-
const auto & ptcur = landmarksPerView[viewCur];
142+
bool ConnexityGraph::build(const sfmData::SfMData & sfmData,
143+
const track::TracksPerView& tracksPerViews,
144+
const std::set<IndexT> & viewsOfInterest)
145+
{
146+
lemon::ListGraph graph;
147+
std::map<IndexT, lemon::ListGraph::Node> nodePerViewId;
148+
std::map<lemon::ListGraph::Node, IndexT> viewIdPerNode;
80149

81-
std::vector<IndexT> intersection;
82-
std::set_intersection(ptref.begin(), ptref.end(), ptcur.begin(), ptcur.end(),
83-
std::back_inserter(intersection));
150+
if (!updateCocardinalities(sfmData, tracksPerViews))
151+
{
152+
return false;
153+
}
84154

85-
size_t s = intersection.size();
86-
if (s == 0)
87-
{
88-
continue;
89-
}
155+
ALICEVISION_LOG_INFO("Connexity graph begin");
156+
//Reset result
157+
_distancesPerPoseId.clear();
90158

91-
covisibility[viewRef].push_back({viewCur, s});
92-
covisibility[viewCur].push_back({viewRef, s});
159+
//Create a list of reconstructed views
160+
std::vector<IndexT> views;
161+
for (const auto & [viewId, view] : sfmData.getViews())
162+
{
163+
if (sfmData.isPoseAndIntrinsicDefined(viewId))
164+
{
165+
views.push_back(viewId);
166+
167+
lemon::ListGraph::Node newNode = graph.addNode();
168+
nodePerViewId[viewId] = newNode;
169+
viewIdPerNode[newNode] = viewId;
93170
}
94171
}
95172

173+
174+
//For all possible unique pairs
175+
std::map<IndexT, std::vector<viewIdScored>> covisibility;
176+
for (const auto & [pair, s]: _cocardinalities)
177+
{
178+
covisibility[pair.first].push_back({pair.second, s});
179+
covisibility[pair.second].push_back({pair.first, s});
180+
}
181+
96182
//Filter out connexions without enough information
97183
for (auto & item : covisibility)
98184
{
99185
auto & vec = item.second;
186+
187+
//Just skip filtering if we don't have more than _minLinksPerView links
100188
if (vec.size() < _minLinksPerView)
101189
{
102190
continue;
103191
}
104192

193+
//Sort the vector by descending order of shared observations
105194
std::sort(vec.begin(), vec.end(), std::greater<>());
106195

196+
//Count the number of items with enough observations
107197
size_t pos = 0;
108198
for (; pos < vec.size(); pos++)
109199
{
@@ -113,6 +203,7 @@ bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<Inde
113203
}
114204
}
115205

206+
//Keep at LEAST _minLinksPerView
116207
pos = std::max(pos, _minLinksPerView);
117208
vec.resize(pos);
118209
}
@@ -130,8 +221,8 @@ bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<Inde
130221
{
131222
IndexT viewId2 = part.viewId;
132223

133-
const lemon::ListGraph::Node & node1 = _nodePerViewId[viewId1];
134-
const lemon::ListGraph::Node & node2 = _nodePerViewId[viewId2];
224+
const lemon::ListGraph::Node & node1 = nodePerViewId[viewId1];
225+
const lemon::ListGraph::Node & node2 = nodePerViewId[viewId2];
135226

136227
graph.addEdge(node1, node2);
137228
}
@@ -159,8 +250,8 @@ bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<Inde
159250

160251
if (intrinsicIdRef != intrinsicIdCur) continue;
161252

162-
const lemon::ListGraph::Node & node1 = _nodePerViewId[viewRef];
163-
const lemon::ListGraph::Node & node2 = _nodePerViewId[viewCur];
253+
const lemon::ListGraph::Node & node1 = nodePerViewId[viewRef];
254+
const lemon::ListGraph::Node & node2 = nodePerViewId[viewCur];
164255

165256
graph.addEdge(node1, node2);
166257
}
@@ -174,15 +265,15 @@ bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<Inde
174265

175266
for (auto id : viewsOfInterest)
176267
{
177-
auto it = _nodePerViewId.find(id);
178-
if (it != _nodePerViewId.end())
268+
auto it = nodePerViewId.find(id);
269+
if (it != nodePerViewId.end())
179270
{
180271
bfs.addSource(it->second);
181272
}
182273
}
183274

184275
bfs.start();
185-
for (const auto & x : _nodePerViewId)
276+
for (const auto & x : nodePerViewId)
186277
{
187278
//Retrieve the poseId associated to this view
188279
IndexT poseId = sfmData.getView(x.first).getPoseId();
@@ -210,6 +301,8 @@ bool ConnexityGraph::build(const sfmData::SfMData & sfmData, const std::set<Inde
210301
}
211302
}
212303

304+
ALICEVISION_LOG_INFO("Connexity graph end");
305+
213306
return true;
214307
}
215308

src/aliceVision/sfm/pipeline/expanding/ConnexityGraph.hpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,26 @@
88

99
#include <aliceVision/sfmData/SfMData.hpp>
1010
#include <lemon/list_graph.h>
11+
#include <aliceVision/track/Track.hpp>
1112

1213
namespace aliceVision {
1314
namespace sfm {
1415

1516
class ConnexityGraph
1617
{
1718
public:
19+
1820
/**
1921
* Compute distances of all the sfmData views to the viewsOfInterest views
2022
* @param sfmData the sfmData containing all the views
21-
* @param viewsOfInterest the list of views to compute the distance from. Those views must also be in the sfmData !
23+
* @param tracksPerView list of track ids indexed per view
24+
* @param viewsOfInterest the list of views to compute the distance from.
25+
* Those views must also be in the sfmData !
2226
* @return false if an error occurred
2327
*/
24-
bool build(const sfmData::SfMData & sfmData, const std::set<IndexT> & viewsOfInterest);
28+
bool build(const sfmData::SfMData & sfmData,
29+
const track::TracksPerView& tracksPerViews,
30+
const std::set<IndexT> & viewsOfInterest);
2531

2632
/**
2733
* Get the distance for a particular poseId to one view of interest
@@ -31,8 +37,20 @@ class ConnexityGraph
3137
int getDistance(IndexT poseId) const;
3238

3339

40+
private:
41+
/**
42+
* update the coCardinalities
43+
* @param sfmData the sfmData containing all the views
44+
* @param tracksPerView list of track ids indexed per view
45+
* @return false if an error occurred
46+
*/
47+
bool updateCocardinalities(const sfmData::SfMData & sfmData,
48+
const track::TracksPerView& tracksPerViews);
49+
3450
private:
3551
std::map<IndexT, int> _distancesPerPoseId;
52+
std::set<IndexT> _previousViews;
53+
std::map<Pair, size_t> _cocardinalities;
3654

3755
private:
3856
size_t _minLinksPerView = 10;

src/aliceVision/sfm/pipeline/expanding/ExpansionChunk.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ bool ExpansionChunk::process(sfmData::SfMData & sfmData, const track::TracksHand
3232
return false;
3333
}
3434

35+
//Check if historyHandler exists
36+
if (!_historyHandler)
37+
{
38+
return false;
39+
}
40+
3541
//How many views to resect ?
3642
size_t countToProcess = 0;
3743
for (const IndexT viewId : viewsChunk)
@@ -52,6 +58,7 @@ bool ExpansionChunk::process(sfmData::SfMData & sfmData, const track::TracksHand
5258
};
5359

5460
std::vector<IntermediateResectionInfo> intermediateInfos;
61+
std::set<IndexT> addedViews;
5562

5663
ALICEVISION_LOG_INFO("Resection start");
5764
#pragma omp parallel for
@@ -119,6 +126,7 @@ bool ExpansionChunk::process(sfmData::SfMData & sfmData, const track::TracksHand
119126
}
120127

121128
addPose(sfmData, item.viewId, item.pose);
129+
addedViews.insert(item.viewId);
122130
}
123131

124132
// Get a list of valid views
@@ -149,11 +157,8 @@ bool ExpansionChunk::process(sfmData::SfMData & sfmData, const track::TracksHand
149157
{
150158
setConstraints(sfmData, tracksHandler, validViewIds);
151159
}
152-
153-
if (_historyHandler)
154-
{
155-
_historyHandler->saveState(sfmData);
156-
}
160+
161+
_historyHandler->saveState(sfmData);
157162

158163
ALICEVISION_LOG_INFO("ExpansionChunk::process end");
159164

src/aliceVision/sfm/pipeline/expanding/ExpansionHistory.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class ExpansionHistory
5252
return _epoch;
5353
}
5454

55+
/**
56+
* Save the state after a chunk
57+
* @param sfmData the current sfmData
58+
*/
5559
void saveState(const sfmData::SfMData & sfmData);
5660

5761
/**
@@ -73,6 +77,12 @@ class ExpansionHistory
7377
private:
7478
// History of focals per intrinsics
7579
std::map<IndexT, std::vector<std::pair<size_t, double>>> _focalHistory;
80+
81+
// History of added views
82+
std::vector<std::set<IndexT>> _addedViews;
83+
84+
// History of removed views
85+
std::vector<std::set<IndexT>> _removedViews;
7686

7787
// epoch ID
7888
std::size_t _epoch = 0;

0 commit comments

Comments
 (0)