/*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*/

#include "_cvaux.h"

#if 0

//#include "windows.h"

//#define ALPHA_EXPANSION

#ifndef ALPHA_EXPANSION
    #define ALPHA_BETA_EXCHANGE
#endif

#define MAX_LABEL 20

#define CV_MODULE(xxx) \
    ( (xxx) < 0 ? -(xxx) : (xxx) )

#define CV_MAX3(xxx1,xxx2,xxx3) \
    ( (xxx1) > (xxx2) && (xxx1) > (xxx3) ? (xxx1) : \
        (xxx2) > (xxx3) ? (xxx2) : (xxx3) )

#define CV_MIN2(xxx1,xxx2) \
    ( (xxx1) < (xxx2) ? (xxx1) : (xxx2) )

#define getSizeForGraph(xxxType) \
    ( sizeof(xxxType) < 8 ? 8 : sizeof(xxxType) + 4 - sizeof(xxxType) % 4 )

#define INT_INFINITY 1000000000
#define MAX_DIFFERENCE 10


// struct Vertex is used for storing vertices of graph
//      coord       - coordinate corresponding pixel on the real image line
struct Vertex
{
    CvGraphVtx vtx;
    int coord;
};

// struct Edge is used for storing edges of graph
//      weight      - weight of the edge ( maximum flow via the edge )
//      flow        - current flow via the edge
//      srcVtx      - coordinate of source vertex on the real image line
//      destVtx     - coordinate of destination vertex on the real image line
struct Edge
{
    CV_GRAPH_EDGE_FIELDS()
    int weight;
    int flow;
    int srcVtx;
    int destVtx;
};

// function vFunc is energy function which determines the difference
//   between two labels ( alpha and beta )
//      alpha       - label number one
//      beta        - label number two
inline int vFunc( int alpha, int beta )
{
    if( alpha == beta )
        return 0;
    else
        return /*1*//*5*/10;
}

// function dFunc is energy function which determines energy of interaction
//   between pixel ( having coordinate xCoord ) and label
//          leftLine        - line of left image
//          rightLine       - line of right image
//          xCoord          - coordinate of pixel on the left image
//          label           - label corresponding to the pixel
//          width           - width of the image line in pixels
inline int dFunc( unsigned char* leftLine,
                  unsigned char* rightLine,
                  int xCoord,
                  int label,
                  int width)
{
    assert( xCoord >= 0 && xCoord < width );
    int r, g, b;
    int yCoord = xCoord + label;

    if( yCoord >= width )
        yCoord = width;
    if( yCoord < 0 )
        yCoord = 0;

    r = leftLine[ 3 * xCoord     ] - rightLine[ 3 * yCoord     ];
    g = leftLine[ 3 * xCoord + 1 ] - rightLine[ 3 * yCoord + 1 ];
    b = leftLine[ 3 * xCoord + 2 ] - rightLine[ 3 * yCoord + 2 ];

    r = CV_MODULE( r );
    g = CV_MODULE( g );
    b = CV_MODULE( b );

    return CV_MAX3( r, g, b );
}

// function allocTempMem allocates all temporary memory needed for work
//   of some function
//      memPtr          - pointer to pointer to the large block of memory
//      verticesPtr     - pointer to pointer to block of memory for
//                        temporary storing vertices
//      width           - width of line in pixels
void allocTempMem( int** memPtr,
                   int** verticesPtr,
                   int width )
{
    int* tempPtr = ( int* ) malloc( ( width + 2 ) * 7 * sizeof( int ) );
    *verticesPtr = tempPtr;
    *memPtr = *verticesPtr + width + 2;
}

// function freeTempMem frees all allocated by allocTempMem function memory
//      memPtr          - pointer to pointer to the large block of memory
//      verticesPtr     - pointer to pointer to block of memory for
//                        temporary storing vertices
void freeTempMem( int** memPtr,
                  int** verticesPtr )
{
    free( ( void* )( *verticesPtr ) );
    *verticesPtr = NULL;
    *memPtr = NULL;
}

