root/modules/shape/src/emdL1.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getEMDL1
  2. setMaxIteration
  3. initBaseTrees
  4. fillBaseTrees
  5. greedySolution
  6. greedySolution2
  7. greedySolution3
  8. initBVTree
  9. updateSubtree
  10. isOptimal
  11. findNewSolution
  12. findLoopFromEnterBV
  13. compuTotalFlow
  14. EMDL1

/*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.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., 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 the copyright holders 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*/

/*
 * Implementation of an optimized EMD for histograms based in
 * the papers "EMD-L1: An efficient and Robust Algorithm
 * for comparing histogram-based descriptors", by Haibin Ling and
 * Kazunori Okuda; and "The Earth Mover's Distance is the Mallows
 * Distance: Some Insights from Statistics", by Elizaveta Levina and
 * Peter Bickel, based on HAIBIN LING AND KAZUNORI OKADA implementation.
 */

#include "precomp.hpp"
#include "emdL1_def.hpp"
#include <limits>

/****************************************************************************************\
*                                   EMDL1 Class                                         *
\****************************************************************************************/

float EmdL1::getEMDL1(cv::Mat &sig1, cv::Mat &sig2)
{
    // Initialization
    CV_Assert((sig1.rows==sig2.rows) && (sig1.cols==sig2.cols) && (!sig1.empty()) && (!sig2.empty()));
    if(!initBaseTrees(sig1.rows, 1))
        return -1;

    float *H1=new float[sig1.rows], *H2 = new float[sig2.rows];
    for (int ii=0; ii<sig1.rows; ii++)
    {
        H1[ii]=sig1.at<float>(ii,0);
        H2[ii]=sig2.at<float>(ii,0);
    }

    fillBaseTrees(H1,H2); // Initialize histograms
    greedySolution(); // Construct an initial Basic Feasible solution
    initBVTree(); // Initialize BVTree

    // Iteration
    bool bOptimal = false;
    m_nItr = 0;
    while(!bOptimal && m_nItr<nMaxIt)
    {
        // Derive U=(u_ij) for row i and column j
        if(m_nItr==0) updateSubtree(m_pRoot);
        else updateSubtree(m_pEnter->pChild);

        // Optimality test
        bOptimal = isOptimal();

        // Find new solution
        if(!bOptimal)
            findNewSolution();
        ++m_nItr;
    }
    delete [] H1;
    delete [] H2;
    // Output the total flow
    return compuTotalFlow();
}

void EmdL1::setMaxIteration(int _nMaxIt)
{
    nMaxIt=_nMaxIt;
}

//-- SubFunctions called in the EMD algorithm
bool EmdL1::initBaseTrees(int n1, int n2, int n3)
{
    if(binsDim1==n1 && binsDim2==n2 && binsDim3==n3)
        return true;
    binsDim1 = n1;
    binsDim2 = n2;
    binsDim3 = n3;
    if(binsDim1==0 || binsDim2==0) dimension = 0;
    else dimension      = (binsDim3==0)?2:3;

    if(dimension==2)
    {
        m_Nodes.resize(binsDim1);
        m_EdgesUp.resize(binsDim1);
        m_EdgesRight.resize(binsDim1);
        for(int i1=0; i1<binsDim1; i1++)
        {
            m_Nodes[i1].resize(binsDim2);
            m_EdgesUp[i1].resize(binsDim2);
            m_EdgesRight[i1].resize(binsDim2);
        }
        m_NBVEdges.resize(binsDim1*binsDim2*4+2);
        m_auxQueue.resize(binsDim1*binsDim2+2);
        m_fromLoop.resize(binsDim1*binsDim2+2);
        m_toLoop.resize(binsDim1*binsDim2+2);
    }
    else if(dimension==3)
    {
        m_3dNodes.resize(binsDim1);
        m_3dEdgesUp.resize(binsDim1);
        m_3dEdgesRight.resize(binsDim1);
        m_3dEdgesDeep.resize(binsDim1);
        for(int i1=0; i1<binsDim1; i1++)
        {
            m_3dNodes[i1].resize(binsDim2);
            m_3dEdgesUp[i1].resize(binsDim2);
            m_3dEdgesRight[i1].resize(binsDim2);
            m_3dEdgesDeep[i1].resize(binsDim2);
            for(int i2=0; i2<binsDim2; i2++)
            {
                m_3dNodes[i1][i2].resize(binsDim3);
                m_3dEdgesUp[i1][i2].resize(binsDim3);
                m_3dEdgesRight[i1][i2].resize(binsDim3);
                m_3dEdgesDeep[i1][i2].resize(binsDim3);
            }
        }
        m_NBVEdges.resize(binsDim1*binsDim2*binsDim3*6+4);
        m_auxQueue.resize(binsDim1*binsDim2*binsDim3+4);
        m_fromLoop.resize(binsDim1*binsDim2*binsDim3+4);
        m_toLoop.resize(binsDim1*binsDim2*binsDim3+2);
    }
    else
        return false;

    return true;
}

