## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /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; 4j < 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; 4j < 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; 16j < 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.76Gk < 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; 1i < 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.2kk < 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; 1i < 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; 1i < 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; 1i < 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"