// function makeGraph creates initial graph to find maximum flow in it
//      graphPtr        - pointer to pointer to CvGraph structure to be filled
//      leftLine        - pointer to the left image line
//      rightLine       - pointer to the right image line
//      alpha           - label number one for doing exchange
//      beta            - label number two for doing exchange
//      corr            - pointer to array of correspondences ( each element
//                        of array includes disparity of pixel on right image
//                        for pixel each on left image ). This pointer direct
//                        to correspondence ofr one line only
//      width           - width of image lines in pixels
//      storage         - pointer to CvMemStorage structure which contains
//                        memory storage
void makeGraph( CvGraph** graphPtr,
                unsigned char* leftLine,
                unsigned char* rightLine,
                int alpha,
                int beta,
                int* corr,
                int width,
                CvMemStorage* storage )
{
    int i;

    if( *graphPtr )  {
        cvClearGraph( *graphPtr );
    }
    /*else {*/
        *graphPtr = cvCreateGraph( CV_SEQ_KIND_GRAPH | CV_GRAPH_FLAG_ORIENTED,
                                   sizeof( CvGraph ),
                                   getSizeForGraph( Vertex ),
                                   getSizeForGraph( Edge ),
                                   storage );
    /*}*/

    CvGraph* graph = *graphPtr;

    #ifdef ALPHA_BETA_EXCHANGE

    CvGraphVtx* newVtxPtr;
    for( i = 0; i < width; i ++ )
    {
        if( corr[i] == alpha || corr[i] == beta ) {
            cvGraphAddVtx( graph, NULL, &newVtxPtr );
            ( ( Vertex* )newVtxPtr ) -> coord = i;
        }
    } /* for( i = 0; i < width; i ++ ) */
    cvGraphAddVtx( graph, NULL, &newVtxPtr );
    if( newVtxPtr )
        ( ( Vertex* )newVtxPtr ) -> coord = -2; /* adding alpha vertex */
    cvGraphAddVtx( graph, NULL, &newVtxPtr );
    if( newVtxPtr )
        ( ( Vertex* )newVtxPtr ) -> coord = -1; /* adding beta vertex */

    int alphaVtx = graph -> total - 2;
    int betaVtx = graph -> total - 1;
    CvGraphEdge* newEdgePtr;
    CvGraphVtx* vtxPtr;
    if( graph -> total > 2 )
    {
        for( i = 0; i < alphaVtx; i ++ )
        {
            vtxPtr = cvGetGraphVtx( graph, i );

            /* adding edge oriented from alpha vertex to current vertex */
            cvGraphAddEdge( graph, alphaVtx, i, NULL, &newEdgePtr );
            ( ( Edge* )newEdgePtr ) -> weight = dFunc( leftLine,
                rightLine,
                ( ( Vertex* )vtxPtr ) -> coord,
                alpha,
                width );
            ( ( Edge* )newEdgePtr ) -> flow = 0;
            if( i != 0 ) {
                CvGraphVtx* tempVtxPtr = cvGetGraphVtx( graph, i - 1 );
                /* if vertices are neighbours */
                if( ( ( Vertex* )tempVtxPtr ) -> coord + 1 ==
                    ( ( Vertex* )vtxPtr ) -> coord )
                {
                    ( ( Edge* )newEdgePtr ) -> weight +=
                        vFunc( corr[ ( ( Vertex* )tempVtxPtr ) -> coord ],
                               alpha );
                    /* adding neighbour edge oriented from current vertex
                       to the previous one */
                    CvGraphEdge* tempEdgePtr;
                    cvGraphAddEdge( graph, i, i - 1, NULL, &tempEdgePtr );
                    ( ( Edge* )tempEdgePtr ) -> weight = vFunc( alpha, beta );
                    ( ( Edge* )tempEdgePtr ) -> flow = 0;
                    ( ( Edge* )tempEdgePtr ) -> srcVtx =
                        ( ( Vertex* )vtxPtr ) -> coord;
                    ( ( Edge* )tempEdgePtr ) -> destVtx =
                        ( ( Vertex* )tempVtxPtr ) -> coord;
                }
            } /* if( i != 0 ) */
            if( i != alphaVtx - 1 ) {
                CvGraphVtx* tempVtxPtr = cvGetGraphVtx( graph, i + 1 );
                /* if vertices are neighbours */
                if( ( ( Vertex* )tempVtxPtr ) -> coord - 1 ==
                    ( ( Vertex* )vtxPtr ) -> coord )
                {
                    ( ( Edge* )newEdgePtr ) -> weight +=
                        vFunc( corr[ ( ( Vertex* )tempVtxPtr ) -> coord ],
                               alpha );
                    /* adding neighbour edge oriented from current vertex
                       to the next one */
                    CvGraphEdge* tempEdgePtr;
                    cvGraphAddEdge( graph, i, i + 1, NULL, &tempEdgePtr );
                    ( ( Edge* )tempEdgePtr ) -> weight = vFunc( alpha, beta );
                    ( ( Edge* )tempEdgePtr ) -> flow = 0;
                    ( ( Edge* )tempEdgePtr ) -> srcVtx =
                        ( ( Vertex* )vtxPtr ) -> coord;
                    ( ( Edge* )tempEdgePtr ) -> destVtx =
                        ( ( Vertex* )tempVtxPtr ) -> coord;
                }
            } /* if( i != alphaVtx - 1 ) */
            ( ( Edge* )newEdgePtr ) -> flow = 0;
            ( ( Edge* )newEdgePtr ) -> srcVtx = -1; /* source vertex is alpha
                                                       vertex */
            ( ( Edge* )newEdgePtr ) -> destVtx = ( ( Vertex* )vtxPtr ) -> coord;

            /* adding edge oriented from current vertex to beta vertex */
            cvGraphAddEdge( graph, i, betaVtx, NULL, &newEdgePtr );
            ( ( Edge* )newEdgePtr ) -> weight = dFunc( leftLine,
                rightLine,
                ( ( Vertex* )vtxPtr ) -> coord,
                beta,
                width );
            ( ( Edge* )newEdgePtr ) -> flow = 0;
            if( i != 0 ) {
                CvGraphVtx* tempVtxPtr = cvGetGraphVtx( graph, i - 1 );
                /* if vertices are neighbours */
                if( ( ( Vertex* )tempVtxPtr ) -> coord + 1 ==
                    ( ( Vertex* )vtxPtr ) -> coord )
                {
                    ( ( Edge* )newEdgePtr ) -> weight +=
                        vFunc( corr[ ( ( Vertex* )tempVtxPtr ) -> coord ],
                               beta );
                }
            } /* if( i != 0 ) */
            if( i != alphaVtx - 1 ) {
                CvGraphVtx* tempVtxPtr = cvGetGraphVtx( graph, i + 1 );
                /* if vertices are neighbours */
                if( ( ( Vertex* )tempVtxPtr ) -> coord - 1 ==
                    ( ( Vertex* )vtxPtr ) -> coord )
                {
                    ( ( Edge* )newEdgePtr ) -> weight +=
                        vFunc( corr[ ( ( Vertex* )tempVtxPtr ) -> coord ],
                               beta );
                }
            } /* if( i != alphaVtx - 1 ) */
            ( ( Edge* )newEdgePtr ) -> flow = 0;
            ( ( Edge* )newEdgePtr ) -> srcVtx =
                ( ( Vertex* )vtxPtr ) -> coord;
            ( ( Edge* )newEdgePtr ) -> destVtx = -2; /* destination vertex is
                                                        beta vertex */

        } /* for( i = 0; i < graph -> total - 2; i ++ ) */

    } /* if( graph -> total > 2 ) */

    #endif /* #ifdef ALPHA_BETA_EXCHANGE */

    #ifdef ALPHA_EXPANSION
    #endif /* #ifdef ALPHA_EXPANSION */

} /* makeGraph */