bool EmdL1::fillBaseTrees(float *H1, float *H2)
{
    //- Set global counters
    m_pRoot     = NULL;
    // Graph initialization
    float *p1 = H1;
    float *p2 = H2;
    if(dimension==2)
    {
        for(int c=0; c<binsDim2; c++)
        {
            for(int r=0; r<binsDim1; r++)
            {
                //- initialize nodes and links
                m_Nodes[r][c].pos[0] = r;
                m_Nodes[r][c].pos[1] = c;
                m_Nodes[r][c].d = *(p1++)-*(p2++);
                m_Nodes[r][c].pParent = NULL;
                m_Nodes[r][c].pChild = NULL;
                m_Nodes[r][c].iLevel = -1;

                //- initialize edges
                // to the right
                m_EdgesRight[r][c].pParent = &(m_Nodes[r][c]);
                m_EdgesRight[r][c].pChild = &(m_Nodes[r][(c+1)%binsDim2]);
                m_EdgesRight[r][c].flow = 0;
                m_EdgesRight[r][c].iDir = 1;
                m_EdgesRight[r][c].pNxt = NULL;

                // to the upward
                m_EdgesUp[r][c].pParent = &(m_Nodes[r][c]);
                m_EdgesUp[r][c].pChild  = &(m_Nodes[(r+1)%binsDim1][c]);
                m_EdgesUp[r][c].flow = 0;
                m_EdgesUp[r][c].iDir = 1;
                m_EdgesUp[r][c].pNxt = NULL;
            }
        }
    }
    else if(dimension==3)
    {
        for(int z=0; z<binsDim3; z++)
        {
            for(int c=0; c<binsDim2; c++)
            {
                for(int r=0; r<binsDim1; r++)
                {
                    //- initialize nodes and edges
                    m_3dNodes[r][c][z].pos[0] = r;
                    m_3dNodes[r][c][z].pos[1] = c;
                    m_3dNodes[r][c][z].pos[2] = z;
                    m_3dNodes[r][c][z].d = *(p1++)-*(p2++);
                    m_3dNodes[r][c][z].pParent = NULL;
                    m_3dNodes[r][c][z].pChild = NULL;
                    m_3dNodes[r][c][z].iLevel = -1;

                    //- initialize edges
                    // to the upward
                    m_3dEdgesUp[r][c][z].pParent= &(m_3dNodes[r][c][z]);
                    m_3dEdgesUp[r][c][z].pChild = &(m_3dNodes[(r+1)%binsDim1][c][z]);
                    m_3dEdgesUp[r][c][z].flow = 0;
                    m_3dEdgesUp[r][c][z].iDir = 1;
                    m_3dEdgesUp[r][c][z].pNxt = NULL;

                    // to the right
                    m_3dEdgesRight[r][c][z].pParent     = &(m_3dNodes[r][c][z]);
                    m_3dEdgesRight[r][c][z].pChild      = &(m_3dNodes[r][(c+1)%binsDim2][z]);
                    m_3dEdgesRight[r][c][z].flow        = 0;
                    m_3dEdgesRight[r][c][z].iDir        = 1;
                    m_3dEdgesRight[r][c][z].pNxt        = NULL;

                    // to the deep
                    m_3dEdgesDeep[r][c][z].pParent      = &(m_3dNodes[r][c][z]);
                    m_3dEdgesDeep[r][c][z].pChild       = &(m_3dNodes[r][c])[(z+1)%binsDim3];
                    m_3dEdgesDeep[r][c][z].flow = 0;
                    m_3dEdgesDeep[r][c][z].iDir = 1;
                    m_3dEdgesDeep[r][c][z].pNxt = NULL;
                }
            }
        }
    }
    return true;
}

