/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" |