// function makeHelpGraph creates help graph using initial graph
//      graph           - pointer to initial graph ( represented by CvGraph
//                        structure )
//      hlpGraphPtr     - pointer to pointer to new help graph
//      storage         - pointer to CvStorage structure
//      mem             - pointer to memory allocated by allocTempMem function
//      vertices        - pointer to memory allocated by allocTempMem function
//      verticesCountPtr- pointer to value containing number of vertices
//                        in vertices array
//      width           - width of image line in pixels
int makeHelpGraph( CvGraph* graph,
                   CvGraph** hlpGraphPtr,
                   CvMemStorage* storage,
                   int* mem,
                   int* vertices,
                   int* verticesCountPtr,
                   int width )
{
    int u, v;
    int* order = mem;
    int* lengthArr = order + width + 2;
    int s = graph -> total - 2; /* source vertex */
    int t = graph -> total - 1; /* terminate vertex */
    int orderFirst;
    int orderCount;
    int &verticesCount = *verticesCountPtr;
    CvGraph* hlpGraph;

    if( *hlpGraphPtr )  {
        cvClearGraph( *hlpGraphPtr );
    }
    else {
        *hlpGraphPtr = cvCreateGraph( CV_SEQ_KIND_GRAPH |
                                          CV_GRAPH_FLAG_ORIENTED,
                                      sizeof( CvGraph ),
                                      getSizeForGraph( Vertex ),
                                      getSizeForGraph( Edge ),
                                      storage );
    }

    hlpGraph = *hlpGraphPtr;

    /* initialization */
    for( u = 0; u < graph -> total; u ++ )
    {
        lengthArr[ u ] = INT_INFINITY;
        cvGraphAddVtx( hlpGraph, NULL, NULL );
    } /* for( u = 0; u < graph -> total - 1; u ++ ) */

    orderFirst = 0;
    orderCount = 0;
    verticesCount = 0;
    lengthArr[ s ] = 0;

    /* add vertex s to order */
    order[ orderCount ] = s;
    orderCount ++;

    while( orderCount != orderFirst )
    {
        /* getting u from order */
        u = order[ orderFirst ];
        orderFirst ++;

        /* adding u to vertex array */
        vertices[ verticesCount ] = u;
        verticesCount ++;

        int ofs;
        CvGraphVtx* graphVtx = cvGetGraphVtx( graph, u );

        /* processing all vertices outgoing from vertex u */
        CvGraphEdge* graphEdge = graphVtx -> first;
        while( graphEdge )
        {
            int tempVtxIdx = cvGraphVtxIdx( graph, graphEdge -> vtx[1] );
            ofs = tempVtxIdx == u;
            if( !ofs )
            {
                v = tempVtxIdx;

                CvGraphEdge* tempGraphEdge = cvFindGraphEdge( graph, u, v );
                if( ( lengthArr[ u ] < lengthArr[ v ] )
                    && ( lengthArr[ v ] <= lengthArr[ t ] )
                    && ( ( ( Edge* )tempGraphEdge ) -> flow <
                        ( ( Edge* )tempGraphEdge ) -> weight ) )
                {
                    if( lengthArr[ v ] == INT_INFINITY )
                    {
                        /* adding vertex v to order */
                        order[ orderCount ] = v;
                        orderCount ++;

                        lengthArr[ v ] = lengthArr[ u ] + 1;
                        CvGraphEdge* tempGraphEdge2;

                        cvGraphAddEdge( hlpGraph, u, v, NULL, &tempGraphEdge2 );
                        ( ( Edge* )tempGraphEdge2 ) -> flow = 0;

                        ( ( Edge* )tempGraphEdge2 ) -> weight =
                            ( ( Edge* )tempGraphEdge ) -> weight -
                            ( ( Edge* )tempGraphEdge ) -> flow;

                    } /* if( length[ v ] == INT_INFINITY ) */

                } /* if( ( lengthArr[ u ] < lengthArr[ v ] ) ... */

            } /* if( !ofs ) */

            graphEdge = graphEdge -> next[ ofs ];

        } /* while( graphEdge ) */

        /* processing all vertices incoming to vertex u */
        graphEdge = graphVtx -> first;
        while( graphEdge )
        {
            int tempVtxIdx = cvGraphVtxIdx( graph, graphEdge -> vtx[1] );
            ofs = tempVtxIdx == u;
            if( ofs )
            {
                tempVtxIdx = cvGraphVtxIdx( graph, graphEdge -> vtx[0] );
                v = tempVtxIdx;

                CvGraphEdge* tempGraphEdge = cvFindGraphEdge( graph, v, u );
                if( ( lengthArr[ u ] < lengthArr[ v ] )
                    && ( lengthArr[ v ] <= lengthArr[ t ] )
                    && ( ( ( Edge* )tempGraphEdge ) -> flow > 0 ) )
                {
                    if( lengthArr[ v ] == INT_INFINITY )
                    {
                        /* adding vertex v to order */
                        order[ orderCount ] = v;
                        orderCount ++;

                        lengthArr[ v ] = lengthArr[ u ] + 1;
                        CvGraphEdge* tempGraphEdge3 = cvFindGraphEdge( hlpGraph, u, v );

                        if( tempGraphEdge3 == NULL ||
                            ( ( Edge* )tempGraphEdge3 ) -> weight == 0 )
                        {
                            CvGraphEdge* tempGraphEdge2;
                            cvGraphAddEdge( hlpGraph, u, v, NULL,
                                &tempGraphEdge2 );
                            ( ( Edge* )tempGraphEdge2 ) -> flow = 0;
                            ( ( Edge* )tempGraphEdge2 ) -> weight = 0;
                        } /* if( tempGraphEdge3 == NULL || ... */

                        ( ( Edge* )tempGraphEdge3 ) -> weight +=
                            ( ( Edge* )tempGraphEdge ) -> flow;

                    } /* if( length[ v ] == INT_INFINITY ) */

                } /* if( ( lengthArr[ u ] < lengthArr[ v ] ) ... */

            } /* if( ofs ) */

            graphEdge = graphEdge -> next[ ofs ];

        } /* while( graphEdge ) */
  
    } /* while( orderCount != orderFirst ) */

    int i;
    for( i = 0; i < hlpGraph -> total - 2; i ++ )
    {
        CvGraphVtx* hlpGraphVtxTemp = cvGetGraphVtx( hlpGraph, i );
        if( hlpGraphVtxTemp ) {
            if( !hlpGraphVtxTemp -> first ) {
                cvGraphRemoveVtxByPtr( hlpGraph, hlpGraphVtxTemp );
            }
        }
    } /* for( i = 0; i < hlpGraph -> total - 2; i ++ ) */

    return lengthArr[ t ];

} /* makeHelpGraph */

