-
Notifications
You must be signed in to change notification settings - Fork 5
/
DistanceUtils.h
316 lines (282 loc) · 12 KB
/
DistanceUtils.h
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#pragma once
#include <opencv2/core/types_c.h>
//! computes the L1 distance between two integer values
template<typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type L1dist(T a, T b) {
return (size_t)abs((int)a-b);
}
//! computes the L1 distance between two float values
template<typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type L1dist(T a, T b) {
return fabs((float)a-(float)b);
}
//! computes the L1 distance between two generic arrays
template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b) -> decltype(L1dist(*a,*b)) {
decltype(L1dist(*a,*b)) oResult = 0;
for(size_t c=0; c<nChannels; ++c)
oResult += L1dist(a[c],b[c]);
return oResult;
}
//! computes the L1 distance between two generic arrays
template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L1dist<nChannels>(a,b)) {
decltype(L1dist<nChannels>(a,b)) oResult = 0;
size_t nTotElements = nElements*nChannels;
if(m) {
for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i)
if(m[i])
oResult += L1dist<nChannels>(a+n,b+n);
}
else {
for(size_t n=0; n<nTotElements; n+=nChannels)
oResult += L1dist<nChannels>(a+n,b+n);
}
return oResult;
}
//! computes the L1 distance between two generic arrays
template<typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L1dist<3>(a,b,nElements,m)) {
CV_Assert(nChannels>0 && nChannels<=4);
switch(nChannels) {
case 1: return L1dist<1>(a,b,nElements,m);
case 2: return L1dist<2>(a,b,nElements,m);
case 3: return L1dist<3>(a,b,nElements,m);
case 4: return L1dist<4>(a,b,nElements,m);
default: return 0;
}
}
//! computes the L1 distance between two opencv vectors
template<size_t nChannels, typename T> static inline auto L1dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L1dist<nChannels,T>((T*)(0),(T*)(0))) {
T a_array[nChannels], b_array[nChannels];
for(size_t c=0; c<nChannels; ++c) {
a_array[c] = a[(int)c];
b_array[c] = b[(int)c];
}
return L1dist<nChannels>(a_array,b_array);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//! computes the squared L2 distance between two generic variables
template<typename T> static inline auto L2sqrdist(T a, T b) -> decltype(L1dist(a,b)) {
auto oResult = L1dist(a,b);
return oResult*oResult;
}
//! computes the squared L2 distance between two generic arrays
template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b) -> decltype(L2sqrdist(*a,*b)) {
decltype(L2sqrdist(*a,*b)) oResult = 0;
for(size_t c=0; c<nChannels; ++c)
oResult += L2sqrdist(a[c],b[c]);
return oResult;
}
//! computes the squared L2 distance between two generic arrays
template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L2sqrdist<nChannels>(a,b)) {
decltype(L2sqrdist<nChannels>(a,b)) oResult = 0;
size_t nTotElements = nElements*nChannels;
if(m) {
for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i)
if(m[i])
oResult += L2sqrdist<nChannels>(a+n,b+n);
}
else {
for(size_t n=0; n<nTotElements; n+=nChannels)
oResult += L2sqrdist<nChannels>(a+n,b+n);
}
return oResult;
}
//! computes the squared L2 distance between two generic arrays
template<typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L2sqrdist<3>(a,b,nElements,m)) {
CV_Assert(nChannels>0 && nChannels<=4);
switch(nChannels) {
case 1: return L2sqrdist<1>(a,b,nElements,m);
case 2: return L2sqrdist<2>(a,b,nElements,m);
case 3: return L2sqrdist<3>(a,b,nElements,m);
case 4: return L2sqrdist<4>(a,b,nElements,m);
default: return 0;
}
}
//! computes the squared L2 distance between two opencv vectors
template<size_t nChannels, typename T> static inline auto L2sqrdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L2sqrdist<nChannels,T>((T*)(0),(T*)(0))) {
T a_array[nChannels], b_array[nChannels];
for(size_t c=0; c<nChannels; ++c) {
a_array[c] = a[(int)c];
b_array[c] = b[(int)c];
}
return L2sqrdist<nChannels>(a_array,b_array);
}
//! computes the L2 distance between two generic arrays
template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b) {
decltype(L2sqrdist(*a,*b)) oResult = 0;
for(size_t c=0; c<nChannels; ++c)
oResult += L2sqrdist(a[c],b[c]);
return sqrt((float)oResult);
}
//! computes the L2 distance between two generic arrays
template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) {
decltype(L2sqrdist<nChannels>(a,b)) oResult = 0;
size_t nTotElements = nElements*nChannels;
if(m) {
for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i)
if(m[i])
oResult += L2sqrdist<nChannels>(a+n,b+n);
}
else {
for(size_t n=0; n<nTotElements; n+=nChannels)
oResult += L2sqrdist<nChannels>(a+n,b+n);
}
return sqrt((float)oResult);
}
//! computes the squared L2 distance between two generic arrays
template<typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) {
CV_Assert(nChannels>0 && nChannels<=4);
switch(nChannels) {
case 1: return L2dist<1>(a,b,nElements,m);
case 2: return L2dist<2>(a,b,nElements,m);
case 3: return L2dist<3>(a,b,nElements,m);
case 4: return L2dist<4>(a,b,nElements,m);
default: return 0;
}
}
//! computes the L2 distance between two opencv vectors
template<size_t nChannels, typename T> static inline float L2dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) {
T a_array[nChannels], b_array[nChannels];
for(size_t c=0; c<nChannels; ++c) {
a_array[c] = a[(int)c];
b_array[c] = b[(int)c];
}
return L2dist<nChannels>(a_array,b_array);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//! computes the color distortion between two integer arrays
template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cdist(const T* curr, const T* bg) {
static_assert(nChannels>1,"cdist: requires more than one channel");
size_t curr_sqr = 0;
bool bSkip = true;
for(size_t c=0; c<nChannels; ++c) {
curr_sqr += curr[c]*curr[c];
bSkip = bSkip&(bg[c]<=0);
}
if(bSkip)
return (size_t)sqrt((float)curr_sqr);
size_t bg_sqr = 0;
size_t mix = 0;
for(size_t c=0; c<nChannels; ++c) {
bg_sqr += bg[c]*bg[c];
mix += curr[c]*bg[c];
}
return (size_t)sqrt(curr_sqr-((float)(mix*mix)/bg_sqr));
}
//! computes the color distortion between two float arrays
template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cdist(const T* curr, const T* bg) {
static_assert(nChannels>1,"cdist: requires more than one channel");
float curr_sqr = 0;
bool bSkip = true;
for(size_t c=0; c<nChannels; ++c) {
curr_sqr += (float)curr[c]*curr[c];
bSkip = bSkip&(bg[c]<=0);
}
if(bSkip)
return sqrt(curr_sqr);
float bg_sqr = 0;
float mix = 0;
for(size_t c=0; c<nChannels; ++c) {
bg_sqr += (float)bg[c]*bg[c];
mix += (float)curr[c]*bg[c];
}
return sqrt(curr_sqr-((mix*mix)/bg_sqr));
}
//! computes the color distortion between two generic arrays
template<size_t nChannels, typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(cdist<nChannels>(a,b)) {
decltype(cdist<nChannels>(a,b)) oResult = 0;
size_t nTotElements = nElements*nChannels;
if(m) {
for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i)
if(m[i])
oResult += cdist<nChannels>(a+n,b+n);
}
else {
for(size_t n=0; n<nTotElements; n+=nChannels)
oResult += cdist<nChannels>(a+n,b+n);
}
return oResult;
}
//! computes the color distortion between two generic arrays
template<typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(cdist<3>(a,b,nElements,m)) {
CV_Assert(nChannels>1 && nChannels<=4);
switch(nChannels) {
case 2: return cdist<2>(a,b,nElements,m);
case 3: return cdist<3>(a,b,nElements,m);
case 4: return cdist<4>(a,b,nElements,m);
default: return 0;
}
}
//! computes the color distortion between two opencv vectors
template<size_t nChannels, typename T> static inline auto cdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(cdist<nChannels,T>((T*)(0),(T*)(0))) {
T a_array[nChannels], b_array[nChannels];
for(size_t c=0; c<nChannels; ++c) {
a_array[c] = a[(int)c];
b_array[c] = b[(int)c];
}
return cdist<nChannels>(a_array,b_array);
}
//! computes a color distortion-distance mix using two generic distances
template<typename T> static inline T cmixdist(T oL1Distance, T oCDistortion) {
return (oL1Distance/2+oCDistortion*4);
}
//! computes a color distoirtion-distance mix using two generic arrays
template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cmixdist(const T* curr, const T* bg) {
return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg));
}
template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cmixdist(const T* curr, const T* bg) {
return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//! popcount LUT for 8-bit vectors
static const uchar popcount_LUT8[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
//! computes the population count of an N-byte vector using an 8-bit popcount LUT
template<typename T> static inline size_t popcount(T x) {
size_t nBytes = sizeof(T);
size_t nResult = 0;
for(size_t l=0; l<nBytes; ++l)
nResult += popcount_LUT8[(uchar)(x>>l*8)];
return nResult;
}
//! computes the hamming distance between two N-byte vectors using an 8-bit popcount LUT
template<typename T> static inline size_t hdist(T a, T b) {
return popcount(a^b);
}
//! computes the gradient magnitude distance between two N-byte vectors using an 8-bit popcount LUT
template<typename T> static inline size_t gdist(T a, T b) {
return L1dist(popcount(a),popcount(b));
}
//! computes the population count of a (nChannels*N)-byte vector using an 8-bit popcount LUT
template<size_t nChannels, typename T> static inline size_t popcount(const T* x) {
size_t nBytes = sizeof(T);
size_t nResult = 0;
for(size_t c=0; c<nChannels; ++c)
for(size_t l=0; l<nBytes; ++l)
nResult += popcount_LUT8[(uchar)(*(x+c)>>l*8)];
return nResult;
}
//! computes the hamming distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT
template<size_t nChannels, typename T> static inline size_t hdist(const T* a, const T* b) {
T xor_array[nChannels];
for(size_t c=0; c<nChannels; ++c)
xor_array[c] = a[c]^b[c];
return popcount<nChannels>(xor_array);
}
//! computes the gradient magnitude distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT
template<size_t nChannels, typename T> static inline size_t gdist(const T* a, const T* b) {
return L1dist(popcount<nChannels>(a),popcount<nChannels>(b));
}