-
Notifications
You must be signed in to change notification settings - Fork 858
/
emd.js
161 lines (138 loc) · 4.74 KB
/
emd.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
var cv = require('../lib/opencv');
//
// Example of use of EMD distance using histograms
// 1. Build 2 histograms from images using calcHist
// 2. Transform each histogram to a 64 x 4 (hist, b, g, r) x 1 normalized signatures in BGR space
// 3. Compute the cost matrix (64 x 64 x 1), calculating the cost in LUV space
// 4. Run EMD algorithm
//
/// Useful flatten function for step 2
function flatten(array, accu) {
if(!accu){
accu = [];
}
array.forEach(function(a){
if(Array.isArray(a)) {
flatten(a, accu)
} else {
accu.push(a)
}
});
return accu
};
cv.readImage('./files/car1.jpg', function(err, im1) {
if (err) throw err;
if (im1.width() < 1 || im1.height() < 1) throw new Error('Image has no size');
cv.readImage('./files/car2.jpg', function(err, im2) {
if (err) throw err;
if (im2.width() < 1 || im2.height() < 1) throw new Error('Image has no size');
///////////////////
// 1. Build 2 histograms from images using calcHist
//////////////////
var size = [4, 4, 4],
channels = [0, 1, 2],
range = [[0, 256], [0, 256], [0, 256]],
uniform = true,
accumulate = true,
histFile = 'files/chart2.png';
/// Compute 64 (=4^3) histograms:
var firstImageHist64 = cv.histogram.calcHist(im1, channels, size, range, uniform);
var secondImageHist64 = cv.histogram.calcHist(im2, channels, size, range, uniform);
//////////////
// 2. Transform each histogram to a 64 x 4 (hist, b, g, r) x 1 normalized signatures in BGR space
////////////////
var step = 256/4;
var halfStep = Math.round(step/2);
var sum1 = 0;
var sum2 = 0;
firstImageHist64.map(function(bHist, bIndex){
return bHist.map(function(bgHist, gIndex){
return bgHist.map(function(bgrHist, rIndex){
sum1 += bgrHist;
})
})
})
var sig1 = flatten(firstImageHist64.map(function(bHist, bIndex){
return bHist.map(function(bgHist, gIndex){
return bgHist.map(function(bgrHist, rIndex){
return {
data :[
[bgrHist/sum1],
[bIndex*step + halfStep],
[gIndex*step + halfStep],
[rIndex*step + halfStep]
]
}
})
})
})).map(function(a){
// trick to avoid flattening and get a 64 x 4 x 1 image as needed
return a.data;
});
secondImageHist64.map(function(bHist, bIndex){
return bHist.map(function(bgHist, gIndex){
return bgHist.map(function(bgrHist, rIndex){
sum2 += bgrHist;
})
})
})
var sig2 = flatten(secondImageHist64.map(function(bHist, bIndex){
return bHist.map(function(bgHist, gIndex){
return bgHist.map(function(bgrHist, rIndex){
return {
data : [
[bgrHist/sum2],
[bIndex*step + halfStep],
[gIndex*step + halfStep],
[rIndex*step + halfStep]
]
};
})
})
})).map(function(a){
// trick to avoid flattening and get a 64 x 4 x 1 image as needed
return a.data;
});
/////////////
// 3. Compute the cost matrix (64 x 64 x 1), calculating the cost in LUV space
/////////////
//middles is a 1 x 64 x 3 array of the middles positions in RGB used to change to LUV
var middles = [flatten(firstImageHist64.map(function(bHist, bIndex){
return bHist.map(function(bgHist, gIndex){
return bgHist.map(function(bgrHist, rIndex){
return {
data : [
bIndex*step + halfStep,
gIndex*step + halfStep,
rIndex*step + halfStep
]
}
})
})
})).map(function(a){
// trick to avoid flattening and get a 1 x 64 x 3 image as needed
return a.data;
})];
var mat = cv.Matrix.fromArray(middles, cv.Constants.CV_8UC3);
mat.cvtColor("CV_BGR2Luv");
//luvValues is a 1 x 64 x 3 array of the middles positions in LUV
var luvMiddles = mat.toArray();
var distance = function(luv1, luv2){
return Math.sqrt((luv1[0]-luv2[0])*(luv1[0]-luv2[0]) + (luv1[1]-luv2[1])*(luv1[1]-luv2[1]) + (luv1[2]-luv2[2])*(luv1[2]-luv2[2]));
};
var costs = luvMiddles[0].map(function(luvMiddle1){
return luvMiddles[0].map(function(luvMiddle2){
return [distance(luvMiddle1, luvMiddle2)];
})
});
//////
// 4. Run EMD algorithm
/////
var matCosts = cv.Matrix.fromArray(costs, cv.Constants.CV_32FC1);
var matSig1 = cv.Matrix.fromArray(sig1, cv.Constants.CV_32FC1);
var matSig2 = cv.Matrix.fromArray(sig2, cv.Constants.CV_32FC1);
var dist = cv.Constants.CV_DIST_L2;
var emd = cv.histogram.emd(matSig1, matSig2, dist);//, matCosts);
console.log("EMD is ", emd)
});
});