// function makePseudoMaxFlow increases flow in graph by using hlpGraph
//      graph           - pointer to initial graph
//      hlpGraph        - pointer to help graph
//      vertices        - pointer to vertices array
//      verticesCount   - number of vertices in vertices array
//      mem             - pointer to memory allocated by allocTempMem function
//      width           - width of image line in pixels
void makePseudoMaxFlow( CvGraph* graph,
                        CvGraph* hlpGraph,
                        int* vertices,
                        int verticesCount,
                        int* mem,
                        int width )
{
    int stekCount;
    int orderFirst;
    int orderCount;
    int i;
    int v, u;
    int* stek = mem;
    int* order = stek + width + 2;
    int* incomFlow = order + width + 2;
    int* outgoFlow = incomFlow + width + 2;
    int* flow = outgoFlow + width + 2;
    int* cargo = flow+ width + 2;
    int s = graph -> total - 2; /* source vertex */
    int t = graph -> total - 1; /* terminate vertex */
    int realVerticesCount = verticesCount;

    stekCount = 0;

    for( i = 0; i < verticesCount; i ++ )
    {
        v = vertices[ i ];

        incomFlow[ v ] = outgoFlow[ v ] = 0;

        if( v == s ) {
            incomFlow[ v ] = INT_INFINITY;
        } /* if( v == s ) */
        else {
            CvGraphVtx* hlpGraphVtx = cvGetGraphVtx( hlpGraph, v );
            CvGraphEdge* hlpGraphEdge = hlpGraphVtx -> first;
            int ofs;

            while( hlpGraphEdge )
            {
                int vtxIdx = cvGraphVtxIdx( hlpGraph,
                    hlpGraphEdge -> vtx[1] );
                ofs = vtxIdx == v;

                if( ofs )
                {
                    incomFlow[ v ] += ( ( Edge* )hlpGraphEdge ) -> weight;
                } /* if( ofs ) */

                hlpGraphEdge = hlpGraphEdge -> next[ ofs ];
            } /* while( hlpGraphEdge ) */

        } /* if( v == s ) else */

        if( v == t ) {
            outgoFlow[ v ] = INT_INFINITY;
        } /* if( v == t ) */
        else {
            CvGraphVtx* hlpGraphVtx = cvGetGraphVtx( hlpGraph, v );
            CvGraphEdge* hlpGraphEdge = hlpGraphVtx -> first;
            int ofs;

            while( hlpGraphEdge )
            {
                int vtxIdx = cvGraphVtxIdx( hlpGraph,
                    hlpGraphEdge -> vtx[1] );
                ofs = vtxIdx == v;

                if( !ofs )
                {
                    outgoFlow[ v ] += ( ( Edge* )hlpGraphEdge ) -> weight;
                } /* if( ofs ) */

                hlpGraphEdge = hlpGraphEdge -> next[ ofs ];
            } /* while( hlpGraphEdge ) */

        } /* if( v == t ) else */

        flow[ v ] = CV_MIN2( incomFlow[ v ], outgoFlow[ v ] );

        if( !flow[ v ] ) {
            stek[ stekCount ] = v;
            stekCount ++;
        } /* if( !flow[ v ] ) */

    } /* for( i = 0; i < verticesCount; i ++ ) */

    for( i = 0; i < verticesCount; i ++ )
    {
        v = vertices[ i ];
        cargo[ v ] = 0;
    } /* for( i = 0; i < verticesCount; i ++ ) */

    while( realVerticesCount > 2 )
    {
        /* deleting all vertices included in stek */
        while( stekCount )
        {
            v = stek[ stekCount - 1 ];
            stekCount --;

            /* deleting edges incoming to v and outgoing from v */
            int ofs;
            CvGraphVtx* hlpGraphVtx = cvGetGraphVtx( hlpGraph, v );
            CvGraphEdge* hlpGraphEdge;
            if( hlpGraphVtx ) {
                hlpGraphEdge = hlpGraphVtx -> first;
            }
            else {
                hlpGraphEdge = NULL;
            }
            while( hlpGraphEdge )
            {
                CvGraphVtx* hlpGraphVtx2 = hlpGraphEdge -> vtx[ 1 ];
                int hlpGraphVtxIdx2 = cvGraphVtxIdx( hlpGraph,
                    hlpGraphVtx2 );
                ofs = hlpGraphVtxIdx2 == v;

                if( ofs )
                {
                    /* hlpGraphEdge is incoming edge */
                    CvGraphVtx* hlpGraphVtx3 = hlpGraphEdge -> vtx[0];
                    u = cvGraphVtxIdx( hlpGraph,
                                       hlpGraphVtx3 );
                    outgoFlow[ u ] -= ( ( Edge* )hlpGraphEdge ) -> weight
                        - ( ( Edge* )hlpGraphEdge ) -> flow;
                    cvGraphRemoveEdgeByPtr( hlpGraph,
                                            hlpGraphVtx3,
                                            hlpGraphVtx2 );
                    if( flow[ u ] != 0 ) {
                        flow[ u ] = CV_MIN2( incomFlow[u], outgoFlow[u] );
                        if( flow[ u ] == 0 ) {
                            stek[ stekCount ] = u;
                            stekCount ++;
                        }
                    }
                } /* if( ofs ) */
                else
                {
                    /* hlpGraphEdge is outgoing edge */
                    CvGraphVtx* hlpGraphVtx3 = hlpGraphEdge -> vtx[1];
                    int u = cvGraphVtxIdx( hlpGraph,
                                              hlpGraphVtx3 );
                    incomFlow[ u ] -= ( ( Edge* )hlpGraphEdge ) -> weight
                        - ( ( Edge* )hlpGraphEdge ) -> flow;
                    cvGraphRemoveEdgeByPtr( hlpGraph,
                                            hlpGraphVtx2,
                                            hlpGraphVtx3 );
                    if( flow[ u ] != 0 ) {
                        flow[ u ] = CV_MIN2( incomFlow[u], outgoFlow[u] );
                        if( flow[ u ] == 0 ) {
                            stek[ stekCount ] = u;
                            stekCount ++;
                        }
                    }
                } /* if( ofs ) else */

                hlpGraphEdge = hlpGraphEdge -> next[ ofs ];

            } /* while( hlpGraphEdge ) */

            /* deleting vertex v */
            cvGraphRemoveVtx( hlpGraph, v );
            realVerticesCount --;

        } /* while( stekCount ) */

        if( realVerticesCount > 2 ) /* the flow is not max still */
        {
            int p = INT_INFINITY;
            int r = -1;
            CvGraphVtx* graphVtx;

            if( realVerticesCount == 3 ) {
                r = r;
            }
            for( i = 0; i < hlpGraph -> total - 2; i ++ )
            {
                graphVtx = cvGetGraphVtx( hlpGraph, i );
                if( graphVtx ) {
                    v = cvGraphVtxIdx( hlpGraph, graphVtx );
                    if( flow[ v ] < p ) {
                        r = v;
                        p = flow[ v ];
                    }
                }

            } /* for( i = 0; i < hlpGraph -> total - 2; i ++ ) */

            /* building of size p flow from r to t */
            orderCount = orderFirst = 0;
            order[ orderCount ] = r;
            orderCount ++;

            v = order[ orderFirst ];
            orderFirst ++;

            cargo[ r ] = p;
            do /* while( v != t ) */
            {
                incomFlow[ v ] -= cargo[ v ];
                outgoFlow[ v ] -= cargo[ v ];
                flow[ v ] -= cargo[ v ];

                if( flow[ v ] == 0 ) {
                    stek[ stekCount ] = v;
                    stekCount ++;
                }

                if( v == t ) {
                    cargo[ v ] = p;
                }
                else
                {
                    int ofs;
                    CvGraphVtx* hlpGraphVtx2;
                    CvGraphVtx* hlpGraphVtx = cvGetGraphVtx( hlpGraph, v );
                    CvGraphEdge* hlpGraphEdge = hlpGraphVtx -> first;
                    CvGraphEdge* hlpGraphEdge2 = NULL;

                    while( hlpGraphEdge && cargo[ v ] > 0 )
                    {
                        hlpGraphVtx2 = hlpGraphEdge -> vtx[ 1 ];
                        u = cvGraphVtxIdx( hlpGraph, hlpGraphVtx2 );
                        ofs = u == v;

                        if( !ofs )
                        {
                            if( cargo[ u ] == 0 ) {
                                order[ orderCount ] = u;
                                orderCount ++;
                            }
                            int delta = ( ( Edge* )hlpGraphEdge ) -> weight
                                - ( ( Edge* )hlpGraphEdge ) -> flow;
                            delta = CV_MIN2( cargo[ v ], delta );
                            ( ( Edge* )hlpGraphEdge ) -> flow += delta;
                            cargo[ v ] -= delta;
                            cargo[ u ] += delta;
                            if( ( ( Edge* )hlpGraphEdge ) -> weight ==
                                ( ( Edge* )hlpGraphEdge ) -> flow )
                            {
                                /* deleting hlpGraphEdge */
                                hlpGraphEdge2 = hlpGraphEdge -> next[ ofs ];
                                CvGraphEdge* graphEdge =
                                    cvFindGraphEdge( graph, v, u );
                                ( ( Edge* )graphEdge ) -> flow +=
                                    ( ( Edge* )hlpGraphEdge ) -> flow;
                                cvGraphRemoveEdgeByPtr( hlpGraph,
                                    hlpGraphEdge -> vtx[0],
                                    hlpGraphEdge -> vtx[1] );
                            }
                        } /* if( !ofs ) */

                        if( hlpGraphEdge2 ) {
                            hlpGraphEdge = hlpGraphEdge2;
                            hlpGraphEdge2 = NULL;
                        }
                        else {
                            hlpGraphEdge = hlpGraphEdge -> next[ ofs ];
                        }
                    } /* while( hlpGraphEdge && cargo[ v ] > 0 ) */

                } /* if( v == t ) else */

                v = order[ orderFirst ];
                orderFirst ++;

            } while( v != t ); /* do */

            /* building of size p flow from s to r */
            orderCount = orderFirst = 0;
            order[ orderCount ] = r;
            orderCount ++;

            v = order[ orderFirst ];
            orderFirst ++;

            cargo[ r ] = p;
            do /* while( v != s ) */
            {
                if( v != r )
                {
                    incomFlow[ v ] -= cargo[ v ];
                    outgoFlow[ v ] -= cargo[ v ];
                    flow[ v ] -= cargo[ v ];
                    if( flow[ v ] == 0 ) {
                        stek[ stekCount ] = v;
                        stekCount ++;
                    }
                } /* if( v != r ) */

                if( v == s ) {
                    cargo[ v ] = 0;
                } /* if( v == s ) */
                else
                {
                    int ofs;

                    CvGraphVtx* hlpGraphVtx = cvGetGraphVtx( hlpGraph, v );
                    CvGraphEdge* hlpGraphEdge = hlpGraphVtx -> first;
                    CvGraphEdge* hlpGraphEdge2 = NULL;
                    while( hlpGraphEdge && cargo[ v ] > 0 )
                    {
                        u = cvGraphVtxIdx( hlpGraph,
                                hlpGraphEdge -> vtx[ 1 ] );
                        ofs = u == v;

                        if( ofs )
                        {
                            u = cvGraphVtxIdx( hlpGraph,
                                    hlpGraphEdge -> vtx[ 0 ] );

                            if( cargo[ u ] == 0 ) {
                                order[ orderCount ] = u;
                                orderCount ++;
                            }

                            int delta = ( ( Edge* )hlpGraphEdge ) -> weight
                                - ( ( Edge* )hlpGraphEdge ) -> flow;

                            delta = CV_MIN2( cargo[ v ], delta );
                            
                            (( ( Edge* )hlpGraphEdge ) -> flow) += delta;

                            cargo[ v ] -= delta;
                            cargo[ u ] += delta;

                            if( ( ( Edge* )hlpGraphEdge ) -> weight ==
                                ( ( Edge* )hlpGraphEdge ) -> flow )
                            {
                                hlpGraphEdge2 = hlpGraphEdge -> next[ ofs ];
                                CvGraphEdge* graphEdge =
                                    cvFindGraphEdge( graph, u, v );
                                ( ( Edge* )graphEdge ) -> flow +=
                                    ( ( Edge* )hlpGraphEdge ) -> flow;
                                cvGraphRemoveEdgeByPtr( hlpGraph,
                                    hlpGraphEdge -> vtx[0],
                                    hlpGraphEdge -> vtx[1] );
                            }
                        } /* if( ofs ) */

                        if( hlpGraphEdge2 ) {
                            hlpGraphEdge = hlpGraphEdge2;
                            hlpGraphEdge2 = NULL;
                        }
                        else {
                            hlpGraphEdge = hlpGraphEdge -> next[ ofs ];
                        }
                    } /* while( hlpGraphEdge && cargo[ v ] > 0 ) */

                } /* if( v == s ) else */

                v = order[ orderFirst ]; //added
                orderFirst ++; //added

            } while( v != s ); /* do */

        } /* if( hlpGraph -> total > 2 ) */

    } /* while( hlpGraph -> total > 2 ) */

} /* makePseudoMaxFlow */