bool EmdL1::greedySolution()
{
    return dimension==2?greedySolution2():greedySolution3();
}

bool EmdL1::greedySolution2()
{
    //- Prepare auxiliary array, D=H1-H2
    int c,r;
    floatArray2D D(binsDim1);
    for(r=0; r<binsDim1; r++)
    {
        D[r].resize(binsDim2);
        for(c=0; c<binsDim2; c++) D[r][c] = m_Nodes[r][c].d;
    }
    // compute integrated values along each dimension
    std::vector<float> d2s(binsDim2);
    d2s[0] = 0;
    for(c=0; c<binsDim2-1; c++)
    {
        d2s[c+1] = d2s[c];
        for(r=0; r<binsDim1; r++) d2s[c+1]-= D[r][c];
    }

    std::vector<float> d1s(binsDim1);
    d1s[0] = 0;
    for(r=0; r<binsDim1-1; r++)
    {
        d1s[r+1] = d1s[r];
        for(c=0; c<binsDim2; c++) d1s[r+1]-= D[r][c];
    }

    //- Greedy algorithm for initial solution
    cvPEmdEdge pBV;
    float dFlow;
    bool bUpward = false;
    nNBV = 0; // number of NON-BV edges

    for(c=0; c<binsDim2-1; c++)
    for(r=0; r<binsDim1; r++)
    {
        dFlow = D[r][c];
        bUpward = (r<binsDim1-1) && (fabs(dFlow+d2s[c+1]) > fabs(dFlow+d1s[r+1]));      // Move upward or right

        // modify basic variables, record BV and related values
        if(bUpward)
        {
            // move to up
            pBV = &(m_EdgesUp[r][c]);
            m_NBVEdges[nNBV++]  = &(m_EdgesRight[r][c]);
            D[r+1][c] += dFlow;         // auxilary matrix maintanence
            d1s[r+1] += dFlow;          // auxilary matrix maintanence
        }
        else
        {
            // move to right, no other choice
            pBV = &(m_EdgesRight[r][c]);
            if(r<binsDim1-1)
                m_NBVEdges[nNBV++]      = &(m_EdgesUp[r][c]);

            D[r][c+1] += dFlow;         // auxilary matrix maintanence
            d2s[c+1] += dFlow;          // auxilary matrix maintanence
        }
        pBV->pParent->pChild = pBV;
        pBV->flow = fabs(dFlow);
        pBV->iDir = dFlow>0;            // 1:outward, 0:inward
    }

    //- rightmost column, no choice but move upward
    c = binsDim2-1;
    for(r=0; r<binsDim1-1; r++)
    {
        dFlow = D[r][c];
        pBV = &(m_EdgesUp[r][c]);
        D[r+1][c] += dFlow;             // auxilary matrix maintanence
        pBV->pParent->pChild= pBV;
        pBV->flow = fabs(dFlow);
        pBV->iDir = dFlow>0;            // 1:outward, 0:inward
    }
    return true;
}

