// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de) #ifndef COLMAP_SRC_BASE_GRAPH_CUT_H_ #define COLMAP_SRC_BASE_GRAPH_CUT_H_ #include #include #include #include #include #include #include "util/logging.h" namespace colmap { // Compute the min-cut of a undirected graph using the Stoer Wagner algorithm. void ComputeMinGraphCutStoerWagner( const std::vector>& edges, const std::vector& weights, int* cut_weight, std::vector* cut_labels); // Compute the normalized min-cut of an undirected graph using Metis. // Partitions the graph into clusters and returns the cluster labels per vertex. std::unordered_map ComputeNormalizedMinGraphCut( const std::vector>& edges, const std::vector& weights, const int num_parts); // Compute the minimum graph cut of a directed S-T graph using the // Boykov-Kolmogorov max-flow min-cut algorithm, as descibed in: // "An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy // Minimization in Vision". Yuri Boykov and Vladimir Kolmogorov. PAMI, 2004. template class MinSTGraphCut { public: typedef boost::adjacency_list_traits graph_traits_t; typedef graph_traits_t::edge_descriptor edge_descriptor_t; typedef graph_traits_t::vertices_size_type vertices_size_t; struct Edge { value_t capacity; value_t residual; edge_descriptor_t reverse; }; typedef boost::adjacency_list graph_t; MinSTGraphCut(const size_t num_nodes); // Count the number of nodes and edges in the graph. size_t NumNodes() const; size_t NumEdges() const; // Add node to the graph. void AddNode(const node_t node_idx, const value_t source_capacity, const value_t sink_capacity); // Add edge to the graph. void AddEdge(const node_t node_idx1, const node_t node_idx2, const value_t capacity, const value_t reverse_capacity); // Compute the min-cut using the max-flow algorithm. Returns the flow. value_t Compute(); // Check whether node is connected to source or sink after computing the cut. bool IsConnectedToSource(const node_t node_idx) const; bool IsConnectedToSink(const node_t node_idx) const; private: const node_t S_node_; const node_t T_node_; graph_t graph_; std::vector colors_; }; //////////////////////////////////////////////////////////////////////////////// // Implementation //////////////////////////////////////////////////////////////////////////////// template MinSTGraphCut::MinSTGraphCut(const size_t num_nodes) : S_node_(num_nodes), T_node_(num_nodes + 1), graph_(num_nodes + 2) {} template size_t MinSTGraphCut::NumNodes() const { return boost::num_vertices(graph_) - 2; } template size_t MinSTGraphCut::NumEdges() const { return boost::num_edges(graph_); } template void MinSTGraphCut::AddNode(const node_t node_idx, const value_t source_capacity, const value_t sink_capacity) { CHECK_GE(node_idx, 0); CHECK_LE(node_idx, boost::num_vertices(graph_)); CHECK_GE(source_capacity, 0); CHECK_GE(sink_capacity, 0); if (source_capacity > 0) { const edge_descriptor_t edge = boost::add_edge(S_node_, node_idx, graph_).first; const edge_descriptor_t edge_reverse = boost::add_edge(node_idx, S_node_, graph_).first; graph_[edge].capacity = source_capacity; graph_[edge].reverse = edge_reverse; graph_[edge_reverse].reverse = edge; } if (sink_capacity > 0) { const edge_descriptor_t edge = boost::add_edge(node_idx, T_node_, graph_).first; const edge_descriptor_t edge_reverse = boost::add_edge(T_node_, node_idx, graph_).first; graph_[edge].capacity = sink_capacity; graph_[edge].reverse = edge_reverse; graph_[edge_reverse].reverse = edge; } } template void MinSTGraphCut::AddEdge(const node_t node_idx1, const node_t node_idx2, const value_t capacity, const value_t reverse_capacity) { CHECK_GE(node_idx1, 0); CHECK_LE(node_idx1, boost::num_vertices(graph_)); CHECK_GE(node_idx2, 0); CHECK_LE(node_idx2, boost::num_vertices(graph_)); CHECK_GE(capacity, 0); CHECK_GE(reverse_capacity, 0); const edge_descriptor_t edge = boost::add_edge(node_idx1, node_idx2, graph_).first; const edge_descriptor_t edge_reverse = boost::add_edge(node_idx2, node_idx1, graph_).first; graph_[edge].capacity = capacity; graph_[edge_reverse].capacity = reverse_capacity; graph_[edge].reverse = edge_reverse; graph_[edge_reverse].reverse = edge; } template value_t MinSTGraphCut::Compute() { const vertices_size_t num_vertices = boost::num_vertices(graph_); colors_.resize(num_vertices); std::vector predecessors(num_vertices); std::vector distances(num_vertices); return boost::boykov_kolmogorov_max_flow( graph_, boost::get(&Edge::capacity, graph_), boost::get(&Edge::residual, graph_), boost::get(&Edge::reverse, graph_), predecessors.data(), colors_.data(), distances.data(), boost::get(boost::vertex_index, graph_), S_node_, T_node_); } template bool MinSTGraphCut::IsConnectedToSource( const node_t node_idx) const { return colors_.at(node_idx) != boost::white_color; } template bool MinSTGraphCut::IsConnectedToSink( const node_t node_idx) const { return colors_.at(node_idx) == boost::white_color; } } // namespace colmap #endif // COLMAP_SRC_BASE_GRAPH_CUT_H_