bugfixes in swfc
[swftools.git] / lib / graphcut.c
blobf8216b7fa3f85621322dfb85888476532328429d
1 /*
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/>.
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <math.h>
25 #include <memory.h>
26 #include "graphcut.h"
27 #include "mem.h"
29 //#define DEBUG
31 //#define CHECKS
33 #ifdef DEBUG
34 #define DBG
35 #include <assert.h>
36 #else
37 #define DBG if(0)
38 #define assert(x) (x)
39 #endif
41 #define ACTIVE 0x10
42 #define IN_TREE 0x20
44 #define TWOTREES
46 typedef struct _posqueue_entry {
47 node_t*pos;
48 struct _posqueue_entry*next;
49 } posqueue_entry_t;
51 typedef struct _posqueue {
52 posqueue_entry_t*list;
53 } posqueue_t;
55 typedef struct _graphcut_workspace {
56 unsigned char*flags1;
57 unsigned char*flags2;
58 halfedge_t**back;
59 graph_t*graph;
60 node_t*pos1;
61 node_t*pos2;
62 posqueue_t*queue1;
63 posqueue_t*queue2;
64 posqueue_t*tmpqueue;
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));
71 return m;
73 static void posqueue_delete(posqueue_t*q)
75 posqueue_entry_t*l = q->list;
76 while(l) {
77 posqueue_entry_t*next = l->next;
78 free(l);
79 l = next;
81 free(q);
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;
93 node_t*pos;
94 if(!item)
95 return 0;
96 pos = item->pos;
97 queue->list = queue->list->next;
98 free(item);
99 return pos;
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;
111 while(e) {
112 halfedge_t*back = w->back[NR(e->pos)];
113 printf("%d(%d) ", NR(e->pos), back?NR(back->fwd->node):-1);
114 e = e->next;
116 printf("\n");
118 static void posqueue_purge(posqueue_t*queue)
120 posqueue_entry_t*e = queue->list;
121 while(e) {
122 posqueue_entry_t*next = e->next;
123 e->next = 0;free(e);
124 e = next;
126 queue->list = 0;
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);
134 int t;
135 for(t=0;t<num_nodes;t++) {
136 graph->nodes[t].nr = t;
138 return graph;
141 void graph_delete(graph_t*graph)
143 int t;
144 for(t=0;t<graph->num_nodes;t++) {
145 halfedge_t*e = graph->nodes[t].edges;
146 while(e) {
147 halfedge_t*next = e->next;
148 free(e);
149 e = next;
152 free(graph->nodes);graph->nodes=0;
153 free(graph);
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();
168 return workspace;
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;
178 free(w);
181 typedef struct _path {
182 node_t**pos;
183 halfedge_t**dir;
184 unsigned char*firsthalf;
185 int length;
186 } path_t;
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);
194 p->length = len;
195 return p;
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;
202 free(path);
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)
207 int t;
208 node_t*p = pos;
209 node_t*nodes = work->graph->nodes;
210 int len1 = 0;
211 /* walk up tree1 */
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);
216 node_t*old = p;
217 p = work->back[NR(p)]->fwd->node;
218 assert(p!=old);
219 len1++;
221 p = newpos;
222 int len2 = 0;
223 DBG printf("walk back up (2) to %d\n", NR(work->pos2));
224 /* walk up tree2 */
225 while(p != work->pos2) {
226 DBG printf("walk backward (2): %d\n", NR(p));
227 p = work->back[NR(p)]->fwd->node;
228 len2++;
230 path_t*path = path_new(len1+len2+2);
232 t = len1;
233 path->pos[t] = p = pos;
234 path->dir[t] = dir;
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);
240 p = dir->fwd->node;
241 t--;
242 path->pos[t] = p;
243 path->dir[t] = dir->fwd;
244 path->firsthalf[t] = 1;
246 assert(!t);
248 t = len1+1;
250 p = newpos;
251 while(p != work->pos2) {
252 assert(othertree[NR(p)]&IN_TREE);
253 halfedge_t*dir = work->back[NR(p)];
254 path->pos[t] = p;
255 path->dir[t] = dir;
256 path->firsthalf[t] = 0;
257 p = dir->fwd->node;
258 t++;
261 /* terminator */
262 path->pos[t] = p;
263 path->dir[t] = 0; // last node
264 path->firsthalf[t] = 0;
266 assert(t == len1+len2+1);
267 return path;
270 static void path_print(path_t*path)
272 int t;
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",
278 path->dir[t]->used,
279 path->dir[t]->fwd->used);
280 } else {
281 printf("\n");
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));
291 printf("\n");
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)
303 if(!assertion) {
304 printf("Assertion %s:%d (%s) failed:\n", file, line, func);
305 workspace_print(w);
306 exit(0);
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;
315 int dir;
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));
319 return 0;
322 halfedge_t*e = pos->edges;
323 for(;e;e=e->next) {
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
328 if(weight) {
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
333 path_t*path;
334 if(reverse) {
335 path = extract_path(w, othertree, mytree, newpos, pos, e->fwd);
336 } else {
337 path = extract_path(w, mytree, othertree, pos, newpos, e);
339 return path;
340 } else {
341 DBG printf("advance from %d to new pos %d\n", NR(pos), NR(newpos));
342 w->back[NR(newpos)] = e->fwd;
343 e->used = 1;
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;
350 return 0;
353 static int node_count_edges(node_t*node)
355 halfedge_t*e = node->edges;
356 int num = 0;
357 while(e) {
358 num++;
359 e = e->next;
361 return num;
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;
367 posqueue_purge(q);
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;
374 while(e) {
375 if(e->used) {
376 posqueue_addpos(q, e->fwd->node);
378 e = e->next;
383 static weight_t decrease_weights(graph_t*map, path_t*path)
385 int t;
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;
394 assert(min);
395 if(min<=0)
396 return 0;
398 for(t=0;t<path->length-1;t++) {
399 path->dir[t]->weight-=min;
400 path->dir[t]->fwd->weight+=min;
402 return 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;
410 for(;e;e=e->next) {
411 node_t*newpos = e->fwd->node;
412 int weight;
413 if(!reverse) {
414 weight = e->fwd->weight;
415 } else {
416 weight = e->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;
423 e->fwd->used = 1;
424 return 1;
427 return 0;
430 static void clear_node(graphcut_workspace_t*w, node_t*n)
432 w->flags1[NR(n)] = 0;
433 w->flags2[NR(n)] = 0;
434 w->back[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;
444 posqueue_purge(q);
445 posqueue_addpos(q, pos);
447 while(posqueue_notempty(q)) {
448 node_t*p = posqueue_extract(q);
449 halfedge_t*e = p->edges;
450 while(e) {
451 node_t*newpos = e->fwd->node;
452 if(e->used) {
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;
461 e = e->next;
464 clear_node(w, p);
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;
472 int t;
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;
477 if(!dir->weight) {
478 /* disconnect node */
479 DBG printf("remove link %d -> %d from tree 1\n", NR(pos), NR(newpos));
481 dir->used = 0;
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);
488 } else {
489 destroy_subtree(w, w->flags1, newpos, q1);
490 break;
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));
505 dir->used = 0;
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);
512 } else {
513 destroy_subtree(w, w->flags2, newpos, q2);
514 break;
520 static void check_graph(graph_t*g)
522 int t;
523 for(t=0;t<g->num_nodes;t++) {
524 assert(g->nodes[t].nr==t);
525 halfedge_t*e = g->nodes[t].edges;
526 while(e) {
527 assert(!e->used || !e->fwd->used);
528 e = e->next;
533 void graph_reset(graph_t*g)
535 int t;
536 for(t=0;t<g->num_nodes;t++) {
537 g->nodes[t].nr = t;
538 assert(g->nodes[t].nr==t);
539 halfedge_t*e = g->nodes[t].edges;
540 while(e) {
541 e->used = 0;
542 e->weight = e->init_weight;
543 e = e->next;
548 weight_t graph_maxflow(graph_t*graph, node_t*pos1, node_t*pos2)
550 int max_flow = 0;
551 graphcut_workspace_t* w = graphcut_workspace_new(graph, pos1, pos2);
553 graph_reset(graph);
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);
560 while(1) {
561 path_t*path;
562 while(1) {
563 char done1=0,done2=0;
564 node_t* p1 = posqueue_extract(w->queue1);
565 if(!p1) {
566 graphcut_workspace_delete(w);
567 return max_flow;
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);
571 if(path)
572 break;
573 DBG workspace_print(w);
575 #ifdef TWOTREES
576 node_t* p2 = posqueue_extract(w->queue2);
577 if(!p2) {
578 graphcut_workspace_delete(w);
579 return max_flow;
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);
583 if(path)
584 break;
585 DBG workspace_print(w);
586 #endif
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);
602 path_delete(path);
604 graphcut_workspace_delete(w);
605 return max_flow;
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));
612 e1->fwd = e2;
613 e2->fwd = e1;
614 e1->node = from;
615 e2->node = to;
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;
622 from->edges = e1;
623 e2->next = to->edges;
624 to->edges = e2;
625 return e1;
628 static void do_dfs(node_t*n, int color)
630 int t;
631 n->tmp = color;
632 halfedge_t*e = n->edges;
633 while(e) {
634 if(e->fwd->node->tmp<0)
635 do_dfs(e->fwd->node, color);
636 e = e->next;
640 int graph_find_components(graph_t*g)
642 int t;
643 int count = 0;
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++);
652 return count;
655 #ifdef MAIN
656 int main()
658 int t;
659 int s;
660 for(s=0;s<10;s++) {
661 int width = (lrand48()%8)+1;
662 graph_t*g = graph_new(width*width);
663 for(t=0;t<width*width;t++) {
664 int x = t%width;
665 int y = t/width;
666 int w = 1;
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);
676 graph_delete(g);
679 #endif