1 // Tencent is pleased to support the open source community by making ncnn available.
2 //
3 // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
4 //
5 // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // https://opensource.org/licenses/BSD-3-Clause
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14 
15 #include "mat.h"
16 #include "prng.h"
17 
18 #include <math.h>
19 #include <string.h>
20 
21 static struct prng_rand_t g_prng_rand_state;
22 #define SRAND(seed) prng_srand(seed, &g_prng_rand_state)
23 #define RAND()      prng_rand(&g_prng_rand_state)
24 
generate_ncnn_logo(int w,int h)25 static ncnn::Mat generate_ncnn_logo(int w, int h)
26 {
27     // clang-format off
28     // *INDENT-OFF*
29     static const unsigned char ncnn_logo_data[16][16] =
30     {
31         {245, 245,  33, 245, 245, 245, 245, 245, 245, 245, 245, 245, 245,  33, 245, 245},
32         {245,  33,  33,  33, 245, 245, 245, 245, 245, 245, 245, 245,  33,  33,  33, 245},
33         {245,  33, 158, 158,  33, 245, 245, 245, 245, 245, 245,  33, 158, 158,  33, 245},
34         { 33, 117, 158, 224, 158,  33, 245, 245, 245, 245,  33, 158, 224, 158, 117,  33},
35         { 33, 117, 224, 224, 224,  66,  33,  33,  33,  33,  66, 224, 224, 224, 117,  33},
36         { 33, 189, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 189,  33},
37         { 33, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224,  33},
38         { 33, 224, 224,  97,  97,  97,  97, 224, 224,  97,  97,  97,  97, 224, 224,  33},
39         { 33, 224, 224,  97,  33,   0, 189, 224, 224,  97,   0,  33,  97, 224, 224,  33},
40         { 33, 224, 224,  97,  33,   0, 189, 224, 224,  97,   0,  33,  97, 224, 224,  33},
41         { 33, 224, 224,  97,  97,  97,  97, 224, 224,  97, 189, 189,  97, 224, 224,  33},
42         { 33,  66,  66,  66, 224, 224, 224, 224, 224, 224, 224, 224,  66,  66,  66,  33},
43         { 66, 158, 158,  66,  66, 224, 224, 224, 224, 224, 224,  66, 158, 158,  66,  66},
44         { 66, 158, 158, 208,  66, 224, 224, 224, 224, 224, 224,  66, 158, 158, 208,  66},
45         { 66, 224, 202, 158,  66, 224, 224, 224, 224, 224, 224,  66, 224, 202, 158,  66},
46         { 66, 158, 224, 158,  66, 224, 224, 224, 224, 224, 224,  66, 158, 224, 158,  66}
47     };
48     // *INDENT-ON*
49     // clang-format on
50 
51     ncnn::Mat m(w, h, (size_t)1, 1);
52     resize_bilinear_c1((const unsigned char*)ncnn_logo_data, 16, 16, m, w, h);
53     return m;
54 }
55 
RandomMat(int w,int h,int elempack)56 static ncnn::Mat RandomMat(int w, int h, int elempack)
57 {
58     ncnn::Mat image = generate_ncnn_logo(w, h);
59 
60     ncnn::Mat m(w, h, 1, (size_t)elempack, elempack);
61 
62     for (int i = 0; i < h; i++)
63     {
64         unsigned char* p = m.row<unsigned char>(i);
65         const unsigned char* pb = image.row<const unsigned char>(i);
66         for (int j = 0; j < w; j++)
67         {
68             for (int k = 0; k < elempack; k++)
69             {
70                 p[k] = pb[0];
71             }
72 
73             p += elempack;
74             pb += 1;
75         }
76     }
77 
78     return m;
79 }
80 
NearlyEqual(unsigned char a,unsigned char b)81 static bool NearlyEqual(unsigned char a, unsigned char b)
82 {
83     return abs(a - b) <= 10;
84 }
85 
CompareNearlyEqual(const ncnn::Mat & a,const ncnn::Mat & b)86 static int CompareNearlyEqual(const ncnn::Mat& a, const ncnn::Mat& b)
87 {
88     for (int i = 0; i < a.h; i++)
89     {
90         const unsigned char* pa = a.row<const unsigned char>(i);
91         const unsigned char* pb = b.row<const unsigned char>(i);
92         for (int j = 0; j < a.w; j++)
93         {
94             for (int k = 0; k < a.elempack; k++)
95             {
96                 if (!NearlyEqual(pa[k], pb[k]))
97                 {
98                     fprintf(stderr, "value not match  at  h:%d w:%d [%d]   expect %d but got %d\n", i, j, k, pa[k], pb[k]);
99                     return -1;
100                 }
101             }
102 
103             pa += a.elempack;
104             pb += a.elempack;
105         }
106     }
107 
108     return 0;
109 }
110 
test_mat_pixel_affine_a(int w,int h)111 static int test_mat_pixel_affine_a(int w, int h)
112 {
113     for (int c = 1; c <= 4; c++)
114     {
115         ncnn::Mat a0 = RandomMat(w, h, c);
116 
117         float tm[6];
118         float tm_inv[6];
119         ncnn::get_rotation_matrix(10.f, 0.15f, w / 2, h / 2, tm);
120         ncnn::invert_affine_transform(tm, tm_inv);
121 
122         ncnn::Mat a1(w / 2, h / 2, (size_t)c, c);
123         ncnn::Mat a2 = a0.clone();
124 
125         if (c == 1)
126         {
127             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w / 2, h / 2, tm, 0);
128             ncnn::warpaffine_bilinear_c1(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
129         }
130         if (c == 2)
131         {
132             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w / 2, h / 2, tm, 0);
133             ncnn::warpaffine_bilinear_c2(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
134         }
135         if (c == 3)
136         {
137             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w / 2, h / 2, tm, 0);
138             ncnn::warpaffine_bilinear_c3(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
139         }
140         if (c == 4)
141         {
142             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w / 2, h / 2, tm, 0);
143             ncnn::warpaffine_bilinear_c4(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
144         }
145 
146         if (CompareNearlyEqual(a0, a2) != 0)
147         {
148             fprintf(stderr, "test_mat_pixel_affine_a failed w=%d h=%d c=%d\n", w, h, c);
149             return -1;
150         }
151     }
152 
153     return 0;
154 }
155 
test_mat_pixel_affine_b(int w,int h)156 static int test_mat_pixel_affine_b(int w, int h)
157 {
158     for (int c = 1; c <= 4; c++)
159     {
160         ncnn::Mat a0 = RandomMat(w, h, c);
161 
162         float tm[6];
163         float tm_inv[6];
164         ncnn::get_rotation_matrix(20.f, 0.25f, w / 4, h / 4, tm);
165         ncnn::invert_affine_transform(tm, tm_inv);
166 
167         ncnn::Mat a1(w / 4, h / 4, (size_t)c, c);
168         ncnn::Mat a2 = a0.clone();
169 
170         if (c == 1)
171         {
172             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w / 4, h / 4, tm, 0);
173             ncnn::warpaffine_bilinear_c1(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
174         }
175         if (c == 2)
176         {
177             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w / 4, h / 4, tm, 0);
178             ncnn::warpaffine_bilinear_c2(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
179         }
180         if (c == 3)
181         {
182             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w / 4, h / 4, tm, 0);
183             ncnn::warpaffine_bilinear_c3(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
184         }
185         if (c == 4)
186         {
187             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w / 4, h / 4, tm, 0);
188             ncnn::warpaffine_bilinear_c4(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
189         }
190 
191         if (CompareNearlyEqual(a0, a2) != 0)
192         {
193             fprintf(stderr, "test_mat_pixel_affine_b failed w=%d h=%d c=%d\n", w, h, c);
194             return -1;
195         }
196     }
197 
198     return 0;
199 }
200 
test_mat_pixel_affine_c(int w,int h)201 static int test_mat_pixel_affine_c(int w, int h)
202 {
203     for (int c = 1; c <= 4; c++)
204     {
205         ncnn::Mat a0 = RandomMat(w, h, c);
206 
207         float tm[6];
208         float tm_inv[6];
209         ncnn::get_rotation_matrix(-30.f, 0.6f, w / 2, h / 2, tm);
210         ncnn::invert_affine_transform(tm, tm_inv);
211 
212         ncnn::Mat a1(w / 2, h / 2, (size_t)c, c);
213         ncnn::Mat a2 = a0.clone();
214 
215         if (c == 1)
216         {
217             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w / 2, h / 2, tm, 0);
218             ncnn::warpaffine_bilinear_c1(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
219         }
220         if (c == 2)
221         {
222             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w / 2, h / 2, tm, 0);
223             ncnn::warpaffine_bilinear_c2(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
224         }
225         if (c == 3)
226         {
227             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w / 2, h / 2, tm, 0);
228             ncnn::warpaffine_bilinear_c3(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
229         }
230         if (c == 4)
231         {
232             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w / 2, h / 2, tm, 0);
233             ncnn::warpaffine_bilinear_c4(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
234         }
235 
236         if (CompareNearlyEqual(a0, a2) != 0)
237         {
238             fprintf(stderr, "test_mat_pixel_affine_c failed w=%d h=%d c=%d\n", w, h, c);
239             return -1;
240         }
241     }
242 
243     return 0;
244 }
245 
test_mat_pixel_affine_d(int w,int h)246 static int test_mat_pixel_affine_d(int w, int h)
247 {
248     for (int c = 1; c <= 4; c++)
249     {
250         ncnn::Mat a0 = RandomMat(w, h, c);
251 
252         const float points_from[4] = {w / 8.f, h / 8.f, w / 8.f + 1.f, h / 8.f + 3.f};
253         const float points_to[4] = {w / 2.f, h / 2.f, w / 2.f + 2.f, h / 2.f};
254 
255         float tm[6];
256         float tm_inv[6];
257         ncnn::get_affine_transform(points_from, points_to, 2, tm);
258         ncnn::invert_affine_transform(tm, tm_inv);
259 
260         ncnn::Mat a1(w / 4, h / 4, (size_t)c, c);
261         ncnn::Mat a2 = a0.clone();
262 
263         if (c == 1)
264         {
265             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w / 4, h / 4, tm, 0);
266             ncnn::warpaffine_bilinear_c1(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
267         }
268         if (c == 2)
269         {
270             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w / 4, h / 4, tm, 0);
271             ncnn::warpaffine_bilinear_c2(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
272         }
273         if (c == 3)
274         {
275             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w / 4, h / 4, tm, 0);
276             ncnn::warpaffine_bilinear_c3(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
277         }
278         if (c == 4)
279         {
280             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w / 4, h / 4, tm, 0);
281             ncnn::warpaffine_bilinear_c4(a1, w / 4, h / 4, a2, w, h, tm_inv, -233);
282         }
283 
284         if (CompareNearlyEqual(a0, a2) != 0)
285         {
286             fprintf(stderr, "test_mat_pixel_affine_d failed w=%d h=%d c=%d\n", w, h, c);
287             return -1;
288         }
289     }
290 
291     return 0;
292 }
293 
test_mat_pixel_affine_e(int w,int h)294 static int test_mat_pixel_affine_e(int w, int h)
295 {
296     for (int c = 1; c <= 4; c++)
297     {
298         ncnn::Mat a0 = RandomMat(w, h, c);
299 
300         float tm[6];
301         float tm_inv[6];
302         ncnn::get_rotation_matrix(-180.f, 0.5f, w / 2, h / 2, tm);
303         ncnn::invert_affine_transform(tm, tm_inv);
304 
305         ncnn::Mat a1(w, h, (size_t)c, c);
306         ncnn::Mat a2 = a0.clone();
307 
308         if (c == 1)
309         {
310             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w, h, tm, 0);
311             ncnn::warpaffine_bilinear_c1(a1, w, h, a2, w, h, tm_inv, -233);
312         }
313         if (c == 2)
314         {
315             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w, h, tm, 0);
316             ncnn::warpaffine_bilinear_c2(a1, w, h, a2, w, h, tm_inv, -233);
317         }
318         if (c == 3)
319         {
320             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w, h, tm, 0);
321             ncnn::warpaffine_bilinear_c3(a1, w, h, a2, w, h, tm_inv, -233);
322         }
323         if (c == 4)
324         {
325             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w, h, tm, 0);
326             ncnn::warpaffine_bilinear_c4(a1, w, h, a2, w, h, tm_inv, -233);
327         }
328 
329         if (CompareNearlyEqual(a0, a2) != 0)
330         {
331             fprintf(stderr, "test_mat_pixel_affine_e failed w=%d h=%d c=%d\n", w, h, c);
332             return -1;
333         }
334     }
335 
336     return 0;
337 }
338 
test_mat_pixel_affine_f(int w,int h)339 static int test_mat_pixel_affine_f(int w, int h)
340 {
341     for (int c = 1; c <= 4; c++)
342     {
343         ncnn::Mat a0 = RandomMat(w, h, c);
344 
345         float tm[6];
346         float tm_inv[6];
347         ncnn::get_rotation_matrix(0.002f, 1.0f, w / 2, h / 2, tm);
348         ncnn::invert_affine_transform(tm, tm_inv);
349 
350         ncnn::Mat a1(w * 2, h * 2, (size_t)c, c);
351         ncnn::Mat a2 = a0.clone();
352 
353         if (c == 1)
354         {
355             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w * 2, h * 2, tm, 0);
356             ncnn::warpaffine_bilinear_c1(a1, w * 2, h * 2, a2, w, h, tm_inv, -233);
357         }
358         if (c == 2)
359         {
360             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w * 2, h * 2, tm, 0);
361             ncnn::warpaffine_bilinear_c2(a1, w * 2, h * 2, a2, w, h, tm_inv, -233);
362         }
363         if (c == 3)
364         {
365             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w * 2, h * 2, tm, 0);
366             ncnn::warpaffine_bilinear_c3(a1, w * 2, h * 2, a2, w, h, tm_inv, -233);
367         }
368         if (c == 4)
369         {
370             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w * 2, h * 2, tm, 0);
371             ncnn::warpaffine_bilinear_c4(a1, w * 2, h * 2, a2, w, h, tm_inv, -233);
372         }
373 
374         if (CompareNearlyEqual(a0, a2) != 0)
375         {
376             fprintf(stderr, "test_mat_pixel_affine_f failed w=%d h=%d c=%d\n", w, h, c);
377             return -1;
378         }
379     }
380 
381     return 0;
382 }
383 
test_mat_pixel_affine_g(int w,int h)384 static int test_mat_pixel_affine_g(int w, int h)
385 {
386     for (int c = 1; c <= 4; c++)
387     {
388         ncnn::Mat a0 = RandomMat(w, h, c);
389 
390         float tm[6];
391         float tm_inv[6];
392         ncnn::get_rotation_matrix(0.f, 0.5f, w / 2 + 4, h / 2 - 3, tm);
393         ncnn::invert_affine_transform(tm, tm_inv);
394 
395         ncnn::Mat a1(w, h, (size_t)c, c);
396         ncnn::Mat a2 = a0.clone();
397 
398         if (c == 1)
399         {
400             ncnn::warpaffine_bilinear_c1(a0, w, h, a1, w, h, tm, 0);
401             ncnn::warpaffine_bilinear_c1(a1, w, h, a2, w, h, tm_inv, -233);
402         }
403         if (c == 2)
404         {
405             ncnn::warpaffine_bilinear_c2(a0, w, h, a1, w, h, tm, 0);
406             ncnn::warpaffine_bilinear_c2(a1, w, h, a2, w, h, tm_inv, -233);
407         }
408         if (c == 3)
409         {
410             ncnn::warpaffine_bilinear_c3(a0, w, h, a1, w, h, tm, 0);
411             ncnn::warpaffine_bilinear_c3(a1, w, h, a2, w, h, tm_inv, -233);
412         }
413         if (c == 4)
414         {
415             ncnn::warpaffine_bilinear_c4(a0, w, h, a1, w, h, tm, 0);
416             ncnn::warpaffine_bilinear_c4(a1, w, h, a2, w, h, tm_inv, -233);
417         }
418 
419         if (CompareNearlyEqual(a0, a2) != 0)
420         {
421             fprintf(stderr, "test_mat_pixel_affine_g failed w=%d h=%d c=%d\n", w, h, c);
422             return -1;
423         }
424     }
425 
426     return 0;
427 }
428 
test_mat_pixel_affine_0()429 static int test_mat_pixel_affine_0()
430 {
431     return 0
432            || test_mat_pixel_affine_a(60, 70)
433            || test_mat_pixel_affine_b(60, 70)
434            || test_mat_pixel_affine_c(60, 70)
435            || test_mat_pixel_affine_d(60, 70)
436            || test_mat_pixel_affine_e(60, 70)
437            || test_mat_pixel_affine_f(60, 70)
438            || test_mat_pixel_affine_g(60, 70)
439 
440            || test_mat_pixel_affine_a(120, 160)
441            || test_mat_pixel_affine_b(120, 160)
442            || test_mat_pixel_affine_c(120, 160)
443            || test_mat_pixel_affine_d(120, 160)
444            || test_mat_pixel_affine_e(120, 160)
445            || test_mat_pixel_affine_f(120, 160)
446            || test_mat_pixel_affine_g(120, 160)
447 
448            || test_mat_pixel_affine_a(220, 330)
449            || test_mat_pixel_affine_b(220, 330)
450            || test_mat_pixel_affine_c(220, 330)
451            || test_mat_pixel_affine_d(220, 330)
452            || test_mat_pixel_affine_e(220, 330)
453            || test_mat_pixel_affine_f(220, 330)
454            || test_mat_pixel_affine_g(220, 330);
455 }
456 
test_mat_pixel_affine_yuv420sp(int w,int h)457 static int test_mat_pixel_affine_yuv420sp(int w, int h)
458 {
459     ncnn::Mat a0(w, h * 3 / 2, (size_t)1u, 1);
460 
461     ncnn::Mat a0_y = RandomMat(w, h, 1);
462     ncnn::Mat a0_uv = RandomMat(w / 2, h / 2, 2);
463     memcpy(a0, a0_y, w * h);
464     memcpy((unsigned char*)a0 + w * h, a0_uv, w * h / 2);
465 
466     float tm[6];
467     float tm_inv[6];
468     ncnn::get_rotation_matrix(-70.f, 0.3f, w / 2, h / 2, tm);
469     ncnn::invert_affine_transform(tm, tm_inv);
470 
471     ncnn::Mat a1(w / 2, (h / 2) * 3 / 2, (size_t)1u, 1);
472     ncnn::Mat a2 = a0.clone();
473 
474     ncnn::warpaffine_bilinear_yuv420sp(a0, w, h, a1, w / 2, h / 2, tm, 0);
475     ncnn::warpaffine_bilinear_yuv420sp(a1, w / 2, h / 2, a2, w, h, tm_inv, -233);
476 
477     // Y
478     if (CompareNearlyEqual(ncnn::Mat(w, h, (unsigned char*)a0, (size_t)1u, 1), ncnn::Mat(w, h, (unsigned char*)a2, (size_t)1u, 1)) != 0)
479     {
480         fprintf(stderr, "test_mat_pixel_affine_yuv420sp Y failed w=%d h=%d\n", w, h);
481         return -1;
482     }
483 
484     // UV
485     if (CompareNearlyEqual(ncnn::Mat(w / 2, h / 2, (unsigned char*)a0 + w * h, (size_t)2u, 2), ncnn::Mat(w / 2, h / 2, (unsigned char*)a2 + w * h, (size_t)2u, 2)) != 0)
486     {
487         fprintf(stderr, "test_mat_pixel_affine_yuv420sp UV failed w=%d h=%d\n", w, h);
488         return -1;
489     }
490 
491     return 0;
492 }
493 
test_mat_pixel_affine_1()494 static int test_mat_pixel_affine_1()
495 {
496     return 0
497            || test_mat_pixel_affine_yuv420sp(40, 40)
498            || test_mat_pixel_affine_yuv420sp(120, 160)
499            || test_mat_pixel_affine_yuv420sp(220, 340);
500 }
501 
main()502 int main()
503 {
504     SRAND(7767517);
505 
506     return test_mat_pixel_affine_0() || test_mat_pixel_affine_1();
507 }
508