bool EmdL1::greedySolution3()
{
    //- Prepare auxiliary array, D=H1-H2
    int i1,i2,i3;
    std::vector<floatArray2D> D(binsDim1);
    for(i1=0; i1<binsDim1; i1++)
    {
        D[i1].resize(binsDim2);
        for(i2=0; i2<binsDim2; i2++)
        {
            D[i1][i2].resize(binsDim3);
            for(i3=0; i3<binsDim3; i3++)
                D[i1][i2][i3] = m_3dNodes[i1][i2][i3].d;
        }
    }

    // compute integrated values along each dimension
    std::vector<float> d1s(binsDim1);
    d1s[0] = 0;
    for(i1=0; i1<binsDim1-1; i1++)
    {
        d1s[i1+1] = d1s[i1];
        for(i2=0; i2<binsDim2; i2++)
        {
            for(i3=0; i3<binsDim3; i3++)
                d1s[i1+1] -= D[i1][i2][i3];
        }
    }

    std::vector<float> d2s(binsDim2);
    d2s[0] = 0;
    for(i2=0; i2<binsDim2-1; i2++)
    {
        d2s[i2+1] = d2s[i2];
        for(i1=0; i1<binsDim1; i1++)
        {
            for(i3=0; i3<binsDim3; i3++)
                d2s[i2+1] -= D[i1][i2][i3];
        }
    }

    std::vector<float> d3s(binsDim3);
    d3s[0] = 0;
    for(i3=0; i3<binsDim3-1; i3++)
    {
        d3s[i3+1]       = d3s[i3];
        for(i1=0; i1<binsDim1; i1++)
        {
            for(i2=0; i2<binsDim2; i2++)
                d3s[i3+1] -= D[i1][i2][i3];
        }
    }

    //- Greedy algorithm for initial solution
    cvPEmdEdge pBV;
    float dFlow, f1,f2,f3;
    nNBV = 0; // number of NON-BV edges
    for(i3=0; i3<binsDim3; i3++)
    {
        for(i2=0; i2<binsDim2; i2++)
        {
            for(i1=0; i1<binsDim1; i1++)
            {
                if(i3==binsDim3-1 && i2==binsDim2-1 && i1==binsDim1-1) break;

                //- determine which direction to move, either right or upward
                dFlow = D[i1][i2][i3];
                f1 = (i1<(binsDim1-1))?fabs(dFlow+d1s[i1+1]):std::numeric_limits<float>::max();
                f2 = (i2<(binsDim2-1))?fabs(dFlow+d2s[i2+1]):std::numeric_limits<float>::max();
                f3 = (i3<(binsDim3-1))?fabs(dFlow+d3s[i3+1]):std::numeric_limits<float>::max();

                if(f1<f2 && f1<f3)
                {
                    pBV = &(m_3dEdgesUp[i1][i2][i3]); // up
                    if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]);       // right
                    if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep
                    D[i1+1][i2][i3]     += dFlow; // maintain auxilary matrix
                    d1s[i1+1] += dFlow;
                }
                else if(f2<f3)
                {
                    pBV = &(m_3dEdgesRight[i1][i2][i3]); // right
                    if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up
                    if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep
                    D[i1][i2+1][i3]     += dFlow; // maintain auxilary matrix
                    d2s[i2+1] += dFlow;
                }
                else
                {
                    pBV = &(m_3dEdgesDeep[i1][i2][i3]); // deep
                    if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]);       // right
                    if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up
                    D[i1][i2][i3+1]     += dFlow; // maintain auxilary matrix
                    d3s[i3+1] += dFlow;
                }

                pBV->flow = fabs(dFlow);
                pBV->iDir = dFlow>0; // 1:outward, 0:inward
                pBV->pParent->pChild= pBV;
            }
        }
    }
    return true;
}