// function oneStep produces one alpha-beta exchange for one line of images
//      leftLine        - pointer to the left image line
//      rightLine       - pointer to the right image line
//      alpha           - label number one
//      beta            - label number two
//      corr            - pointer to correspondence array for this line
//      width           - width of image line in pixels
//      mem             - pointer to memory allocated by allocTempMem function
//      vertices        - pointer to vertices array allocated by allocTempMem
//                        function
//      storage         - pointer to CvMemStorage structure
bool oneStep( unsigned char* leftLine,
              unsigned char* rightLine,
              int alpha,
              int beta,
              int* corr,
              int width,
              int* mem,
              int* vertices,
              CvMemStorage* storage )
{
    CvGraph* graph = NULL;
    CvGraph* hlpGraph = NULL;
    CvMemStoragePos storagePos;
    int i;
    bool change = false;
    cvSaveMemStoragePos( storage, &storagePos );

    int verticesCount;

    makeGraph( &graph, leftLine, rightLine, alpha, beta, corr, width, storage );

    int s = graph -> total - 2; /* source vertex - alpha vertex */
    //int t = graph -> total - 1; /* terminate vertex - beta vertex */

    int length = makeHelpGraph( graph,
                                &hlpGraph,
                                storage,
                                mem,
                                vertices,
                                &verticesCount,
                                width );
    while( length != INT_INFINITY )
    {
        change = true;
        makePseudoMaxFlow( graph,
                           hlpGraph,
                           vertices,
                           verticesCount,
                           mem,
                           width );
        cvClearGraph( hlpGraph );
        length = makeHelpGraph( graph,
                                &hlpGraph,
                                storage,
                                mem,
                                vertices,
                                &verticesCount,
                                width );
    } /* while( length != INT_INFINITY ) */

    int coord;
    CvGraphVtx* graphVtx;
    for( i = 0; i < s; i ++ )
    {
        CvGraphEdge* graphEdge = cvFindGraphEdge( graph, s, i );

        if( ( ( Edge* )graphEdge ) -> weight ==
            ( ( Edge* )graphEdge ) -> flow )
        { /* this vertex must have alpha label */
            graphVtx = cvGetGraphVtx( graph, i );
            coord = ( ( Vertex* )graphVtx )-> coord;
            if( corr[ coord ] != alpha ) {
                corr[ coord ] = alpha; //added
                change = true;
            }
            else {
                corr[ coord ] = alpha;
            }
        } /* if( ( ( Edge* )graphEdge ) -> weight == ... */
        else
        { /* this vertex must have beta label */
            graphVtx = cvGetGraphVtx( graph, i );
            coord = ( ( Vertex* )graphVtx )-> coord;
            if( corr[ coord ] != beta ) {
                corr[ coord ] = beta; //added
                change = true;
            }
            else {
                corr[ coord ] = beta;
            }
        } /* if( ( ( Edge* )graphEdge ) -> weight == ... else */

    } /* for( i = 0; i < s; i ++ ) */

    cvClearGraph( hlpGraph );
    cvClearGraph( graph );

    cvRestoreMemStoragePos( storage, &storagePos );

    return change;

} /* oneStep */

