2 /* Vladimir Kolmogorov (vnk@cs.cornell.edu), 2003. */
\r
5 This software implements an energy minimization technique described in
\r
7 What Energy Functions can be Minimized via Graph Cuts?
\r
8 Vladimir Kolmogorov and Ramin Zabih.
\r
9 To appear in IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI).
\r
10 Earlier version appeared in European Conference on Computer Vision (ECCV), May 2002.
\r
12 More specifically, it computes the global minimum of a function E of binary
\r
13 variables x_1, ..., x_n which can be written as a sum of terms involving
\r
14 at most three variables at a time:
\r
16 E(x_1, ..., x_n) = \sum_{i} E^{i} (x_i)
\r
17 + \sum_{i,j} E^{i,j} (x_i, x_j)
\r
18 + \sum_{i,j,k} E^{i,j,k}(x_i, x_j, x_k)
\r
20 The method works only if each term is "regular". Definitions of regularity
\r
21 for terms E^{i}, E^{i,j}, E^{i,j,k} are given below as comments to functions
\r
22 add_term1(), add_term2(), add_term3().
\r
24 This software can be used only for research purposes. IF YOU USE THIS SOFTWARE,
\r
25 YOU SHOULD CITE THE AFOREMENTIONED PAPER IN ANY RESULTING PUBLICATION.
\r
27 In order to use it, you will also need a MAXFLOW software which can be
\r
28 obtained from http://www.cs.cornell.edu/People/vnk/software.html
\r
32 (Minimizes the following function of 3 binary variables:
\r
33 E(x, y, z) = x - 2*y + 3*(1-z) - 4*x*y + 5*|y-z|):
\r
35 ///////////////////////////////////////////////////
\r
42 // Minimize the following function of 3 binary variables:
\r
43 // E(x, y, z) = x - 2*y + 3*(1-z) - 4*x*y + 5*|y-z|
\r
45 Energy::Var varx, vary, varz;
\r
46 Energy *e = new Energy();
\r
48 varx = e -> add_variable();
\r
49 vary = e -> add_variable();
\r
50 varz = e -> add_variable();
\r
52 e -> add_term1(varx, 0, 1); // add term x
\r
53 e -> add_term1(vary, 0, -2); // add term -2*y
\r
54 e -> add_term1(varz, 3, 0); // add term 3*(1-z)
\r
56 e -> add_term2(x, y, 0, 0, 0, -4); // add term -4*x*y
\r
57 e -> add_term2(y, z, 0, 5, 5, 0); // add term 5*|y-z|
\r
59 Energy::TotalValue Emin = e -> minimize();
\r
61 printf("Minimum = %d\n", Emin);
\r
62 printf("Optimal solution:\n");
\r
63 printf("x = %d\n", e->get_var(varx));
\r
64 printf("y = %d\n", e->get_var(vary));
\r
65 printf("z = %d\n", e->get_var(varz));
\r
70 ///////////////////////////////////////////////////
\r
73 #ifndef __ENERGY_H__
\r
74 #define __ENERGY_H__
\r
76 #include "bagon_assert.h" // <assert.h>
\r
81 class Energy : Graph
\r
84 typedef node_id Var;
\r
87 /* Types of energy values.
\r
88 Value is a type of a value in a single term
\r
89 TotalValue is a type of a value of the total energy.
\r
90 By default Value = short, TotalValue = int.
\r
91 To change it, change the corresponding types in graph.h */
\r
92 typedef captype Value;
\r
93 typedef flowtype TotalValue;
\r
95 /* interface functions */
\r
97 /* Constructor. Optional argument is the pointer to the
\r
98 function which will be called if an error occurs;
\r
99 an error message is passed to this function. If this
\r
100 argument is omitted, exit(1) will be called. */
\r
101 Energy(void (*err_function)(const char *) = NULL, bool tf = false);
\r
107 /* Adds a new binary variable */
\r
108 Var add_variable();
\r
110 /* Adds a constant E to the energy function */
\r
111 void add_constant(Value E);
\r
113 /* Adds a new term E(x) of one binary variable
\r
114 to the energy function, where
\r
115 E(0) = E0, E(1) = E1
\r
116 E0 and E1 can be arbitrary */
\r
117 void add_term1(Var x,
\r
118 Value E0, Value E1);
\r
120 /* Adds a new term E(x,y) of two binary variables
\r
121 to the energy function, where
\r
122 E(0,0) = E00, E(0,1) = E01
\r
123 E(1,0) = E10, E(1,1) = E11
\r
124 The term must be regular, i.e. E00 + E11 <= E01 + E10 */
\r
125 void add_term2(Var x, Var y,
\r
126 Value E00, Value E01,
\r
127 Value E10, Value E11);
\r
129 /* Adds a new term E(x,y,z) of three binary variables
\r
130 to the energy function, where
\r
131 E(0,0,0) = E000, E(0,0,1) = E001
\r
132 E(0,1,0) = E010, E(0,1,1) = E011
\r
133 E(1,0,0) = E100, E(1,0,1) = E101
\r
134 E(1,1,0) = E110, E(1,1,1) = E111
\r
135 The term must be regular. It means that if one
\r
136 of the variables is fixed (for example, y=1), then
\r
137 the resulting function of two variables must be regular.
\r
138 Since there are 6 ways to fix one variable
\r
139 (3 variables times 2 binary values - 0 and 1),
\r
140 this is equivalent to 6 inequalities */
\r
141 void add_term3(Var x, Var y, Var z,
\r
142 Value E000, Value E001,
\r
143 Value E010, Value E011,
\r
144 Value E100, Value E101,
\r
145 Value E110, Value E111);
\r
147 /* After the energy function has been constructed,
\r
148 call this function to minimize it.
\r
149 Returns the minimum of the function */
\r
150 TotalValue minimize();
\r
152 /* After 'minimize' has been called, this function
\r
153 can be used to determine the value of variable 'x'
\r
154 in the optimal solution.
\r
155 Returns either 0 or 1 */
\r
156 int get_var(Var x);
\r
158 /***********************************************************************/
\r
159 /***********************************************************************/
\r
160 /***********************************************************************/
\r
163 /* internal variables and functions */
\r
166 void (*error_function)(const char *); /* this function is called if a error occurs,
\r
167 with a corresponding error message
\r
168 (or exit(1) is called if it's NULL) */
\r
170 bool truncate_flag;
\r
188 /***********************************************************************/
\r
189 /************************ Implementation ******************************/
\r
190 /***********************************************************************/
\r
193 inline Energy::Energy(void (*err_function)(const char *), bool tf) : Graph(err_function), truncate_flag(tf)
\r
196 error_function = err_function;
\r
201 inline Energy::~Energy() {}
\r
204 inline Energy::Var Energy::add_variable() { return add_node(); }
\r
207 inline void Energy::add_constant(Value A) { Econst += A; }
\r
210 inline void Energy::add_term1(Var x,
\r
213 add_tweights(x, B, A);
\r
217 inline void Energy::add_term2(Var x, Var y,
\r
225 if ( truncate_flag && ( A + D > C + B ) ) {
\r
227 Value delta = .5*(D-B-C);
\r
234 Add edges for the first term
\r
236 add_tweights(x, D, A);
\r
239 /* now need to represent
\r
244 assert(B + C >= 0); /* check regularity (i.e., triangle inequality) */
\r
251 add_tweights(x, 0, B); /* first term */
\r
252 add_tweights(y, 0, -B); /* second term */
\r
253 add_edge(x, y, 0, B+C); /* third term */
\r
258 -C -C + C 0 + 0 B+C
\r
261 add_tweights(x, 0, -C); /* first term */
\r
262 add_tweights(y, 0, C); /* second term */
\r
263 add_edge(x, y, B+C, 0); /* third term */
\r
265 else /* B >= 0, C >= 0 */
\r
267 add_edge(x, y, B, C);
\r
271 inline void Energy::add_term3(Var x, Var y, Var z,
\r
272 Value E000, Value E001,
\r
273 Value E010, Value E011,
\r
274 Value E100, Value E101,
\r
275 Value E110, Value E111)
\r
277 register Value pi = (E000 + E011 + E101 + E110) - (E100 + E010 + E001 + E111);
\r
278 register Value delta;
\r
283 Econst += E111 - (E011 + E101 + E110);
\r
285 add_tweights(x, E101, E001);
\r
286 add_tweights(y, E110, E100);
\r
287 add_tweights(z, E011, E010);
\r
289 delta = (E010 + E001) - (E000 + E011); /* -pi(E[x=0]) */
\r
290 assert(delta >= 0); /* check regularity */
\r
291 add_edge(y, z, delta, 0);
\r
293 delta = (E100 + E001) - (E000 + E101); /* -pi(E[y=0]) */
\r
294 assert(delta >= 0); /* check regularity */
\r
295 add_edge(z, x, delta, 0);
\r
297 delta = (E100 + E010) - (E000 + E110); /* -pi(E[z=0]) */
\r
298 assert(delta >= 0); /* check regularity */
\r
299 add_edge(x, y, delta, 0);
\r
303 u = add_variable();
\r
304 add_edge(x, u, pi, 0);
\r
305 add_edge(y, u, pi, 0);
\r
306 add_edge(z, u, pi, 0);
\r
307 add_tweights(u, 0, pi);
\r
312 Econst += E000 - (E100 + E010 + E001);
\r
314 add_tweights(x, E110, E010);
\r
315 add_tweights(y, E011, E001);
\r
316 add_tweights(z, E101, E100);
\r
318 delta = (E110 + E101) - (E100 + E111); /* -pi(E[x=1]) */
\r
319 assert(delta >= 0); /* check regularity */
\r
320 add_edge(z, y, delta, 0);
\r
322 delta = (E110 + E011) - (E010 + E111); /* -pi(E[y=1]) */
\r
323 assert(delta >= 0); /* check regularity */
\r
324 add_edge(x, z, delta, 0);
\r
326 delta = (E101 + E011) - (E001 + E111); /* -pi(E[z=1]) */
\r
327 assert(delta >= 0); /* check regularity */
\r
328 add_edge(y, x, delta, 0);
\r
330 u = add_variable();
\r
331 add_edge(u, x, -pi, 0);
\r
332 add_edge(u, y, -pi, 0);
\r
333 add_edge(u, z, -pi, 0);
\r
334 add_tweights(u, -pi, 0);
\r
338 inline Energy::TotalValue Energy::minimize() { return Econst + maxflow(); }
\r
340 inline int Energy::get_var(Var x) { return (int) what_segment(x); }
\r