void EmdL1::initBVTree()
{
    // initialize BVTree from the initial BF solution
    //- Using the center of the graph as the root
    int r = (int)(0.5*binsDim1-.5);
    int c = (int)(0.5*binsDim2-.5);
    int z = (int)(0.5*binsDim3-.5);
    m_pRoot     = dimension==2 ? &(m_Nodes[r][c]) : &(m_3dNodes[r][c][z]);
    m_pRoot->u = 0;
    m_pRoot->iLevel     = 0;
    m_pRoot->pParent= NULL;
    m_pRoot->pPEdge     = NULL;

    //- Prepare a queue
    m_auxQueue[0] = m_pRoot;
    int nQueue = 1; // length of queue
    int iQHead = 0; // head of queue

    //- Recursively build subtrees
    cvPEmdEdge pCurE=NULL, pNxtE=NULL;
    cvPEmdNode pCurN=NULL, pNxtN=NULL;
    int nBin = binsDim1*binsDim2*std::max(binsDim3,1);
    while(iQHead<nQueue && nQueue<nBin)
    {
        pCurN = m_auxQueue[iQHead++];   // pop out from queue
        r = pCurN->pos[0];
        c = pCurN->pos[1];
        z = pCurN->pos[2];

        // check connection from itself
        pCurE = pCurN->pChild;  // the initial child from initial solution
        if(pCurE)
        {
            pNxtN = pCurE->pChild;
            pNxtN->pParent = pCurN;
            pNxtN->pPEdge = pCurE;
            m_auxQueue[nQueue++] = pNxtN;
        }

        // check four neighbor nodes
        int     nNB     = dimension==2?4:6;
        for(int k=0;k<nNB;k++)
        {
            if(dimension==2)
            {
                if(k==0 && c>0) pNxtN = &(m_Nodes[r][c-1]);             // left
                else if(k==1 && r>0) pNxtN      = &(m_Nodes[r-1][c]);           // down
                else if(k==2 && c<binsDim2-1) pNxtN     = &(m_Nodes[r][c+1]);           // right
                else if(k==3 && r<binsDim1-1) pNxtN     = &(m_Nodes[r+1][c]);           // up
                else continue;
            }
            else if(dimension==3)
            {
                if(k==0 && c>0) pNxtN = &(m_3dNodes[r][c-1][z]); // left
                else if(k==1 && c<binsDim2-1) pNxtN     = &(m_3dNodes[r][c+1][z]); // right
                else if(k==2 && r>0) pNxtN      = &(m_3dNodes[r-1][c][z]); // down
                else if(k==3 && r<binsDim1-1) pNxtN     = &(m_3dNodes[r+1][c][z]); // up
                else if(k==4 && z>0) pNxtN = &(m_3dNodes[r][c][z-1]); // shallow
                else if(k==5 && z<binsDim3-1) pNxtN     = &(m_3dNodes[r][c][z+1]); // deep
                else continue;
            }
            if(pNxtN != pCurN->pParent)
            {
                pNxtE = pNxtN->pChild;
                if(pNxtE && pNxtE->pChild==pCurN) // has connection
                {
                    pNxtN->pParent = pCurN;
                    pNxtN->pPEdge = pNxtE;
                    pNxtN->pChild = NULL;
                    m_auxQueue[nQueue++] = pNxtN;

                    pNxtE->pParent = pCurN; // reverse direction
                    pNxtE->pChild = pNxtN;
                    pNxtE->iDir = !pNxtE->iDir;

                    if(pCurE) pCurE->pNxt = pNxtE;      // add to edge list
                    else pCurN->pChild = pNxtE;
                    pCurE = pNxtE;
                }
            }
        }
    }
}

void EmdL1::updateSubtree(cvPEmdNode pRoot)
{
    // Initialize auxiliary queue
    m_auxQueue[0] = pRoot;
    int nQueue = 1; // queue length
    int iQHead = 0; // head of queue

    // BFS browing
    cvPEmdNode pCurN=NULL,pNxtN=NULL;
    cvPEmdEdge pCurE=NULL;
    while(iQHead<nQueue)
    {
        pCurN = m_auxQueue[iQHead++];   // pop out from queue
        pCurE = pCurN->pChild;

        // browsing all children
        while(pCurE)
        {
            pNxtN = pCurE->pChild;
            pNxtN->iLevel = pCurN->iLevel+1;
            pNxtN->u = pCurE->iDir ? (pCurN->u - 1) : (pCurN->u + 1);
            pCurE = pCurE->pNxt;
            m_auxQueue[nQueue++] = pNxtN;
        }
    }
}