// function initCorr fills correspondence array with initial values
//      corr                - pointer to correspondence array for this line
//      width               -  width of image line in pixels
//      maxPixelDifference  - maximum value of difference between the same
//                            point painted on two images
void initCorr( int* corr, int width, int maxPixelDifference )
{
    int i;
    int pixelDifferenceRange = maxPixelDifference * 2 + 1;

    for( i = 0; i < width; i ++ )
    {
        corr[ i ] = i % pixelDifferenceRange - maxPixelDifference;
    }
} /* initCorr */

// function oneLineCorr fully computes one line of images
//      leftLine                - pointer to the left image line
//      rightLine               - pointer to the right image line
//      corr                    - pointer to the correspondence array for one
//                                line
//      mem                     - pointer to memory allocated by allocTempMem
//                                function
//      vertices                - pointer to memory allocated by allocTempMem
//                                function
//      width                   - width of image line in pixels
//      maxPixelDifference      - maximum value of pixel differences in pixels
//      storage                 - pointer to CvMemStorage struct which
//                                contains memory storage
void oneLineCorr( unsigned char* leftLine,
                  unsigned char* rightLine,
                  int* corr,
                  int* mem,
                  int* vertices,
                  int width,
                  int maxPixelDifference,
                  CvMemStorage* storage )
{
    int result = 1;
    int count = 0;
    int i, j;

    initCorr( corr, width, maxPixelDifference );
    while( result )
    {
        result = 0;

        for( i = - maxPixelDifference; i < maxPixelDifference; i ++ )
        for( j = i + 1; j <= maxPixelDifference; j ++ )
        {
            result += (int)oneStep( leftLine,
                               rightLine,
                               i,
                               j,
                               corr,
                               width,
                               mem,
                               vertices,
                               storage );
        } /* for( j = i + 1; j < width; j ++ ) */

        count ++;
        if( count > /*0*//*1*/2 ) {
            break;
        }

    } /* while( result ) */

} /* oneLineCorr */

