/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #ifndef OPENCV_IMGPROC_DETAIL_GCGRAPH_HPP #define OPENCV_IMGPROC_DETAIL_GCGRAPH_HPP //! @cond IGNORED namespace cv { namespace detail { template class GCGraph { public: GCGraph(); GCGraph( unsigned int vtxCount, unsigned int edgeCount ); ~GCGraph(); void create( unsigned int vtxCount, unsigned int edgeCount ); int addVtx(); void addEdges( int i, int j, TWeight w, TWeight revw ); void addTermWeights( int i, TWeight sourceW, TWeight sinkW ); TWeight maxFlow(); bool inSourceSegment( int i ); private: class Vtx { public: Vtx *next; // initialized and used in maxFlow() only int parent; int first; int ts; int dist; TWeight weight; uchar t; }; class Edge { public: int dst; int next; TWeight weight; }; std::vector vtcs; std::vector edges; TWeight flow; }; template GCGraph::GCGraph() { flow = 0; } template GCGraph::GCGraph( unsigned int vtxCount, unsigned int edgeCount ) { create( vtxCount, edgeCount ); } template GCGraph::~GCGraph() { } template void GCGraph::create( unsigned int vtxCount, unsigned int edgeCount ) { vtcs.reserve( vtxCount ); edges.reserve( edgeCount + 2 ); flow = 0; } template int GCGraph::addVtx() { Vtx v; memset( &v, 0, sizeof(Vtx)); vtcs.push_back(v); return (int)vtcs.size() - 1; } template void GCGraph::addEdges( int i, int j, TWeight w, TWeight revw ) { CV_Assert( i>=0 && i<(int)vtcs.size() ); CV_Assert( j>=0 && j<(int)vtcs.size() ); CV_Assert( w>=0 && revw>=0 ); CV_Assert( i != j ); if( !edges.size() ) edges.resize( 2 ); Edge fromI, toI; fromI.dst = j; fromI.next = vtcs[i].first; fromI.weight = w; vtcs[i].first = (int)edges.size(); edges.push_back( fromI ); toI.dst = i; toI.next = vtcs[j].first; toI.weight = revw; vtcs[j].first = (int)edges.size(); edges.push_back( toI ); } template void GCGraph::addTermWeights( int i, TWeight sourceW, TWeight sinkW ) { CV_Assert( i>=0 && i<(int)vtcs.size() ); TWeight dw = vtcs[i].weight; if( dw > 0 ) sourceW += dw; else sinkW -= dw; flow += (sourceW < sinkW) ? sourceW : sinkW; vtcs[i].weight = sourceW - sinkW; } template TWeight GCGraph::maxFlow() { CV_Assert(!vtcs.empty()); CV_Assert(!edges.empty()); const int TERMINAL = -1, ORPHAN = -2; Vtx stub, *nilNode = &stub, *first = nilNode, *last = nilNode; int curr_ts = 0; stub.next = nilNode; Vtx *vtxPtr = &vtcs[0]; Edge *edgePtr = &edges[0]; std::vector orphans; // initialize the active queue and the graph vertices for( int i = 0; i < (int)vtcs.size(); i++ ) { Vtx* v = vtxPtr + i; v->ts = 0; if( v->weight != 0 ) { last = last->next = v; v->dist = 1; v->parent = TERMINAL; v->t = v->weight < 0; } else v->parent = 0; } first = first->next; last->next = nilNode; nilNode->next = 0; // run the search-path -> augment-graph -> restore-trees loop for(;;) { Vtx* v, *u; int e0 = -1, ei = 0, ej = 0; TWeight minWeight, weight; uchar vt; // grow S & T search trees, find an edge connecting them while( first != nilNode ) { v = first; if( v->parent ) { vt = v->t; for( ei = v->first; ei != 0; ei = edgePtr[ei].next ) { if( edgePtr[ei^vt].weight == 0 ) continue; u = vtxPtr+edgePtr[ei].dst; if( !u->parent ) { u->t = vt; u->parent = ei ^ 1; u->ts = v->ts; u->dist = v->dist + 1; if( !u->next ) { u->next = nilNode; last = last->next = u; } continue; } if( u->t != vt ) { e0 = ei ^ vt; break; } if( u->dist > v->dist+1 && u->ts <= v->ts ) { // reassign the parent u->parent = ei ^ 1; u->ts = v->ts; u->dist = v->dist + 1; } } if( e0 > 0 ) break; } // exclude the vertex from the active list first = first->next; v->next = 0; } if( e0 <= 0 ) break; // find the minimum edge weight along the path minWeight = edgePtr[e0].weight; CV_Assert( minWeight > 0 ); // k = 1: source tree, k = 0: destination tree for( int k = 1; k >= 0; k-- ) { for( v = vtxPtr+edgePtr[e0^k].dst;; v = vtxPtr+edgePtr[ei].dst ) { if( (ei = v->parent) < 0 ) break; weight = edgePtr[ei^k].weight; minWeight = MIN(minWeight, weight); CV_Assert( minWeight > 0 ); } weight = fabs(v->weight); minWeight = MIN(minWeight, weight); CV_Assert( minWeight > 0 ); } // modify weights of the edges along the path and collect orphans edgePtr[e0].weight -= minWeight; edgePtr[e0^1].weight += minWeight; flow += minWeight; // k = 1: source tree, k = 0: destination tree for( int k = 1; k >= 0; k-- ) { for( v = vtxPtr+edgePtr[e0^k].dst;; v = vtxPtr+edgePtr[ei].dst ) { if( (ei = v->parent) < 0 ) break; edgePtr[ei^(k^1)].weight += minWeight; if( (edgePtr[ei^k].weight -= minWeight) == 0 ) { orphans.push_back(v); v->parent = ORPHAN; } } v->weight = v->weight + minWeight*(1-k*2); if( v->weight == 0 ) { orphans.push_back(v); v->parent = ORPHAN; } } // restore the search trees by finding new parents for the orphans curr_ts++; while( !orphans.empty() ) { Vtx* v2 = orphans.back(); orphans.pop_back(); int d, minDist = INT_MAX; e0 = 0; vt = v2->t; for( ei = v2->first; ei != 0; ei = edgePtr[ei].next ) { if( edgePtr[ei^(vt^1)].weight == 0 ) continue; u = vtxPtr+edgePtr[ei].dst; if( u->t != vt || u->parent == 0 ) continue; // compute the distance to the tree root for( d = 0;; ) { if( u->ts == curr_ts ) { d += u->dist; break; } ej = u->parent; d++; if( ej < 0 ) { if( ej == ORPHAN ) d = INT_MAX-1; else { u->ts = curr_ts; u->dist = 1; } break; } u = vtxPtr+edgePtr[ej].dst; } // update the distance if( ++d < INT_MAX ) { if( d < minDist ) { minDist = d; e0 = ei; } for( u = vtxPtr+edgePtr[ei].dst; u->ts != curr_ts; u = vtxPtr+edgePtr[u->parent].dst ) { u->ts = curr_ts; u->dist = --d; } } } if( (v2->parent = e0) > 0 ) { v2->ts = curr_ts; v2->dist = minDist; continue; } /* no parent is found */ v2->ts = 0; for( ei = v2->first; ei != 0; ei = edgePtr[ei].next ) { u = vtxPtr+edgePtr[ei].dst; ej = u->parent; if( u->t != vt || !ej ) continue; if( edgePtr[ei^(vt^1)].weight && !u->next ) { u->next = nilNode; last = last->next = u; } if( ej > 0 && vtxPtr+edgePtr[ej].dst == v2 ) { orphans.push_back(u); u->parent = ORPHAN; } } } } return flow; } template bool GCGraph::inSourceSegment( int i ) { CV_Assert( i>=0 && i<(int)vtcs.size() ); return vtcs[i].t == 0; } }} // namespace detail, cv //! @endcond #endif // OPENCV_IMGPROC_DETAIL_GCGRAPH_HPP