bool EmdL1::isOptimal()
{
    int iC, iMinC = 0;
    cvPEmdEdge pE;
    m_pEnter = NULL;
    m_iEnter = -1;

    // test each NON-BV edges
    for(int k=0; k<nNBV; ++k)
    {
        pE = m_NBVEdges[k];
        iC = 1 - pE->pParent->u + pE->pChild->u;
        if(iC<iMinC)
        {
            iMinC = iC;
            m_iEnter= k;
        }
        else
        {
            // Try reversing the direction
            iC  = 1 + pE->pParent->u - pE->pChild->u;
            if(iC<iMinC)
            {
                iMinC = iC;
                m_iEnter= k;
            }
        }
    }

    if(m_iEnter>=0)
    {
        m_pEnter = m_NBVEdges[m_iEnter];
        if(iMinC == (1 - m_pEnter->pChild->u + m_pEnter->pParent->u))   {
            // reverse direction
            cvPEmdNode pN = m_pEnter->pParent;
            m_pEnter->pParent = m_pEnter->pChild;
            m_pEnter->pChild = pN;
        }

        m_pEnter->iDir = 1;
    }
    return m_iEnter==-1;
}

void EmdL1::findNewSolution()
{
    // Find loop formed by adding the Enter BV edge.
    findLoopFromEnterBV();
    // Modify flow values along the loop
    cvPEmdEdge pE = NULL;
    float       minFlow = m_pLeave->flow;
    int k;
    for(k=0; k<m_iFrom; k++)
    {
        pE = m_fromLoop[k];
        if(pE->iDir) pE->flow += minFlow; // outward
        else pE->flow -= minFlow; // inward
    }
    for(k=0; k<m_iTo; k++)
    {
        pE = m_toLoop[k];
        if(pE->iDir) pE->flow -= minFlow; // outward
        else pE->flow += minFlow; // inward
    }

    // Update BV Tree, removing the Leaving-BV edge
    cvPEmdNode pLParentN = m_pLeave->pParent;
    cvPEmdNode pLChildN = m_pLeave->pChild;
    cvPEmdEdge pPreE = pLParentN->pChild;
    if(pPreE==m_pLeave)
    {
        pLParentN->pChild = m_pLeave->pNxt; // Leaving-BV is the first child
    }
    else
    {
        while(pPreE->pNxt != m_pLeave)
            pPreE       = pPreE->pNxt;
        pPreE->pNxt     = m_pLeave->pNxt; // remove Leaving-BV from child list
    }
    pLChildN->pParent = NULL;
    pLChildN->pPEdge = NULL;

    m_NBVEdges[m_iEnter]= m_pLeave; // put the leaving-BV into the NBV array

    // Add the Enter BV edge
    cvPEmdNode pEParentN = m_pEnter->pParent;
    cvPEmdNode pEChildN = m_pEnter->pChild;
    m_pEnter->flow = minFlow;
    m_pEnter->pNxt = pEParentN->pChild;         // insert the Enter BV as the first child
    pEParentN->pChild = m_pEnter;                                       //              of its parent

    // Recursively update the tree start from pEChildN
    cvPEmdNode pPreN = pEParentN;
    cvPEmdNode pCurN = pEChildN;
    cvPEmdNode pNxtN;
    cvPEmdEdge pNxtE, pPreE0;
    pPreE = m_pEnter;
    while(pCurN)
    {
        pNxtN = pCurN->pParent;
        pNxtE = pCurN->pPEdge;
        pCurN->pParent = pPreN;
        pCurN->pPEdge = pPreE;
        if(pNxtN)
        {
            // remove the edge from pNxtN's child list
            if(pNxtN->pChild==pNxtE)
            {
                pNxtN->pChild   = pNxtE->pNxt;                  // first child
            }
            else
            {
                pPreE0  = pNxtN->pChild;
                while(pPreE0->pNxt != pNxtE)
                    pPreE0      = pPreE0->pNxt;
                pPreE0->pNxt    = pNxtE->pNxt;                  // remove Leaving-BV from child list
            }
            // reverse the parent-child direction
            pNxtE->pParent = pCurN;
            pNxtE->pChild = pNxtN;
            pNxtE->iDir = !pNxtE->iDir;
            pNxtE->pNxt = pCurN->pChild;
            pCurN->pChild = pNxtE;
            pPreE = pNxtE;
            pPreN = pCurN;
        }
        pCurN = pNxtN;
    }

    // Update U at the child of the Enter BV
    pEChildN->u = m_pEnter->iDir?(pEParentN->u-1):(pEParentN->u + 1);
    pEChildN->iLevel = pEParentN->iLevel+1;
}

