Open Source Computer Vision Library https://opencv.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

793 lines
25 KiB

/*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);
}