// function allLinesCorr computes all lines on the images
//      leftImage           - pointer to the left image
//      leftLineStep        - size of line on the left image in bytes
//      rightImage          - pointer to the right image
//      rightLineStep       - size of line on the right image in bytes
//      width               - width of line in pixels
//      height              - height of image in pixels
//      corr                - pointer to correspondence array for all lines
//      maxPixelDifference  - maximum value of difference between the same
//                            point painted on two images
//      storage             - pointer to CvMemStorage which contains memory
//                            storage
void allLinesCorr( unsigned char* leftImage,
                   int leftLineStep,
                   unsigned char* rightImage,
                   int rightLineStep,
                   int width,
                   int height,
                   int* corr,
                   int maxPixelDifference,
                   CvMemStorage* storage )
{
    int i;
    unsigned char* leftLine = leftImage;
    unsigned char* rightLine = rightImage;
    int* mem;
    int* vertices;

    allocTempMem( &mem,
                  &vertices,
                  width );

    for( i = 0; i < height; i ++ )
    {
        oneLineCorr( leftLine,
                     rightLine,
                     corr + i * width,
                     mem,
                     vertices,
                     width,
                     maxPixelDifference,
                     storage );
        leftLine += leftLineStep;
        rightLine += rightLineStep;
    } /* for( i = 0; i < height; i ++ ) */

    freeTempMem( &mem,
                 &vertices );

} /* allLinesCorr */

// This function produces morphing of two images into one image, which includes morphed
// image or depth map
//      _leftImage              - pointer to left image
//      _leftLineStep           - size of line on left image in bytes
//      _rightImage             - pointer to right image
//      _rightLineStep          - size of line on right image in bytes
//      _resultImage            - pointer to result morphed image
//      _resultLineStep         - size of line on result image in bytes
//      _corrArray              - pointer to array with correspondences
//      _numCorrArray           - pointer to array with numbers correspondeces on each line
//      width                   - width of images
//      height                  - height of images
//      alpha                   - position of virtual camera ( 0 corresponds to left image, 1 - to right one )
//      imageNeed               - defines your wishes. if you want to see normal morphed image you have to set
//                                this parameter to morphNormalImage ( this is default value ), else if you want
//                                to see depth map you have to set this parameter to morphDepthMap and set the
//                                next parameter ( maxPixelDifference ) to real value
//      maxPixelDifference      - maximum value of pixel difference on two images
void CCvGraphCutMorpher::Morph( unsigned char* _leftImage,
            int _leftLineStep,
            unsigned char* _rightImage,
            int _rightLineStep,
            unsigned char* _resultImage,
            int _resultLineStep,
            int* _corrArray,
            int width,
            int height,
            float alpha,
            morphImageType imageNeed,
            int maxDifference
          )
{
    unsigned char* leftArray    = _leftImage;
    unsigned char* middleArray  = _resultImage;
    unsigned char* rightArray   = _rightImage;
    int leftLineSize            = _leftLineStep;
    int middleLineSize          = _resultLineStep;
    int rightLineSize           = _rightLineStep;

    int lineNumber;
    unsigned char* leftTemp;
    unsigned char* middleTemp;
    unsigned char* rightTemp;
    int leftPixel;
    int prevLeftPixel;
    int middlePixel;
    int prevMiddlePixel;
    int rightPixel;
    int prevRightPixel;
    int leftPixel3;
    int middlePixel3;
    int rightPixel3;
    int i;
    int j;
    int tempIndex;
    int* result;
    int number;
    float alpha1        = 1.0f - alpha;
    
    for( lineNumber = 0; lineNumber < height; lineNumber ++ )
    {
        leftTemp    = leftArray + leftLineSize * lineNumber;
        middleTemp  = middleArray + middleLineSize * lineNumber;
        rightTemp   = rightArray + rightLineSize * lineNumber;
        memset( ( void* )middleTemp, 0, middleLineSize );

        result = _corrArray + width * lineNumber;
        number = width;
        
        prevLeftPixel   = 0;
        prevRightPixel  = prevLeftPixel + result[ 0 ];
        if( prevRightPixel >= width ) {
            prevRightPixel = width - 1;
        }
        else if ( prevRightPixel < 0 ) {
            prevRightPixel = 0;
        }
        prevMiddlePixel =
            (int)( prevLeftPixel * alpha1 + prevRightPixel * alpha );
        for( i = 0; i < number - 1; i ++ )
        {
            leftPixel       = i;
            rightPixel      = i + result[ i ];
            if( rightPixel >= width ) {
                rightPixel = width - 1;
            }
            else if( rightPixel < 0 ) {
                rightPixel = 0;
            }
            middlePixel     =
                (int)( leftPixel * alpha1 + rightPixel * alpha );
            leftPixel3      = leftPixel * 3;
            middlePixel3    = middlePixel * 3;
            rightPixel3     = rightPixel * 3;
            
            if( imageNeed == morphDepthMap ) {
                int t   = leftPixel - rightPixel + maxDifference;
                t       = t < 0 ? -t : t;
                t       = t * 255 / maxDifference / 2;
                middleTemp[ middlePixel3 ]      = ( unsigned char )t;
                middleTemp[ middlePixel3 + 1 ]  = ( unsigned char )t;
                middleTemp[ middlePixel3 + 2 ]  = ( unsigned char )t;
            } // if( imageNeed == morphDepthMap )
            else
            {
                middleTemp[ middlePixel3 ] =
                    (unsigned char)( leftTemp[ leftPixel3 ] * alpha1
                    + rightTemp[ rightPixel3 ] * alpha );
                middleTemp[ middlePixel3 + 1 ] =
                    (unsigned char)( leftTemp[ leftPixel3 + 1 ] * alpha1
                    + rightTemp[ rightPixel3 + 1 ] * alpha );
                middleTemp[ middlePixel3 + 2 ] =
                    (unsigned char)( leftTemp[ leftPixel3 + 2 ] * alpha1
                    + rightTemp[ rightPixel3 + 2 ] * alpha );

                if( middlePixel - prevMiddlePixel > 1 ) // occlusion
                {
                    if( leftPixel - prevLeftPixel > 1 )
                    {
                        int LenSrc  = leftPixel - prevLeftPixel - 2;
                        int LenDest = middlePixel - prevMiddlePixel - 1;
                        for( j = prevMiddlePixel + 1; j < middlePixel; j ++ )
                        {
                            tempIndex   = prevLeftPixel + 1 + LenSrc *
                                ( j - prevMiddlePixel - 1 ) / LenDest;
                            middleTemp[ j * 3 ]     =
                                leftTemp[ tempIndex * 3 ];
                            middleTemp[ j * 3 + 1 ] =
                                leftTemp[ tempIndex * 3 + 1 ];
                            middleTemp[ j * 3 + 2 ] =
                                leftTemp[ tempIndex * 3 + 2 ];
                        }
                    } // if( leftPixel - prevLeftPixel > 1 )
                    else
                    {
                        int LenSrc  = rightPixel - prevRightPixel - 2;
                        int LenDest = middlePixel - prevMiddlePixel - 1;
                        for( j = prevMiddlePixel + 1; j < middlePixel; j ++ )
                        {
                            tempIndex   = prevRightPixel + 1 + LenSrc *
                                ( j - prevMiddlePixel - 1 ) / LenDest;
                            middleTemp[ j * 3 ]     =
                                rightTemp[ tempIndex * 3 ];
                            middleTemp[ j * 3 + 1 ] =
                                rightTemp[ tempIndex * 3 + 1 ];
                            middleTemp[ j * 3 + 2 ] =
                                rightTemp[ tempIndex * 3 + 2 ];
                        }
                    } // if( leftPixel - prevLeftPixel > 1 ) else
                    
                } // if( middlePixel - prevMiddlePixel > 1 )

            } // if( imageNeed == morphDepthMap ) else

            if( middlePixel > prevMiddlePixel ) {
                if( leftPixel > prevLeftPixel )
                    prevLeftPixel   = leftPixel;
                if( rightPixel > prevRightPixel )
                    prevRightPixel  = rightPixel;
                prevMiddlePixel = middlePixel;
            }
        } // for( i = number - 1; i >= 0; i -- )
        
    } // for( lineNumber = 0; lineNumber < LeftImage -> m_Raster -> GetHeight() )

} // Morph

