changes after review - removing positiveEdgeWeights

release/4.3a0
akrishnan86 2020-07-25 13:39:58 -07:00
parent 698ec27e44
commit b25809d5a3
3 changed files with 59 additions and 20 deletions

View File

@ -1,3 +1,10 @@
/**
* @file MFAS.cpp
* @brief Source file for the MFAS class
* @author Akshay Krishnan
* @date July 2020
*/
#include <gtsam/sfm/MFAS.h> #include <gtsam/sfm/MFAS.h>
using namespace gtsam; using namespace gtsam;
@ -26,14 +33,20 @@ std::vector<Key> MFAS::computeOrdering() const {
FastMap<Key, int> ordered_positions; // map from node to its position in the output order FastMap<Key, int> ordered_positions; // map from node to its position in the output order
// populate neighbors and weights // populate neighbors and weights
// Since the weights could be obtained by projection, they can be either
// negative or positive. Ideally, the weights should be positive in the
// direction of the edge. So, we define the direction of the edge as
// edge.first -> edge.second if weight is positive and
// edge.second -> edge.first if weight is negative. Once we know the
// direction, we only use the magnitude of the weights.
for (auto it = edgeWeights_.begin(); it != edgeWeights_.end(); it++) { for (auto it = edgeWeights_.begin(); it != edgeWeights_.end(); it++) {
const KeyPair &edge = it->first; const KeyPair &edge = it->first;
const double weight = it->second; const double weight = it->second;
Key edge_source = weight >= 0 ? edge.first : edge.second; Key edge_source = weight >= 0 ? edge.first : edge.second;
Key edge_dest = weight >= 0 ? edge.second : edge.first; Key edge_dest = weight >= 0 ? edge.second : edge.first;
in_weights[edge_dest] += weight; in_weights[edge_dest] += std::abs(weight);
out_weights[edge_source] += weight; out_weights[edge_source] += std::abs(weight);
in_neighbors[edge_dest].push_back(edge_source); in_neighbors[edge_dest].push_back(edge_source);
out_neighbors[edge_source].push_back(edge_dest); out_neighbors[edge_source].push_back(edge_dest);
} }

View File

@ -11,6 +11,13 @@
#pragma once #pragma once
/**
* @file MFAS.h
* @brief MFAS class to solve Minimum Feedback Arc Set graph problem
* @author Akshay Krishnan
* @date July 2020
*/
#include <gtsam/geometry/Unit3.h> #include <gtsam/geometry/Unit3.h>
#include <gtsam/inference/Key.h> #include <gtsam/inference/Key.h>
@ -29,13 +36,24 @@ namespace gtsam {
Given a weighted directed graph, the objective in a Minimum feedback arc set Given a weighted directed graph, the objective in a Minimum feedback arc set
problem is to obtain a directed acyclic graph by removing problem is to obtain a directed acyclic graph by removing
edges such that the total weight of removed edges is minimum. edges such that the total weight of removed edges is minimum.
Although MFAS is a general graph problem and can be applied in many areas, this
classed was designed for the purpose of outlier rejection in a
translation averaging for SfM setting. For more details, refer to the above paper.
The nodes of the graph in this context represents cameras in 3D and the edges
between them represent unit translations in the world coordinate frame, i.e
w_aZb is the unit translation from a to b expressed in the world coordinate frame.
The weights for the edges are obtained by projecting the unit translations in a
projection direction.
@addtogroup SFM @addtogroup SFM
*/ */
class MFAS { class MFAS {
public: public:
// used to represent edges between two nodes in the graph // used to represent edges between two nodes in the graph. When used in
// translation averaging for global SfM
using KeyPair = std::pair<Key, Key>; using KeyPair = std::pair<Key, Key>;
using TranslationEdges = std::map<KeyPair, Unit3>; using TranslationEdges = std::map<KeyPair, Unit3>;
private: private:
// pointer to nodes in the graph // pointer to nodes in the graph
const std::shared_ptr<std::vector<Key>> nodes_; const std::shared_ptr<std::vector<Key>> nodes_;
@ -47,44 +65,45 @@ class MFAS {
public: public:
/** /**
* @brief Construct from the nodes in a graph and weighted directed edges * @brief Construct from the nodes in a graph and weighted directed edges
* between the graph. A shared pointer to the nodes is used as input parameter. * between the nodes. Each node is identified by a Key.
* This is because, MFAS ordering is usually used to compute the ordering of a * A shared pointer to the nodes is used as input parameter
* large graph that is already stored in memory. It is unnecessary to copy the * because, MFAS ordering is usually used to compute the ordering of a
* set of nodes in this class. * large graph that is already stored in memory. It is unnecessary make a
* copy of the nodes in this class.
* @param nodes: Nodes in the graph * @param nodes: Nodes in the graph
* @param edgeWeights: weights of edges in the graph (map from pair of keys * @param edgeWeights: weights of edges in the graph
* to signed double)
*/ */
MFAS(const std::shared_ptr<std::vector<Key>> &nodes, MFAS(const std::shared_ptr<std::vector<Key>> &nodes,
const std::map<KeyPair, double> &edgeWeights) : const std::map<KeyPair, double> &edgeWeights) :
nodes_(nodes), edgeWeights_(edgeWeights) {} nodes_(nodes), edgeWeights_(edgeWeights) {}
/** /**
* @brief Constructor for using in the context of translation averaging. Here, * @brief Constructor to be used in the context of translation averaging. Here,
* the nodes of the graph are cameras in 3D and the edges have a unit translation * the nodes of the graph are cameras in 3D and the edges have a unit translation
* direction between them. The weights of the edges is computed by projecting * direction between them. The weights of the edges is computed by projecting
* them along a projection direction. * them along a projection direction.
* @param nodes Nodes in the graph * @param nodes cameras in the epipolar graph (each camera is identified by a Key)
* @param relativeTranslations translation directions between nodes * @param relativeTranslations translation directions between the cameras
* @param projectionDirection direction in which edges are to be projected * @param projectionDirection direction in which edges are to be projected
*/ */
MFAS(const std::shared_ptr<std::vector<Key>> &nodes, MFAS(const std::shared_ptr<std::vector<Key>> &nodes,
const TranslationEdges& relativeTranslations, const TranslationEdges& relativeTranslations,
const Unit3 &projectionDirection); const Unit3 &projectionDirection);
/**
* @brief Computes the "outlier weights" of the graph. We define the outlier weight
* of a edge to be zero if the edge is an inlier and the magnitude of its edgeWeight
* if it is an outlier.
* @return outlierWeights: map from an edge to its outlier weight.
*/
std::map<KeyPair, double> computeOutlierWeights() const;
/** /**
* @brief Computes the 1D MFAS ordering of nodes in the graph * @brief Computes the 1D MFAS ordering of nodes in the graph
* @return orderedNodes: vector of nodes in the obtained order * @return orderedNodes: vector of nodes in the obtained order
*/ */
std::vector<Key> computeOrdering() const; std::vector<Key> computeOrdering() const;
/**
* @brief Computes the "outlier weights" of the graph. We define the outlier weight
* of a edge to be zero if the edge is an inlier and the magnitude of its edgeWeight
* if it is an outlier. This function internally calls computeOrdering and uses the
* obtained ordering to identify outlier edges.
* @return outlierWeights: map from an edge to its outlier weight.
*/
std::map<KeyPair, double> computeOutlierWeights() const;
}; };
} // namespace gtsam } // namespace gtsam

View File

@ -1,3 +1,10 @@
/**
* @file testMFAS.cpp
* @brief Unit tests for the MFAS class
* @author Akshay Krishnan
* @date July 2020
*/
#include <gtsam/sfm/MFAS.h> #include <gtsam/sfm/MFAS.h>
#include <iostream> #include <iostream>
#include <CppUnitLite/TestHarness.h> #include <CppUnitLite/TestHarness.h>