void EmdL1::findLoopFromEnterBV()
{
    // Initialize Leaving-BV edge
    float minFlow       = std::numeric_limits<float>::max();
    cvPEmdEdge pE = NULL;
    int iLFlag = 0;     // 0: in the FROM list, 1: in the TO list

    // Using two loop list to store the loop nodes
    cvPEmdNode pFrom = m_pEnter->pParent;
    cvPEmdNode pTo = m_pEnter->pChild;
    m_iFrom     = 0;
    m_iTo = 0;
    m_pLeave = NULL;

    // Trace back to make pFrom and pTo at the same level
    while(pFrom->iLevel > pTo->iLevel)
    {
        pE = pFrom->pPEdge;
        m_fromLoop[m_iFrom++] = pE;
        if(!pE->iDir && pE->flow<minFlow)
        {
            minFlow = pE->flow;
            m_pLeave = pE;
            iLFlag = 0; // 0: in the FROM list
        }
        pFrom = pFrom->pParent;
    }

    while(pTo->iLevel > pFrom->iLevel)
    {
        pE = pTo->pPEdge;
        m_toLoop[m_iTo++] = pE;
        if(pE->iDir && pE->flow<minFlow)
        {
            minFlow = pE->flow;
            m_pLeave = pE;
            iLFlag = 1; // 1: in the TO list
        }
        pTo     = pTo->pParent;
    }

    // Trace pTo and pFrom simultaneously till find their common ancester
    while(pTo!=pFrom)
    {
        pE = pFrom->pPEdge;
        m_fromLoop[m_iFrom++] = pE;
        if(!pE->iDir && pE->flow<minFlow)
        {
            minFlow = pE->flow;
            m_pLeave = pE;
            iLFlag = 0; // 0: in the FROM list, 1: in the TO list
        }
        pFrom = pFrom->pParent;

        pE = pTo->pPEdge;
        m_toLoop[m_iTo++] = pE;
        if(pE->iDir && pE->flow<minFlow)
        {
            minFlow = pE->flow;
            m_pLeave = pE;
            iLFlag = 1; // 0: in the FROM list, 1: in the TO list
        }
        pTo     = pTo->pParent;
    }

    // Reverse the direction of the Enter BV edge if necessary
    if(iLFlag==0)
    {
        cvPEmdNode pN = m_pEnter->pParent;
        m_pEnter->pParent = m_pEnter->pChild;
        m_pEnter->pChild = pN;
        m_pEnter->iDir = !m_pEnter->iDir;
    }
}

float EmdL1::compuTotalFlow()
{
    // Computing the total flow as the final distance
    float f = 0;

    // Initialize auxiliary queue
    m_auxQueue[0] = m_pRoot;
    int nQueue = 1; // length of queue
    int iQHead = 0; // head of queue

    // BFS browing the tree
    cvPEmdNode pCurN=NULL,pNxtN=NULL;
    cvPEmdEdge pCurE=NULL;
    while(iQHead<nQueue)
    {
        pCurN = m_auxQueue[iQHead++];   // pop out from queue
        pCurE = pCurN->pChild;

        // browsing all children
        while(pCurE)
        {
            f += pCurE->flow;
            pNxtN = pCurE->pChild;
            pCurE = pCurE->pNxt;
            m_auxQueue[nQueue++] = pNxtN;
        }
    }
    return f;
}

/****************************************************************************************\
*                                   EMDL1 Function                                      *
\****************************************************************************************/

float cv::EMDL1(InputArray _signature1, InputArray _signature2)
{
    Mat signature1 = _signature1.getMat(), signature2 = _signature2.getMat();
    EmdL1 emdl1;
    return emdl1.getEMDL1(signature1, signature2);
}

/* [<][>][^][v][top][bottom][index][help] */