2 graphcut- a graphcut implementation based on the Boykov Kolmogorov algorithm
4 Part of the swftools package.
6 Copyright (c) 2007,2008,2009 Matthias Kramm <kramm@quiss.org>
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
46 typedef struct _posqueue_entry
{
48 struct _posqueue_entry
*next
;
51 typedef struct _posqueue
{
52 posqueue_entry_t
*list
;
55 typedef struct _graphcut_workspace
{
65 } graphcut_workspace_t
;
67 static posqueue_t
*posqueue_new()
69 posqueue_t
*m
= (posqueue_t
*)malloc(sizeof(posqueue_t
));
70 memset(m
, 0, sizeof(posqueue_t
));
73 static void posqueue_delete(posqueue_t
*q
)
75 posqueue_entry_t
*l
= q
->list
;
77 posqueue_entry_t
*next
= l
->next
;
83 static inline void posqueue_addpos(posqueue_t
*queue
, node_t
*pos
)
85 posqueue_entry_t
*old
= queue
->list
;
86 queue
->list
= malloc(sizeof(posqueue_entry_t
));
87 queue
->list
->pos
= pos
;
88 queue
->list
->next
= old
;
90 static inline node_t
* posqueue_extract(posqueue_t
*queue
)
92 posqueue_entry_t
*item
= queue
->list
;
97 queue
->list
= queue
->list
->next
;
101 static inline int posqueue_notempty(posqueue_t
*queue
)
103 return (int)queue
->list
;
106 #define NR(p) ((p)->nr)
108 static void posqueue_print(graphcut_workspace_t
*w
, posqueue_t
*queue
)
110 posqueue_entry_t
*e
= queue
->list
;
112 halfedge_t
*back
= w
->back
[NR(e
->pos
)];
113 printf("%d(%d) ", NR(e
->pos
), back
?NR(back
->fwd
->node
):-1);
118 static void posqueue_purge(posqueue_t
*queue
)
120 posqueue_entry_t
*e
= queue
->list
;
122 posqueue_entry_t
*next
= e
->next
;
129 graph_t
* graph_new(int num_nodes
)
131 graph_t
*graph
= rfx_calloc(sizeof(graph_t
));
132 graph
->num_nodes
= num_nodes
;
133 graph
->nodes
= rfx_calloc(sizeof(node_t
)*num_nodes
);
135 for(t
=0;t
<num_nodes
;t
++) {
136 graph
->nodes
[t
].nr
= t
;
141 void graph_delete(graph_t
*graph
)
144 for(t
=0;t
<graph
->num_nodes
;t
++) {
145 halfedge_t
*e
= graph
->nodes
[t
].edges
;
147 halfedge_t
*next
= e
->next
;
152 free(graph
->nodes
);graph
->nodes
=0;
156 static graphcut_workspace_t
*graphcut_workspace_new(graph_t
*graph
, node_t
*pos1
, node_t
*pos2
)
158 graphcut_workspace_t
*workspace
= malloc(sizeof(graphcut_workspace_t
));
159 workspace
->flags1
= rfx_calloc(graph
->num_nodes
);
160 workspace
->flags2
= rfx_calloc(graph
->num_nodes
);
161 workspace
->back
= rfx_calloc(graph
->num_nodes
*sizeof(halfedge_t
*));
162 workspace
->pos1
= pos1
;
163 workspace
->pos2
= pos2
;
164 workspace
->graph
= graph
;
165 workspace
->queue1
= posqueue_new();
166 workspace
->queue2
= posqueue_new();
167 workspace
->tmpqueue
= posqueue_new();
170 static void graphcut_workspace_delete(graphcut_workspace_t
*w
)
172 posqueue_delete(w
->queue1
);w
->queue1
=0;
173 posqueue_delete(w
->queue2
);w
->queue2
=0;
174 posqueue_delete(w
->tmpqueue
);w
->tmpqueue
=0;
175 if(w
->flags1
) free(w
->flags1
);w
->flags1
=0;
176 if(w
->flags2
) free(w
->flags2
);w
->flags2
=0;
177 if(w
->back
) free(w
->back
);w
->back
=0;
181 typedef struct _path
{
184 unsigned char*firsthalf
;
188 static path_t
*path_new(int len
)
190 path_t
*p
= malloc(sizeof(path_t
));
191 p
->pos
= malloc(sizeof(node_t
*)*len
);
192 p
->dir
= malloc(sizeof(halfedge_t
*)*len
);
193 p
->firsthalf
= malloc(sizeof(unsigned char)*len
);
197 static void path_delete(path_t
*path
)
199 free(path
->pos
);path
->pos
= 0;
200 free(path
->dir
);path
->dir
= 0;
201 free(path
->firsthalf
);path
->firsthalf
= 0;
205 static path_t
*extract_path(graphcut_workspace_t
*work
, unsigned char*mytree
, unsigned char*othertree
, node_t
*pos
, node_t
*newpos
, halfedge_t
*dir
)
209 node_t
*nodes
= work
->graph
->nodes
;
212 DBG
printf("walk back up (1) to %d\n", NR(work
->pos1
));
213 while(p
!= work
->pos1
) {
214 halfedge_t
*back
= work
->back
[NR(p
)];
215 DBG
printf("walk backward (1): %d %d\n", NR(p
), back
?NR(back
->fwd
->node
):-1);
217 p
= work
->back
[NR(p
)]->fwd
->node
;
223 DBG
printf("walk back up (2) to %d\n", NR(work
->pos2
));
225 while(p
!= work
->pos2
) {
226 DBG
printf("walk backward (2): %d\n", NR(p
));
227 p
= work
->back
[NR(p
)]->fwd
->node
;
230 path_t
*path
= path_new(len1
+len2
+2);
233 path
->pos
[t
] = p
= pos
;
235 path
->firsthalf
[t
] = 1;
236 while(p
!= work
->pos1
) {
237 assert(mytree
[NR(p
)]&IN_TREE
);
238 halfedge_t
*dir
= work
->back
[NR(p
)];
239 assert(dir
->node
== p
);
243 path
->dir
[t
] = dir
->fwd
;
244 path
->firsthalf
[t
] = 1;
251 while(p
!= work
->pos2
) {
252 assert(othertree
[NR(p
)]&IN_TREE
);
253 halfedge_t
*dir
= work
->back
[NR(p
)];
256 path
->firsthalf
[t
] = 0;
263 path
->dir
[t
] = 0; // last node
264 path
->firsthalf
[t
] = 0;
266 assert(t
== len1
+len2
+1);
270 static void path_print(path_t
*path
)
273 for(t
=0;t
<path
->length
;t
++) {
274 node_t
*n
= path
->pos
[t
];
275 printf("%d (firsthalf: %d)", NR(n
), path
->firsthalf
[t
]);
276 if(t
<path
->length
-1) {
277 printf(" -(%d/%d)-> \n",
279 path
->dir
[t
]->fwd
->used
);
285 for(t
=0;t
<path
->length
-1;t
++) {
286 if(path
->firsthalf
[t
]==path
->firsthalf
[t
+1]) {
287 assert(( path
->firsthalf
[t
] && path
->dir
[t
]->used
) ||
288 (!path
->firsthalf
[t
] && path
->dir
[t
]->fwd
->used
));
295 static void workspace_print(graphcut_workspace_t
*w
)
297 printf("queue1: ");posqueue_print(w
, w
->queue1
);
298 printf("queue2: ");posqueue_print(w
, w
->queue2
);
301 static void myassert(graphcut_workspace_t
*w
, char assertion
, const char*file
, int line
, const char*func
)
304 printf("Assertion %s:%d (%s) failed:\n", file
, line
, func
);
310 #define ASSERT(w,c) {myassert(w,c,__FILE__,__LINE__,__func__);}
312 static path_t
* expand_pos(graphcut_workspace_t
*w
, posqueue_t
*queue
, node_t
*pos
, char reverse
, unsigned char*mytree
, unsigned char*othertree
)
314 graph_t
*graph
= w
->graph
;
316 if((mytree
[NR(pos
)]&(IN_TREE
|ACTIVE
)) != (IN_TREE
|ACTIVE
)) {
317 /* this node got deleted or marked inactive in the meantime. ignore it */
318 DBG
printf("node %d is deleted or inactive\n", NR(pos
));
322 halfedge_t
*e
= pos
->edges
;
324 node_t
*newpos
= e
->fwd
->node
;
325 weight_t weight
= reverse
?e
->fwd
->weight
:e
->weight
;
326 if(mytree
[NR(newpos
)]) continue; // already known
329 if(othertree
[NR(newpos
)]) {
330 DBG
printf("found connection: %d connects to %d\n", NR(pos
), NR(newpos
));
331 posqueue_addpos(queue
, pos
); mytree
[NR(pos
)] |= ACTIVE
; // re-add, this vertex might have other connections
335 path
= extract_path(w
, othertree
, mytree
, newpos
, pos
, e
->fwd
);
337 path
= extract_path(w
, mytree
, othertree
, pos
, newpos
, e
);
341 DBG
printf("advance from %d to new pos %d\n", NR(pos
), NR(newpos
));
342 w
->back
[NR(newpos
)] = e
->fwd
;
344 posqueue_addpos(queue
, newpos
); mytree
[NR(newpos
)] |= ACTIVE
|IN_TREE
; // add
348 /* if we can't expand this node anymore, it's now an inactive node */
349 mytree
[NR(pos
)] &= ~ACTIVE
;
353 static int node_count_edges(node_t
*node
)
355 halfedge_t
*e
= node
->edges
;
364 static void bool_op(graphcut_workspace_t
*w
, unsigned char*flags
, node_t
*pos
, unsigned char and, unsigned char or)
366 posqueue_t
*q
= w
->tmpqueue
;
368 posqueue_addpos(q
, pos
);
370 while(posqueue_notempty(q
)) {
371 node_t
*p
= posqueue_extract(q
);
372 flags
[NR(p
)] = (flags
[NR(p
)]&and)|or;
373 halfedge_t
*e
= p
->edges
;
376 posqueue_addpos(q
, e
->fwd
->node
);
383 static weight_t
decrease_weights(graph_t
*map
, path_t
*path
)
386 assert(path
->length
);
388 weight_t min
= path
->dir
[0]->weight
;
389 for(t
=0;t
<path
->length
-1;t
++) {
390 int w
= path
->dir
[t
]->weight
;
391 DBG
printf("%d->%d (%d)\n", NR(path
->dir
[t
]->node
), NR(path
->dir
[t
]->fwd
->node
), w
);
392 if(t
==0 || w
< min
) min
= w
;
398 for(t
=0;t
<path
->length
-1;t
++) {
399 path
->dir
[t
]->weight
-=min
;
400 path
->dir
[t
]->fwd
->weight
+=min
;
405 static int reconnect(graphcut_workspace_t
*w
, unsigned char*flags
, node_t
*pos
, char reverse
)
407 graph_t
*graph
= w
->graph
;
409 halfedge_t
*e
= pos
->edges
;
411 node_t
*newpos
= e
->fwd
->node
;
414 weight
= e
->fwd
->weight
;
418 if(weight
&& (flags
[NR(newpos
)]&IN_TREE
)) {
419 DBG
printf("successfully reconnected node %d to %d (%d->%d) (reverse:%d)\n",
420 NR(pos
), NR(newpos
), NR(e
->node
), NR(e
->fwd
->node
), reverse
);
422 w
->back
[NR(pos
)] = e
;
430 static void clear_node(graphcut_workspace_t
*w
, node_t
*n
)
432 w
->flags1
[NR(n
)] = 0;
433 w
->flags2
[NR(n
)] = 0;
435 halfedge_t
*e
= n
->edges
;
436 while(e
) {e
->used
= 0;e
=e
->next
;}
439 static void destroy_subtree(graphcut_workspace_t
*w
, unsigned char*flags
, node_t
*pos
, posqueue_t
*posqueue
)
441 DBG
printf("destroying subtree starting with %d\n", NR(pos
));
443 posqueue_t
*q
= w
->tmpqueue
;
445 posqueue_addpos(q
, pos
);
447 while(posqueue_notempty(q
)) {
448 node_t
*p
= posqueue_extract(q
);
449 halfedge_t
*e
= p
->edges
;
451 node_t
*newpos
= e
->fwd
->node
;
453 posqueue_addpos(q
, newpos
);
454 } else if((flags
[NR(newpos
)]&(ACTIVE
|IN_TREE
)) == IN_TREE
) {
455 // re-activate all nodes that surround our subtree.
456 // TODO: we should check the weight of the edge from that other
457 // node to our node. if it's zero, we don't need to activate that node.
458 posqueue_addpos(posqueue
, newpos
);
459 flags
[NR(newpos
)]|=ACTIVE
;
465 DBG
printf("removed pos %d\n", NR(p
));
469 static void combust_tree(graphcut_workspace_t
*w
, posqueue_t
*q1
, posqueue_t
*q2
, path_t
*path
)
471 graph_t
*graph
= w
->graph
;
473 for(t
=0;t
<path
->length
-1 && path
->firsthalf
[t
+1];t
++) {
474 node_t
*pos
= path
->pos
[t
];
475 halfedge_t
*dir
= path
->dir
[t
];
476 node_t
*newpos
= dir
->fwd
->node
;
478 /* disconnect node */
479 DBG
printf("remove link %d -> %d from tree 1\n", NR(pos
), NR(newpos
));
482 w
->flags1
[NR(newpos
)] &= ACTIVE
;
483 bool_op(w
, w
->flags1
, newpos
, ~IN_TREE
, 0);
485 /* try to reconnect the path to some other tree part */
486 if(reconnect(w
, w
->flags1
, newpos
, 0)) {
487 bool_op(w
, w
->flags1
, newpos
, ~0, IN_TREE
);
489 destroy_subtree(w
, w
->flags1
, newpos
, q1
);
495 for(t
=path
->length
-1;t
>0 && !path
->firsthalf
[t
-1];t
--) {
496 node_t
*pos
= path
->pos
[t
];
497 node_t
*newpos
= path
->pos
[t
-1];
498 halfedge_t
*dir
= path
->dir
[t
-1]->fwd
;
499 node_t
*newpos2
= dir
->fwd
->node
;
500 assert(newpos
== newpos2
);
501 if(!dir
->fwd
->weight
) {
502 /* disconnect node */
503 DBG
printf("remove link %d->%d from tree 2\n", NR(pos
), NR(newpos
));
506 w
->flags2
[NR(newpos
)] &= ACTIVE
;
507 bool_op(w
, w
->flags2
, newpos
, ~IN_TREE
, 0);
509 /* try to reconnect the path to some other tree part */
510 if(reconnect(w
, w
->flags2
, newpos
, 1)) {
511 bool_op(w
, w
->flags2
, newpos
, ~0, IN_TREE
);
513 destroy_subtree(w
, w
->flags2
, newpos
, q2
);
520 static void check_graph(graph_t
*g
)
523 for(t
=0;t
<g
->num_nodes
;t
++) {
524 assert(g
->nodes
[t
].nr
==t
);
525 halfedge_t
*e
= g
->nodes
[t
].edges
;
527 assert(!e
->used
|| !e
->fwd
->used
);
533 void graph_reset(graph_t
*g
)
536 for(t
=0;t
<g
->num_nodes
;t
++) {
538 assert(g
->nodes
[t
].nr
==t
);
539 halfedge_t
*e
= g
->nodes
[t
].edges
;
542 e
->weight
= e
->init_weight
;
548 weight_t
graph_maxflow(graph_t
*graph
, node_t
*pos1
, node_t
*pos2
)
551 graphcut_workspace_t
* w
= graphcut_workspace_new(graph
, pos1
, pos2
);
554 DBG
check_graph(graph
);
556 posqueue_addpos(w
->queue1
, pos1
); w
->flags1
[pos1
->nr
] |= ACTIVE
|IN_TREE
;
557 posqueue_addpos(w
->queue2
, pos2
); w
->flags2
[pos2
->nr
] |= ACTIVE
|IN_TREE
;
558 DBG
workspace_print(w
);
563 char done1
=0,done2
=0;
564 node_t
* p1
= posqueue_extract(w
->queue1
);
566 graphcut_workspace_delete(w
);
569 DBG
printf("extend 1 from %d (%d edges)\n", NR(p1
), node_count_edges(p1
));
570 path
= expand_pos(w
, w
->queue1
, p1
, 0, w
->flags1
, w
->flags2
);
573 DBG
workspace_print(w
);
576 node_t
* p2
= posqueue_extract(w
->queue2
);
578 graphcut_workspace_delete(w
);
581 DBG
printf("extend 2 from %d (%d edges)\n", NR(p2
), node_count_edges(p2
));
582 path
= expand_pos(w
, w
->queue2
, p2
, 1, w
->flags2
, w
->flags1
);
585 DBG
workspace_print(w
);
589 DBG
printf("found connection between tree1 and tree2\n");
590 DBG
path_print(path
);
592 DBG
printf("decreasing weights\n");
593 max_flow
+= decrease_weights(graph
, path
);
594 DBG
workspace_print(w
);
596 DBG
printf("destroying trees\n");
597 combust_tree(w
, w
->queue1
, w
->queue2
, path
);
598 DBG
workspace_print(w
);
600 DBG
check_graph(w
->graph
);
604 graphcut_workspace_delete(w
);
608 halfedge_t
*graph_add_edge(node_t
*from
, node_t
*to
, weight_t forward_weight
, weight_t backward_weight
)
610 halfedge_t
*e1
= (halfedge_t
*)rfx_calloc(sizeof(halfedge_t
));
611 halfedge_t
*e2
= (halfedge_t
*)rfx_calloc(sizeof(halfedge_t
));
616 e1
->init_weight
= forward_weight
;
617 e2
->init_weight
= backward_weight
;
618 e1
->weight
= forward_weight
;
619 e2
->weight
= backward_weight
;
621 e1
->next
= from
->edges
;
623 e2
->next
= to
->edges
;
628 static void do_dfs(node_t
*n
, int color
)
632 halfedge_t
*e
= n
->edges
;
634 if(e
->fwd
->node
->tmp
<0)
635 do_dfs(e
->fwd
->node
, color
);
640 int graph_find_components(graph_t
*g
)
644 for(t
=0;t
<g
->num_nodes
;t
++) {
645 g
->nodes
[t
].tmp
= -1;
647 for(t
=0;t
<g
->num_nodes
;t
++) {
648 if(g
->nodes
[t
].tmp
<0) {
649 do_dfs(&g
->nodes
[t
], count
++);
661 int width
= (lrand48()%8)+1;
662 graph_t
*g
= graph_new(width
*width
);
663 for(t
=0;t
<width
*width
;t
++) {
667 #define R (lrand48()%32)
668 if(x
>0) graph_add_edge(&g
->nodes
[t
], &g
->nodes
[t
-1], R
, R
);
669 if(x
<width
-1) graph_add_edge(&g
->nodes
[t
], &g
->nodes
[t
+1], R
, R
);
670 if(y
>0) graph_add_edge(&g
->nodes
[t
], &g
->nodes
[t
-width
], R
, R
);
671 if(y
<width
-1) graph_add_edge(&g
->nodes
[t
], &g
->nodes
[t
+width
], R
, R
);
674 int x
= graph_maxflow(g
, &g
->nodes
[0], &g
->nodes
[width
*width
-1]);
675 printf("max flow: %d\n", x
);