summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/graph_cuts
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/graph_cuts')
-rw-r--r--ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts.h959
-rw-r--r--ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts_abstract.h636
-rw-r--r--ml/dlib/dlib/graph_cuts/general_flow_graph.h172
-rw-r--r--ml/dlib/dlib/graph_cuts/general_potts_problem.h99
-rw-r--r--ml/dlib/dlib/graph_cuts/graph_labeler.h211
-rw-r--r--ml/dlib/dlib/graph_cuts/graph_labeler_abstract.h185
-rw-r--r--ml/dlib/dlib/graph_cuts/min_cut.h571
-rw-r--r--ml/dlib/dlib/graph_cuts/min_cut_abstract.h476
8 files changed, 3309 insertions, 0 deletions
diff --git a/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts.h b/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts.h
new file mode 100644
index 000000000..f035442bf
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts.h
@@ -0,0 +1,959 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_Hh_
+#define DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_Hh_
+
+#include "find_max_factor_graph_potts_abstract.h"
+#include "../matrix.h"
+#include "min_cut.h"
+#include "general_potts_problem.h"
+#include "../algs.h"
+#include "../graph_utils.h"
+#include "../array2d.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ namespace impl
+ {
+
+ template <
+ typename potts_problem,
+ typename T = void
+ >
+ class flows_container
+ {
+ /*
+ This object notionally represents a matrix of flow values. It's
+ overloaded to represent this matrix efficiently though. In this case
+ it represents the matrix using a sparse representation.
+ */
+
+ typedef typename potts_problem::value_type edge_type;
+ std::vector<std::vector<edge_type> > flows;
+ public:
+
+ void setup(
+ const potts_problem& p
+ )
+ {
+ flows.resize(p.number_of_nodes());
+ for (unsigned long i = 0; i < flows.size(); ++i)
+ {
+ flows[i].resize(p.number_of_neighbors(i));
+ }
+ }
+
+ edge_type& operator() (
+ const long r,
+ const long c
+ ) { return flows[r][c]; }
+
+ const edge_type& operator() (
+ const long r,
+ const long c
+ ) const { return flows[r][c]; }
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_problem
+ >
+ class flows_container<potts_problem,
+ typename enable_if_c<potts_problem::max_number_of_neighbors!=0>::type>
+ {
+ /*
+ This object notionally represents a matrix of flow values. It's
+ overloaded to represent this matrix efficiently though. In this case
+ it represents the matrix using a dense representation.
+
+ */
+ typedef typename potts_problem::value_type edge_type;
+ const static unsigned long max_number_of_neighbors = potts_problem::max_number_of_neighbors;
+ matrix<edge_type,0,max_number_of_neighbors> flows;
+ public:
+
+ void setup(
+ const potts_problem& p
+ )
+ {
+ flows.set_size(p.number_of_nodes(), max_number_of_neighbors);
+ }
+
+ edge_type& operator() (
+ const long r,
+ const long c
+ ) { return flows(r,c); }
+
+ const edge_type& operator() (
+ const long r,
+ const long c
+ ) const { return flows(r,c); }
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_problem
+ >
+ class potts_flow_graph
+ {
+ public:
+ typedef typename potts_problem::value_type edge_type;
+ private:
+ /*!
+ This is a utility class used by dlib::min_cut to convert a potts_problem
+ into the kind of flow graph expected by the min_cut object's main block
+ of code.
+
+ Within this object, we will use the convention that one past
+ potts_problem::number_of_nodes() is the source node and two past is
+ the sink node.
+ !*/
+
+ potts_problem& g;
+
+ // flows(i,j) == the flow from node id i to it's jth neighbor
+ flows_container<potts_problem> flows;
+ // source_flows(i,0) == flow from source to node i,
+ // source_flows(i,1) == flow from node i to source
+ matrix<edge_type,0,2> source_flows;
+
+ // sink_flows(i,0) == flow from sink to node i,
+ // sink_flows(i,1) == flow from node i to sink
+ matrix<edge_type,0,2> sink_flows;
+
+ node_label source_label, sink_label;
+ public:
+
+ potts_flow_graph(
+ potts_problem& g_
+ ) : g(g_)
+ {
+ flows.setup(g);
+
+ source_flows.set_size(g.number_of_nodes(), 2);
+ sink_flows.set_size(g.number_of_nodes(), 2);
+ source_flows = 0;
+ sink_flows = 0;
+
+ source_label = FREE_NODE;
+ sink_label = FREE_NODE;
+
+ // setup flows based on factor potentials
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ const edge_type temp = g.factor_value(i);
+ if (temp < 0)
+ source_flows(i,0) = -temp;
+ else
+ sink_flows(i,1) = temp;
+
+ for (unsigned long j = 0; j < g.number_of_neighbors(i); ++j)
+ {
+ flows(i,j) = g.factor_value_disagreement(i, g.get_neighbor(i,j));
+ }
+ }
+ }
+
+ class out_edge_iterator
+ {
+ friend class potts_flow_graph;
+ unsigned long idx; // base node idx
+ unsigned long cnt; // count over the neighbors of idx
+ public:
+
+ out_edge_iterator(
+ ):idx(0),cnt(0){}
+
+ out_edge_iterator(
+ unsigned long idx_,
+ unsigned long cnt_
+ ):idx(idx_),cnt(cnt_)
+ {}
+
+ bool operator!= (
+ const out_edge_iterator& item
+ ) const { return cnt != item.cnt; }
+
+ out_edge_iterator& operator++(
+ )
+ {
+ ++cnt;
+ return *this;
+ }
+ };
+
+ class in_edge_iterator
+ {
+ friend class potts_flow_graph;
+ unsigned long idx; // base node idx
+ unsigned long cnt; // count over the neighbors of idx
+ public:
+
+ in_edge_iterator(
+ ):idx(0),cnt(0)
+ {}
+
+
+ in_edge_iterator(
+ unsigned long idx_,
+ unsigned long cnt_
+ ):idx(idx_),cnt(cnt_)
+ {}
+
+ bool operator!= (
+ const in_edge_iterator& item
+ ) const { return cnt != item.cnt; }
+
+ in_edge_iterator& operator++(
+ )
+ {
+ ++cnt;
+ return *this;
+ }
+ };
+
+ unsigned long number_of_nodes (
+ ) const { return g.number_of_nodes() + 2; }
+
+ out_edge_iterator out_begin(
+ const unsigned long& it
+ ) const { return out_edge_iterator(it, 0); }
+
+ in_edge_iterator in_begin(
+ const unsigned long& it
+ ) const { return in_edge_iterator(it, 0); }
+
+ out_edge_iterator out_end(
+ const unsigned long& it
+ ) const
+ {
+ if (it >= g.number_of_nodes())
+ return out_edge_iterator(it, g.number_of_nodes());
+ else
+ return out_edge_iterator(it, g.number_of_neighbors(it)+2);
+ }
+
+ in_edge_iterator in_end(
+ const unsigned long& it
+ ) const
+ {
+ if (it >= g.number_of_nodes())
+ return in_edge_iterator(it, g.number_of_nodes());
+ else
+ return in_edge_iterator(it, g.number_of_neighbors(it)+2);
+ }
+
+
+ template <typename iterator_type>
+ unsigned long node_id (
+ const iterator_type& it
+ ) const
+ {
+ // if this isn't an iterator over the source or sink nodes
+ if (it.idx < g.number_of_nodes())
+ {
+ const unsigned long num = g.number_of_neighbors(it.idx);
+ if (it.cnt < num)
+ return g.get_neighbor(it.idx, it.cnt);
+ else if (it.cnt == num)
+ return g.number_of_nodes();
+ else
+ return g.number_of_nodes()+1;
+ }
+ else
+ {
+ return it.cnt;
+ }
+ }
+
+
+ edge_type get_flow (
+ const unsigned long& it1,
+ const unsigned long& it2
+ ) const
+ {
+ if (it1 >= g.number_of_nodes())
+ {
+ // if it1 is the source
+ if (it1 == g.number_of_nodes())
+ {
+ return source_flows(it2,0);
+ }
+ else // if it1 is the sink
+ {
+ return sink_flows(it2,0);
+ }
+ }
+ else if (it2 >= g.number_of_nodes())
+ {
+ // if it2 is the source
+ if (it2 == g.number_of_nodes())
+ {
+ return source_flows(it1,1);
+ }
+ else // if it2 is the sink
+ {
+ return sink_flows(it1,1);
+ }
+ }
+ else
+ {
+ return flows(it1, g.get_neighbor_idx(it1, it2));
+ }
+
+ }
+
+ edge_type get_flow (
+ const out_edge_iterator& it
+ ) const
+ {
+ if (it.idx < g.number_of_nodes())
+ {
+ const unsigned long num = g.number_of_neighbors(it.idx);
+ if (it.cnt < num)
+ return flows(it.idx, it.cnt);
+ else if (it.cnt == num)
+ return source_flows(it.idx,1);
+ else
+ return sink_flows(it.idx,1);
+ }
+ else
+ {
+ // if it.idx is the source
+ if (it.idx == g.number_of_nodes())
+ {
+ return source_flows(it.cnt,0);
+ }
+ else // if it.idx is the sink
+ {
+ return sink_flows(it.cnt,0);
+ }
+ }
+ }
+
+ edge_type get_flow (
+ const in_edge_iterator& it
+ ) const
+ {
+ return get_flow(node_id(it), it.idx);
+ }
+
+ void adjust_flow (
+ const unsigned long& it1,
+ const unsigned long& it2,
+ const edge_type& value
+ )
+ {
+ if (it1 >= g.number_of_nodes())
+ {
+ // if it1 is the source
+ if (it1 == g.number_of_nodes())
+ {
+ source_flows(it2,0) += value;
+ source_flows(it2,1) -= value;
+ }
+ else // if it1 is the sink
+ {
+ sink_flows(it2,0) += value;
+ sink_flows(it2,1) -= value;
+ }
+ }
+ else if (it2 >= g.number_of_nodes())
+ {
+ // if it2 is the source
+ if (it2 == g.number_of_nodes())
+ {
+ source_flows(it1,1) += value;
+ source_flows(it1,0) -= value;
+ }
+ else // if it2 is the sink
+ {
+ sink_flows(it1,1) += value;
+ sink_flows(it1,0) -= value;
+ }
+ }
+ else
+ {
+ flows(it1, g.get_neighbor_idx(it1, it2)) += value;
+ flows(it2, g.get_neighbor_idx(it2, it1)) -= value;
+ }
+
+ }
+
+ void set_label (
+ const unsigned long& it,
+ node_label value
+ )
+ {
+ if (it < g.number_of_nodes())
+ g.set_label(it, value);
+ else if (it == g.number_of_nodes())
+ source_label = value;
+ else
+ sink_label = value;
+ }
+
+ node_label get_label (
+ const unsigned long& it
+ ) const
+ {
+ if (it < g.number_of_nodes())
+ return g.get_label(it);
+ if (it == g.number_of_nodes())
+ return source_label;
+ else
+ return sink_label;
+ }
+
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename label_image_type,
+ typename image_potts_model
+ >
+ class potts_grid_problem
+ {
+ label_image_type& label_img;
+ long nc;
+ long num_nodes;
+ unsigned char* labels;
+ const image_potts_model& model;
+
+ public:
+ const static unsigned long max_number_of_neighbors = 4;
+
+ potts_grid_problem (
+ label_image_type& label_img_,
+ const image_potts_model& image_potts_model_
+ ) :
+ label_img(label_img_),
+ model(image_potts_model_)
+ {
+ num_nodes = model.nr()*model.nc();
+ nc = model.nc();
+ labels = &label_img[0][0];
+ }
+
+ unsigned long number_of_nodes (
+ ) const { return num_nodes; }
+
+ unsigned long number_of_neighbors (
+ unsigned long
+ ) const
+ {
+ return 4;
+ }
+
+ unsigned long get_neighbor_idx (
+ long node_id1,
+ long node_id2
+ ) const
+ {
+ long diff = node_id2-node_id1;
+ if (diff > nc)
+ diff -= (long)number_of_nodes();
+ else if (diff < -nc)
+ diff += (long)number_of_nodes();
+
+ if (diff == 1)
+ return 0;
+ else if (diff == -1)
+ return 1;
+ else if (diff == nc)
+ return 2;
+ else
+ return 3;
+ }
+
+ unsigned long get_neighbor (
+ long node_id,
+ long idx
+ ) const
+ {
+ switch(idx)
+ {
+ case 0:
+ {
+ long temp = node_id+1;
+ if (temp < (long)number_of_nodes())
+ return temp;
+ else
+ return temp - (long)number_of_nodes();
+ }
+ case 1:
+ {
+ long temp = node_id-1;
+ if (node_id >= 1)
+ return temp;
+ else
+ return temp + (long)number_of_nodes();
+ }
+ case 2:
+ {
+ long temp = node_id+nc;
+ if (temp < (long)number_of_nodes())
+ return temp;
+ else
+ return temp - (long)number_of_nodes();
+ }
+ case 3:
+ {
+ long temp = node_id-nc;
+ if (node_id >= nc)
+ return temp;
+ else
+ return temp + (long)number_of_nodes();
+ }
+ }
+ return 0;
+ }
+
+ void set_label (
+ const unsigned long& idx,
+ node_label value
+ )
+ {
+ *(labels+idx) = value;
+ }
+
+ node_label get_label (
+ const unsigned long& idx
+ ) const
+ {
+ return *(labels+idx);
+ }
+
+ typedef typename image_potts_model::value_type value_type;
+
+ value_type factor_value (unsigned long idx) const
+ {
+ return model.factor_value(idx);
+ }
+
+ value_type factor_value_disagreement (unsigned long idx1, unsigned long idx2) const
+ {
+ return model.factor_value_disagreement(idx1,idx2);
+ }
+
+ };
+
+ }
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_model
+ >
+ typename potts_model::value_type potts_model_score (
+ const potts_model& prob
+ )
+ {
+#ifdef ENABLE_ASSERTS
+ for (unsigned long i = 0; i < prob.number_of_nodes(); ++i)
+ {
+ for (unsigned long jj = 0; jj < prob.number_of_neighbors(i); ++jj)
+ {
+ unsigned long j = prob.get_neighbor(i,jj);
+ DLIB_ASSERT(prob.factor_value_disagreement(i,j) >= 0,
+ "\t value_type potts_model_score(prob)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t i: " << i
+ << "\n\t j: " << j
+ << "\n\t prob.factor_value_disagreement(i,j): " << prob.factor_value_disagreement(i,j)
+ );
+ DLIB_ASSERT(prob.factor_value_disagreement(i,j) == prob.factor_value_disagreement(j,i),
+ "\t value_type potts_model_score(prob)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t i: " << i
+ << "\n\t j: " << j
+ << "\n\t prob.factor_value_disagreement(i,j): " << prob.factor_value_disagreement(i,j)
+ << "\n\t prob.factor_value_disagreement(j,i): " << prob.factor_value_disagreement(j,i)
+ );
+ }
+ }
+#endif
+
+ typename potts_model::value_type score = 0;
+ for (unsigned long i = 0; i < prob.number_of_nodes(); ++i)
+ {
+ const bool label = (prob.get_label(i)!=0);
+ if (label)
+ score += prob.factor_value(i);
+ }
+
+ for (unsigned long i = 0; i < prob.number_of_nodes(); ++i)
+ {
+ for (unsigned long n = 0; n < prob.number_of_neighbors(i); ++n)
+ {
+ const unsigned long idx2 = prob.get_neighbor(i,n);
+ const bool label_i = (prob.get_label(i)!=0);
+ const bool label_idx2 = (prob.get_label(idx2)!=0);
+ if (label_i != label_idx2 && i < idx2)
+ score -= prob.factor_value_disagreement(i, idx2);
+ }
+ }
+
+ return score;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename graph_type
+ >
+ typename graph_type::edge_type potts_model_score (
+ const graph_type& g,
+ const std::vector<node_label>& labels
+ )
+ {
+ DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
+ "\t edge_type potts_model_score(g,labels)"
+ << "\n\t Invalid inputs were given to this function."
+ );
+ typedef typename graph_type::edge_type edge_type;
+ typedef typename graph_type::type type;
+
+ // The edges and node's have to use the same type to represent factor weights!
+ COMPILE_TIME_ASSERT((is_same_type<edge_type, type>::value == true));
+
+#ifdef ENABLE_ASSERTS
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ for (unsigned long jj = 0; jj < g.node(i).number_of_neighbors(); ++jj)
+ {
+ unsigned long j = g.node(i).neighbor(jj).index();
+ DLIB_ASSERT(edge(g,i,j) >= 0,
+ "\t edge_type potts_model_score(g,labels)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t i: " << i
+ << "\n\t j: " << j
+ << "\n\t edge(g,i,j): " << edge(g,i,j)
+ );
+ }
+ }
+#endif
+
+ typename graph_type::edge_type score = 0;
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ const bool label = (labels[i]!=0);
+ if (label)
+ score += g.node(i).data;
+ }
+
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ for (unsigned long n = 0; n < g.node(i).number_of_neighbors(); ++n)
+ {
+ const unsigned long idx2 = g.node(i).neighbor(n).index();
+ const bool label_i = (labels[i]!=0);
+ const bool label_idx2 = (labels[idx2]!=0);
+ if (label_i != label_idx2 && i < idx2)
+ score -= g.node(i).edge(n);
+ }
+ }
+
+ return score;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_grid_problem,
+ typename mem_manager
+ >
+ typename potts_grid_problem::value_type potts_model_score (
+ const potts_grid_problem& prob,
+ const array2d<node_label,mem_manager>& labels
+ )
+ {
+ DLIB_ASSERT(prob.nr() == labels.nr() && prob.nc() == labels.nc(),
+ "\t value_type potts_model_score(prob,labels)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t prob.nr(): " << labels.nr()
+ << "\n\t prob.nc(): " << labels.nc()
+ );
+ typedef array2d<node_label,mem_manager> image_type;
+ // This const_cast is ok because the model object won't actually modify labels
+ dlib::impl::potts_grid_problem<image_type,potts_grid_problem> model(const_cast<image_type&>(labels),prob);
+ return potts_model_score(model);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_model
+ >
+ void find_max_factor_graph_potts (
+ potts_model& prob
+ )
+ {
+#ifdef ENABLE_ASSERTS
+ for (unsigned long node_i = 0; node_i < prob.number_of_nodes(); ++node_i)
+ {
+ for (unsigned long jj = 0; jj < prob.number_of_neighbors(node_i); ++jj)
+ {
+ unsigned long node_j = prob.get_neighbor(node_i,jj);
+ DLIB_ASSERT(prob.get_neighbor_idx(node_j,node_i) < prob.number_of_neighbors(node_j),
+ "\t void find_max_factor_graph_potts(prob)"
+ << "\n\t The supplied potts problem defines an invalid graph."
+ << "\n\t node_i: " << node_i
+ << "\n\t node_j: " << node_j
+ << "\n\t prob.get_neighbor_idx(node_j,node_i): " << prob.get_neighbor_idx(node_j,node_i)
+ << "\n\t prob.number_of_neighbors(node_j): " << prob.number_of_neighbors(node_j)
+ );
+
+ DLIB_ASSERT(prob.get_neighbor_idx(node_i,prob.get_neighbor(node_i,jj)) == jj,
+ "\t void find_max_factor_graph_potts(prob)"
+ << "\n\t The get_neighbor_idx() and get_neighbor() functions must be inverses of each other."
+ << "\n\t node_i: " << node_i
+ << "\n\t jj: " << jj
+ << "\n\t prob.get_neighbor(node_i,jj): " << prob.get_neighbor(node_i,jj)
+ << "\n\t prob.get_neighbor_idx(node_i,prob.get_neighbor(node_i,jj)): " << prob.get_neighbor_idx(node_i,node_j)
+ );
+
+ DLIB_ASSERT(prob.get_neighbor(node_j,prob.get_neighbor_idx(node_j,node_i))==node_i,
+ "\t void find_max_factor_graph_potts(prob)"
+ << "\n\t The get_neighbor_idx() and get_neighbor() functions must be inverses of each other."
+ << "\n\t node_i: " << node_i
+ << "\n\t node_j: " << node_j
+ << "\n\t prob.get_neighbor_idx(node_j,node_i): " << prob.get_neighbor_idx(node_j,node_i)
+ << "\n\t prob.get_neighbor(node_j,prob.get_neighbor_idx(node_j,node_i)): " << prob.get_neighbor(node_j,prob.get_neighbor_idx(node_j,node_i))
+ );
+
+ DLIB_ASSERT(prob.factor_value_disagreement(node_i,node_j) >= 0,
+ "\t void find_max_factor_graph_potts(prob)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t node_i: " << node_i
+ << "\n\t node_j: " << node_j
+ << "\n\t prob.factor_value_disagreement(node_i,node_j): " << prob.factor_value_disagreement(node_i,node_j)
+ );
+ DLIB_ASSERT(prob.factor_value_disagreement(node_i,node_j) == prob.factor_value_disagreement(node_j,node_i),
+ "\t void find_max_factor_graph_potts(prob)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t node_i: " << node_i
+ << "\n\t node_j: " << node_j
+ << "\n\t prob.factor_value_disagreement(node_i,node_j): " << prob.factor_value_disagreement(node_i,node_j)
+ << "\n\t prob.factor_value_disagreement(node_j,node_i): " << prob.factor_value_disagreement(node_j,node_i)
+ );
+ }
+ }
+#endif
+ COMPILE_TIME_ASSERT(is_signed_type<typename potts_model::value_type>::value);
+ min_cut mc;
+ dlib::impl::potts_flow_graph<potts_model> pfg(prob);
+ mc(pfg, prob.number_of_nodes(), prob.number_of_nodes()+1);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename graph_type
+ >
+ void find_max_factor_graph_potts (
+ const graph_type& g,
+ std::vector<node_label>& labels
+ )
+ {
+ DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
+ "\t void find_max_factor_graph_potts(g,labels)"
+ << "\n\t Invalid inputs were given to this function."
+ );
+ typedef typename graph_type::edge_type edge_type;
+ typedef typename graph_type::type type;
+
+ // The edges and node's have to use the same type to represent factor weights!
+ COMPILE_TIME_ASSERT((is_same_type<edge_type, type>::value == true));
+ COMPILE_TIME_ASSERT(is_signed_type<edge_type>::value);
+
+#ifdef ENABLE_ASSERTS
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ for (unsigned long jj = 0; jj < g.node(i).number_of_neighbors(); ++jj)
+ {
+ unsigned long j = g.node(i).neighbor(jj).index();
+ DLIB_ASSERT(edge(g,i,j) >= 0,
+ "\t void find_max_factor_graph_potts(g,labels)"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t i: " << i
+ << "\n\t j: " << j
+ << "\n\t edge(g,i,j): " << edge(g,i,j)
+ );
+ }
+ }
+#endif
+
+ dlib::impl::general_potts_problem<graph_type> gg(g, labels);
+ find_max_factor_graph_potts(gg);
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_grid_problem,
+ typename mem_manager
+ >
+ void find_max_factor_graph_potts (
+ const potts_grid_problem& prob,
+ array2d<node_label,mem_manager>& labels
+ )
+ {
+ typedef array2d<node_label,mem_manager> image_type;
+ labels.set_size(prob.nr(), prob.nc());
+ dlib::impl::potts_grid_problem<image_type,potts_grid_problem> model(labels,prob);
+ find_max_factor_graph_potts(model);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ namespace impl
+ {
+ template <
+ typename pixel_type1,
+ typename pixel_type2,
+ typename model_type
+ >
+ struct potts_grid_image_pair_model
+ {
+ const pixel_type1* data1;
+ const pixel_type2* data2;
+ const model_type& model;
+ const long nr_;
+ const long nc_;
+ template <typename image_type1, typename image_type2>
+ potts_grid_image_pair_model(
+ const model_type& model_,
+ const image_type1& img1,
+ const image_type2& img2
+ ) :
+ model(model_),
+ nr_(img1.nr()),
+ nc_(img1.nc())
+ {
+ data1 = &img1[0][0];
+ data2 = &img2[0][0];
+ }
+
+ typedef typename model_type::value_type value_type;
+
+ long nr() const { return nr_; }
+ long nc() const { return nc_; }
+
+ value_type factor_value (
+ unsigned long idx
+ ) const
+ {
+ return model.factor_value(*(data1 + idx), *(data2 + idx));
+ }
+
+ value_type factor_value_disagreement (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const
+ {
+ return model.factor_value_disagreement(*(data1 + idx1), *(data1 + idx2));
+ }
+ };
+
+ // ----------------------------------------------------------------------------------------
+
+ template <
+ typename image_type,
+ typename model_type
+ >
+ struct potts_grid_image_single_model
+ {
+ const typename image_type::type* data1;
+ const model_type& model;
+ const long nr_;
+ const long nc_;
+ potts_grid_image_single_model(
+ const model_type& model_,
+ const image_type& img1
+ ) :
+ model(model_),
+ nr_(img1.nr()),
+ nc_(img1.nc())
+ {
+ data1 = &img1[0][0];
+ }
+
+ typedef typename model_type::value_type value_type;
+
+ long nr() const { return nr_; }
+ long nc() const { return nc_; }
+
+ value_type factor_value (
+ unsigned long idx
+ ) const
+ {
+ return model.factor_value(*(data1 + idx));
+ }
+
+ value_type factor_value_disagreement (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const
+ {
+ return model.factor_value_disagreement(*(data1 + idx1), *(data1 + idx2));
+ }
+ };
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename pair_image_model,
+ typename pixel_type1,
+ typename pixel_type2,
+ typename mem_manager
+ >
+ impl::potts_grid_image_pair_model<pixel_type1, pixel_type2, pair_image_model> make_potts_grid_problem (
+ const pair_image_model& model,
+ const array2d<pixel_type1,mem_manager>& img1,
+ const array2d<pixel_type2,mem_manager>& img2
+ )
+ {
+ DLIB_ASSERT(get_rect(img1) == get_rect(img2),
+ "\t potts_grid_problem make_potts_grid_problem()"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t get_rect(img1): " << get_rect(img1)
+ << "\n\t get_rect(img2): " << get_rect(img2)
+ );
+ typedef impl::potts_grid_image_pair_model<pixel_type1, pixel_type2, pair_image_model> potts_type;
+ return potts_type(model,img1,img2);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename single_image_model,
+ typename pixel_type,
+ typename mem_manager
+ >
+ impl::potts_grid_image_single_model<array2d<pixel_type,mem_manager>, single_image_model> make_potts_grid_problem (
+ const single_image_model& model,
+ const array2d<pixel_type,mem_manager>& img
+ )
+ {
+ typedef impl::potts_grid_image_single_model<array2d<pixel_type,mem_manager>, single_image_model> potts_type;
+ return potts_type(model,img);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_Hh_
+
diff --git a/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts_abstract.h b/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts_abstract.h
new file mode 100644
index 000000000..69aa59256
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/find_max_factor_graph_potts_abstract.h
@@ -0,0 +1,636 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_ABSTRACT_Hh_
+#ifdef DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_ABSTRACT_Hh_
+
+#include "../matrix.h"
+#include "min_cut_abstract.h"
+#include "../graph_utils.h"
+#include "../array2d/array2d_kernel_abstract.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ class potts_problem
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object represents a boolean valued factor graph or graphical model
+ that can be efficiently operated on using graph cuts. In particular, this
+ object defines the interface a MAP problem on a factor graph must
+ implement if it is to be solved using the find_max_factor_graph_potts()
+ routine defined at the bottom of this file.
+
+ Note that there is no dlib::potts_problem object. What you are looking
+ at here is simply the interface definition for a Potts problem. You must
+ implement your own version of this object for the problem you wish to
+ solve and then pass it to the find_max_factor_graph_potts() routine.
+
+ Note also that a factor graph should not have any nodes which are
+ neighbors with themselves. Additionally, the graph is undirected. This
+ mean that if A is a neighbor of B then B must be a neighbor of A for
+ the MAP problem to be valid.
+ !*/
+
+ public:
+
+ unsigned long number_of_nodes (
+ ) const;
+ /*!
+ ensures
+ - returns the number of nodes in the factor graph. Or in other words,
+ returns the number of variables in the MAP problem/Potts model.
+ !*/
+
+ unsigned long number_of_neighbors (
+ unsigned long idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns the number of neighbors of node idx.
+ !*/
+
+ // This is an optional variable which specifies a number that is always
+ // greater than or equal to number_of_neighbors(idx). If you don't know
+ // the value at compile time then either don't include max_number_of_neighbors
+ // in your potts_problem object or set it to 0.
+ const static unsigned long max_number_of_neighbors = 0;
+
+ unsigned long get_neighbor (
+ unsigned long idx,
+ unsigned long n
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ - n < number_of_neighbors(idx)
+ ensures
+ - returns the node index value of the n-th neighbor of
+ the node with index value idx.
+ - The neighbor relationship is reciprocal. That is, if
+ get_neighbor(A,i)==B then there is a value of j such
+ that get_neighbor(B,j)==A.
+ - A node is never its own neighbor. That is, there is
+ no i such that get_neighbor(idx,i)==idx.
+ !*/
+
+ unsigned long get_neighbor_idx (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const;
+ /*!
+ requires
+ - idx1 < number_of_nodes()
+ - idx2 < number_of_nodes()
+ ensures
+ - This function is basically the inverse of get_neighbor().
+ - returns a number IDX such that:
+ - get_neighbor(idx1,IDX) == idx2
+ - IDX < number_of_neighbors(idx1)
+ !*/
+
+ void set_label (
+ const unsigned long& idx,
+ node_label value
+ );
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - #get_label(idx) == value
+ !*/
+
+ node_label get_label (
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns the current label for the idx-th node. This is a value which is
+ 0 if the node's label is false and is any other value if it is true.
+
+ Note that this value is not used by factor_value() or factor_value_disagreement().
+ It is simply here to provide a mechanism for find_max_factor_graph_potts()
+ to return its labeled result. Additionally, the reason it returns a
+ node_label rather than a bool is because doing it this way facilitates
+ use of a graph cut algorithm for the solution of the MAP problem. For
+ more of an explanation you should read the paper referenced by the min_cut
+ object.
+ !*/
+
+ // This typedef should be for a type like int or double. It
+ // must also be capable of representing signed values.
+ typedef an_integer_or_real_type value_type;
+
+ value_type factor_value (
+ unsigned long idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns a value which indicates how "good" it is to assign the idx-th
+ node the label of true. The larger the value, the more desirable it is
+ to give it this label. Similarly, a negative value indicates that it is
+ better to give the node a label of false.
+ - It is valid for the returned value to be positive or negative infinity.
+ A value of positive infinity indicates that the idx-th node must be labeled
+ true while negative infinity means it must be labeled false.
+ !*/
+
+ value_type factor_value_disagreement (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const;
+ /*!
+ requires
+ - idx1 < number_of_nodes()
+ - idx2 < number_of_nodes()
+ - idx1 != idx2
+ - the idx1-th node and idx2-th node are neighbors in the graph. That is,
+ get_neighbor(idx1,i)==idx2 for some value of i.
+ ensures
+ - returns a number >= 0. This is the penalty for giving node idx1 and idx2
+ different labels. Larger values indicate a larger penalty.
+ - this function is symmetric. That is, it is true that:
+ factor_value_disagreement(i,j) == factor_value_disagreement(j,i)
+ - It is valid for the returned value to be positive infinity. Returning
+ infinity indicates that the idx1-th and idx2-th nodes must share the same
+ label.
+ !*/
+
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ class potts_grid_problem
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object is a specialization of a potts_problem to the case where
+ the graph is a regular grid where each node is connected to its four
+ neighbors. An example of this is an image where each pixel is a node
+ and is connected to its four immediate neighboring pixels. Therefore,
+ this object defines the interface this special kind of MAP problem
+ must implement if it is to be solved by the find_max_factor_graph_potts(potts_grid_problem,array2d)
+ routine defined at the end of this file.
+
+
+ Note that all nodes always have four neighbors, even nodes on the edge
+ of the graph. This is because these border nodes are connected to
+ the border nodes on the other side of the graph. That is, the graph
+ "wraps" around at the borders.
+ !*/
+
+ public:
+
+ // This typedef should be for a type like int or double. It
+ // must also be capable of representing signed values.
+ typedef an_integer_or_real_type value_type;
+
+ long nr(
+ ) const;
+ /*!
+ ensures
+ - returns the number of rows in the grid
+ !*/
+
+ long nc(
+ ) const;
+ /*!
+ ensures
+ - returns the number of columns in the grid
+ !*/
+
+ value_type factor_value (
+ unsigned long idx
+ ) const;
+ /*!
+ requires
+ - idx < nr()*nc()
+ ensures
+ - The grid is represented in row-major-order format. Therefore, idx
+ identifies a node according to its position in the row-major-order
+ representation of the grid graph. Or in other words, idx corresponds
+ to the following row and column location in the graph:
+ - row == idx/nc()
+ - col == idx%nc()
+ - returns a value which indicates how "good" it is to assign the idx-th
+ node the label of true. The larger the value, the more desirable it is
+ to give it this label. Similarly, a negative value indicates that it is
+ better to give the node a label of false.
+ - It is valid for the returned value to be positive or negative infinity.
+ A value of positive infinity indicates that the idx-th node must be labeled
+ true while negative infinity means it must be labeled false.
+ !*/
+
+ value_type factor_value_disagreement (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const;
+ /*!
+ requires
+ - idx1 < nr()*nc()
+ - idx2 < nr()*nc()
+ - idx1 != idx2
+ - the idx1-th node and idx2-th node are neighbors in the grid graph.
+ ensures
+ - The grid is represented in row-major-order format. Therefore, idx1 and
+ idx2 identify nodes according to their positions in the row-major-order
+ representation of the grid graph. For example, idx1 corresponds
+ to the following row and column location in the graph:
+ - row == idx1/nc()
+ - col == idx1%nc()
+ - returns a number >= 0. This is the penalty for giving node idx1 and idx2
+ different labels. Larger values indicate a larger penalty.
+ - this function is symmetric. That is, it is true that:
+ factor_value_disagreement(i,j) == factor_value_disagreement(j,i)
+ - It is valid for the returned value to be positive infinity. Returning
+ infinity indicates that the idx1-th and idx2-th nodes must share the same
+ label.
+ !*/
+ };
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_problem
+ >
+ typename potts_problem::value_type potts_model_score (
+ const potts_problem& prob
+ );
+ /*!
+ requires
+ - potts_problem == an object with an interface compatible with the potts_problem
+ object defined at the top of this file.
+ - for all valid i and j:
+ - prob.factor_value_disagreement(i,j) >= 0
+ - prob.factor_value_disagreement(i,j) == prob.factor_value_disagreement(j,i)
+ ensures
+ - computes the model score for the given potts_problem. We define this
+ precisely below:
+ - let L(i) == the boolean label of the i-th variable in prob. Or in other
+ words, L(i) == (prob.get_label(i) != 0).
+ - let F == the sum of values of prob.factor_value(i) for only i values
+ where L(i) == true.
+ - Let D == the sum of values of prob.factor_value_disagreement(i,j)
+ for only i and j values which meet the following conditions:
+ - i and j are neighbors in the graph defined by prob, that is,
+ it is valid to call prob.factor_value_disagreement(i,j).
+ - L(i) != L(j)
+ - i < j
+ (i.e. We want to make sure to only count the edge between i and j once)
+
+ - Then this function returns F - D
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename graph_type
+ >
+ typename graph_type::edge_type potts_model_score (
+ const graph_type& g,
+ const std::vector<node_label>& labels
+ );
+ /*!
+ requires
+ - graph_type is an implementation of dlib/graph/graph_kernel_abstract.h
+ - graph_type::edge_type is some signed type such as int or double
+ - graph_type::type must be the same type as graph_type::edge_type
+ - graph_contains_length_one_cycle(g) == false
+ - for all valid i and j:
+ - edge(g,i,j) >= 0
+ ensures
+ - This function does the same thing as the version of potts_model_score()
+ defined above, except that this version operates on a dlib::graph
+ instead of a potts_problem object.
+ - computes the model score for the given graph and labeling. We define this
+ precisely below:
+ - let L(i) == the boolean label of the i-th variable in g. Or in other
+ words, L(i) == (labels[i] != 0).
+ - let F == the sum of values of g.node(i).data for only i values
+ where L(i) == true.
+ - Let D == the sum of values of edge(g,i,j) for only i and j
+ values which meet the following conditions:
+ - i and j are neighbors in the graph defined by g, that is,
+ it is valid to call edge(g,i,j).
+ - L(i) != L(j)
+ - i < j
+ (i.e. We want to make sure to only count the edge between i and j once)
+
+ - Then this function returns F - D
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_grid_problem,
+ typename mem_manager
+ >
+ typename potts_grid_problem::value_type potts_model_score (
+ const potts_grid_problem& prob,
+ const array2d<node_label,mem_manager>& labels
+ );
+ /*!
+ requires
+ - prob.nr() == labels.nr()
+ - prob.nc() == labels.nc()
+ - potts_grid_problem == an object with an interface compatible with the
+ potts_grid_problem object defined above.
+ - for all valid i and j:
+ - prob.factor_value_disagreement(i,j) >= 0
+ - prob.factor_value_disagreement(i,j) == prob.factor_value_disagreement(j,i)
+ ensures
+ - computes the model score for the given potts_grid_problem. We define this
+ precisely below:
+ - let L(i) == the boolean label of the i-th variable in prob. Or in other
+ words, L(i) == (labels[i/labels.nc()][i%labels.nc()] != 0).
+ - let F == the sum of values of prob.factor_value(i) for only i values
+ where L(i) == true.
+ - Let D == the sum of values of prob.factor_value_disagreement(i,j)
+ for only i and j values which meet the following conditions:
+ - i and j are neighbors in the graph defined by prob, that is,
+ it is valid to call prob.factor_value_disagreement(i,j).
+ - L(i) != L(j)
+ - i < j
+ (i.e. We want to make sure to only count the edge between i and j once)
+
+ - Then this function returns F - D
+ !*/
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_problem
+ >
+ void find_max_factor_graph_potts (
+ potts_problem& prob
+ );
+ /*!
+ requires
+ - potts_problem == an object with an interface compatible with the potts_problem
+ object defined at the top of this file.
+ - for all valid i and j:
+ - prob.factor_value_disagreement(i,j) >= 0
+ - prob.factor_value_disagreement(i,j) == prob.factor_value_disagreement(j,i)
+ ensures
+ - This function is a tool for exactly solving the MAP problem in a Potts
+ model. In particular, this means that this function finds the assignments
+ to all the labels in prob which maximizes potts_model_score(#prob).
+ - The optimal labels are stored in #prob.
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename graph_type
+ >
+ void find_max_factor_graph_potts (
+ const graph_type& g,
+ std::vector<node_label>& labels
+ );
+ /*!
+ requires
+ - graph_type is an implementation of dlib/graph/graph_kernel_abstract.h
+ - graph_type::edge_type is some signed type such as int or double
+ - graph_type::type must be the same type as graph_type::edge_type
+ - graph_contains_length_one_cycle(g) == false
+ - for all valid i and j:
+ - edge(g,i,j) >= 0
+ ensures
+ - This routine simply converts g into a potts_problem and calls the
+ version of find_max_factor_graph_potts() defined above on it. Therefore,
+ this routine is just a convenience wrapper that lets you use a dlib::graph
+ to represent a potts problem. This means that this function maximizes
+ the value of potts_model_score(g, #labels).
+ - #labels.size() == g.number_of_nodes()
+ - for all valid i:
+ - #labels[i] == the optimal label for g.node(i)
+ - The correspondence between g and a potts_problem is the following:
+ - the factor_value() for a node is stored in g.node(i).data.
+ - the factor_value_disagreement(i,j) is stored in edge(g,i,j).
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename potts_grid_problem,
+ typename mem_manager
+ >
+ void find_max_factor_graph_potts (
+ const potts_grid_problem& prob,
+ array2d<node_label,mem_manager>& labels
+ );
+ /*!
+ requires
+ - potts_grid_problem == an object with an interface compatible with the
+ potts_grid_problem object defined above.
+ - for all valid i and j:
+ - prob.factor_value_disagreement(i,j) >= 0
+ - prob.factor_value_disagreement(i,j) == prob.factor_value_disagreement(j,i)
+ ensures
+ - This routine solves a version of a potts problem where the graph is a
+ regular grid where each node is connected to its four immediate neighbors.
+ In particular, this means that this function finds the assignments
+ to all the labels in prob which maximizes potts_model_score(prob,#labels).
+ - The optimal labels are stored in #labels.
+ - #labels.nr() == prob.nr()
+ - #labels.nc() == prob.nc()
+ !*/
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+// The following functions and interface definitions are convenience routines for use
+// with the potts grid problem version of find_max_factor_graph_potts() defined above.
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ struct single_image_model
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object defines a slightly more convenient interface for creating
+ potts_grid_problems which operate on an image. In this case, the goal
+ is to assign a binary label to each pixel in an image. In particular,
+ this object defines the interface used by the make_potts_grid_problem()
+ routine defined below.
+
+ In the following comments, we will refer to the image supplied to
+ make_potts_grid_problem() as IMG.
+ !*/
+
+ // This typedef should be for a type like int or double. It
+ // must also be capable of representing signed values.
+ typedef an_integer_or_real_type value_type;
+
+ template <typename pixel_type>
+ value_type factor_value (
+ const pixel_type& v
+ ) const;
+ /*!
+ requires
+ - v is a pixel value from IMG.
+ ensures
+ - returns a value which indicates how "good" it is to assign the location
+ in IMG corresponding to v with the label of true. The larger the value,
+ the more desirable it is to give it this label. Similarly, a negative
+ value indicates that it is better to give the node a label of false.
+ - It is valid for the returned value to be positive or negative infinity.
+ A value of positive infinity indicates that the pixel must be labeled
+ true while negative infinity means it must be labeled false.
+ !*/
+
+ template <typename pixel_type>
+ value_type factor_value_disagreement (
+ const pixel_type& v1,
+ const pixel_type& v2
+ ) const;
+ /*!
+ requires
+ - v1 and v2 are pixel values from neighboring pixels in the IMG image.
+ ensures
+ - returns a number >= 0. This is the penalty for giving neighboring pixels
+ with values v1 and v2 different labels. Larger values indicate a larger
+ penalty.
+ - this function is symmetric. That is, it is true that:
+ factor_value_disagreement(i,j) == factor_value_disagreement(j,i)
+ - It is valid for the returned value to be positive infinity. Returning
+ infinity indicates that the idx1-th and idx2-th nodes must share the same
+ label.
+ !*/
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ struct pair_image_model
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object defines a slightly more convenient interface for creating
+ potts_grid_problems which operate on a pair of identically sized images.
+ In this case, the goal is to assign a label to each pixel in the first
+ image of the pair. In particular, this object defines the interface
+ used by the make_potts_grid_problem() routine defined below.
+
+ In the following comments, we will refer to the two images supplied to
+ make_potts_grid_problem() as IMG1 and IMG2. The goal of the potts
+ problem will be to assign labels to each pixel in IMG1 (IMG2 is
+ not labeled, it is simply used as a place to keep auxiliary data).
+ !*/
+
+ // This typedef should be for a type like int or double. It
+ // must also be capable of representing signed values.
+ typedef an_integer_or_real_type value_type;
+
+ template <typename pixel_type1, typename pixel_type2>
+ value_type factor_value (
+ const pixel_type1& v1,
+ const pixel_type2& v2
+ ) const;
+ /*!
+ requires
+ - v1 and v2 are corresponding pixels from IMG1 and IMG2 respectively.
+ That is, both pixel values have the same coordinates in the images.
+ So for example, if v1 is the value of IMG1[4][5] then v2 is the value
+ of IMG2[4][5].
+ ensures
+ - returns a value which indicates how "good" it is to assign the location
+ in IMG1 corresponding to v1 with the label of true. The larger the value,
+ the more desirable it is to give it this label. Similarly, a negative
+ value indicates that it is better to give the node a label of false.
+ - It is valid for the returned value to be positive or negative infinity.
+ A value of positive infinity indicates that the pixel must be labeled
+ true while negative infinity means it must be labeled false.
+ !*/
+
+ template <typename pixel_type>
+ value_type factor_value_disagreement (
+ const pixel_type& v1,
+ const pixel_type& v2
+ ) const;
+ /*!
+ requires
+ - v1 and v2 are pixel values from neighboring pixels in the IMG1 image.
+ ensures
+ - returns a number >= 0. This is the penalty for giving neighboring pixels
+ with values v1 and v2 different labels. Larger values indicate a larger
+ penalty.
+ - this function is symmetric. That is, it is true that:
+ factor_value_disagreement(i,j) == factor_value_disagreement(j,i)
+ - It is valid for the returned value to be positive infinity. Returning
+ infinity indicates that the idx1-th and idx2-th nodes must share the same
+ label.
+ !*/
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename single_image_model,
+ typename pixel_type,
+ typename mem_manager
+ >
+ potts_grid_problem make_potts_grid_problem (
+ const single_image_model& model,
+ const array2d<pixel_type,mem_manager>& img
+ );
+ /*!
+ requires
+ - single_image_model == an object with an interface compatible with the
+ single_image_model object defined above.
+ - for all valid i and j:
+ - model.factor_value_disagreement(i,j) >= 0
+ - model.factor_value_disagreement(i,j) == model.factor_value_disagreement(j,i)
+ ensures
+ - returns a potts_grid_problem which can be solved using the
+ find_max_factor_graph_potts(prob,array2d) routine defined above. That is,
+ given an image to store the labels, the following statement would solve the
+ potts problem defined by the given model and image:
+ find_max_factor_graph_potts(make_potts_grid_problem(model,img),labels);
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename pair_image_model,
+ typename pixel_type1,
+ typename pixel_type2,
+ typename mem_manager
+ >
+ potts_grid_problem make_potts_grid_problem (
+ const pair_image_model& model,
+ const array2d<pixel_type1,mem_manager>& img1,
+ const array2d<pixel_type2,mem_manager>& img2
+ );
+ /*!
+ requires
+ - get_rect(img1) == get_rect(img2)
+ (i.e. img1 and img2 have the same dimensions)
+ - pair_image_model == an object with an interface compatible with the
+ pair_image_model object defined above.
+ - for all valid i and j:
+ - model.factor_value_disagreement(i,j) >= 0
+ - model.factor_value_disagreement(i,j) == model.factor_value_disagreement(j,i)
+ ensures
+ - returns a potts_grid_problem which can be solved using the
+ find_max_factor_graph_potts(prob,array2d) routine defined above. That is,
+ given an image to store the labels, the following statement would solve the
+ potts problem defined by the given model and pair of images:
+ find_max_factor_graph_potts(make_potts_grid_problem(model,img1,img2),labels);
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_FIND_MAX_FACTOR_GRAPH_PoTTS_ABSTRACT_Hh_
+
+
diff --git a/ml/dlib/dlib/graph_cuts/general_flow_graph.h b/ml/dlib/dlib/graph_cuts/general_flow_graph.h
new file mode 100644
index 000000000..d0b93e311
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/general_flow_graph.h
@@ -0,0 +1,172 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_GENERAL_FLOW_GRaPH_Hh_
+#define DLIB_GENERAL_FLOW_GRaPH_Hh_
+
+#include "../graph_utils.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ namespace impl
+ {
+
+ template <
+ typename directed_graph_type
+ >
+ class general_flow_graph
+ {
+ /*!
+ this is a utility class used by dlib::min_cut to convert a directed_graph
+ into the kind of flow graph expected by the min_cut object's main block
+ of code.
+ !*/
+
+ directed_graph_type& g;
+
+ typedef typename directed_graph_type::node_type node_type;
+ typedef typename directed_graph_type::type node_label;
+
+ public:
+
+ general_flow_graph(
+ directed_graph_type& g_
+ ) : g(g_)
+ {
+ }
+
+ class out_edge_iterator
+ {
+ friend class general_flow_graph;
+ unsigned long idx; // base node idx
+ unsigned long cnt; // count over the neighbors of idx
+ public:
+
+ out_edge_iterator(
+ ):idx(0),cnt(0) {}
+
+ out_edge_iterator(
+ unsigned long idx_,
+ unsigned long cnt_
+ ):idx(idx_),cnt(cnt_)
+ {}
+
+ bool operator!= (
+ const out_edge_iterator& item
+ ) const { return cnt != item.cnt; }
+
+ out_edge_iterator& operator++(
+ )
+ {
+ ++cnt;
+ return *this;
+ }
+ };
+
+ class in_edge_iterator
+ {
+
+ friend class general_flow_graph;
+ unsigned long idx; // base node idx
+ unsigned long cnt; // count over the neighbors of idx
+ public:
+
+ in_edge_iterator(
+ ):idx(0),cnt(0) {}
+
+ in_edge_iterator(
+ unsigned long idx_,
+ unsigned long cnt_
+ ):idx(idx_),cnt(cnt_)
+ {}
+
+ bool operator!= (
+ const in_edge_iterator& item
+ ) const { return cnt != item.cnt; }
+
+ in_edge_iterator& operator++(
+ )
+ {
+ ++cnt;
+ return *this;
+ }
+ };
+
+ unsigned long number_of_nodes (
+ ) const { return g.number_of_nodes(); }
+
+ out_edge_iterator out_begin(
+ const unsigned long& it
+ ) const { return out_edge_iterator(it, 0); }
+
+ in_edge_iterator in_begin(
+ const unsigned long& it
+ ) const { return in_edge_iterator(it, 0); }
+
+ out_edge_iterator out_end(
+ const unsigned long& it
+ ) const { return out_edge_iterator(it, g.node(it).number_of_children()); }
+
+ in_edge_iterator in_end(
+ const unsigned long& it
+ ) const { return in_edge_iterator(it, g.node(it).number_of_parents()); }
+
+ unsigned long node_id (
+ const out_edge_iterator& it
+ ) const { return g.node(it.idx).child(it.cnt).index(); }
+ unsigned long node_id (
+ const in_edge_iterator& it
+ ) const { return g.node(it.idx).parent(it.cnt).index(); }
+
+ typedef typename directed_graph_type::edge_type edge_type;
+
+ edge_type get_flow (const unsigned long& it1, const unsigned long& it2) const
+ {
+ return edge(g, it1, it2);
+ }
+ edge_type get_flow (const out_edge_iterator& it) const
+ {
+ return g.node(it.idx).child_edge(it.cnt);
+ }
+ edge_type get_flow (const in_edge_iterator& it) const
+ {
+ return g.node(it.idx).parent_edge(it.cnt);
+ }
+
+ void adjust_flow (
+ const unsigned long& it1,
+ const unsigned long& it2,
+ const edge_type& value
+ )
+ {
+ edge(g, it1, it2) += value;
+ edge(g, it2, it1) -= value;
+ }
+
+ void set_label (
+ const unsigned long& it,
+ node_label value
+ )
+ {
+ g.node(it).data = value;
+ }
+
+ node_label get_label (
+ const unsigned long& it
+ ) const
+ {
+ return g.node(it).data;
+ }
+
+ };
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_GENERAL_FLOW_GRaPH_Hh_
+
diff --git a/ml/dlib/dlib/graph_cuts/general_potts_problem.h b/ml/dlib/dlib/graph_cuts/general_potts_problem.h
new file mode 100644
index 000000000..ebedbc572
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/general_potts_problem.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_GENERAL_POTTS_PRoBLEM_Hh_
+#define DLIB_GENERAL_POTTS_PRoBLEM_Hh_
+
+#include "../graph_utils.h"
+#include "min_cut.h"
+#include <vector>
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ namespace impl
+ {
+ template <
+ typename graph_type
+ >
+ class general_potts_problem
+ {
+
+ const graph_type& g;
+ std::vector<node_label>& labels;
+ public:
+ general_potts_problem (
+ const graph_type& g_,
+ std::vector<node_label>& labels_
+ ) : g(g_), labels(labels_)
+ {
+ labels.resize(g.number_of_nodes());
+ }
+
+ unsigned long number_of_nodes (
+ ) const { return g.number_of_nodes(); }
+
+ unsigned long number_of_neighbors (
+ unsigned long idx
+ ) const { return g.node(idx).number_of_neighbors(); }
+
+ unsigned long get_neighbor (
+ unsigned long idx,
+ unsigned long n
+ ) const { return g.node(idx).neighbor(n).index(); }
+
+ unsigned long get_neighbor_idx (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const
+ {
+ for (unsigned long i = 0; i < g.node(idx1).number_of_neighbors(); ++i)
+ {
+ if (g.node(idx1).neighbor(i).index() == idx2)
+ return i;
+ }
+
+ // This should never ever execute
+ return 0;
+ }
+
+ void set_label (
+ const unsigned long& idx,
+ node_label value
+ )
+ {
+ labels[idx] = value;
+ }
+
+ node_label get_label (
+ const unsigned long& idx
+ ) const { return labels[idx]; }
+
+ typedef typename graph_type::edge_type value_type;
+
+ value_type factor_value (
+ unsigned long idx
+ ) const
+ {
+ return g.node(idx).data;
+ }
+
+ value_type factor_value_disagreement (
+ unsigned long idx1,
+ unsigned long idx2
+ ) const
+ {
+ return edge(g, idx1, idx2);
+ }
+
+ };
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_GENERAL_POTTS_PRoBLEM_Hh_
+
+
diff --git a/ml/dlib/dlib/graph_cuts/graph_labeler.h b/ml/dlib/dlib/graph_cuts/graph_labeler.h
new file mode 100644
index 000000000..34c3fcb5e
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/graph_labeler.h
@@ -0,0 +1,211 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_GRAPH_LaBELER_Hh_
+#define DLIB_GRAPH_LaBELER_Hh_
+
+#include "graph_labeler_abstract.h"
+#include "../matrix.h"
+#include "../string.h"
+#include <vector>
+#include "find_max_factor_graph_potts.h"
+#include "../svm/sparse_vector.h"
+#include "../graph.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ class graph_labeler
+ {
+
+ public:
+
+ typedef std::vector<bool> label_type;
+ typedef label_type result_type;
+
+ graph_labeler()
+ {
+ }
+
+ graph_labeler(
+ const vector_type& edge_weights_,
+ const vector_type& node_weights_
+ ) :
+ edge_weights(edge_weights_),
+ node_weights(node_weights_)
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT(edge_weights.size() == 0 || min(edge_weights) >= 0,
+ "\t graph_labeler::graph_labeler()"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t min(edge_weights): " << min(edge_weights)
+ << "\n\t this: " << this
+ );
+ }
+
+ const vector_type& get_edge_weights (
+ ) const { return edge_weights; }
+
+ const vector_type& get_node_weights (
+ ) const { return node_weights; }
+
+ template <typename graph_type>
+ void operator() (
+ const graph_type& sample,
+ std::vector<bool>& labels
+ ) const
+ {
+ // make sure requires clause is not broken
+#ifdef ENABLE_ASSERTS
+ DLIB_ASSERT(graph_contains_length_one_cycle(sample) == false,
+ "\t void graph_labeler::operator()"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t get_edge_weights().size(): " << get_edge_weights().size()
+ << "\n\t get_node_weights().size(): " << get_node_weights().size()
+ << "\n\t graph_contains_length_one_cycle(sample): " << graph_contains_length_one_cycle(sample)
+ << "\n\t this: " << this
+ );
+ for (unsigned long i = 0; i < sample.number_of_nodes(); ++i)
+ {
+ if (is_matrix<vector_type>::value &&
+ is_matrix<typename graph_type::type>::value)
+ {
+ // check that dot() is legal.
+ DLIB_ASSERT((unsigned long)get_node_weights().size() == (unsigned long)sample.node(i).data.size(),
+ "\t void graph_labeler::operator()"
+ << "\n\t The size of the node weight vector must match the one in the node."
+ << "\n\t get_node_weights().size(): " << get_node_weights().size()
+ << "\n\t sample.node(i).data.size(): " << sample.node(i).data.size()
+ << "\n\t i: " << i
+ << "\n\t this: " << this
+ );
+ }
+
+ for (unsigned long n = 0; n < sample.node(i).number_of_neighbors(); ++n)
+ {
+ if (is_matrix<vector_type>::value &&
+ is_matrix<typename graph_type::edge_type>::value)
+ {
+ // check that dot() is legal.
+ DLIB_ASSERT((unsigned long)get_edge_weights().size() == (unsigned long)sample.node(i).edge(n).size(),
+ "\t void graph_labeler::operator()"
+ << "\n\t The size of the edge weight vector must match the one in graph's edge."
+ << "\n\t get_edge_weights().size(): " << get_edge_weights().size()
+ << "\n\t sample.node(i).edge(n).size(): " << sample.node(i).edge(n).size()
+ << "\n\t i: " << i
+ << "\n\t this: " << this
+ );
+ }
+
+ DLIB_ASSERT(sample.node(i).edge(n).size() == 0 || min(sample.node(i).edge(n)) >= 0,
+ "\t void graph_labeler::operator()"
+ << "\n\t No edge vectors are allowed to have negative elements."
+ << "\n\t min(sample.node(i).edge(n)): " << min(sample.node(i).edge(n))
+ << "\n\t i: " << i
+ << "\n\t n: " << n
+ << "\n\t this: " << this
+ );
+ }
+ }
+#endif
+
+
+ graph<double,double>::kernel_1a g;
+ copy_graph_structure(sample, g);
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ g.node(i).data = dot(node_weights, sample.node(i).data);
+
+ for (unsigned long n = 0; n < g.node(i).number_of_neighbors(); ++n)
+ {
+ const unsigned long j = g.node(i).neighbor(n).index();
+ // Don't compute an edge weight more than once.
+ if (i < j)
+ {
+ g.node(i).edge(n) = dot(edge_weights, sample.node(i).edge(n));
+ }
+ }
+
+ }
+
+ labels.clear();
+ std::vector<node_label> temp;
+ find_max_factor_graph_potts(g, temp);
+ for (unsigned long i = 0; i < temp.size(); ++i)
+ {
+ if (temp[i] != 0)
+ labels.push_back(true);
+ else
+ labels.push_back(false);
+ }
+ }
+
+ template <typename graph_type>
+ std::vector<bool> operator() (
+ const graph_type& sample
+ ) const
+ {
+ std::vector<bool> temp;
+ (*this)(sample, temp);
+ return temp;
+ }
+
+ private:
+
+ vector_type edge_weights;
+ vector_type node_weights;
+ };
+
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ void serialize (
+ const graph_labeler<vector_type>& item,
+ std::ostream& out
+ )
+ {
+ int version = 1;
+ serialize(version, out);
+ serialize(item.get_edge_weights(), out);
+ serialize(item.get_node_weights(), out);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ void deserialize (
+ graph_labeler<vector_type>& item,
+ std::istream& in
+ )
+ {
+ int version = 0;
+ deserialize(version, in);
+ if (version != 1)
+ {
+ throw dlib::serialization_error("While deserializing graph_labeler, found unexpected version number of " +
+ cast_to_string(version) + ".");
+ }
+
+ vector_type edge_weights, node_weights;
+ deserialize(edge_weights, in);
+ deserialize(node_weights, in);
+
+ item = graph_labeler<vector_type>(edge_weights, node_weights);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_GRAPH_LaBELER_Hh_
+
+
diff --git a/ml/dlib/dlib/graph_cuts/graph_labeler_abstract.h b/ml/dlib/dlib/graph_cuts/graph_labeler_abstract.h
new file mode 100644
index 000000000..a0821b696
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/graph_labeler_abstract.h
@@ -0,0 +1,185 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_GRAPH_LaBELER_ABSTRACT_Hh_
+#ifdef DLIB_GRAPH_LaBELER_ABSTRACT_Hh_
+
+#include "find_max_factor_graph_potts_abstract.h"
+#include "../graph/graph_kernel_abstract.h"
+#include "../matrix/matrix_abstract.h"
+#include <vector>
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ class graph_labeler
+ {
+ /*!
+ REQUIREMENTS ON vector_type
+ - vector_type is a dlib::matrix capable of representing column
+ vectors or it is a sparse vector type as defined in dlib/svm/sparse_vector_abstract.h.
+
+ WHAT THIS OBJECT REPRESENTS
+ This object is a tool for labeling each node in a graph with a value
+ of true or false, subject to a labeling consistency constraint between
+ nodes that share an edge. In particular, this object is useful for
+ representing a graph labeling model learned via some machine learning
+ method.
+
+ To elaborate, suppose we have a graph we want to label. Moreover,
+ suppose we can assign a score to each node which represents how much
+ we want to label the node as true, and we also have scores for each
+ edge which represent how much we wanted the nodes sharing the edge to
+ have the same label. If we could do this then we could find the optimal
+ labeling using the find_max_factor_graph_potts() routine. Therefore,
+ the graph_labeler is just an object which contains the necessary data
+ to compute these score functions and then call find_max_factor_graph_potts().
+ Additionally, this object uses linear functions to represent these score
+ functions.
+
+ THREAD SAFETY
+ It is always safe to use distinct instances of this object in different
+ threads. However, when a single instance is shared between threads then
+ the following rules apply:
+ It is safe to call the const members of this object from multiple
+ threads. This is because the const members are purely read-only
+ operations. However, any operation that modifies a graph_labeler is
+ not threadsafe.
+ !*/
+
+ public:
+
+ typedef std::vector<bool> label_type;
+ typedef label_type result_type;
+
+ graph_labeler(
+ );
+ /*!
+ ensures
+ - this object is properly initialized
+ - #get_node_weights() == an initial value of type vector_type.
+ - #get_edge_weights() == an initial value of type vector_type.
+ !*/
+
+ graph_labeler(
+ const vector_type& edge_weights,
+ const vector_type& node_weights
+ );
+ /*!
+ requires
+ - min(edge_weights) >= 0
+ ensures
+ - #get_edge_weights() == edge_weights
+ - #get_node_weights() == node_weights
+ !*/
+
+ const vector_type& get_edge_weights (
+ ) const;
+ /*!
+ ensures
+ - Recall that the score function for an edge is a linear function of
+ the vector stored at that edge. This means there is some vector, E,
+ which we dot product with the vector in the graph to compute the
+ score. Therefore, this function returns that E vector which defines
+ the edge score function.
+ !*/
+
+ const vector_type& get_node_weights (
+ ) const;
+ /*!
+ ensures
+ - Recall that the score function for a node is a linear function of
+ the vector stored in that node. This means there is some vector, W,
+ which we dot product with the vector in the graph to compute the score.
+ Therefore, this function returns that W vector which defines the node
+ score function.
+ !*/
+
+ template <typename graph_type>
+ void operator() (
+ const graph_type& sample,
+ std::vector<bool>& labels
+ ) const;
+ /*!
+ requires
+ - graph_type is an implementation of dlib/graph/graph_kernel_abstract.h
+ - graph_type::type and graph_type::edge_type must be either matrix objects
+ capable of representing column vectors or some kind of sparse vector
+ type as defined in dlib/svm/sparse_vector_abstract.h.
+ - graph_contains_length_one_cycle(sample) == false
+ - for all valid i and j:
+ - min(edge(sample,i,j)) >= 0
+ - it must be legal to call dot(edge(sample,i,j), get_edge_weights())
+ - it must be legal to call dot(sample.node(i).data, get_node_weights())
+ ensures
+ - Computes a labeling for each node in the given graph and stores the result
+ in #labels.
+ - #labels.size() == sample.number_of_nodes()
+ - for all valid i:
+ - #labels[i] == the label of the node sample.node(i).
+ - The labels are computed by creating a graph, G, with scalar values on each node
+ and edge. The scalar values are calculated according to the following:
+ - for all valid i:
+ - G.node(i).data == dot(get_node_weights(), sample.node(i).data)
+ - for all valid i and j:
+ - edge(G,i,j) == dot(get_edge_weights(), edge(sample,i,j))
+ Then the labels are computed by calling find_max_factor_graph_potts(G,#labels).
+ !*/
+
+ template <typename graph_type>
+ std::vector<bool> operator() (
+ const graph_type& sample
+ ) const;
+ /*!
+ requires
+ - graph_type is an implementation of dlib/graph/graph_kernel_abstract.h
+ - graph_contains_length_one_cycle(sample) == false
+ - for all valid i and j:
+ - min(edge(sample,i,j)) >= 0
+ - it must be legal to call dot(edge(sample,i,j), get_edge_weights())
+ - it must be legal to call dot(sample.node(i).data, get_node_weights())
+ ensures
+ - Performs (*this)(sample, labels); return labels;
+ (i.e. This is just another version of the above operator() routine
+ but instead of returning the labels via the second argument, it
+ returns them as the normal return value).
+ !*/
+
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ void serialize (
+ const graph_labeler<vector_type>& item,
+ std::ostream& out
+ );
+ /*!
+ provides serialization support
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename vector_type
+ >
+ void deserialize (
+ graph_labeler<vector_type>& item,
+ std::istream& in
+ );
+ /*!
+ provides deserialization support
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_GRAPH_LaBELER_ABSTRACT_Hh_
+
diff --git a/ml/dlib/dlib/graph_cuts/min_cut.h b/ml/dlib/dlib/graph_cuts/min_cut.h
new file mode 100644
index 000000000..6bbb57608
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/min_cut.h
@@ -0,0 +1,571 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_MIN_CuT_Hh_
+#define DLIB_MIN_CuT_Hh_
+
+#include "min_cut_abstract.h"
+#include "../matrix.h"
+#include "general_flow_graph.h"
+#include "../is_kind.h"
+
+#include <iostream>
+#include <fstream>
+#include <deque>
+
+
+// ----------------------------------------------------------------------------------------
+
+
+namespace dlib
+{
+
+ typedef unsigned char node_label;
+
+// ----------------------------------------------------------------------------------------
+
+ const node_label SOURCE_CUT = 0;
+ const node_label SINK_CUT = 254;
+ const node_label FREE_NODE = 255;
+
+// ----------------------------------------------------------------------------------------
+
+ template <typename flow_graph>
+ typename disable_if<is_directed_graph<flow_graph>,typename flow_graph::edge_type>::type
+ graph_cut_score (
+ const flow_graph& g
+ )
+ {
+ typedef typename flow_graph::edge_type edge_weight_type;
+ edge_weight_type score = 0;
+ typedef typename flow_graph::out_edge_iterator out_edge_iterator;
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ if (g.get_label(i) != SOURCE_CUT)
+ continue;
+
+ for (out_edge_iterator n = g.out_begin(i); n != g.out_end(i); ++n)
+ {
+ if (g.get_label(g.node_id(n)) != SOURCE_CUT)
+ {
+ score += g.get_flow(n);
+ }
+ }
+ }
+
+ return score;
+ }
+
+ template <typename directed_graph>
+ typename enable_if<is_directed_graph<directed_graph>,typename directed_graph::edge_type>::type
+ graph_cut_score (
+ const directed_graph& g
+ )
+ {
+ return graph_cut_score(dlib::impl::general_flow_graph<const directed_graph>(g));
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ class min_cut
+ {
+
+ public:
+
+ min_cut()
+ {
+ }
+
+ min_cut( const min_cut& )
+ {
+ // Intentionally left empty since all the member variables
+ // don't logically contribute to the state of this object.
+ // This copy constructor is here to explicitly avoid the overhead
+ // of copying these transient variables.
+ }
+
+ template <
+ typename directed_graph
+ >
+ typename enable_if<is_directed_graph<directed_graph> >::type operator() (
+ directed_graph& g,
+ const unsigned long source_node,
+ const unsigned long sink_node
+ ) const
+ {
+ DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
+ "\t void min_cut::operator()"
+ << "\n\t Invalid arguments were given to this function."
+ );
+ DLIB_ASSERT(graph_has_symmetric_edges(g) == true,
+ "\t void min_cut::operator()"
+ << "\n\t Invalid arguments were given to this function."
+ );
+
+ dlib::impl::general_flow_graph<directed_graph> temp(g);
+ (*this)(temp, source_node, sink_node);
+ }
+
+ template <
+ typename flow_graph
+ >
+ typename disable_if<is_directed_graph<flow_graph> >::type operator() (
+ flow_graph& g,
+ const unsigned long source_node,
+ const unsigned long sink_node
+ ) const
+ {
+#ifdef ENABLE_ASSERTS
+ DLIB_ASSERT(source_node != sink_node &&
+ source_node < g.number_of_nodes() &&
+ sink_node < g.number_of_nodes(),
+ "\t void min_cut::operator()"
+ << "\n\t Invalid arguments were given to this function."
+ << "\n\t g.number_of_nodes(): " << g.number_of_nodes()
+ << "\n\t source_node: " << source_node
+ << "\n\t sink_node: " << sink_node
+ << "\n\t this: " << this
+ );
+
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ {
+ typename flow_graph::out_edge_iterator j, end = g.out_end(i);
+ for (j = g.out_begin(i); j != end; ++j)
+ {
+ const unsigned long jj = g.node_id(j);
+ DLIB_ASSERT(g.get_flow(i,jj) >= 0,
+ "\t void min_cut::operator()"
+ << "\n\t Invalid inputs were given to this function."
+ << "\n\t i: "<< i
+ << "\n\t jj: "<< jj
+ << "\n\t g.get_flow(i,jj): "<< g.get_flow(i,jj)
+ << "\n\t this: "<< this
+ );
+
+ }
+ }
+#endif
+ parent.clear();
+ active.clear();
+ orphans.clear();
+
+ typedef typename flow_graph::edge_type edge_type;
+ COMPILE_TIME_ASSERT(is_signed_type<edge_type>::value);
+
+ typedef typename flow_graph::out_edge_iterator out_edge_iterator;
+ typedef typename flow_graph::in_edge_iterator in_edge_iterator;
+
+ // initialize labels
+ for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
+ g.set_label(i, FREE_NODE);
+
+ g.set_label(source_node, SOURCE_CUT);
+ g.set_label(sink_node, SINK_CUT);
+
+ // used to indicate "no parent"
+ const unsigned long no_parent = g.number_of_nodes();
+
+ parent.assign(g.number_of_nodes(), no_parent);
+
+ time = 1;
+ dist.assign(g.number_of_nodes(), 0);
+ ts.assign(g.number_of_nodes(), time);
+
+ active.push_back(source_node);
+ active.push_back(sink_node);
+
+
+ in_edge_iterator in_begin = g.in_begin(active[0]);
+ out_edge_iterator out_begin = g.out_begin(active[0]);
+
+ unsigned long source_side, sink_side;
+ while (grow(g,source_side,sink_side, in_begin, out_begin))
+ {
+ ++time;
+ ts[source_node] = time;
+ ts[sink_node] = time;
+
+ augment(g, source_node, sink_node, source_side, sink_side);
+ adopt(g, source_node, sink_node);
+ }
+
+ }
+
+
+ private:
+
+ unsigned long distance_to_origin (
+ const unsigned long no_parent,
+ unsigned long p,
+ unsigned long
+ ) const
+ {
+ unsigned long start = p;
+ unsigned long count = 0;
+ while (p != no_parent)
+ {
+ if (ts[p] == time)
+ {
+ count += dist[p];
+
+ unsigned long count_down = count;
+ // adjust the dist and ts for the nodes on this path.
+ while (start != p)
+ {
+ ts[start] = time;
+ dist[start] = count_down;
+ --count_down;
+ start = parent[start];
+ }
+
+ return count;
+ }
+ p = parent[p];
+ ++count;
+ }
+
+ return std::numeric_limits<unsigned long>::max();
+ }
+
+ template <typename flow_graph>
+ void adopt (
+ flow_graph& g,
+ const unsigned long source,
+ const unsigned long sink
+ ) const
+ {
+ typedef typename flow_graph::out_edge_iterator out_edge_iterator;
+ typedef typename flow_graph::in_edge_iterator in_edge_iterator;
+
+ // used to indicate "no parent"
+ const unsigned long no_parent = g.number_of_nodes();
+
+ while (orphans.size() > 0)
+ {
+ const unsigned long p = orphans.back();
+ orphans.pop_back();
+
+ const unsigned char label_p = g.get_label(p);
+
+ // Try to find a valid parent for p.
+ if (label_p == SOURCE_CUT)
+ {
+ const in_edge_iterator begin(g.in_begin(p));
+ const in_edge_iterator end(g.in_end(p));
+ unsigned long best_dist = std::numeric_limits<unsigned long>::max();
+ unsigned long best_node = 0;
+ for(in_edge_iterator q = begin; q != end; ++q)
+ {
+ const unsigned long id = g.node_id(q);
+
+ if (g.get_label(id) != label_p || g.get_flow(q) <= 0 )
+ continue;
+
+ unsigned long temp = distance_to_origin(no_parent, id,source);
+ if (temp < best_dist)
+ {
+ best_dist = temp;
+ best_node = id;
+ }
+
+ }
+ if (best_dist != std::numeric_limits<unsigned long>::max())
+ {
+ parent[p] = best_node;
+ dist[p] = dist[best_node] + 1;
+ ts[p] = time;
+ }
+
+ // if we didn't find a parent for p
+ if (parent[p] == no_parent)
+ {
+ for(in_edge_iterator q = begin; q != end; ++q)
+ {
+ const unsigned long id = g.node_id(q);
+
+ if (g.get_label(id) != SOURCE_CUT)
+ continue;
+
+ if (g.get_flow(q) > 0)
+ active.push_back(id);
+
+ if (parent[id] == p)
+ {
+ parent[id] = no_parent;
+ orphans.push_back(id);
+ }
+ }
+ g.set_label(p, FREE_NODE);
+ }
+ }
+ else
+ {
+ unsigned long best_node = 0;
+ unsigned long best_dist = std::numeric_limits<unsigned long>::max();
+ const out_edge_iterator begin(g.out_begin(p));
+ const out_edge_iterator end(g.out_end(p));
+ for(out_edge_iterator q = begin; q != end; ++q)
+ {
+ const unsigned long id = g.node_id(q);
+ if (g.get_label(id) != label_p || g.get_flow(q) <= 0)
+ continue;
+
+ unsigned long temp = distance_to_origin(no_parent, id,sink);
+
+ if (temp < best_dist)
+ {
+ best_dist = temp;
+ best_node = id;
+ }
+ }
+
+ if (best_dist != std::numeric_limits<unsigned long>::max())
+ {
+ parent[p] = best_node;
+ dist[p] = dist[best_node] + 1;
+ ts[p] = time;
+ }
+
+ // if we didn't find a parent for p
+ if (parent[p] == no_parent)
+ {
+ for(out_edge_iterator q = begin; q != end; ++q)
+ {
+ const unsigned long id = g.node_id(q);
+
+ if (g.get_label(id) != SINK_CUT)
+ continue;
+
+ if (g.get_flow(q) > 0)
+ active.push_back(id);
+
+ if (parent[id] == p)
+ {
+ parent[id] = no_parent;
+ orphans.push_back(id);
+ }
+ }
+
+ g.set_label(p, FREE_NODE);
+ }
+ }
+
+
+ }
+
+ }
+
+ template <typename flow_graph>
+ void augment (
+ flow_graph& g,
+ const unsigned long& source,
+ const unsigned long& sink,
+ const unsigned long& source_side,
+ const unsigned long& sink_side
+ ) const
+ {
+ typedef typename flow_graph::edge_type edge_type;
+
+ // used to indicate "no parent"
+ const unsigned long no_parent = g.number_of_nodes();
+
+ unsigned long s = source_side;
+ unsigned long t = sink_side;
+ edge_type min_cap = g.get_flow(s,t);
+
+ // find the bottleneck capacity on the current path.
+
+ // check from source_side back to the source for the min capacity link.
+ t = s;
+ while (t != source)
+ {
+ s = parent[t];
+ const edge_type temp = g.get_flow(s, t);
+ if (temp < min_cap)
+ {
+ min_cap = temp;
+ }
+ t = s;
+ }
+
+ // check from sink_side back to the sink for the min capacity link
+ s = sink_side;
+ while (s != sink)
+ {
+ t = parent[s];
+ const edge_type temp = g.get_flow(s, t);
+ if (temp < min_cap)
+ {
+ min_cap = temp;
+ }
+ s = t;
+ }
+
+
+ // now push the max possible amount of flow though the path
+ s = source_side;
+ t = sink_side;
+ g.adjust_flow(t,s, min_cap);
+
+ // trace back towards the source
+ t = s;
+ while (t != source)
+ {
+ s = parent[t];
+ g.adjust_flow(t,s, min_cap);
+ if (g.get_flow(s,t) <= 0)
+ {
+ parent[t] = no_parent;
+ orphans.push_back(t);
+ }
+
+ t = s;
+ }
+
+ // trace back towards the sink
+ s = sink_side;
+ while (s != sink)
+ {
+ t = parent[s];
+ g.adjust_flow(t,s, min_cap);
+ if (g.get_flow(s,t) <= 0)
+ {
+ parent[s] = no_parent;
+ orphans.push_back(s);
+ }
+ s = t;
+ }
+ }
+
+ template <typename flow_graph>
+ bool grow (
+ flow_graph& g,
+ unsigned long& source_side,
+ unsigned long& sink_side,
+ typename flow_graph::in_edge_iterator& in_begin,
+ typename flow_graph::out_edge_iterator& out_begin
+ ) const
+ /*!
+ ensures
+ - if (an augmenting path was found) then
+ - returns true
+ - (#source_side, #sink_side) == the point where the two trees meet.
+ #source_side is part of the source tree and #sink_side is part of
+ the sink tree.
+ - else
+ - returns false
+ !*/
+ {
+ typedef typename flow_graph::out_edge_iterator out_edge_iterator;
+ typedef typename flow_graph::in_edge_iterator in_edge_iterator;
+
+
+ while (active.size() != 0)
+ {
+ // pick an active node
+ const unsigned long A = active[0];
+
+ const unsigned char label_A = g.get_label(A);
+
+ // process its neighbors
+ if (label_A == SOURCE_CUT)
+ {
+ const out_edge_iterator out_end = g.out_end(A);
+ for(out_edge_iterator& i = out_begin; i != out_end; ++i)
+ {
+ if (g.get_flow(i) > 0)
+ {
+ const unsigned long id = g.node_id(i);
+ const unsigned char label_i = g.get_label(id);
+ if (label_i == FREE_NODE)
+ {
+ active.push_back(id);
+ g.set_label(id,SOURCE_CUT);
+ parent[id] = A;
+ ts[id] = ts[A];
+ dist[id] = dist[A] + 1;
+ }
+ else if (label_A != label_i)
+ {
+ source_side = A;
+ sink_side = id;
+ return true;
+ }
+ else if (is_closer(A, id))
+ {
+ parent[id] = A;
+ ts[id] = ts[A];
+ dist[id] = dist[A] + 1;
+ }
+ }
+ }
+ }
+ else if (label_A == SINK_CUT)
+ {
+ const in_edge_iterator in_end = g.in_end(A);
+ for(in_edge_iterator& i = in_begin; i != in_end; ++i)
+ {
+ if (g.get_flow(i) > 0)
+ {
+ const unsigned long id = g.node_id(i);
+ const unsigned char label_i = g.get_label(id);
+ if (label_i == FREE_NODE)
+ {
+ active.push_back(id);
+ g.set_label(id,SINK_CUT);
+ parent[id] = A;
+ ts[id] = ts[A];
+ dist[id] = dist[A] + 1;
+ }
+ else if (label_A != label_i)
+ {
+ sink_side = A;
+ source_side = id;
+ return true;
+ }
+ else if (is_closer(A, id))
+ {
+ parent[id] = A;
+ ts[id] = ts[A];
+ dist[id] = dist[A] + 1;
+ }
+ }
+ }
+ }
+
+ active.pop_front();
+ if (active.size() != 0)
+ {
+ in_begin = g.in_begin(active[0]);
+ out_begin = g.out_begin(active[0]);
+ }
+ }
+
+ return false;
+ }
+
+ inline bool is_closer (
+ unsigned long p,
+ unsigned long q
+ ) const
+ {
+ // return true if p is closer to a terminal than q
+ return ts[q] <= ts[p] && dist[q] > dist[p];
+ }
+
+ mutable std::vector<uint32> dist;
+ mutable std::vector<uint32> ts;
+ mutable uint32 time;
+ mutable std::vector<unsigned long> parent;
+
+ mutable std::deque<unsigned long> active;
+ mutable std::vector<unsigned long> orphans;
+ };
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+// ----------------------------------------------------------------------------------------
+
+#endif // DLIB_MIN_CuT_Hh_
+
diff --git a/ml/dlib/dlib/graph_cuts/min_cut_abstract.h b/ml/dlib/dlib/graph_cuts/min_cut_abstract.h
new file mode 100644
index 000000000..748aca950
--- /dev/null
+++ b/ml/dlib/dlib/graph_cuts/min_cut_abstract.h
@@ -0,0 +1,476 @@
+// Copyright (C) 2012 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_MIN_CuT_ABSTRACT_Hh_
+#ifdef DLIB_MIN_CuT_ABSTRACT_Hh_
+
+#include "../graph_utils.h"
+
+// ----------------------------------------------------------------------------------------
+
+namespace dlib
+{
+ /*!A node_label
+ The node_label type is the type used to label which part of a graph cut
+ a node is on. It is used by all the graph cut tools. The three possible
+ values of a node label are SOURCE_CUT, SINK_CUT, or FREE_NODE.
+ !*/
+
+ typedef unsigned char node_label;
+ const node_label SOURCE_CUT = 0;
+ const node_label SINK_CUT = 254;
+ const node_label FREE_NODE = 255;
+
+// ----------------------------------------------------------------------------------------
+
+ class flow_graph
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object represents a flow capacity graph for use with the
+ min_cut algorithm defined below. In particular, this object
+ is a kind of directed graph where the edge weights specify the
+ flow capacities.
+
+ Note that there is no dlib::flow_graph object. What you are
+ looking at here is simply the interface definition for a graph
+ which can be used with the min_cut algorithm. You must implement
+ your own version of this object for the graph you wish to work with
+ and then pass it to the min_cut::operator() routine.
+
+ It's also worth pointing out that this graph has symmetric edge
+ connections. That is, if there is an edge from node A to node B
+ then there must also be an edge from node B to node A.
+ !*/
+
+ public:
+
+ class out_edge_iterator
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is a simple forward iterator for iterating over the neighbors
+ of a node in the graph. It also represents the fact that the neighbors
+ are on the end of an outgoing edge. That is, the edge represents
+ the amount of flow which can flow towards the neighbor.
+ !*/
+
+ public:
+ out_edge_iterator(
+ );
+ /*!
+ ensures
+ - constructs an iterator in an undefined state. It can't
+ be used until assigned with a valid iterator.
+ !*/
+
+ out_edge_iterator(
+ const out_edge_iterator& item
+ );
+ /*!
+ ensures
+ - #*this is a copy of item
+ !*/
+
+ out_edge_iterator& operator=(
+ const out_edge_iterator& item
+ );
+ /*!
+ ensures
+ - #*this is a copy of item
+ - returns #*this
+ !*/
+
+ bool operator!= (
+ const out_edge_iterator& item
+ ) const;
+ /*!
+ requires
+ - *this and item are iterators over the neighbors for the
+ same node.
+ ensures
+ - returns false if *this and item both reference the same
+ node in the graph and true otherwise.
+ !*/
+
+ out_edge_iterator& operator++(
+ );
+ /*!
+ ensures
+ - advances *this to the next neighbor node.
+ - returns a reference to the updated *this
+ (i.e. this is the ++object form of the increment operator)
+ !*/
+ };
+
+ class in_edge_iterator
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is a simple forward iterator for iterating over the neighbors
+ of a node in the graph. It also represents the fact that the neighbors
+ are on the end of an incoming edge. That is, the edge represents
+ the amount of flow which can flow out of the neighbor node.
+ !*/
+
+ public:
+
+ in_edge_iterator(
+ );
+ /*!
+ ensures
+ - constructs an iterator in an undefined state. It can't
+ be used until assigned with a valid iterator.
+ !*/
+
+ in_edge_iterator(
+ const in_edge_iterator& item
+ );
+ /*!
+ ensures
+ - #*this is a copy of item
+ !*/
+
+ in_edge_iterator& operator=(
+ const in_edge_iterator& item
+ );
+ /*!
+ ensures
+ - #*this is a copy of item
+ - returns #*this
+ !*/
+
+ bool operator!= (
+ const in_edge_iterator& item
+ ) const;
+ /*!
+ requires
+ - *this and item are iterators over the neighbors for the
+ same node.
+ ensures
+ - returns false if *this and item both reference the same
+ node in the graph and true otherwise.
+ !*/
+
+ in_edge_iterator& operator++(
+ );
+ /*!
+ ensures
+ - advances *this to the next neighbor node.
+ - returns a reference to the updated *this
+ (i.e. this is the ++object form of the increment operator)
+ !*/
+ };
+
+
+
+ unsigned long number_of_nodes (
+ ) const;
+ /*!
+ ensures
+ - returns the number of nodes in the graph.
+ !*/
+
+ out_edge_iterator out_begin(
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns an iterator pointing to the first neighboring node of
+ the idx-th node. If no such node exists then returns out_end(idx).
+ - The returned iterator also represents the directed edge going from
+ node idx to the neighbor.
+ !*/
+
+ in_edge_iterator in_begin(
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns an iterator pointing to the first neighboring node of
+ the idx-th node. If no such node exists then returns in_end(idx).
+ - The returned iterator also represents the directed edge going from
+ the neighbor to node idx.
+ !*/
+
+ out_edge_iterator out_end(
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns an iterator to one past the last neighboring node of
+ the idx-th node.
+ !*/
+
+ in_edge_iterator in_end(
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns an iterator to one past the last neighboring node of
+ the idx-th node.
+ !*/
+
+
+ unsigned long node_id (
+ const out_edge_iterator& it
+ ) const;
+ /*!
+ requires
+ - it == a valid iterator (i.e. it must be in the range [out_begin(idx), out_end(idx))
+ for some valid idx)
+ ensures
+ - returns a number IDX such that:
+ - 0 <= IDX < number_of_nodes()
+ - IDX == The index which uniquely identifies the node pointed to by the
+ iterator it. This number can be used with any member function in this
+ object which expect a node index. (e.g. get_label(IDX) == the label for the
+ node pointed to by it)
+ !*/
+
+ unsigned long node_id (
+ const in_edge_iterator& it
+ ) const;
+ /*!
+ requires
+ - it == a valid iterator (i.e. it must be in the range [in_begin(idx), in_end(idx))
+ for some valid idx)
+ ensures
+ - returns a number IDX such that:
+ - 0 <= IDX < number_of_nodes()
+ - IDX == The index which uniquely identifies the node pointed to by the
+ iterator it. This number can be used with any member function in this
+ object which expect a node index. (e.g. get_label(IDX) == the label for the
+ node pointed to by it)
+ !*/
+
+ // This typedef should be for a type like int or double. It
+ // must also be capable of representing signed values.
+ typedef an_integer_or_real_type edge_type;
+
+ edge_type get_flow (
+ const unsigned long& idx1,
+ const unsigned long& idx2
+ ) const;
+ /*!
+ requires
+ - idx1 < number_of_nodes()
+ - idx2 < number_of_nodes()
+ - idx1 and idx2 are neighbors in the graph
+ ensures
+ - returns the residual flow capacity from the idx1-th node to the idx2-th node.
+ - It is valid for this function to return a floating point value of infinity.
+ This value means this edge has an unlimited capacity.
+ !*/
+
+ edge_type get_flow (
+ const out_edge_iterator& it
+ ) const;
+ /*!
+ requires
+ - it == a valid iterator (i.e. it must be in the range [out_begin(idx), out_end(idx))
+ for some valid idx)
+ ensures
+ - let IDX = node_id(it)
+ - it represents the directed edge from a node, call it H, to the node IDX. Therefore,
+ this function returns get_flow(H,IDX)
+ - It is valid for this function to return a floating point value of infinity.
+ This value means this edge has an unlimited capacity.
+ !*/
+
+ edge_type get_flow (
+ const in_edge_iterator& it
+ ) const;
+ /*!
+ requires
+ - it == a valid iterator (i.e. it must be in the range [in_begin(idx), in_end(idx))
+ for some valid idx)
+ ensures
+ - let IDX = node_id(it)
+ - it represents the directed edge from node IDX to another node, call it H. Therefore,
+ this function returns get_flow(IDX,H)
+ - It is valid for this function to return a floating point value of infinity.
+ This value means this edge has an unlimited capacity.
+ !*/
+
+ void adjust_flow (
+ const unsigned long& idx1,
+ const unsigned long& idx2,
+ const edge_type& value
+ );
+ /*!
+ requires
+ - idx1 < number_of_nodes()
+ - idx2 < number_of_nodes()
+ - idx1 and idx2 are neighbors in the graph
+ ensures
+ - #get_flow(idx1,idx2) == get_flow(idx1,idx2) + value
+ - #get_flow(idx2,idx1) == get_flow(idx2,idx1) - value
+ !*/
+
+ void set_label (
+ const unsigned long& idx,
+ node_label value
+ );
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - #get_label(idx) == value
+ !*/
+
+ node_label get_label (
+ const unsigned long& idx
+ ) const;
+ /*!
+ requires
+ - idx < number_of_nodes()
+ ensures
+ - returns the label for the idx-th node in the graph.
+ !*/
+
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename flow_graph
+ >
+ typename flow_graph::edge_type graph_cut_score (
+ const flow_graph& g
+ );
+ /*!
+ requires
+ - flow_graph == an object with an interface compatible with the flow_graph
+ object defined at the top of this file, or, an implementation of
+ dlib/directed_graph/directed_graph_kernel_abstract.h.
+ ensures
+ - returns the sum of the outgoing flows from nodes with a label of SOURCE_CUT
+ to nodes with a label != SOURCE_CUT. Note that for a directed_graph object,
+ the labels are stored in the node's data field.
+ !*/
+
+// ----------------------------------------------------------------------------------------
+
+ class min_cut
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is a function object which can be used to find the min cut
+ on a graph.
+
+ The implementation is based on the method described in the following
+ paper:
+ An Experimental Comparison of Min-Cut/Max-Flow Algorithms for
+ Energy Minimization in Vision, by Yuri Boykov and Vladimir Kolmogorov,
+ in PAMI 2004.
+
+ !*/
+
+ public:
+
+ min_cut(
+ );
+ /*!
+ ensures
+ - this object is properly initialized
+ !*/
+
+ template <
+ typename flow_graph
+ >
+ void operator() (
+ flow_graph& g,
+ const unsigned long source_node,
+ const unsigned long sink_node
+ ) const;
+ /*!
+ requires
+ - flow_graph == an object with an interface compatible with the flow_graph
+ object defined at the top of this file.
+ - source_node != sink_node
+ - source_node < g.number_of_nodes()
+ - sink_node < g.number_of_nodes()
+ - for all valid i and j:
+ - g.get_flow(i,j) >= 0
+ (i.e. all the flow capacities/edge weights are non-negative)
+ - g does not contain any self loops. That is, no nodes are neighbors with
+ themselves.
+ ensures
+ - Finds the minimum cut on the given graph. That is, this function finds
+ a labeling of nodes in g such that graph_cut_score(g) would be minimized. Note
+ that the flow values in #g are modified by this algorithm so if you want
+ to obtain the min cut score you must call min_cut::operator(), then copy
+ the flow values back into #g, and then call graph_cut_score(#g). But in most
+ cases you don't care about the value of the min cut score, rather, you
+ just want the labels in #g.
+ - #g.get_label(source_node) == SOURCE_CUT
+ - #g.get_label(sink_node) == SINK_CUT
+ - for all valid i:
+ - #g.get_label(i) == SOURCE_CUT, SINK_CUT, or FREE_NODE
+ - if (#g.get_label(i) == SOURCE_CUT) then
+ - The minimum cut of g places node i into the source side of the cut.
+ - if (#g.get_label(i) == SINK_CUT) then
+ - The minimum cut of g places node i into the sink side of the cut.
+ - if (#g.get_label(i) == FREE_NODE) then
+ - Node i can be labeled SOURCE_CUT or SINK_CUT. Both labelings
+ result in the same cut score.
+ - When interpreting g as a graph of flow capacities from the source_node
+ to the sink_node we can say that the min cut problem is equivalent to
+ the max flow problem. This equivalent problem is to find out how to push
+ as much "flow" from the source node to the sink node as possible.
+ Upon termination, #g will contain the final flow residuals in addition to
+ the graph cut labels. That is, for all valid i and j:
+ - #g.get_flow(i,j) == the residual flow capacity left after the max
+ possible amount of flow is passing from the source node to the sink
+ node. For example, this means that #g.get_flow(i,j) == 0 whenever
+ node i is in the SOURCE_CUT and j is in the SINK_CUT.
+ - #g.get_flow(i,j) >= 0
+ !*/
+
+ template <
+ typename directed_graph
+ >
+ void operator() (
+ directed_graph& g,
+ const unsigned long source_node,
+ const unsigned long sink_node
+ ) const;
+ /*!
+ requires
+ - directed_graph == an implementation of dlib/directed_graph/directed_graph_kernel_abstract.h
+ - directed_graph::type == node_label
+ - directed_graph::edge_type == and integer or double type
+ - source_node != sink_node
+ - source_node < g.number_of_nodes()
+ - sink_node < g.number_of_nodes()
+ - for all valid i and j:
+ - edge(g,i,j) >= 0
+ (i.e. all the flow capacities/edge weights are positive)
+ - graph_contains_length_one_cycle(g) == false
+ - graph_has_symmetric_edges(g) == true
+ ensures
+ - This routine simply converts g into a flow graph and calls the version
+ of operator() defined above. Note that the conversion is done in O(1)
+ time, it's just an interface adaptor.
+ - edge weights in g correspond to network flows while the .data field of
+ each node in g corresponds to the graph node labels.
+ - upon termination, the flows and labels in g will have been modified
+ as described in the above operator() routine.
+ !*/
+ };
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_MIN_CuT_ABSTRACT_Hh_
+
+