Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/shape/src/emdL1.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
/*
44
* Implementation of an optimized EMD for histograms based in
45
* the papers "EMD-L1: An efficient and Robust Algorithm
46
* for comparing histogram-based descriptors", by Haibin Ling and
47
* Kazunori Okuda; and "The Earth Mover's Distance is the Mallows
48
* Distance: Some Insights from Statistics", by Elizaveta Levina and
49
* Peter Bickel, based on HAIBIN LING AND KAZUNORI OKADA implementation.
50
*/
51
52
#include "precomp.hpp"
53
#include "emdL1_def.hpp"
54
#include <limits>
55
56
/****************************************************************************************\
57
* EMDL1 Class *
58
\****************************************************************************************/
59
60
float EmdL1::getEMDL1(cv::Mat &sig1, cv::Mat &sig2)
61
{
62
// Initialization
63
CV_Assert((sig1.rows==sig2.rows) && (sig1.cols==sig2.cols) && (!sig1.empty()) && (!sig2.empty()));
64
if(!initBaseTrees(sig1.rows, 1))
65
return -1;
66
67
float *H1=new float[sig1.rows], *H2 = new float[sig2.rows];
68
for (int ii=0; ii<sig1.rows; ii++)
69
{
70
H1[ii]=sig1.at<float>(ii,0);
71
H2[ii]=sig2.at<float>(ii,0);
72
}
73
74
fillBaseTrees(H1,H2); // Initialize histograms
75
greedySolution(); // Construct an initial Basic Feasible solution
76
initBVTree(); // Initialize BVTree
77
78
// Iteration
79
bool bOptimal = false;
80
m_nItr = 0;
81
while(!bOptimal && m_nItr<nMaxIt)
82
{
83
// Derive U=(u_ij) for row i and column j
84
if(m_nItr==0) updateSubtree(m_pRoot);
85
else updateSubtree(m_pEnter->pChild);
86
87
// Optimality test
88
bOptimal = isOptimal();
89
90
// Find new solution
91
if(!bOptimal)
92
findNewSolution();
93
++m_nItr;
94
}
95
delete [] H1;
96
delete [] H2;
97
// Output the total flow
98
return compuTotalFlow();
99
}
100
101
void EmdL1::setMaxIteration(int _nMaxIt)
102
{
103
nMaxIt=_nMaxIt;
104
}
105
106
//-- SubFunctions called in the EMD algorithm
107
bool EmdL1::initBaseTrees(int n1, int n2, int n3)
108
{
109
if(binsDim1==n1 && binsDim2==n2 && binsDim3==n3)
110
return true;
111
binsDim1 = n1;
112
binsDim2 = n2;
113
binsDim3 = n3;
114
if(binsDim1==0 || binsDim2==0) dimension = 0;
115
else dimension = (binsDim3==0)?2:3;
116
117
if(dimension==2)
118
{
119
m_Nodes.resize(binsDim1);
120
m_EdgesUp.resize(binsDim1);
121
m_EdgesRight.resize(binsDim1);
122
for(int i1=0; i1<binsDim1; i1++)
123
{
124
m_Nodes[i1].resize(binsDim2);
125
m_EdgesUp[i1].resize(binsDim2);
126
m_EdgesRight[i1].resize(binsDim2);
127
}
128
m_NBVEdges.resize(binsDim1*binsDim2*4+2);
129
m_auxQueue.resize(binsDim1*binsDim2+2);
130
m_fromLoop.resize(binsDim1*binsDim2+2);
131
m_toLoop.resize(binsDim1*binsDim2+2);
132
}
133
else if(dimension==3)
134
{
135
m_3dNodes.resize(binsDim1);
136
m_3dEdgesUp.resize(binsDim1);
137
m_3dEdgesRight.resize(binsDim1);
138
m_3dEdgesDeep.resize(binsDim1);
139
for(int i1=0; i1<binsDim1; i1++)
140
{
141
m_3dNodes[i1].resize(binsDim2);
142
m_3dEdgesUp[i1].resize(binsDim2);
143
m_3dEdgesRight[i1].resize(binsDim2);
144
m_3dEdgesDeep[i1].resize(binsDim2);
145
for(int i2=0; i2<binsDim2; i2++)
146
{
147
m_3dNodes[i1][i2].resize(binsDim3);
148
m_3dEdgesUp[i1][i2].resize(binsDim3);
149
m_3dEdgesRight[i1][i2].resize(binsDim3);
150
m_3dEdgesDeep[i1][i2].resize(binsDim3);
151
}
152
}
153
m_NBVEdges.resize(binsDim1*binsDim2*binsDim3*6+4);
154
m_auxQueue.resize(binsDim1*binsDim2*binsDim3+4);
155
m_fromLoop.resize(binsDim1*binsDim2*binsDim3+4);
156
m_toLoop.resize(binsDim1*binsDim2*binsDim3+2);
157
}
158
else
159
return false;
160
161
return true;
162
}
163
164
bool EmdL1::fillBaseTrees(float *H1, float *H2)
165
{
166
//- Set global counters
167
m_pRoot = NULL;
168
// Graph initialization
169
float *p1 = H1;
170
float *p2 = H2;
171
if(dimension==2)
172
{
173
for(int c=0; c<binsDim2; c++)
174
{
175
for(int r=0; r<binsDim1; r++)
176
{
177
//- initialize nodes and links
178
m_Nodes[r][c].pos[0] = r;
179
m_Nodes[r][c].pos[1] = c;
180
m_Nodes[r][c].d = *(p1++)-*(p2++);
181
m_Nodes[r][c].pParent = NULL;
182
m_Nodes[r][c].pChild = NULL;
183
m_Nodes[r][c].iLevel = -1;
184
185
//- initialize edges
186
// to the right
187
m_EdgesRight[r][c].pParent = &(m_Nodes[r][c]);
188
m_EdgesRight[r][c].pChild = &(m_Nodes[r][(c+1)%binsDim2]);
189
m_EdgesRight[r][c].flow = 0;
190
m_EdgesRight[r][c].iDir = 1;
191
m_EdgesRight[r][c].pNxt = NULL;
192
193
// to the upward
194
m_EdgesUp[r][c].pParent = &(m_Nodes[r][c]);
195
m_EdgesUp[r][c].pChild = &(m_Nodes[(r+1)%binsDim1][c]);
196
m_EdgesUp[r][c].flow = 0;
197
m_EdgesUp[r][c].iDir = 1;
198
m_EdgesUp[r][c].pNxt = NULL;
199
}
200
}
201
}
202
else if(dimension==3)
203
{
204
for(int z=0; z<binsDim3; z++)
205
{
206
for(int c=0; c<binsDim2; c++)
207
{
208
for(int r=0; r<binsDim1; r++)
209
{
210
//- initialize nodes and edges
211
m_3dNodes[r][c][z].pos[0] = r;
212
m_3dNodes[r][c][z].pos[1] = c;
213
m_3dNodes[r][c][z].pos[2] = z;
214
m_3dNodes[r][c][z].d = *(p1++)-*(p2++);
215
m_3dNodes[r][c][z].pParent = NULL;
216
m_3dNodes[r][c][z].pChild = NULL;
217
m_3dNodes[r][c][z].iLevel = -1;
218
219
//- initialize edges
220
// to the upward
221
m_3dEdgesUp[r][c][z].pParent= &(m_3dNodes[r][c][z]);
222
m_3dEdgesUp[r][c][z].pChild = &(m_3dNodes[(r+1)%binsDim1][c][z]);
223
m_3dEdgesUp[r][c][z].flow = 0;
224
m_3dEdgesUp[r][c][z].iDir = 1;
225
m_3dEdgesUp[r][c][z].pNxt = NULL;
226
227
// to the right
228
m_3dEdgesRight[r][c][z].pParent = &(m_3dNodes[r][c][z]);
229
m_3dEdgesRight[r][c][z].pChild = &(m_3dNodes[r][(c+1)%binsDim2][z]);
230
m_3dEdgesRight[r][c][z].flow = 0;
231
m_3dEdgesRight[r][c][z].iDir = 1;
232
m_3dEdgesRight[r][c][z].pNxt = NULL;
233
234
// to the deep
235
m_3dEdgesDeep[r][c][z].pParent = &(m_3dNodes[r][c][z]);
236
m_3dEdgesDeep[r][c][z].pChild = &(m_3dNodes[r][c])[(z+1)%binsDim3];
237
m_3dEdgesDeep[r][c][z].flow = 0;
238
m_3dEdgesDeep[r][c][z].iDir = 1;
239
m_3dEdgesDeep[r][c][z].pNxt = NULL;
240
}
241
}
242
}
243
}
244
return true;
245
}
246
247
bool EmdL1::greedySolution()
248
{
249
return dimension==2?greedySolution2():greedySolution3();
250
}
251
252
bool EmdL1::greedySolution2()
253
{
254
//- Prepare auxiliary array, D=H1-H2
255
int c,r;
256
floatArray2D D(binsDim1);
257
for(r=0; r<binsDim1; r++)
258
{
259
D[r].resize(binsDim2);
260
for(c=0; c<binsDim2; c++) D[r][c] = m_Nodes[r][c].d;
261
}
262
// compute integrated values along each dimension
263
std::vector<float> d2s(binsDim2);
264
d2s[0] = 0;
265
for(c=0; c<binsDim2-1; c++)
266
{
267
d2s[c+1] = d2s[c];
268
for(r=0; r<binsDim1; r++) d2s[c+1]-= D[r][c];
269
}
270
271
std::vector<float> d1s(binsDim1);
272
d1s[0] = 0;
273
for(r=0; r<binsDim1-1; r++)
274
{
275
d1s[r+1] = d1s[r];
276
for(c=0; c<binsDim2; c++) d1s[r+1]-= D[r][c];
277
}
278
279
//- Greedy algorithm for initial solution
280
cvPEmdEdge pBV;
281
float dFlow;
282
bool bUpward = false;
283
nNBV = 0; // number of NON-BV edges
284
285
for(c=0; c<binsDim2-1; c++)
286
for(r=0; r<binsDim1; r++)
287
{
288
dFlow = D[r][c];
289
bUpward = (r<binsDim1-1) && (fabs(dFlow+d2s[c+1]) > fabs(dFlow+d1s[r+1])); // Move upward or right
290
291
// modify basic variables, record BV and related values
292
if(bUpward)
293
{
294
// move to up
295
pBV = &(m_EdgesUp[r][c]);
296
m_NBVEdges[nNBV++] = &(m_EdgesRight[r][c]);
297
D[r+1][c] += dFlow; // auxiliary matrix maintenance
298
d1s[r+1] += dFlow; // auxiliary matrix maintenance
299
}
300
else
301
{
302
// move to right, no other choice
303
pBV = &(m_EdgesRight[r][c]);
304
if(r<binsDim1-1)
305
m_NBVEdges[nNBV++] = &(m_EdgesUp[r][c]);
306
307
D[r][c+1] += dFlow; // auxiliary matrix maintenance
308
d2s[c+1] += dFlow; // auxiliary matrix maintenance
309
}
310
pBV->pParent->pChild = pBV;
311
pBV->flow = fabs(dFlow);
312
pBV->iDir = dFlow>0; // 1:outward, 0:inward
313
}
314
315
//- rightmost column, no choice but move upward
316
c = binsDim2-1;
317
for(r=0; r<binsDim1-1; r++)
318
{
319
dFlow = D[r][c];
320
pBV = &(m_EdgesUp[r][c]);
321
D[r+1][c] += dFlow; // auxiliary matrix maintenance
322
pBV->pParent->pChild= pBV;
323
pBV->flow = fabs(dFlow);
324
pBV->iDir = dFlow>0; // 1:outward, 0:inward
325
}
326
return true;
327
}
328
329
bool EmdL1::greedySolution3()
330
{
331
//- Prepare auxiliary array, D=H1-H2
332
int i1,i2,i3;
333
std::vector<floatArray2D> D(binsDim1);
334
for(i1=0; i1<binsDim1; i1++)
335
{
336
D[i1].resize(binsDim2);
337
for(i2=0; i2<binsDim2; i2++)
338
{
339
D[i1][i2].resize(binsDim3);
340
for(i3=0; i3<binsDim3; i3++)
341
D[i1][i2][i3] = m_3dNodes[i1][i2][i3].d;
342
}
343
}
344
345
// compute integrated values along each dimension
346
std::vector<float> d1s(binsDim1);
347
d1s[0] = 0;
348
for(i1=0; i1<binsDim1-1; i1++)
349
{
350
d1s[i1+1] = d1s[i1];
351
for(i2=0; i2<binsDim2; i2++)
352
{
353
for(i3=0; i3<binsDim3; i3++)
354
d1s[i1+1] -= D[i1][i2][i3];
355
}
356
}
357
358
std::vector<float> d2s(binsDim2);
359
d2s[0] = 0;
360
for(i2=0; i2<binsDim2-1; i2++)
361
{
362
d2s[i2+1] = d2s[i2];
363
for(i1=0; i1<binsDim1; i1++)
364
{
365
for(i3=0; i3<binsDim3; i3++)
366
d2s[i2+1] -= D[i1][i2][i3];
367
}
368
}
369
370
std::vector<float> d3s(binsDim3);
371
d3s[0] = 0;
372
for(i3=0; i3<binsDim3-1; i3++)
373
{
374
d3s[i3+1] = d3s[i3];
375
for(i1=0; i1<binsDim1; i1++)
376
{
377
for(i2=0; i2<binsDim2; i2++)
378
d3s[i3+1] -= D[i1][i2][i3];
379
}
380
}
381
382
//- Greedy algorithm for initial solution
383
cvPEmdEdge pBV;
384
float dFlow, f1,f2,f3;
385
nNBV = 0; // number of NON-BV edges
386
for(i3=0; i3<binsDim3; i3++)
387
{
388
for(i2=0; i2<binsDim2; i2++)
389
{
390
for(i1=0; i1<binsDim1; i1++)
391
{
392
if(i3==binsDim3-1 && i2==binsDim2-1 && i1==binsDim1-1) break;
393
394
//- determine which direction to move, either right or upward
395
dFlow = D[i1][i2][i3];
396
f1 = (i1<(binsDim1-1))?fabs(dFlow+d1s[i1+1]):std::numeric_limits<float>::max();
397
f2 = (i2<(binsDim2-1))?fabs(dFlow+d2s[i2+1]):std::numeric_limits<float>::max();
398
f3 = (i3<(binsDim3-1))?fabs(dFlow+d3s[i3+1]):std::numeric_limits<float>::max();
399
400
if(f1<f2 && f1<f3)
401
{
402
pBV = &(m_3dEdgesUp[i1][i2][i3]); // up
403
if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]); // right
404
if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep
405
D[i1+1][i2][i3] += dFlow; // maintain auxiliary matrix
406
d1s[i1+1] += dFlow;
407
}
408
else if(f2<f3)
409
{
410
pBV = &(m_3dEdgesRight[i1][i2][i3]); // right
411
if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up
412
if(i3<binsDim3-1) m_NBVEdges[nNBV++] = &(m_3dEdgesDeep[i1][i2][i3]); // deep
413
D[i1][i2+1][i3] += dFlow; // maintain auxiliary matrix
414
d2s[i2+1] += dFlow;
415
}
416
else
417
{
418
pBV = &(m_3dEdgesDeep[i1][i2][i3]); // deep
419
if(i2<binsDim2-1) m_NBVEdges[nNBV++] = &(m_3dEdgesRight[i1][i2][i3]); // right
420
if(i1<binsDim1-1) m_NBVEdges[nNBV++] = &(m_3dEdgesUp[i1][i2][i3]); // up
421
D[i1][i2][i3+1] += dFlow; // maintain auxiliary matrix
422
d3s[i3+1] += dFlow;
423
}
424
425
pBV->flow = fabs(dFlow);
426
pBV->iDir = dFlow>0; // 1:outward, 0:inward
427
pBV->pParent->pChild= pBV;
428
}
429
}
430
}
431
return true;
432
}
433
434
void EmdL1::initBVTree()
435
{
436
// initialize BVTree from the initial BF solution
437
//- Using the center of the graph as the root
438
int r = (int)(0.5*binsDim1-.5);
439
int c = (int)(0.5*binsDim2-.5);
440
int z = (int)(0.5*binsDim3-.5);
441
m_pRoot = dimension==2 ? &(m_Nodes[r][c]) : &(m_3dNodes[r][c][z]);
442
m_pRoot->u = 0;
443
m_pRoot->iLevel = 0;
444
m_pRoot->pParent= NULL;
445
m_pRoot->pPEdge = NULL;
446
447
//- Prepare a queue
448
m_auxQueue[0] = m_pRoot;
449
int nQueue = 1; // length of queue
450
int iQHead = 0; // head of queue
451
452
//- Recursively build subtrees
453
cvPEmdEdge pCurE=NULL, pNxtE=NULL;
454
cvPEmdNode pCurN=NULL, pNxtN=NULL;
455
int nBin = binsDim1*binsDim2*std::max(binsDim3,1);
456
while(iQHead<nQueue && nQueue<nBin)
457
{
458
pCurN = m_auxQueue[iQHead++]; // pop out from queue
459
r = pCurN->pos[0];
460
c = pCurN->pos[1];
461
z = pCurN->pos[2];
462
463
// check connection from itself
464
pCurE = pCurN->pChild; // the initial child from initial solution
465
if(pCurE)
466
{
467
pNxtN = pCurE->pChild;
468
pNxtN->pParent = pCurN;
469
pNxtN->pPEdge = pCurE;
470
m_auxQueue[nQueue++] = pNxtN;
471
}
472
473
// check four neighbor nodes
474
int nNB = dimension==2?4:6;
475
for(int k=0;k<nNB;k++)
476
{
477
if(dimension==2)
478
{
479
if(k==0 && c>0) pNxtN = &(m_Nodes[r][c-1]); // left
480
else if(k==1 && r>0) pNxtN = &(m_Nodes[r-1][c]); // down
481
else if(k==2 && c<binsDim2-1) pNxtN = &(m_Nodes[r][c+1]); // right
482
else if(k==3 && r<binsDim1-1) pNxtN = &(m_Nodes[r+1][c]); // up
483
else continue;
484
}
485
else if(dimension==3)
486
{
487
if(k==0 && c>0) pNxtN = &(m_3dNodes[r][c-1][z]); // left
488
else if(k==1 && c<binsDim2-1) pNxtN = &(m_3dNodes[r][c+1][z]); // right
489
else if(k==2 && r>0) pNxtN = &(m_3dNodes[r-1][c][z]); // down
490
else if(k==3 && r<binsDim1-1) pNxtN = &(m_3dNodes[r+1][c][z]); // up
491
else if(k==4 && z>0) pNxtN = &(m_3dNodes[r][c][z-1]); // shallow
492
else if(k==5 && z<binsDim3-1) pNxtN = &(m_3dNodes[r][c][z+1]); // deep
493
else continue;
494
}
495
if(pNxtN != pCurN->pParent)
496
{
497
CV_Assert(pNxtN != NULL);
498
pNxtE = pNxtN->pChild;
499
if(pNxtE && pNxtE->pChild==pCurN) // has connection
500
{
501
pNxtN->pParent = pCurN;
502
pNxtN->pPEdge = pNxtE;
503
pNxtN->pChild = NULL;
504
m_auxQueue[nQueue++] = pNxtN;
505
506
pNxtE->pParent = pCurN; // reverse direction
507
pNxtE->pChild = pNxtN;
508
pNxtE->iDir = !pNxtE->iDir;
509
510
if(pCurE) pCurE->pNxt = pNxtE; // add to edge list
511
else pCurN->pChild = pNxtE;
512
pCurE = pNxtE;
513
}
514
}
515
}
516
}
517
}
518
519
void EmdL1::updateSubtree(cvPEmdNode pRoot)
520
{
521
// Initialize auxiliary queue
522
m_auxQueue[0] = pRoot;
523
int nQueue = 1; // queue length
524
int iQHead = 0; // head of queue
525
526
// BFS browing
527
cvPEmdNode pCurN=NULL,pNxtN=NULL;
528
cvPEmdEdge pCurE=NULL;
529
while(iQHead<nQueue)
530
{
531
pCurN = m_auxQueue[iQHead++]; // pop out from queue
532
pCurE = pCurN->pChild;
533
534
// browsing all children
535
while(pCurE)
536
{
537
pNxtN = pCurE->pChild;
538
pNxtN->iLevel = pCurN->iLevel+1;
539
pNxtN->u = pCurE->iDir ? (pCurN->u - 1) : (pCurN->u + 1);
540
pCurE = pCurE->pNxt;
541
m_auxQueue[nQueue++] = pNxtN;
542
}
543
}
544
}
545
546
bool EmdL1::isOptimal()
547
{
548
int iC, iMinC = 0;
549
cvPEmdEdge pE;
550
m_pEnter = NULL;
551
m_iEnter = -1;
552
553
// test each NON-BV edges
554
for(int k=0; k<nNBV; ++k)
555
{
556
pE = m_NBVEdges[k];
557
iC = 1 - pE->pParent->u + pE->pChild->u;
558
if(iC<iMinC)
559
{
560
iMinC = iC;
561
m_iEnter= k;
562
}
563
else
564
{
565
// Try reversing the direction
566
iC = 1 + pE->pParent->u - pE->pChild->u;
567
if(iC<iMinC)
568
{
569
iMinC = iC;
570
m_iEnter= k;
571
}
572
}
573
}
574
575
if(m_iEnter>=0)
576
{
577
m_pEnter = m_NBVEdges[m_iEnter];
578
if(iMinC == (1 - m_pEnter->pChild->u + m_pEnter->pParent->u)) {
579
// reverse direction
580
cvPEmdNode pN = m_pEnter->pParent;
581
m_pEnter->pParent = m_pEnter->pChild;
582
m_pEnter->pChild = pN;
583
}
584
585
m_pEnter->iDir = 1;
586
}
587
return m_iEnter==-1;
588
}
589
590
void EmdL1::findNewSolution()
591
{
592
// Find loop formed by adding the Enter BV edge.
593
findLoopFromEnterBV();
594
// Modify flow values along the loop
595
cvPEmdEdge pE = NULL;
596
CV_Assert(m_pLeave != NULL);
597
float minFlow = m_pLeave->flow;
598
int k;
599
for(k=0; k<m_iFrom; k++)
600
{
601
pE = m_fromLoop[k];
602
if(pE->iDir) pE->flow += minFlow; // outward
603
else pE->flow -= minFlow; // inward
604
}
605
for(k=0; k<m_iTo; k++)
606
{
607
pE = m_toLoop[k];
608
if(pE->iDir) pE->flow -= minFlow; // outward
609
else pE->flow += minFlow; // inward
610
}
611
612
// Update BV Tree, removing the Leaving-BV edge
613
cvPEmdNode pLParentN = m_pLeave->pParent;
614
cvPEmdNode pLChildN = m_pLeave->pChild;
615
cvPEmdEdge pPreE = pLParentN->pChild;
616
if(pPreE==m_pLeave)
617
{
618
pLParentN->pChild = m_pLeave->pNxt; // Leaving-BV is the first child
619
}
620
else
621
{
622
while(pPreE->pNxt != m_pLeave)
623
pPreE = pPreE->pNxt;
624
pPreE->pNxt = m_pLeave->pNxt; // remove Leaving-BV from child list
625
}
626
pLChildN->pParent = NULL;
627
pLChildN->pPEdge = NULL;
628
629
m_NBVEdges[m_iEnter]= m_pLeave; // put the leaving-BV into the NBV array
630
631
// Add the Enter BV edge
632
cvPEmdNode pEParentN = m_pEnter->pParent;
633
cvPEmdNode pEChildN = m_pEnter->pChild;
634
m_pEnter->flow = minFlow;
635
m_pEnter->pNxt = pEParentN->pChild; // insert the Enter BV as the first child
636
pEParentN->pChild = m_pEnter; // of its parent
637
638
// Recursively update the tree start from pEChildN
639
cvPEmdNode pPreN = pEParentN;
640
cvPEmdNode pCurN = pEChildN;
641
cvPEmdNode pNxtN;
642
cvPEmdEdge pNxtE, pPreE0;
643
pPreE = m_pEnter;
644
while(pCurN)
645
{
646
pNxtN = pCurN->pParent;
647
pNxtE = pCurN->pPEdge;
648
pCurN->pParent = pPreN;
649
pCurN->pPEdge = pPreE;
650
if(pNxtN)
651
{
652
// remove the edge from pNxtN's child list
653
if(pNxtN->pChild==pNxtE)
654
{
655
pNxtN->pChild = pNxtE->pNxt; // first child
656
}
657
else
658
{
659
pPreE0 = pNxtN->pChild;
660
while(pPreE0->pNxt != pNxtE)
661
pPreE0 = pPreE0->pNxt;
662
pPreE0->pNxt = pNxtE->pNxt; // remove Leaving-BV from child list
663
}
664
// reverse the parent-child direction
665
pNxtE->pParent = pCurN;
666
pNxtE->pChild = pNxtN;
667
pNxtE->iDir = !pNxtE->iDir;
668
pNxtE->pNxt = pCurN->pChild;
669
pCurN->pChild = pNxtE;
670
pPreE = pNxtE;
671
pPreN = pCurN;
672
}
673
pCurN = pNxtN;
674
}
675
676
// Update U at the child of the Enter BV
677
pEChildN->u = m_pEnter->iDir?(pEParentN->u-1):(pEParentN->u + 1);
678
pEChildN->iLevel = pEParentN->iLevel+1;
679
}
680
681
void EmdL1::findLoopFromEnterBV()
682
{
683
// Initialize Leaving-BV edge
684
float minFlow = std::numeric_limits<float>::max();
685
cvPEmdEdge pE = NULL;
686
int iLFlag = 0; // 0: in the FROM list, 1: in the TO list
687
688
// Using two loop list to store the loop nodes
689
cvPEmdNode pFrom = m_pEnter->pParent;
690
cvPEmdNode pTo = m_pEnter->pChild;
691
m_iFrom = 0;
692
m_iTo = 0;
693
m_pLeave = NULL;
694
695
// Trace back to make pFrom and pTo at the same level
696
while(pFrom->iLevel > pTo->iLevel)
697
{
698
pE = pFrom->pPEdge;
699
m_fromLoop[m_iFrom++] = pE;
700
if(!pE->iDir && pE->flow<minFlow)
701
{
702
minFlow = pE->flow;
703
m_pLeave = pE;
704
iLFlag = 0; // 0: in the FROM list
705
}
706
pFrom = pFrom->pParent;
707
}
708
709
while(pTo->iLevel > pFrom->iLevel)
710
{
711
pE = pTo->pPEdge;
712
m_toLoop[m_iTo++] = pE;
713
if(pE->iDir && pE->flow<minFlow)
714
{
715
minFlow = pE->flow;
716
m_pLeave = pE;
717
iLFlag = 1; // 1: in the TO list
718
}
719
pTo = pTo->pParent;
720
}
721
722
// Trace pTo and pFrom simultaneously till find their common ancester
723
while(pTo!=pFrom)
724
{
725
pE = pFrom->pPEdge;
726
m_fromLoop[m_iFrom++] = pE;
727
if(!pE->iDir && pE->flow<minFlow)
728
{
729
minFlow = pE->flow;
730
m_pLeave = pE;
731
iLFlag = 0; // 0: in the FROM list, 1: in the TO list
732
}
733
pFrom = pFrom->pParent;
734
735
pE = pTo->pPEdge;
736
m_toLoop[m_iTo++] = pE;
737
if(pE->iDir && pE->flow<minFlow)
738
{
739
minFlow = pE->flow;
740
m_pLeave = pE;
741
iLFlag = 1; // 0: in the FROM list, 1: in the TO list
742
}
743
pTo = pTo->pParent;
744
}
745
746
// Reverse the direction of the Enter BV edge if necessary
747
if(iLFlag==0)
748
{
749
cvPEmdNode pN = m_pEnter->pParent;
750
m_pEnter->pParent = m_pEnter->pChild;
751
m_pEnter->pChild = pN;
752
m_pEnter->iDir = !m_pEnter->iDir;
753
}
754
}
755
756
float EmdL1::compuTotalFlow()
757
{
758
// Computing the total flow as the final distance
759
float f = 0;
760
761
// Initialize auxiliary queue
762
m_auxQueue[0] = m_pRoot;
763
int nQueue = 1; // length of queue
764
int iQHead = 0; // head of queue
765
766
// BFS browing the tree
767
cvPEmdNode pCurN=NULL,pNxtN=NULL;
768
cvPEmdEdge pCurE=NULL;
769
while(iQHead<nQueue)
770
{
771
pCurN = m_auxQueue[iQHead++]; // pop out from queue
772
pCurE = pCurN->pChild;
773
774
// browsing all children
775
while(pCurE)
776
{
777
f += pCurE->flow;
778
pNxtN = pCurE->pChild;
779
pCurE = pCurE->pNxt;
780
m_auxQueue[nQueue++] = pNxtN;
781
}
782
}
783
return f;
784
}
785
786
/****************************************************************************************\
787
* EMDL1 Function *
788
\****************************************************************************************/
789
790
float cv::EMDL1(InputArray _signature1, InputArray _signature2)
791
{
792
CV_INSTRUMENT_REGION();
793
794
Mat signature1 = _signature1.getMat(), signature2 = _signature2.getMat();
795
EmdL1 emdl1;
796
return emdl1.getEMDL1(signature1, signature2);
797
}
798
799