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
|
diff --git a/third_party/opencv/src/emd.cpp b/third_party/opencv/src/emd.cpp
index 20ab6feafbc8..d7b29707a90b 100644
--- a/third_party/opencv/src/emd.cpp
+++ b/third_party/opencv/src/emd.cpp
@@ -56,12 +56,36 @@
E-Mail: rubner@cs.stanford.edu URL: http://vision.stanford.edu/~rubner
==========================================================================
*/
+#ifdef CHROMIUM_OPENCV
+#include "emd_wrapper.h"
+
+#include <algorithm>
+#include <vector>
+#include <cassert>
+#include <cstring>
+
+#include "base/numerics/checked_math.h"
+#else
#include "precomp.hpp"
+#endif
#define MAX_ITERATIONS 500
#define CV_EMD_INF ((float)1e20)
#define CV_EMD_EPS ((float)1e-5)
+#ifdef CHROMIUM_OPENCV
+namespace cv {
+template <typename T>
+using AutoBuffer = std::vector<T>;
+}
+typedef void CvArr;
+typedef float (* CvDistanceFunction)( const float* a, const float* b, void* user_param );
+
+#define cvSqrt(value) ((float)sqrt(value))
+#define CV_Error(code, msg) do { (void)(msg); abort(); } while (0)
+#define CV_Assert(expr) do { if (!(expr)) abort(); } while (0)
+#endif
+
/* CvNode1D is used for lists, representing 1D sparse array */
typedef struct CvNode1D
{
@@ -144,8 +168,25 @@ static float icvDistL2( const float *x, const float *y, void *user_param );
static float icvDistL1( const float *x, const float *y, void *user_param );
static float icvDistC( const float *x, const float *y, void *user_param );
+#ifdef CHROMIUM_OPENCV
+std::vector<float> GetDataFromPointDistribution(const opencv::PointDistribution& distribution) {
+ std::vector<float> data;
+ data.reserve((distribution.dimensions + 1) * distribution.positions.size());
+ for (size_t i = 0; i < distribution.positions.size(); i++) {
+ data.push_back(distribution.weights[i]);
+ data.insert(data.end(), distribution.positions[i].begin(),
+ distribution.positions[i].end());
+ }
+
+ return data;
+}
+#endif
+
/* The main function */
-CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
+#ifndef CHROMIUM_OPENCV
+CV_IMPL
+#endif
+float cvCalcEMD2( const CvArr* signature_arr1,
const CvArr* signature_arr2,
int dist_type,
CvDistanceFunction dist_func,
@@ -164,6 +205,23 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
int result = 0;
float eps, min_delta;
CvNode2D *xp = 0;
+#ifdef CHROMIUM_OPENCV
+ opencv::PointDistribution* signature1 =
+ (opencv::PointDistribution*)signature_arr1;
+ opencv::PointDistribution* signature2 =
+ (opencv::PointDistribution*)signature_arr2;
+
+ int dims = signature1->dimensions;
+ int size1 = signature1->positions.size();
+ int size2 = signature2->positions.size();
+ dist_func = icvDistL1;
+
+ std::vector<float> signature1_data =
+ GetDataFromPointDistribution(*signature1);
+ std::vector<float> signature2_data =
+ GetDataFromPointDistribution(*signature2);
+ user_param = (void*)(size_t)dims;
+#else
CvMat sign_stub1, *signature1 = (CvMat*)signature_arr1;
CvMat sign_stub2, *signature2 = (CvMat*)signature_arr2;
CvMat cost_stub, *cost = &cost_stub;
@@ -245,12 +303,19 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
CV_Error( CV_StsBadFlag, "Bad or unsupported metric type" );
}
}
+#endif
+#ifdef CHROMIUM_OPENCV
+ result = icvInitEMD(signature1_data.data(), size1, signature2_data.data(),
+ size2, dims, icvDistL2, user_param, nullptr, 0, &state,
+ lower_bound, local_buf);
+#else
result = icvInitEMD( signature1->data.fl, size1,
signature2->data.fl, size2,
dims, dist_func, user_param,
cost->data.fl, cost->step,
&state, lower_bound, local_buf );
+#endif
if( result > 0 && lower_bound )
{
@@ -307,8 +372,10 @@ CV_IMPL float cvCalcEMD2( const CvArr* signature_arr1,
if( ci >= 0 && cj >= 0 )
{
total_cost += (double)val * state.cost[i][j];
+#ifndef CHROMIUM_OPENCV
if( flow )
((float*)(flow->data.ptr + flow->step*ci))[cj] = val;
+#endif
}
}
@@ -357,7 +424,11 @@ static int icvInitEMD( const float* signature1, int size1,
}
/* allocate buffers */
+#ifdef CHROMIUM_OPENCV
+ _buffer.resize(buffer_size);
+#else
_buffer.allocate(buffer_size);
+#endif
state->buffer = buffer = _buffer.data();
buffer_end = buffer + buffer_size;
@@ -1146,7 +1217,89 @@ icvDistC( const float *x, const float *y, void *user_param )
return (float)s;
}
+#ifdef CHROMIUM_OPENCV
+namespace opencv {
+
+namespace {
+
+bool ValidatePointDistribution(const PointDistribution& distribution) {
+ if (distribution.positions.size() != distribution.weights.size()) {
+ return false;
+ }
+
+ if (distribution.weights.empty()) {
+ return false;
+ }
+
+ for (const float f : distribution.weights) {
+ if (f < 0) {
+ return false;
+ }
+ }
+
+ if (std::all_of(distribution.weights.begin(), distribution.weights.end(),
+ [](float f) { return f == 0; })) {
+ return false;
+ }
+
+ for (const std::vector<float>& point : distribution.positions) {
+ if (point.size() != distribution.dimensions) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Makes sure the buffers allocated during the EMD computation don't overflow
+// in size.
+bool ValidateSize(const PointDistribution& distribution1,
+ const PointDistribution& distribution2) {
+ base::CheckedNumeric<size_t> size1(distribution1.positions.size());
+ size1 *= distribution1.dimensions + 1;
+
+ base::CheckedNumeric<size_t> size2(distribution2.positions.size());
+ size2 *= distribution2.dimensions + 1;
+
+ // This computation should always match the buffer allocation in icvInitEMD
+ base::CheckedNumeric<size_t> total_size =
+ (size1 + 1) * (size2 + 1) *
+ (sizeof(float) + sizeof(char) + sizeof(float)) +
+ (size1 + size2 + 2) *
+ (sizeof(CvNode2D) + sizeof(CvNode2D*) + sizeof(CvNode1D) +
+ sizeof(float) + sizeof(int) + sizeof(CvNode2D*)) +
+ (size1 + 1) * (sizeof(float*) + sizeof(char*) + sizeof(float*)) + 256;
+
+ return total_size.IsValid();
+}
+
+} // namespace
+
+base::Optional<double> EMD(const PointDistribution& distribution1,
+ const PointDistribution& distribution2) {
+ if (!ValidatePointDistribution(distribution1) ||
+ !ValidatePointDistribution(distribution2)) {
+ return base::nullopt;
+ }
+
+ if (distribution1.dimensions != distribution2.dimensions) {
+ return base::nullopt;
+ }
+
+ if (distribution1.dimensions == 0) {
+ return base::nullopt;
+ }
+
+ if (!ValidateSize(distribution1,distribution2)) {
+ return base::nullopt;
+ }
+
+ return cvCalcEMD2(&distribution1, &distribution2, 0, 0, nullptr, nullptr,
+ nullptr, nullptr);
+}
+} // namespace opencv
+#else
float cv::EMD( InputArray _signature1, InputArray _signature2,
int distType, InputArray _cost,
float* lowerBound, OutputArray _flow )
@@ -1177,5 +1330,6 @@ float cv::wrapperEMD(InputArray _signature1, InputArray _signature2,
{
return EMD(_signature1, _signature2, distType, _cost, lowerBound.get(), _flow);
}
+#endif
/* End of file. */
|