Shape module added
This commit is contained in:

committed by
Vadim Pisarevsky

parent
4b203f7b1a
commit
61c27ac81e
794
modules/shape/src/emdL1.cpp
Normal file
794
modules/shape/src/emdL1.cpp
Normal file
@@ -0,0 +1,794 @@
|
||||
/*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"
|
||||
|
||||
|
||||
/****************************************************************************************\
|
||||
* 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]):VHIGH;
|
||||
f2 = i2<(binsDim2-1)?fabs(dFlow+d2s[i2+1]):VHIGH;
|
||||
f3 = i3<(binsDim3-1)?fabs(dFlow+d3s[i3+1]):VHIGH;
|
||||
|
||||
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 = VHIGH;
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user