bool  CCvGraphCutMorpher::OnCalculateStereo()
{
    CvSize imageSizeLeft = GetImageSize( m_left_img ),
           imageSizeRight = GetImageSize( m_right_img );

    if( ( imageSizeLeft.width != imageSizeRight.width )
        || ( imageSizeLeft.height != imageSizeRight.height ) )
    {
        return false;
    }

    if( m_corr ) {
        free( m_corr );
        m_corr = NULL;
    }
    m_corr = ( int* ) malloc( m_left_img -> width
        * m_left_img -> height
        * sizeof( int ) );

    if( !m_storage ) {
        m_storage = cvCreateMemStorage( 0 );
        m_isStorageAllocated = true;
    }
    // Find correspondence for full image and store it to corr array
    allLinesCorr( ( unsigned char* )m_left_img -> imageData,
                  m_left_img -> widthStep,
                  ( unsigned char* )m_right_img -> imageData,
                  m_right_img -> widthStep,
                  m_left_img -> width,
                  m_left_img -> height,
                  m_corr,
                  m_maxPixelDifference,
                  m_storage );

    m_isStereoReady = true;

    return true;
}

bool  CCvGraphCutMorpher::OnCalculateVirtualImage()
{
    // Output image to ResultImage window
    Morph( ( unsigned char* )m_left_img -> imageData,
           m_left_img ->widthStep,
           ( unsigned char* )m_right_img -> imageData,
           m_right_img -> widthStep,
           ( unsigned char* )m_virtual_img -> imageData,
           m_virtual_img -> widthStep,
           m_corr,
           m_left_img -> width,
           m_left_img -> height,
           m_pan );

    m_isVirtualImageReady = true;

    return true;
}

bool  CCvGraphCutMorpher::OnCalculateDisparity()
{
    Morph( ( unsigned char* )m_left_img -> imageData,
           m_left_img ->widthStep,
           ( unsigned char* )m_right_img -> imageData,
           m_right_img -> widthStep,
           ( unsigned char* )m_disparity_img -> imageData,
           m_disparity_img -> widthStep,
           m_corr,
           m_left_img -> width,
           m_left_img -> height,
           m_pan,
           morphDepthMap,
           m_maxPixelDifference );

    return true;
}

bool  CCvGraphCutMorpher::OnCalculateDisparityImage()
{
    Morph( ( unsigned char* )m_left_img -> imageData,
           m_left_img ->widthStep,
           ( unsigned char* )m_right_img -> imageData,
           m_right_img -> widthStep,
           ( unsigned char* )m_disparity_img -> imageData,
           m_disparity_img -> widthStep,
           m_corr,
           m_left_img -> width,
           m_left_img -> height,
           m_pan,
           morphDepthMap,
           m_maxPixelDifference );

    return true;
}

CCvGraphCutMorpher::CCvGraphCutMorpher()
{
    m_maxPixelDifference = MAX_DIFFERENCE;
    m_corr = 0;
    m_isStereoReady = false;
    m_isVirtualImageReady = false;
    m_isDisparityReady = false;
    m_storage = NULL;
    m_isStorageAllocated = false;
}

#endif

/* End of file */
