Coverage Report

Created: 2024-08-18 16:21

/home/liu/actions-runner/_work/ccv/ccv/test/unit/numeric.tests.c
Line
Count
Source
1
#include "ccv.h"
2
#include "case.h"
3
#include "ccv_case.h"
4
#include "3rdparty/dsfmt/dSFMT.h"
5
6
/* numeric tests are more like functional tests rather than unit tests:
7
 * the following tests contain:
8
 * 1. compute eigenvectors / eigenvalues on a random symmetric matrix and verify these are eigenvectors / eigenvalues;
9
 * 2. minimization of the famous rosenbrock function;
10
 * 3. compute ssd with ccv_filter, and compare the result with naive method
11
 * 4. compare the result from ccv_distance_transform (linear time) with reference implementation from voc-release4 (O(nlog(n))) */
12
13
TEST_CASE("compute eigenvectors and eigenvalues of a symmetric matrix")
14
1
{
15
1
  dsfmt_t dsfmt;
16
1
  dsfmt_init_gen_rand(&dsfmt, 0xdead);
17
1
  ccv_dense_matrix_t* a = ccv_dense_matrix_new(4, 4, CCV_64F | CCV_C1, 0, 0);
18
1
  int i, j, k;
19
5
  for (i = 0; i < 4; 
i++4
)
20
14
    
for (j = i; 4
j < 4;
j++10
)
21
10
      a->data.f64[i * 4 + j] = dsfmt_genrand_close_open(&dsfmt) * 10;
22
5
  for (i = 0; i < 4; 
i++4
)
23
10
    
for (j = 0; 4
j < i;
j++6
)
24
6
      a->data.f64[i * 4 + j] = a->data.f64[j * 4 + i];
25
1
  ccv_dense_matrix_t* evec = 0;
26
1
  ccv_dense_matrix_t* eval = 0;
27
1
  ccv_eigen(a, &evec, &eval, 0, 1e-6);
28
5
  for (k = 0; k < 4; 
k++4
)
29
4
  {
30
4
    double veca[4] = {
31
4
      0, 0, 0, 0,
32
4
    };
33
20
    for (i = 0; i < 4; 
i++16
)
34
80
      
for (j = 0; 16
j < 4;
j++64
)
35
64
        veca[i] += a->data.f64[i * 4 + j] * evec->data.f64[k * 4 + j];
36
4
    double vece[4];
37
20
    for (i = 0; i < 4; 
i++16
)
38
16
      vece[i] = eval->data.f64[k] * evec->data.f64[k * 4 + i];
39
4
    REQUIRE_ARRAY_EQ_WITH_TOLERANCE(double, veca, vece, 4, 1e-6, "verify %d(th) eigenvectors and eigenvalues with Ax = rx", k + 1);
40
4
  }
41
1
  ccv_matrix_free(a);
42
1
  ccv_matrix_free(evec);
43
1
  ccv_matrix_free(eval);
44
1
}
45
46
static int rosenbrock(const ccv_dense_matrix_t* x, double* f, ccv_dense_matrix_t* df, void* data)
47
147
{
48
147
  int* steps = (int*)data;
49
147
  (*steps)++;
50
147
  int i;
51
147
  double rf = 0;
52
147
  double* x_vec = x->data.f64;
53
294
  for (i = 0; i < 1; 
i++147
)
54
147
    rf += 100 * (x_vec[i + 1] - x_vec[i] * x_vec[i]) * (x_vec[i + 1] - x_vec[i] * x_vec[i]) + (1 - x_vec[i]) * (1 - x_vec[i]);
55
147
  *f = rf;
56
147
  double* df_vec = df->data.f64;
57
147
  ccv_zero(df);
58
147
  df_vec[0] = df_vec[1] = 0;
59
294
  for (i = 0; i < 1; 
i++147
)
60
147
    df_vec[i] = -400 * x_vec[i] * (x_vec[i+1] - x_vec[i] * x_vec[i]) - 2 * (1 - x_vec[i]);
61
294
  for (i = 1; i < 2; 
i++147
)
62
147
    df_vec[i] += 200 * (x_vec[i] - x_vec[i - 1] * x_vec[i - 1]);
63
147
  return 0;
64
147
}
65
66
TEST_CASE("minimize rosenbrock")
67
1
{
68
1
  ccv_dense_matrix_t* x = ccv_dense_matrix_new(1, 2, CCV_64F | CCV_C1, 0, 0);
69
1
  ccv_zero(x);
70
1
  int steps = 0;
71
1
  ccv_minimize(x, 25, 1.0, rosenbrock, ccv_minimize_default_params, &steps);
72
1
  double dx[2] = { 1, 1 };
73
1
  REQUIRE_ARRAY_EQ_WITH_TOLERANCE(double, x->data.f64, dx, 2, 1e-6, "the global minimal should be at (1.0, 1.0)");
74
1
  ccv_matrix_free(x);
75
1
}
76
77
double gaussian(double x, double y, void* data)
78
20.2k
{
79
20.2k
  return exp(-(x * x + y * y) / 20) / sqrt(CCV_PI * 20);
80
20.2k
}
81
82
#ifdef HAVE_LIBPNG
83
TEST_CASE("Gaussian blur with kernel size even & odd")
84
1
{
85
1
  ccv_dense_matrix_t* image = 0;
86
  // Different versions of libpng uses different RGB => Gray transform values, therefore, operation on RGB space
87
1
  ccv_read("../../samples/street.png", &image, CCV_IO_ANY_FILE);
88
1
  ccv_dense_matrix_t* kernel = ccv_dense_matrix_new(100, 100, CCV_32F | CCV_GET_CHANNEL(image->type), 0, 0);
89
1
  ccv_filter_kernel(kernel, gaussian, 0);
90
1
  double sum = ccv_sum(kernel, CCV_UNSIGNED);
91
  // Normalize
92
1
  ccv_scale(kernel, (ccv_matrix_t**)&kernel, 0, CCV_GET_CHANNEL(image->type) / sum);
93
1
  ccv_dense_matrix_t* x = 0;
94
1
  ccv_filter(image, kernel, &x, CCV_32F, 0);
95
1
  ccv_matrix_free(kernel);
96
1
  kernel = ccv_dense_matrix_new(101, 101, CCV_32F | CCV_GET_CHANNEL(image->type), 0, 0);
97
1
  ccv_filter_kernel(kernel, gaussian, 0);
98
1
  sum = ccv_sum(kernel, CCV_UNSIGNED);
99
  // Normalize
100
1
  ccv_scale(kernel, (ccv_matrix_t**)&kernel, 0, CCV_GET_CHANNEL(image->type) / sum);
101
1
  ccv_dense_matrix_t* y = 0;
102
1
  ccv_filter(image, kernel, &y, CCV_32F, 0);
103
1
  ccv_matrix_free(kernel);
104
1
  ccv_matrix_free(image);
105
1
  REQUIRE_MATRIX_FILE_EQ(x, "data/street.g100.bin", "should be Gaussian blur of 100x100 (even) on street.png");
106
1
  ccv_matrix_free(x);
107
1
  REQUIRE_MATRIX_FILE_EQ(y, "data/street.g101.bin", "should be Gaussian blur of 101x101 (odd) on street.png");
108
1
  ccv_matrix_free(y);
109
1
}
110
#endif
111
112
TEST_CASE("ccv_filter centre point for even number window size, hint: (size - 1) / 2")
113
1
{
114
1
  ccv_dense_matrix_t* x = ccv_dense_matrix_new(10, 10, CCV_32F | CCV_C1, 0, 0);
115
1
  ccv_dense_matrix_t* y = ccv_dense_matrix_new(10, 10, CCV_32F | CCV_C1, 0, 0);
116
1
  float sum = 0;
117
1
  int i;
118
101
  for (i = 0; i < 100; 
i++100
)
119
100
  {
120
100
    x->data.f32[i] = y->data.f32[99 - i] = i;
121
100
    sum += (99 - i) * i;
122
100
  }
123
1
  ccv_dense_matrix_t* d = 0;
124
1
  ccv_filter(x, y, &d, 0, CCV_NO_PADDING);
125
1
  REQUIRE_EQ_WITH_TOLERANCE(d->data.f32[4 * 10 + 4], sum, 0.1, "filter centre value should match the sum value computed by a for loop");
126
1
  ccv_matrix_free(d);
127
1
  ccv_matrix_free(y);
128
1
  ccv_matrix_free(x);
129
1
}
130
131
TEST_CASE("ccv_filter centre point for odd number window size, hint: (size - 1) / 2")
132
1
{
133
1
  ccv_dense_matrix_t* x = ccv_dense_matrix_new(11, 11, CCV_32F | CCV_C1, 0, 0);
134
1
  ccv_dense_matrix_t* y = ccv_dense_matrix_new(11, 11, CCV_32F | CCV_C1, 0, 0);
135
1
  float sum = 0;
136
1
  int i;
137
122
  for (i = 0; i < 121; 
i++121
)
138
121
  {
139
121
    x->data.f32[i] = y->data.f32[120 - i] = i;
140
121
    sum += (120 - i) * i;
141
121
  }
142
1
  ccv_dense_matrix_t* d = 0;
143
1
  ccv_filter(x, y, &d, 0, CCV_NO_PADDING);
144
1
  REQUIRE_EQ_WITH_TOLERANCE(d->data.f32[5 * 11 + 5], sum, 0.1, "filter centre value should match the sum value computed by a for loop");
145
1
  ccv_matrix_free(d);
146
1
  ccv_matrix_free(y);
147
1
  ccv_matrix_free(x);
148
1
}
149
150
#include "ccv_internal.h"
151
152
#ifdef HAVE_LIBPNG
153
static void naive_ssd(ccv_dense_matrix_t* image, ccv_dense_matrix_t* template, ccv_dense_matrix_t* out)
154
1
{
155
1
  int thw = template->cols / 2;
156
1
  int thh = template->rows / 2;
157
1
  int i, j, k, x, y, ch = CCV_GET_CHANNEL(image->type);
158
1
  unsigned char* i_ptr = image->data.u8 + thh * image->step;
159
1
  double* o = out->data.f64 + out->cols * thh;
160
1
  ccv_zero(out);
161
253
  for (i = thh; i < image->rows - thh - 1; 
i++252
)
162
252
  {
163
143k
    for (j = thw; j < image->cols - thw - 1; 
j++143k
)
164
143k
    {
165
143k
      unsigned char* t_ptr = template->data.u8;
166
143k
      unsigned char* j_ptr = i_ptr - thh * image->step;
167
143k
      o[j] = 0;
168
24.9M
      for (y = -thh; y <= thh; 
y++24.8M
)
169
24.8M
      {
170
1.78G
        for (x = -thw; x <= thw; 
x++1.76G
)
171
7.04G
          
for (k = 0; 1.76G
k < ch;
k++5.28G
)
172
5.28G
            o[j] += (j_ptr[(x + j) * ch + k] - t_ptr[(x + thw) * ch + k]) * (j_ptr[(x + j) * ch + k] - t_ptr[(x + thw) * ch + k]);
173
24.8M
        t_ptr += template->step;
174
24.8M
        j_ptr += image->step;
175
24.8M
      }
176
143k
    }
177
252
    i_ptr += image->step;
178
252
    o += out->cols;
179
252
  }
180
1
}
181
182
TEST_CASE("convolution ssd (sum of squared differences) v.s. naive ssd")
183
1
{
184
1
  ccv_dense_matrix_t* street = 0;
185
1
  ccv_dense_matrix_t* pedestrian = 0;
186
1
  ccv_read("../../samples/pedestrian.png", &pedestrian, CCV_IO_ANY_FILE);
187
1
  ccv_read("../../samples/street.png", &street, CCV_IO_ANY_FILE);
188
1
  ccv_dense_matrix_t* result = 0;
189
1
  ccv_filter(street, pedestrian, &result, CCV_64F, 0);
190
1
  ccv_dense_matrix_t* square = 0;
191
1
  ccv_multiply(street, street, (ccv_matrix_t**)&square, 0);
192
1
  ccv_dense_matrix_t* sat = 0;
193
1
  ccv_sat(square, &sat, 0, CCV_PADDING_ZERO);
194
1
  ccv_matrix_free(square);
195
1
  double sum[] = {0, 0, 0};
196
1
  int i, j, k;
197
1
  int ch = CCV_GET_CHANNEL(street->type);
198
1
  unsigned char* p_ptr = pedestrian->data.u8;
199
1
#define for_block(_, _for_get) \
200
174
  
for (i = 0; 1
i < pedestrian->rows;
i++173
) \
201
173
  { \
202
12.4k
    for (j = 0; j < pedestrian->cols; 
j++12.2k
) \
203
49.1k
      
for (k = 0; 12.2k
k < ch;
k++36.8k
) \
204
36.8k
        sum[k] += _for_get(p_ptr, j * ch + k, 0) * 
_for_get173
(p_ptr, j * ch + k, 0); \
205
173
    p_ptr += pedestrian->step; \
206
173
  }
207
1
  ccv_matrix_getter(pedestrian->type, for_block);
208
1
#undef for_block
209
1
  int phw = pedestrian->cols / 2;
210
1
  int phh = pedestrian->rows / 2;
211
1
  ccv_dense_matrix_t* output = ccv_dense_matrix_new(street->rows, street->cols, CCV_64F | CCV_C1, 0, 0);
212
1
  ccv_zero(output);
213
1
  unsigned char* s_ptr = sat->data.u8 + sat->step * phh;
214
1
  unsigned char* r_ptr = result->data.u8 + result->step * phh;
215
1
  double* o_ptr = output->data.f64 + output->cols * phh;
216
1
#define for_block(_for_get_s, _for_get_r) \
217
253
  
for (i = phh; 1
i < output->rows - phh - 1;
i++252
) \
218
252
  { \
219
143k
    for (j = phw; j < output->cols - phw - 1; 
j++143k
) \
220
143k
    { \
221
143k
      o_ptr[j] = 0; \
222
573k
      for (k = 0; k < ch; 
k++430k
) \
223
430k
      { \
224
430k
        o_ptr[j] += (_for_get_s(s_ptr + sat->step * ccv_min(phh + 1, sat->rows - i - 1), ccv_min(j + phw + 1, sat->cols - 1) * ch + k, 0) \
225
430k
              - _for_get_s(s_ptr + sat->step * ccv_min(phh + 1, sat->rows - i - 1), ccv_max(j - phw, 0) * ch + k, 0) \
226
430k
              + _for_get_s(s_ptr + sat->step * ccv_max(-phh, -i), ccv_max(j - phw, 0) * ch + k, 0) \
227
430k
              - _for_get_s(s_ptr + sat->step * ccv_max(-phh, -i), ccv_min(j + phw + 1, sat->cols - 1) * ch + k, 0)) \
228
430k
              + sum[k] - 2.0 * _for_get_r(r_ptr, j * ch + k, 0); \
229
430k
      } \
230
143k
    } \
231
252
    s_ptr += sat->step; \
232
252
    r_ptr += result->step; \
233
252
    o_ptr += output->cols; \
234
252
  }
235
1
  ccv_matrix_getter(sat->type, ccv_matrix_getter_a, result->type, for_block);
236
1
#undef for_block
237
1
  ccv_matrix_free(result);
238
1
  ccv_matrix_free(sat);
239
1
  ccv_dense_matrix_t* final = 0;
240
1
  ccv_slice(output, (ccv_matrix_t**)&final, 0, phh, phw, output->rows - phh * 2, output->cols - phw * 2);
241
1
  ccv_zero(output);
242
1
  naive_ssd(street, pedestrian, output);
243
1
  ccv_dense_matrix_t* ref = 0;
244
1
  ccv_slice(output, (ccv_matrix_t**)&ref, 0, phh, phw, output->rows - phh * 2, output->cols - phw * 2);
245
1
  ccv_matrix_free(output);
246
1
  ccv_matrix_free(pedestrian);
247
1
  ccv_matrix_free(street);
248
1
  REQUIRE_MATRIX_EQ(ref, final, "ssd computed by convolution doesn't match the one computed by naive method");
249
1
  ccv_matrix_free(final);
250
1
  ccv_matrix_free(ref);
251
1
}
252
#endif
253
254
// divide & conquer method for distance transform (copied directly from dpm-matlab (voc-release4)
255
256
9.40M
static inline int square(int x) { return x*x; }
257
258
// dt helper function
259
void dt_min_helper(float *src, float *dst, int *ptr, int step, 
260
520k
         int s1, int s2, int d1, int d2, float a, float b) {
261
520k
 if (d2 >= d1) {
262
260k
   int d = (d1+d2) >> 1;
263
260k
   int s = s1;
264
260k
   int p;
265
2.48M
   for (p = s1+1; p <= s2; 
p++2.22M
)
266
2.22M
     if (src[s*step] + a*square(d-s) + b*(d-s) > 
267
2.22M
   src[p*step] + a*square(d-p) + b*(d-p))
268
1.01M
  s = p;
269
260k
   dst[d*step] = src[s*step] + a*square(d-s) + b*(d-s);
270
260k
   ptr[d*step] = s;
271
260k
   dt_min_helper(src, dst, ptr, step, s1, s, d1, d-1, a, b);
272
260k
   dt_min_helper(src, dst, ptr, step, s, s2, d+1, d2, a, b);
273
260k
 }
274
520k
}
275
276
// dt of 1d array
277
void dt_min1d(float *src, float *dst, int *ptr, int step, int n, 
278
725
    float a, float b) {
279
725
  dt_min_helper(src, dst, ptr, step, 0, n-1, 0, n-1, a, b);
280
725
}
281
282
void daq_min_distance_transform(ccv_dense_matrix_t* a, ccv_dense_matrix_t** b, double dx, double dy, double dxx, double dyy)
283
1
{
284
1
  ccv_dense_matrix_t* dc = ccv_dense_matrix_new(a->rows, a->cols, CCV_32F | CCV_C1, 0, 0);
285
1
  ccv_dense_matrix_t* db = *b = ccv_dense_matrix_new(a->rows, a->cols, CCV_32F | CCV_C1, 0, 0);
286
1
  unsigned char* a_ptr = a->data.u8;
287
1
  float* b_ptr = db->data.f32;
288
1
  int i, j;
289
1
#define for_block(_, _for_get) \
290
326
  
for (i = 0; 1
i < a->rows;
i++325
) \
291
325
  { \
292
130k
    for (j = 0; j < a->cols; 
j++130k
) \
293
130k
      b_ptr[j] = 
_for_get325
(a_ptr, j, 0); \
294
325
    b_ptr += db->cols; \
295
325
    a_ptr += a->step; \
296
325
  }
297
1
  ccv_matrix_getter(a->type, for_block);
298
1
#undef for_block
299
1
  int* ix = (int*)calloc(a->cols * a->rows, sizeof(int));
300
1
  int* iy = (int*)calloc(a->cols * a->rows, sizeof(int));
301
1
  b_ptr = db->data.f32;
302
1
  float* c_ptr = dc->data.f32;
303
326
  for (i = 0; i < a->rows; 
i++325
)
304
325
    dt_min1d(b_ptr + i * a->cols, c_ptr + i * a->cols, ix + i * a->cols, 1, a->cols, dxx, dx);
305
401
  for (j = 0; j < a->cols; 
j++400
)
306
400
    dt_min1d(c_ptr + j, b_ptr + j, iy + j, a->cols, a->rows, dyy, dy);
307
1
  free(ix);
308
1
  free(iy);
309
1
  ccv_matrix_free(dc);
310
1
}
311
312
#ifdef HAVE_LIBPNG
313
TEST_CASE("ccv_distance_transform (linear time) v.s. distance transform using divide & conquer (O(nlog(n)))")
314
1
{
315
1
  ccv_dense_matrix_t* geometry = 0;
316
1
  ccv_read("../../samples/geometry.png", &geometry, CCV_IO_GRAY | CCV_IO_ANY_FILE);
317
1
  ccv_dense_matrix_t* distance = 0;
318
1
  double dx = 0;
319
1
  double dy = 0;
320
1
  double dxx = 1;
321
1
  double dyy = 1;
322
1
  ccv_distance_transform(geometry, &distance, 0, 0, 0, 0, 0, dx, dy, dxx, dyy, CCV_GSEDT);
323
1
  ccv_dense_matrix_t* ref = 0;
324
1
  daq_min_distance_transform(geometry, &ref, dx, dy, dxx, dyy);
325
1
  ccv_matrix_free(geometry);
326
1
  REQUIRE_MATRIX_EQ(distance, ref, "distance transform computed by ccv_distance_transform doesn't match the one computed by divide & conquer (voc-release4)");
327
1
  ccv_matrix_free(ref);
328
1
  ccv_matrix_free(distance);
329
1
}
330
#endif
331
332
// dt helper function
333
void dt_max_helper(float *src, float *dst, int *ptr, int step, 
334
520k
         int s1, int s2, int d1, int d2, float a, float b) {
335
520k
 if (d2 >= d1) {
336
260k
   int d = (d1+d2) >> 1;
337
260k
   int s = s1;
338
260k
   int p;
339
2.48M
   for (p = s1+1; p <= s2; 
p++2.22M
)
340
2.22M
     if (src[s*step] - a*square(d-s) - b*(d-s) <
341
2.22M
   src[p*step] - a*square(d-p) - b*(d-p))
342
1.05M
  s = p;
343
260k
   dst[d*step] = src[s*step] - a*square(d-s) - b*(d-s);
344
260k
   ptr[d*step] = s;
345
260k
   dt_max_helper(src, dst, ptr, step, s1, s, d1, d-1, a, b);
346
260k
   dt_max_helper(src, dst, ptr, step, s, s2, d+1, d2, a, b);
347
260k
 }
348
520k
}
349
350
// dt of 1d array
351
void dt_max1d(float *src, float *dst, int *ptr, int step, int n, 
352
725
    float a, float b) {
353
725
  dt_max_helper(src, dst, ptr, step, 0, n-1, 0, n-1, a, b);
354
725
}
355
356
void daq_max_distance_transform(ccv_dense_matrix_t* a, ccv_dense_matrix_t** b, double dx, double dy, double dxx, double dyy)
357
1
{
358
1
  ccv_dense_matrix_t* dc = ccv_dense_matrix_new(a->rows, a->cols, CCV_32F | CCV_C1, 0, 0);
359
1
  ccv_dense_matrix_t* db = *b = ccv_dense_matrix_new(a->rows, a->cols, CCV_32F | CCV_C1, 0, 0);
360
1
  unsigned char* a_ptr = a->data.u8;
361
1
  float* b_ptr = db->data.f32;
362
1
  int i, j;
363
1
#define for_block(_, _for_get) \
364
326
  
for (i = 0; 1
i < a->rows;
i++325
) \
365
325
  { \
366
130k
    for (j = 0; j < a->cols; 
j++130k
) \
367
130k
      b_ptr[j] = 
_for_get325
(a_ptr, j, 0); \
368
325
    b_ptr += db->cols; \
369
325
    a_ptr += a->step; \
370
325
  }
371
1
  ccv_matrix_getter(a->type, for_block);
372
1
#undef for_block
373
1
  int* ix = (int*)calloc(a->cols * a->rows, sizeof(int));
374
1
  int* iy = (int*)calloc(a->cols * a->rows, sizeof(int));
375
1
  b_ptr = db->data.f32;
376
1
  float* c_ptr = dc->data.f32;
377
326
  for (i = 0; i < a->rows; 
i++325
)
378
325
    dt_max1d(b_ptr + i * a->cols, c_ptr + i * a->cols, ix + i * a->cols, 1, a->cols, dxx, dx);
379
401
  for (j = 0; j < a->cols; 
j++400
)
380
400
    dt_max1d(c_ptr + j, b_ptr + j, iy + j, a->cols, a->rows, dyy, dy);
381
1
  free(ix);
382
1
  free(iy);
383
1
  ccv_matrix_free(dc);
384
1
}
385
386
#ifdef HAVE_LIBPNG
387
TEST_CASE("ccv_distance_transform to compute max distance")
388
1
{
389
1
  ccv_dense_matrix_t* geometry = 0;
390
1
  ccv_read("../../samples/geometry.png", &geometry, CCV_IO_GRAY | CCV_IO_ANY_FILE);
391
1
  ccv_dense_matrix_t* distance = 0;
392
1
  double dx = 1;
393
1
  double dy = 1;
394
1
  double dxx = 0.4;
395
1
  double dyy = 0.4;
396
1
  ccv_distance_transform(geometry, &distance, 0, 0, 0, 0, 0, dx, dy, dxx, dyy, CCV_NEGATIVE | CCV_GSEDT);
397
1
  ccv_dense_matrix_t* ref = 0;
398
1
  daq_max_distance_transform(geometry, &ref, dx, dy, dxx, dyy);
399
1
  ccv_matrix_free(geometry);
400
1
  int i;
401
130k
  for (i = 0; i < distance->rows * distance->cols; 
i++130k
)
402
130k
    distance->data.f32[i] = -distance->data.f32[i];
403
1
  REQUIRE_MATRIX_EQ(distance, ref, "maximum distance transform computed by negate ccv_distance_transform doesn't match the one computed by divide & conquer");
404
1
  ccv_matrix_free(ref);
405
1
  ccv_matrix_free(distance);
406
1
}
407
408
TEST_CASE("ccv_kmeans1d to compute the 1D K-means")
409
1
{
410
1
  ccv_dense_matrix_t* a = ccv_dense_matrix_new(1, 100, CCV_32F | CCV_C1, 0, 0);
411
1
  int i;
412
101
  for (i = 0; i < 100; 
i++100
)
413
100
    a->data.f32[i] = i > 80 ? 
i - 80 + 0.519
:
50 - i81
;
414
1
  int* const indices = ccmalloc(sizeof(int) * 100);
415
1
  double* const centroids = ccmalloc(sizeof(double) * 10);
416
1
  ccv_kmeans1d(a, 10, indices, centroids);
417
  // Compute centroids again to see if it matches.
418
1
  int counts[10] = {};
419
1
  double means[10] = {};
420
101
  for (i = 0; i < 100; 
i++100
)
421
100
  {
422
100
    means[indices[i]] += a->data.f32[i];
423
100
    ++counts[indices[i]];
424
100
  }
425
11
  for (i = 0; i < 10; 
i++10
)
426
10
    means[i] = means[i] / counts[i];
427
1
  REQUIRE_ARRAY_EQ_WITH_TOLERANCE(double, means, centroids, 10, 1e-6, "centroids should match the recompute");
428
1
  ccfree(centroids);
429
  // indices should be continuous up until 80. And then it should be between 4 and 6.
430
1
  int minIndex = indices[0];
431
82
  for (i = 0; i <= 80; 
i++81
)
432
81
  {
433
81
    REQUIRE(indices[i] >= 0 && indices[i] <= minIndex, "should be decrement.");
434
81
    minIndex = ccv_min(indices[i], minIndex);
435
81
  }
436
1
  minIndex = indices[81];
437
20
  for (i = 81; i < 100; 
i++19
)
438
19
  {
439
19
    REQUIRE(indices[i] <= 9 && indices[i] >= minIndex, "should be increment.");
440
19
    minIndex = ccv_max(indices[i], minIndex);
441
19
  }
442
1
  ccfree(indices);
443
1
  ccv_matrix_free(a);
444
1
}
445
#endif
446
447
#include "case_main.h"