1217 lines
37 KiB
C++
1217 lines
37 KiB
C++
// Copyright (C) 2012 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <dlib/graph_cuts.h>
|
|
#include <dlib/graph_utils.h>
|
|
#include <dlib/directed_graph.h>
|
|
#include <dlib/graph.h>
|
|
#include <dlib/rand.h>
|
|
#include <dlib/hash.h>
|
|
#include <dlib/image_transforms.h>
|
|
|
|
#include "tester.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace test;
|
|
using namespace dlib;
|
|
using namespace std;
|
|
|
|
|
|
logger dlog("test.graph_cuts");
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class dense_potts_problem
|
|
{
|
|
public:
|
|
typedef double value_type;
|
|
private:
|
|
|
|
matrix<value_type,0,1> factors1;
|
|
matrix<value_type> factors2;
|
|
matrix<node_label,0,1> labels;
|
|
public:
|
|
|
|
dense_potts_problem (
|
|
unsigned long num_nodes,
|
|
dlib::rand& rnd
|
|
)
|
|
{
|
|
factors1 = -7*(randm(num_nodes, 1, rnd)-0.5);
|
|
factors2 = make_symmetric(randm(num_nodes, num_nodes, rnd) > 0.5);
|
|
labels.set_size(num_nodes);
|
|
labels = FREE_NODE;
|
|
}
|
|
|
|
unsigned long number_of_nodes (
|
|
) const { return factors1.nr(); }
|
|
|
|
unsigned long number_of_neighbors (
|
|
unsigned long // idx
|
|
) const { return number_of_nodes()-1; }
|
|
|
|
unsigned long get_neighbor_idx (
|
|
unsigned long node_id1,
|
|
unsigned long node_id2
|
|
) const
|
|
{
|
|
if (node_id2 < node_id1)
|
|
return node_id2;
|
|
else
|
|
return node_id2-1;
|
|
}
|
|
|
|
unsigned long get_neighbor (
|
|
unsigned long node_id,
|
|
unsigned long idx
|
|
) const
|
|
{
|
|
DLIB_TEST(node_id < number_of_nodes());
|
|
DLIB_TEST(idx < number_of_neighbors(node_id));
|
|
if (idx < node_id)
|
|
return idx;
|
|
else
|
|
return idx+1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
value_type factor_value (unsigned long idx) const
|
|
{
|
|
DLIB_TEST(idx < number_of_nodes());
|
|
|
|
return factors1(idx);
|
|
}
|
|
|
|
value_type factor_value_disagreement (unsigned long idx1, unsigned long idx2) const
|
|
{
|
|
DLIB_TEST(idx1 != idx2);
|
|
DLIB_TEST(idx1 < number_of_nodes());
|
|
DLIB_TEST(idx2 < number_of_nodes());
|
|
DLIB_TEST(get_neighbor_idx(idx1,idx2) < number_of_neighbors(idx1));
|
|
DLIB_TEST(get_neighbor_idx(idx2,idx1) < number_of_neighbors(idx2));
|
|
|
|
return factors2(idx1, idx2);
|
|
}
|
|
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class image_potts_problem
|
|
{
|
|
public:
|
|
typedef double value_type;
|
|
const static unsigned long max_number_of_neighbors = 4;
|
|
private:
|
|
|
|
matrix<value_type,0,1> factors1;
|
|
matrix<value_type> factors2;
|
|
matrix<node_label,0,1> labels;
|
|
long nr;
|
|
long nc;
|
|
rectangle rect, inner_rect;
|
|
mutable long count;
|
|
public:
|
|
|
|
image_potts_problem (
|
|
long nr_,
|
|
long nc_,
|
|
dlib::rand& rnd
|
|
) : nr(nr_), nc(nc_)
|
|
{
|
|
rect = rectangle(0,0,nc-1,nr-1);
|
|
inner_rect = shrink_rect(rect,1);
|
|
const unsigned long num_nodes = nr*nc;
|
|
factors1 = -7*(randm(num_nodes, 1, rnd));
|
|
factors2 = randm(num_nodes, 4, rnd) > 0.5;
|
|
|
|
//factors1 = 0;
|
|
//set_rowm(factors1, range(0, factors1.nr()/2)) = -1;
|
|
|
|
labels.set_size(num_nodes);
|
|
labels = FREE_NODE;
|
|
|
|
count = 0;
|
|
}
|
|
|
|
~image_potts_problem()
|
|
{
|
|
dlog << LTRACE << "interface calls: " << count;
|
|
dlog << LTRACE << "labels hash: "<< murmur_hash3_128bit(&labels(0), labels.size()*sizeof(labels(0)), 0).first;
|
|
}
|
|
|
|
unsigned long number_of_nodes (
|
|
) const { return factors1.nr(); }
|
|
|
|
unsigned long number_of_neighbors (
|
|
unsigned long idx
|
|
) const
|
|
{
|
|
++count;
|
|
const point& p = get_loc(idx);
|
|
if (inner_rect.contains(p))
|
|
return 4;
|
|
else if (p == rect.tl_corner() ||
|
|
p == rect.bl_corner() ||
|
|
p == rect.tr_corner() ||
|
|
p == rect.br_corner() )
|
|
return 2;
|
|
else
|
|
return 3;
|
|
}
|
|
|
|
unsigned long get_neighbor_idx (
|
|
long node_id1,
|
|
long node_id2
|
|
) const
|
|
{
|
|
++count;
|
|
const point& p = get_loc(node_id1);
|
|
long ret = 0;
|
|
if (rect.contains(p + point(1,0)))
|
|
{
|
|
if (node_id2-node_id1 == 1)
|
|
return ret;
|
|
++ret;
|
|
}
|
|
|
|
if (rect.contains(p - point(1,0)))
|
|
{
|
|
if (node_id2-node_id1 == -1)
|
|
return ret;
|
|
++ret;
|
|
}
|
|
|
|
if (rect.contains(p + point(0,1)))
|
|
{
|
|
if (node_id2-node_id1 == nc)
|
|
return ret;
|
|
++ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned long get_neighbor (
|
|
long node_id,
|
|
long idx
|
|
) const
|
|
{
|
|
++count;
|
|
const point& p = get_loc(node_id);
|
|
if (rect.contains(p + point(1,0)))
|
|
{
|
|
if (idx == 0)
|
|
return node_id+1;
|
|
--idx;
|
|
}
|
|
|
|
if (rect.contains(p - point(1,0)))
|
|
{
|
|
if (idx == 0)
|
|
return node_id-1;
|
|
--idx;
|
|
}
|
|
|
|
if (rect.contains(p + point(0,1)))
|
|
{
|
|
if (idx == 0)
|
|
return node_id+nc;
|
|
--idx;
|
|
}
|
|
|
|
return node_id-nc;
|
|
}
|
|
|
|
void set_label (
|
|
const unsigned long& idx,
|
|
node_label value
|
|
)
|
|
{
|
|
++count;
|
|
labels(idx) = value;
|
|
}
|
|
|
|
node_label get_label (
|
|
const unsigned long& idx
|
|
) const
|
|
{
|
|
++count;
|
|
return labels(idx);
|
|
}
|
|
|
|
value_type factor_value (unsigned long idx) const
|
|
{
|
|
++count;
|
|
DLIB_TEST(idx < (unsigned long)number_of_nodes());
|
|
|
|
return factors1(idx);
|
|
}
|
|
|
|
value_type factor_value_disagreement (unsigned long idx1, unsigned long idx2) const
|
|
{
|
|
++count;
|
|
DLIB_TEST(idx1 != idx2);
|
|
DLIB_TEST(idx1 < (unsigned long)number_of_nodes());
|
|
DLIB_TEST(idx2 < (unsigned long)number_of_nodes());
|
|
|
|
// make this function symmetric
|
|
if (idx1 > idx2)
|
|
swap(idx1,idx2);
|
|
|
|
|
|
DLIB_TEST(get_neighbor(idx1, get_neighbor_idx(idx1, idx2)) == idx2);
|
|
DLIB_TEST(get_neighbor(idx2, get_neighbor_idx(idx2, idx1)) == idx1);
|
|
|
|
// the neighbor relationship better be symmetric
|
|
DLIB_TEST(get_neighbor_idx(idx1,idx2) < number_of_neighbors(idx1));
|
|
DLIB_TEST_MSG(get_neighbor_idx(idx2,idx1) < number_of_neighbors(idx2),
|
|
"\n idx1: "<< idx1 <<
|
|
"\n idx2: "<< idx2 <<
|
|
"\n get_neighbor_idx(idx2,idx1): "<< get_neighbor_idx(idx2,idx1) <<
|
|
"\n number_of_neighbors(idx2): " << number_of_neighbors(idx2) <<
|
|
"\n nr: "<< nr <<
|
|
"\n nc: "<< nc
|
|
);
|
|
|
|
return factors2(idx1, get_neighbor_idx(idx1,idx2));
|
|
}
|
|
|
|
private:
|
|
point get_loc (
|
|
const unsigned long& idx
|
|
) const
|
|
{
|
|
return point(idx%nc, idx/nc);
|
|
}
|
|
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename potts_model>
|
|
void brute_force_potts_model (
|
|
potts_model& g
|
|
)
|
|
{
|
|
potts_model m(g);
|
|
|
|
const unsigned long num = (unsigned long)std::pow(2.0, (double)m.number_of_nodes());
|
|
|
|
double best_score = -std::numeric_limits<double>::infinity();
|
|
for (unsigned long i = 0; i < num; ++i)
|
|
{
|
|
for (unsigned long j = 0; j < m.number_of_nodes(); ++j)
|
|
{
|
|
unsigned long T = (1)<<j;
|
|
T = (T&i);
|
|
if (T != 0)
|
|
m.set_label(j,SINK_CUT);
|
|
else
|
|
m.set_label(j,SOURCE_CUT);
|
|
}
|
|
|
|
|
|
double score = potts_model_score(m);
|
|
if (score > best_score)
|
|
{
|
|
best_score = score;
|
|
g = m;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename graph_type>
|
|
void brute_force_potts_model_on_graph (
|
|
const graph_type& g,
|
|
std::vector<node_label>& labels_
|
|
)
|
|
{
|
|
std::vector<node_label> labels;
|
|
labels.resize(g.number_of_nodes());
|
|
|
|
const unsigned long num = (unsigned long)std::pow(2.0, (double)g.number_of_nodes());
|
|
|
|
double best_score = -std::numeric_limits<double>::infinity();
|
|
for (unsigned long i = 0; i < num; ++i)
|
|
{
|
|
for (unsigned long j = 0; j < g.number_of_nodes(); ++j)
|
|
{
|
|
unsigned long T = (1)<<j;
|
|
T = (T&i);
|
|
if (T != 0)
|
|
labels[j] = SINK_CUT;
|
|
else
|
|
labels[j] = SOURCE_CUT;
|
|
}
|
|
|
|
|
|
double score = potts_model_score(g,labels);
|
|
if (score > best_score)
|
|
{
|
|
best_score = score;
|
|
labels_ = labels;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename graph_type>
|
|
void make_random_undirected_graph(
|
|
dlib::rand& rnd,
|
|
graph_type& g
|
|
)
|
|
{
|
|
typedef typename graph_type::edge_type edge_weight_type;
|
|
g.clear();
|
|
const unsigned int num_nodes = rnd.get_random_32bit_number()%8;
|
|
g.set_number_of_nodes(num_nodes);
|
|
|
|
const unsigned int num_edges = static_cast<unsigned int>(num_nodes*(num_nodes-1)/2*rnd.get_random_double() + 0.5);
|
|
|
|
// add the right number of randomly selected edges
|
|
unsigned int count = 0;
|
|
while (count < num_edges)
|
|
{
|
|
unsigned long i = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
unsigned long j = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
if (i != j && g.has_edge(i, j) == false)
|
|
{
|
|
++count;
|
|
g.add_edge(i, j);
|
|
edge(g, i, j) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
}
|
|
|
|
for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
|
|
{
|
|
g.node(i).data = static_cast<edge_weight_type>(rnd.get_random_gaussian()*200);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void test_graph_potts_model(
|
|
dlib::rand& rnd
|
|
)
|
|
{
|
|
using namespace std;
|
|
double brute_force_score;
|
|
double graph_cut_score;
|
|
|
|
graph<double,double>::kernel_1a_c temp;
|
|
make_random_undirected_graph(rnd,temp);
|
|
|
|
{
|
|
std::vector<node_label> labels;
|
|
|
|
brute_force_potts_model_on_graph(temp, labels);
|
|
|
|
for (unsigned long i = 0; i < temp.number_of_nodes(); ++i)
|
|
{
|
|
dlog << LTRACE << "node " << i << ": "<< (int)labels[i];
|
|
}
|
|
|
|
brute_force_score = potts_model_score(temp, labels);
|
|
dlog << LTRACE << "brute force score: "<< brute_force_score;
|
|
}
|
|
dlog << LTRACE << "******************";
|
|
|
|
{
|
|
std::vector<node_label> labels;
|
|
find_max_factor_graph_potts(temp, labels);
|
|
DLIB_TEST(temp.number_of_nodes() == labels.size());
|
|
|
|
for (unsigned long i = 0; i < temp.number_of_nodes(); ++i)
|
|
{
|
|
dlog << LTRACE << "node " << i << ": "<< (int)labels[i];
|
|
}
|
|
graph_cut_score = potts_model_score(temp, labels);
|
|
dlog << LTRACE << "graph cut score: "<< graph_cut_score;
|
|
}
|
|
|
|
DLIB_TEST_MSG(graph_cut_score == brute_force_score, std::abs(graph_cut_score - brute_force_score));
|
|
|
|
dlog << LTRACE << "##################";
|
|
dlog << LTRACE << "##################";
|
|
dlog << LTRACE << "##################";
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename potts_prob>
|
|
void impl_test_potts_model (
|
|
potts_prob& p
|
|
)
|
|
{
|
|
using namespace std;
|
|
double brute_force_score;
|
|
double graph_cut_score;
|
|
|
|
{
|
|
potts_prob temp(p);
|
|
brute_force_potts_model(temp);
|
|
|
|
for (unsigned long i = 0; i < temp.number_of_nodes(); ++i)
|
|
{
|
|
dlog << LTRACE << "node " << i << ": "<< (int)temp.get_label(i);
|
|
}
|
|
brute_force_score = potts_model_score(temp);
|
|
dlog << LTRACE << "brute force score: "<< brute_force_score;
|
|
}
|
|
dlog << LTRACE << "******************";
|
|
|
|
{
|
|
potts_prob temp(p);
|
|
find_max_factor_graph_potts(temp);
|
|
|
|
for (unsigned long i = 0; i < temp.number_of_nodes(); ++i)
|
|
{
|
|
dlog << LTRACE << "node " << i << ": "<< (int)temp.get_label(i);
|
|
}
|
|
graph_cut_score = potts_model_score(temp);
|
|
dlog << LTRACE << "graph cut score: "<< graph_cut_score;
|
|
}
|
|
|
|
DLIB_TEST_MSG(graph_cut_score == brute_force_score, std::abs(graph_cut_score - brute_force_score));
|
|
|
|
dlog << LTRACE << "##################";
|
|
dlog << LTRACE << "##################";
|
|
dlog << LTRACE << "##################";
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// BASIC MIN CUT STUFF
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename directed_graph>
|
|
void brute_force_min_cut (
|
|
directed_graph& g,
|
|
unsigned long source,
|
|
unsigned long sink
|
|
)
|
|
{
|
|
typedef typename directed_graph::edge_type edge_weight_type;
|
|
const unsigned long num = (unsigned long)std::pow(2.0, (double)g.number_of_nodes());
|
|
|
|
std::vector<node_label> best_cut(g.number_of_nodes(),FREE_NODE);
|
|
|
|
edge_weight_type best_score = std::numeric_limits<edge_weight_type>::max();
|
|
for (unsigned long i = 0; i < num; ++i)
|
|
{
|
|
for (unsigned long j = 0; j < g.number_of_nodes(); ++j)
|
|
{
|
|
unsigned long T = (1)<<j;
|
|
T = (T&i);
|
|
if (T != 0)
|
|
g.node(j).data = SINK_CUT;
|
|
else
|
|
g.node(j).data = SOURCE_CUT;
|
|
}
|
|
|
|
// ignore cuts that don't label the source or sink node the way we want.
|
|
if (g.node(source).data != SOURCE_CUT ||
|
|
g.node(sink).data != SINK_CUT)
|
|
continue;
|
|
|
|
edge_weight_type score = graph_cut_score(g);
|
|
if (score < best_score)
|
|
{
|
|
best_score = score;
|
|
for (unsigned long j = 0; j < g.number_of_nodes(); ++j)
|
|
best_cut[j] = g.node(j).data;
|
|
}
|
|
}
|
|
|
|
for (unsigned long j = 0; j < g.number_of_nodes(); ++j)
|
|
g.node(j).data = best_cut[j];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename directed_graph>
|
|
void print_graph(
|
|
const directed_graph& g
|
|
)
|
|
{
|
|
using namespace std;
|
|
dlog << LTRACE << "number of nodes: "<< g.number_of_nodes();
|
|
for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
|
|
{
|
|
for (unsigned long n = 0; n < g.node(i).number_of_children(); ++n)
|
|
dlog << LTRACE << i << " -(" << g.node(i).child_edge(n) << ")-> " << g.node(i).child(n).index();
|
|
}
|
|
}
|
|
|
|
template <typename directed_graph>
|
|
void copy_edge_weights (
|
|
directed_graph& dest,
|
|
const directed_graph& src
|
|
)
|
|
{
|
|
for (unsigned long i = 0; i < src.number_of_nodes(); ++i)
|
|
{
|
|
for (unsigned long n = 0; n < src.node(i).number_of_children(); ++n)
|
|
{
|
|
dest.node(i).child_edge(n) = src.node(i).child_edge(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename graph_type>
|
|
void pick_random_source_and_sink (
|
|
dlib::rand& rnd,
|
|
const graph_type& g,
|
|
unsigned long& source,
|
|
unsigned long& sink
|
|
)
|
|
{
|
|
source = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
sink = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
while (sink == source)
|
|
sink = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename dgraph_type>
|
|
void make_random_graph(
|
|
dlib::rand& rnd,
|
|
dgraph_type& g,
|
|
unsigned long& source,
|
|
unsigned long& sink
|
|
)
|
|
{
|
|
typedef typename dgraph_type::edge_type edge_weight_type;
|
|
g.clear();
|
|
const unsigned int num_nodes = rnd.get_random_32bit_number()%7 + 2;
|
|
g.set_number_of_nodes(num_nodes);
|
|
|
|
const unsigned int num_edges = static_cast<unsigned int>(num_nodes*(num_nodes-1)/2*rnd.get_random_double() + 0.5);
|
|
|
|
// add the right number of randomly selected edges
|
|
unsigned int count = 0;
|
|
while (count < num_edges)
|
|
{
|
|
unsigned long parent = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
unsigned long child = rnd.get_random_32bit_number()%g.number_of_nodes();
|
|
if (parent != child && g.has_edge(parent, child) == false)
|
|
{
|
|
++count;
|
|
g.add_edge(parent, child);
|
|
edge(g, parent, child) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
|
|
// have to have edges both ways
|
|
swap(parent, child);
|
|
g.add_edge(parent, child);
|
|
edge(g, parent, child) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
}
|
|
|
|
pick_random_source_and_sink(rnd, g, source, sink);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename dgraph_type>
|
|
void make_random_chain_graph(
|
|
dlib::rand& rnd,
|
|
dgraph_type& g,
|
|
unsigned long& source,
|
|
unsigned long& sink
|
|
)
|
|
{
|
|
typedef typename dgraph_type::edge_type edge_weight_type;
|
|
g.clear();
|
|
const unsigned int num_nodes = rnd.get_random_32bit_number()%7 + 2;
|
|
g.set_number_of_nodes(num_nodes);
|
|
|
|
for (unsigned long i = 1; i < g.number_of_nodes(); ++i)
|
|
{
|
|
g.add_edge(i,i-1);
|
|
g.add_edge(i-1,i);
|
|
edge(g, i, i-1) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g, i-1, i) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
|
|
pick_random_source_and_sink(rnd, g, source, sink);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename dgraph_type>
|
|
void make_random_grid_graph(
|
|
dlib::rand& rnd,
|
|
dgraph_type& g,
|
|
unsigned long& source,
|
|
unsigned long& sink
|
|
)
|
|
/*!
|
|
ensures
|
|
- makes a grid graph like the kind used for potts models.
|
|
!*/
|
|
{
|
|
typedef typename dgraph_type::edge_type edge_weight_type;
|
|
g.clear();
|
|
const long nr = rnd.get_random_32bit_number()%2 + 2;
|
|
const long nc = rnd.get_random_32bit_number()%2 + 2;
|
|
g.set_number_of_nodes(nr*nc+2);
|
|
|
|
const rectangle rect(0,0,nc-1,nr-1);
|
|
for (long r = 0; r < nr; ++r)
|
|
{
|
|
for (long c = 0; c < nc; ++c)
|
|
{
|
|
const point p(c,r);
|
|
const unsigned long i = p.y()*nc + p.x();
|
|
|
|
const point n2(c-1,r);
|
|
if (rect.contains(n2))
|
|
{
|
|
const unsigned long j = n2.y()*nc + n2.x();
|
|
g.add_edge(i,j);
|
|
g.add_edge(j,i);
|
|
edge(g,i,j) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g,j,i) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
|
|
const point n4(c,r-1);
|
|
if (rect.contains(n4))
|
|
{
|
|
const unsigned long j = n4.y()*nc + n4.x();
|
|
g.add_edge(i,j);
|
|
g.add_edge(j,i);
|
|
edge(g,i,j) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g,j,i) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
}
|
|
}
|
|
|
|
// use the last two nodes as source and sink. Also connect them to all the other nodes.
|
|
source = g.number_of_nodes()-1;
|
|
sink = g.number_of_nodes()-2;
|
|
for (unsigned long i = 0; i < g.number_of_nodes()-2; ++i)
|
|
{
|
|
g.add_edge(i,source);
|
|
g.add_edge(source,i);
|
|
g.add_edge(i,sink);
|
|
g.add_edge(sink,i);
|
|
|
|
edge(g,i,source) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g,source,i) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g,i,sink) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
edge(g,sink,i) = static_cast<edge_weight_type>(rnd.get_random_double()*50);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename min_cut, typename dgraph_type>
|
|
void run_test_on_graphs (
|
|
const min_cut& mc,
|
|
dgraph_type& g1,
|
|
dgraph_type& g2,
|
|
unsigned long source,
|
|
unsigned long sink
|
|
)
|
|
{
|
|
typedef typename dgraph_type::edge_type edge_weight_type;
|
|
using namespace std;
|
|
|
|
|
|
dlog << LTRACE << "number of nodes: "<< g1.number_of_nodes();
|
|
dlog << LTRACE << "is graph connected: "<< graph_is_connected(g1);
|
|
dlog << LTRACE << "has self loops: "<< graph_contains_length_one_cycle(g1);
|
|
dlog << LTRACE << "SOURCE_CUT: " << source;
|
|
dlog << LTRACE << "SINK_CUT: " << sink;
|
|
mc(g1, source, sink);
|
|
brute_force_min_cut(g2, source, sink);
|
|
|
|
print_graph(g1);
|
|
|
|
// make sure the flow residuals are 0 at the cut locations
|
|
for (unsigned long i = 0; i < g1.number_of_nodes(); ++i)
|
|
{
|
|
for (unsigned long j = 0; j < g1.node(i).number_of_children(); ++j)
|
|
{
|
|
if ((g1.node(i).data == SOURCE_CUT && g1.node(i).child(j).data != SOURCE_CUT) ||
|
|
(g1.node(i).data != SINK_CUT && g1.node(i).child(j).data == SINK_CUT)
|
|
)
|
|
{
|
|
DLIB_TEST_MSG(g1.node(i).child_edge(j) == 0, g1.node(i).child_edge(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy the edge weights from g2 back to g1 so we can compute cut scores
|
|
copy_edge_weights(g1, g2);
|
|
|
|
DLIB_TEST(g1.number_of_nodes() == g2.number_of_nodes());
|
|
for (unsigned long i = 0; i < g1.number_of_nodes(); ++i)
|
|
{
|
|
dlog << LTRACE << "node " << i << ": " << (int)g1.node(i).data << ", " << (int)g2.node(i).data;
|
|
if (g1.node(i).data != g2.node(i).data)
|
|
{
|
|
edge_weight_type cut_score = graph_cut_score(g1);
|
|
edge_weight_type brute_force_score = graph_cut_score(g2);
|
|
dlog << LTRACE << "graph cut score: "<< cut_score;
|
|
dlog << LTRACE << "brute force score: "<< brute_force_score;
|
|
|
|
if (brute_force_score != cut_score)
|
|
print_graph(g1);
|
|
DLIB_TEST_MSG(brute_force_score == cut_score,std::abs(brute_force_score-cut_score));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename min_cut, typename edge_weight_type>
|
|
void test_graph_cuts(dlib::rand& rnd)
|
|
{
|
|
typedef typename dlib::directed_graph<node_label, edge_weight_type>::kernel_1a_c dgraph_type;
|
|
// we will create two identical graphs.
|
|
dgraph_type g1, g2;
|
|
min_cut mc;
|
|
|
|
unsigned long source, sink;
|
|
|
|
dlib::rand rnd_copy(rnd);
|
|
make_random_graph(rnd,g1, source, sink);
|
|
make_random_graph(rnd_copy,g2, source, sink);
|
|
run_test_on_graphs(mc, g1, g2, source, sink);
|
|
|
|
rnd_copy = rnd;
|
|
make_random_grid_graph(rnd,g1, source, sink);
|
|
make_random_grid_graph(rnd_copy,g2, source, sink);
|
|
run_test_on_graphs(mc, g1, g2, source, sink);
|
|
|
|
rnd_copy = rnd;
|
|
make_random_chain_graph(rnd,g1, source, sink);
|
|
make_random_chain_graph(rnd_copy,g2, source, sink);
|
|
run_test_on_graphs(mc, g1, g2, source, sink);
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class test_potts_grid_problem
|
|
{
|
|
public:
|
|
test_potts_grid_problem(int seed_) :seed(seed_){}
|
|
int seed;
|
|
|
|
long nr() const { return 3;}
|
|
long nc() const { return 3;}
|
|
|
|
typedef double value_type;
|
|
|
|
value_type factor_value(unsigned long idx) const
|
|
{
|
|
// Copy idx into a char buffer to avoid warnings about violation of strict aliasing
|
|
// rules when murmur_hash3() gets inlined into this function.
|
|
char buf[sizeof(idx)];
|
|
memcpy(buf,&idx,sizeof(idx));
|
|
// now hash the buffer rather than idx.
|
|
return ((double)murmur_hash3(buf, sizeof(buf), seed) - std::numeric_limits<uint32>::max()/2.0)/1000.0;
|
|
}
|
|
|
|
value_type factor_value_disagreement(unsigned long idx1, unsigned long idx2) const
|
|
{
|
|
return std::abs(factor_value(idx1+idx2)/10.0);
|
|
}
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename prob_type>
|
|
void brute_force_potts_grid_problem(
|
|
const prob_type& prob,
|
|
array2d<unsigned char>& labels
|
|
)
|
|
{
|
|
const unsigned long num = (unsigned long)std::pow(2.0, (double)prob.nr()*prob.nc());
|
|
|
|
array2d<unsigned char> temp(prob.nr(), prob.nc());
|
|
unsigned char* data = &temp[0][0];
|
|
|
|
double best_score = -std::numeric_limits<double>::infinity();
|
|
for (unsigned long i = 0; i < num; ++i)
|
|
{
|
|
for (unsigned long j = 0; j < temp.size(); ++j)
|
|
{
|
|
unsigned long T = (1)<<j;
|
|
T = (T&i);
|
|
if (T != 0)
|
|
*(data + j) = SINK_CUT;
|
|
else
|
|
*(data + j) = SOURCE_CUT;
|
|
}
|
|
|
|
|
|
double score = potts_model_score(prob, temp);
|
|
if (score > best_score)
|
|
{
|
|
best_score = score;
|
|
assign_image(labels, temp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void test_inf()
|
|
{
|
|
graph<double,double>::kernel_1a_c g;
|
|
g.set_number_of_nodes(4);
|
|
g.add_edge(0,1);
|
|
g.add_edge(1,2);
|
|
g.add_edge(2,3);
|
|
g.add_edge(3,0);
|
|
|
|
g.node(0).data = std::numeric_limits<double>::infinity();
|
|
g.node(1).data = -std::numeric_limits<double>::infinity();
|
|
g.node(2).data = std::numeric_limits<double>::infinity();
|
|
g.node(3).data = -std::numeric_limits<double>::infinity();
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 1;
|
|
edge(g,3,0) = 1;
|
|
|
|
std::vector<node_label> labels;
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] != 0);
|
|
DLIB_TEST(labels[1] == 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] == 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = std::numeric_limits<double>::infinity();
|
|
g.node(1).data = 0;
|
|
g.node(2).data = 0;
|
|
g.node(3).data = -3;
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 1;
|
|
edge(g,3,0) = 1;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] != 0);
|
|
DLIB_TEST(labels[1] != 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] == 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = std::numeric_limits<double>::infinity();
|
|
g.node(1).data = 0;
|
|
g.node(2).data = 0;
|
|
g.node(3).data = -0.1;
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 1;
|
|
edge(g,3,0) = 1;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] != 0);
|
|
DLIB_TEST(labels[1] != 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] != 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = std::numeric_limits<double>::infinity();
|
|
g.node(1).data = 0;
|
|
g.node(2).data = 0;
|
|
g.node(3).data = -0.1;
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 0;
|
|
edge(g,3,0) = 0;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] != 0);
|
|
DLIB_TEST(labels[1] != 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] == 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = -std::numeric_limits<double>::infinity();
|
|
g.node(1).data = 0;
|
|
g.node(2).data = 0;
|
|
g.node(3).data = 0.1;
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 0;
|
|
edge(g,3,0) = 0;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] == 0);
|
|
DLIB_TEST(labels[1] == 0);
|
|
DLIB_TEST(labels[2] == 0);
|
|
DLIB_TEST(labels[3] != 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = -std::numeric_limits<double>::infinity();
|
|
g.node(1).data = std::numeric_limits<double>::infinity();
|
|
g.node(2).data = 0;
|
|
g.node(3).data = 0.1;
|
|
|
|
edge(g,0,1) = 1;
|
|
edge(g,1,2) = 1;
|
|
edge(g,2,3) = 0;
|
|
edge(g,3,0) = 0;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] == 0);
|
|
DLIB_TEST(labels[1] != 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] != 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = -10;
|
|
g.node(1).data = std::numeric_limits<double>::infinity();
|
|
g.node(2).data = 0;
|
|
g.node(3).data = 0.1;
|
|
|
|
edge(g,0,1) = std::numeric_limits<double>::infinity();
|
|
edge(g,1,2) = std::numeric_limits<double>::infinity();
|
|
edge(g,2,3) = std::numeric_limits<double>::infinity();
|
|
edge(g,3,0) = std::numeric_limits<double>::infinity();
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] != 0);
|
|
DLIB_TEST(labels[1] != 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] != 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = 10;
|
|
g.node(1).data = -std::numeric_limits<double>::infinity();
|
|
g.node(2).data = 20.05;
|
|
g.node(3).data = -0.1;
|
|
|
|
edge(g,0,1) = std::numeric_limits<double>::infinity();
|
|
edge(g,1,2) = 10;
|
|
edge(g,2,3) = std::numeric_limits<double>::infinity();
|
|
edge(g,3,0) = 10;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] == 0);
|
|
DLIB_TEST(labels[1] == 0);
|
|
DLIB_TEST(labels[2] == 0);
|
|
DLIB_TEST(labels[3] == 0);
|
|
|
|
// --------------------------
|
|
|
|
g.node(0).data = 10;
|
|
g.node(1).data = -std::numeric_limits<double>::infinity();
|
|
g.node(2).data = 20.2;
|
|
g.node(3).data = -0.1;
|
|
|
|
edge(g,0,1) = std::numeric_limits<double>::infinity();
|
|
edge(g,1,2) = 10;
|
|
edge(g,2,3) = std::numeric_limits<double>::infinity();
|
|
edge(g,3,0) = 10;
|
|
|
|
find_max_factor_graph_potts(g, labels);
|
|
|
|
DLIB_TEST(labels[0] == 0);
|
|
DLIB_TEST(labels[1] == 0);
|
|
DLIB_TEST(labels[2] != 0);
|
|
DLIB_TEST(labels[3] != 0);
|
|
}
|
|
|
|
struct potts_pair_image_model
|
|
{
|
|
typedef double value_type;
|
|
|
|
template <typename pixel_type1, typename pixel_type2>
|
|
value_type factor_value (
|
|
const pixel_type1& ,
|
|
const pixel_type2& v2
|
|
) const
|
|
{
|
|
return v2;
|
|
}
|
|
|
|
template <typename pixel_type>
|
|
value_type factor_value_disagreement (
|
|
const pixel_type& v1,
|
|
const pixel_type& v2
|
|
) const
|
|
{
|
|
if (v1 == v2)
|
|
return 10;
|
|
else
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
void test_potts_pair_grid()
|
|
{
|
|
array2d<int> img1(40,40);
|
|
array2d<double> img2(40,40);
|
|
|
|
assign_all_pixels(img1, -1);
|
|
assign_all_pixels(img2, -1);
|
|
|
|
img1[4][4] = 1000;
|
|
|
|
img2[4][3] = 1;
|
|
img2[4][4] = 1;
|
|
img2[4][5] = 1;
|
|
img2[3][3] = 1;
|
|
img2[3][4] = 1;
|
|
img2[3][5] = 1;
|
|
img2[5][3] = 1;
|
|
img2[5][4] = 1;
|
|
img2[5][5] = 1;
|
|
|
|
array2d<unsigned char> labels;
|
|
find_max_factor_graph_potts(make_potts_grid_problem(potts_pair_image_model(),img2,img1), labels);
|
|
|
|
dlog << LINFO << "num true labels: " << sum(matrix_cast<int>(mat(labels)!=0));
|
|
DLIB_TEST(sum(matrix_cast<int>(mat(labels)!=0)) == 9);
|
|
DLIB_TEST(sum(matrix_cast<int>(mat(labels)==0)) == (int)img1.size()-9);
|
|
|
|
DLIB_TEST(labels[4][3] != 0);
|
|
DLIB_TEST(labels[4][4] != 0);
|
|
DLIB_TEST(labels[4][5] != 0);
|
|
DLIB_TEST(labels[3][3] != 0);
|
|
DLIB_TEST(labels[3][4] != 0);
|
|
DLIB_TEST(labels[3][5] != 0);
|
|
DLIB_TEST(labels[5][3] != 0);
|
|
DLIB_TEST(labels[5][4] != 0);
|
|
DLIB_TEST(labels[5][5] != 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class graph_cuts_tester : public tester
|
|
{
|
|
public:
|
|
graph_cuts_tester (
|
|
) :
|
|
tester ("test_graph_cuts",
|
|
"Runs tests on the graph cuts tools.")
|
|
{}
|
|
|
|
dlib::rand rnd;
|
|
|
|
void perform_test (
|
|
)
|
|
{
|
|
test_potts_pair_grid();
|
|
test_inf();
|
|
|
|
for (int i = 0; i < 500; ++i)
|
|
{
|
|
array2d<unsigned char> labels, brute_labels;
|
|
test_potts_grid_problem prob(i);
|
|
find_max_factor_graph_potts(prob, labels);
|
|
brute_force_potts_grid_problem(prob, brute_labels);
|
|
|
|
DLIB_TEST(labels.nr() == brute_labels.nr());
|
|
DLIB_TEST(labels.nc() == brute_labels.nc());
|
|
for (long r = 0; r < labels.nr(); ++r)
|
|
{
|
|
for (long c = 0; c < labels.nc(); ++c)
|
|
{
|
|
bool normal = (labels[r][c] != 0);
|
|
bool brute = (brute_labels[r][c] != 0);
|
|
DLIB_TEST(normal == brute);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 1000; ++i)
|
|
{
|
|
print_spinner();
|
|
dlog << LTRACE << "test_grpah_cuts<short> iter: " << i;
|
|
test_graph_cuts<min_cut,short>(rnd);
|
|
print_spinner();
|
|
dlog << LTRACE << "test_grpah_cuts<double> iter: " << i;
|
|
test_graph_cuts<min_cut,double>(rnd);
|
|
}
|
|
|
|
|
|
for (int k = 0; k < 300; ++k)
|
|
{
|
|
dlog << LTRACE << "image_potts_problem iter " << k;
|
|
print_spinner();
|
|
image_potts_problem p(3,3, rnd);
|
|
impl_test_potts_model(p);
|
|
}
|
|
for (int k = 0; k < 300; ++k)
|
|
{
|
|
dlog << LTRACE << "dense_potts_problem iter " << k;
|
|
print_spinner();
|
|
dense_potts_problem p(6, rnd);
|
|
impl_test_potts_model(p);
|
|
}
|
|
|
|
for (int k = 0; k < 300; ++k)
|
|
{
|
|
dlog << LTRACE << "dense_potts_problem iter " << k;
|
|
print_spinner();
|
|
test_graph_potts_model(rnd);
|
|
}
|
|
}
|
|
} a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|