1 //
2 // ImageProcessTest.cpp
3 // MNNTests
4 //
5 // Created by MNN on 2019/01/10.
6 // Copyright © 2018, Alibaba Group Holding Limited
7 //
8
9 #include <MNN/ImageProcess.hpp>
10 #include <cmath>
11 #include <memory>
12 #include <map>
13 #include "MNNTestSuite.h"
14
15 using namespace MNN;
16 using namespace MNN::CV;
17
genSourceData(int h,int w,int bpp)18 static std::vector<uint8_t> genSourceData(int h, int w, int bpp) {
19 std::vector<uint8_t> source(h * w * bpp);
20 for (int y = 0; y < h; ++y) {
21 auto pixelY = source.data() + w * y * bpp;
22 int magicY = ((h - y) * (h - y)) % 79;
23 for (int x = 0; x < w; ++x) {
24 auto pixelX = pixelY + x * bpp;
25 int magicX = (x * x) % 113;
26 for (int p = 0; p < bpp; ++p) {
27 int magic = (magicX + magicY + p * p * p) % 255;
28 pixelX[p] = magic;
29 }
30 }
31 }
32 return source;
33 }
34
35 // format in {YUV_NV21, YUV_NV12, YUV_I420}
36 // dstFormat in {RGBA, BGRA, RGB, BGR, GRAY}
genYUVData(int h,int w,ImageFormat format,ImageFormat dstFormat,std::vector<uint8_t> & source,std::vector<uint8_t> & dest)37 static int genYUVData(int h, int w, ImageFormat format, ImageFormat dstFormat,
38 std::vector<uint8_t>& source, std::vector<uint8_t>& dest) {
39 // https://www.jianshu.com/p/e67f79f10c65
40 if (format != YUV_NV21 && format != YUV_NV12 && /* YUV420sp(bi-planer): NV12, NV21 */
41 format != YUV_I420 /* YUV420p(planer): I420 or YV12 */) {
42 return -1;
43 }
44 bool yuv420p = (format != YUV_NV12 && format != YUV_NV21);
45
46 int bpp = 0;
47 if (dstFormat == RGBA || dstFormat == BGRA) {
48 bpp = 4;
49 } else if (dstFormat == RGB || dstFormat == BGR) {
50 bpp = 3;
51 } else if (dstFormat == GRAY) {
52 bpp = 1;
53 }
54 if (bpp == 0) {
55 return -2;
56 }
57
58 // YUV420, Y: h*w, UV: (h/2)*(w/2)*2
59 int ySize = h * w, uvSize = (h/2)*(w/2)*2;
60 source.resize(ySize + uvSize);
61 dest.resize(h * w * bpp);
62
63 auto dstData = dest.data();
64 for (int y = 0; y < h; ++y) {
65 auto pixelY = source.data() + w * y;
66 auto pixelUV = source.data() + w * h + (y / 2) * (yuv420p ? w / 2 : w);
67 int magicY = ((h - y) * (h - y)) % 79;
68 for (int x = 0; x < w; ++x) {
69 int magicX = ((x % 113) * (x % 113)) % 113, xx = x / 2;
70 int yVal = (magicX + magicY) % 255;
71
72 int uVal, vVal;
73 int uIndex = (yuv420p ? xx : 2 * xx);
74 int vIndex = (yuv420p ? xx + (h/2)*(w/2) : 2 * xx + 1);
75 if (format != YUV_NV12 && format != YUV_I420) {
76 std::swap(uIndex, vIndex);
77 }
78 if (y % 2 == 0 && x % 2 == 0) {
79 magicX = ((((xx % 283) * (xx % 283)) % 283) * (((xx % 283) * (xx % 283)) % 283)) % 283;
80 uVal = (magicX + magicY) % 255;
81 vVal = (magicX + magicY * 179) % 255;
82 pixelUV[uIndex] = uVal;
83 pixelUV[vIndex] = vVal;
84 } else {
85 uVal = pixelUV[uIndex];
86 vVal = pixelUV[vIndex];
87 }
88 pixelY[x] = yVal;
89
90 int Y = yVal, U = uVal - 128, V = vVal - 128;
91 auto dstData = dest.data() + (y * w + x) * bpp;
92 if (dstFormat == GRAY) {
93 dstData[0] = Y;
94 continue;
95 }
96 Y = Y << 6;
97 #define CLAMP(x, minVal, maxVal) std::min(std::max((x), (minVal)), (maxVal))
98 int r = CLAMP((Y + 73 * V) >> 6, 0, 255);
99 int g = CLAMP((Y - 25 * U - 37 * V) >> 6, 0, 255);
100 int b = CLAMP((Y + 130 * U) >> 6, 0, 255);
101
102 dstData[0] = r;
103 dstData[1] = g;
104 dstData[2] = b;
105 if (dstFormat == BGRA || dstFormat == BGR) {
106 std::swap(dstData[0], dstData[2]);
107 }
108 if (bpp == 4) {
109 dstData[3] = 255;
110 }
111 }
112 }
113 return 0;
114 }
115
116 class ImageProcessGrayToGrayTest : public MNNTestCase {
117 public:
118 virtual ~ImageProcessGrayToGrayTest() = default;
run(int precision)119 virtual bool run(int precision) {
120 int w = 27, h = 1, size = w * h;
121 auto integers = genSourceData(h, w, 1);
122 std::vector<float> floats(size * 4);
123 std::shared_ptr<MNN::Tensor> tensor(
124 MNN::Tensor::create<float>(std::vector<int>{1, 1, h, w}, floats.data(), Tensor::CAFFE_C4));
125
126 ImageProcess::Config config;
127 config.sourceFormat = GRAY;
128 config.destFormat = GRAY;
129 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
130 process->convert(integers.data(), w, h, 0, tensor.get());
131 for (int i = 0; i < floats.size() / 4; ++i) {
132 int s = floats[4 * i + 0];
133 if (s != integers[i]) {
134 MNN_ERROR("Error for turn gray to float:%d, %d -> %f\n", i, integers[i], floats[4 * i]);
135 return false;
136 }
137 }
138 return true;
139 }
140 };
141 MNNTestSuiteRegister(ImageProcessGrayToGrayTest, "cv/image_process/gray_to_gray");
142
143 class ImageProcessGrayToGrayBilinearTransformTest : public MNNTestCase {
144 public:
145 virtual ~ImageProcessGrayToGrayBilinearTransformTest() = default;
run(int precision)146 virtual bool run(int precision) {
147 ImageProcess::Config config;
148 config.sourceFormat = GRAY;
149 config.destFormat = GRAY;
150 config.filterType = BILINEAR;
151 config.wrap = CLAMP_TO_EDGE;
152 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
153
154 int sw = 1280;
155 int sh = 720;
156 int dw = 360;
157 int dh = 640;
158 Matrix tr;
159 tr.setScale(1.0 / sw, 1.0 / sh);
160 tr.postRotate(30, 0.5f, 0.5f);
161 tr.postScale(dw, dh);
162 tr.invert(&tr);
163 process->setMatrix(tr);
164
165 auto integers = genSourceData(sh, sw, 1);
166 std::shared_ptr<Tensor> tensor(
167 Tensor::create<float>(std::vector<int>{1, 1, dw, dh}, nullptr, Tensor::CAFFE_C4));
168 for (int i = 0; i < 10; ++i) {
169 process->convert(integers.data(), sw, sh, 0, tensor.get());
170 }
171 auto floats = tensor->host<float>();
172 int expects[] = {18, 36, 14, 36, 18, 44, 30, 60, 50, 24};
173 for (int v = 0; v < 10; ++v) {
174 if (fabsf(floats[4 * v] - (float)expects[v]) >= 2) {
175 MNN_ERROR("Error for %d, %.f, correct=%d\n", v, floats[4 * v], expects[v]);
176 return false;
177 }
178 }
179 return true;
180 }
181 };
182 MNNTestSuiteRegister(ImageProcessGrayToGrayBilinearTransformTest, "cv/image_process/gray_to_gray_bilinear_transorm");
183
184 class ImageProcessGrayToGrayNearestTransformTest : public MNNTestCase {
185 public:
186 virtual ~ImageProcessGrayToGrayNearestTransformTest() = default;
run(int precision)187 virtual bool run(int precision) {
188 ImageProcess::Config config;
189 config.sourceFormat = GRAY;
190 config.destFormat = GRAY;
191 config.filterType = NEAREST;
192 config.wrap = ZERO;
193 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
194
195 int sw = 1280;
196 int sh = 720;
197 int dw = 360;
198 int dh = 640;
199 Matrix tr;
200 tr.setScale(1.0 / sw, 1.0 / sh);
201 tr.postRotate(90, 0.5f, 0.5f);
202 tr.postScale(dw, dh);
203 tr.invert(&tr);
204 process->setMatrix(tr);
205
206 auto integers = genSourceData(sh, sw, 1);
207 std::shared_ptr<Tensor> tensor(
208 Tensor::create<float>(std::vector<int>{1, 1, dw, dh}, nullptr, Tensor::CAFFE_C4));
209 for (int i = 0; i < 10; ++i) {
210 process->convert(integers.data(), sw, sh, 0, tensor.get());
211 }
212 auto floats = tensor->host<float>();
213 int expect[] = {0, 4, 16, 36, 64, 21, 65, 38, 19, 8};
214 for (int v = 0; v < 10; ++v) {
215 if ((int)(floats[4 * v]) != expect[v]) {
216 MNN_ERROR("Error for %d, %.f, correct=%d\n", v, floats[4 * v], expect[v]);
217 return false;
218 }
219 }
220 return true;
221 }
222 };
223 MNNTestSuiteRegister(ImageProcessGrayToGrayNearestTransformTest, "cv/image_process/gray_to_gray_nearest_transorm");
224
225 class ImageProcessGrayToRGBATest : public MNNTestCase {
226 public:
227 virtual ~ImageProcessGrayToRGBATest() = default;
run(int precision)228 virtual bool run(int precision) {
229 int w = 15, h = 1, size = w * h;
230 auto gray = genSourceData(h, w, 1);
231 std::vector<uint8_t> rgba(size * 4);
232 std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 4}, rgba.data()));
233
234 ImageProcess::Config config;
235 config.sourceFormat = GRAY;
236 config.destFormat = RGBA;
237 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
238 process->convert(gray.data(), w, h, 0, tensor.get());
239 for (int i = 0; i < size; ++i) {
240 int s = gray[i];
241 int r = rgba[4 * i + 0];
242 int g = rgba[4 * i + 1];
243 int b = rgba[4 * i + 2];
244
245 int y = s;
246 int a = rgba[4 * i + 3];
247
248 if (y != r || y != g || y != b || a != 255) {
249 MNN_ERROR("Turn gray to RGBA:%d, %d -> %d,%d,%d,%d\n", i, s, r, g, b, a);
250 return false;
251 }
252 }
253 return true;
254 }
255 };
256 MNNTestSuiteRegister(ImageProcessGrayToRGBATest, "cv/image_process/gray_to_rgba");
257
258 class ImageProcessBGRToGrayTest : public MNNTestCase {
259 public:
260 virtual ~ImageProcessBGRToGrayTest() = default;
run(int precision)261 virtual bool run(int precision) {
262 int w = 15, h = 1, size = w * h;
263 auto bgr = genSourceData(h, w, 3);
264 std::vector<uint8_t> gray(size);
265 std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 1}, gray.data()));
266
267 ImageProcess::Config config;
268 config.sourceFormat = BGR;
269 config.destFormat = GRAY;
270 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
271 process->convert(bgr.data(), w, h, 0, tensor.get());
272 for (int i = 0; i < size; ++i) {
273 int s = gray[i];
274 int r = bgr[3 * i + 2];
275 int g = bgr[3 * i + 1];
276 int b = bgr[3 * i + 0];
277 int y = (19 * r + 38 * g + 7 * b) >> 6;
278 if (abs(y - s) >= 2) {
279 MNN_ERROR("Turn BGR to gray:%d, %d,%d,%d -> %d\n", i, r, g, b, s);
280 return false;
281 }
282 }
283 return true;
284 }
285 };
286 MNNTestSuiteRegister(ImageProcessBGRToGrayTest, "cv/image_process/bgr_to_gray");
287
288 class ImageProcessRGBToBGRTest : public MNNTestCase {
289 public:
run(int precision)290 virtual bool run(int precision) {
291 int w = 27, h = 1, size = w * h;
292 auto integers = genSourceData(h, w, 3);
293 std::vector<uint8_t> resultData(size * 3);
294 std::shared_ptr<MNN::Tensor> tensor(
295 MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 3}, resultData.data(), Tensor::TENSORFLOW));
296 ImageProcess::Config config;
297 config.sourceFormat = RGB;
298 config.destFormat = BGR;
299
300 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
301 process->convert(integers.data(), w, h, 0, tensor.get());
302 for (int i = 0; i < size; ++i) {
303 int r = resultData[3 * i + 2];
304 int g = resultData[3 * i + 1];
305 int b = resultData[3 * i + 0];
306 if (r != integers[3 * i + 0] || g != integers[3 * i + 1] || b != integers[3 * i + 2]) {
307 MNN_ERROR("Error for turn rgb to bgr:\n %d,%d,%d->%d, %d, %d\n", integers[3 * i + 0],
308 integers[3 * i + 1], integers[3 * i + 2], r, g, b);
309 return false;
310 }
311 }
312 return true;
313 }
314 };
315 MNNTestSuiteRegister(ImageProcessRGBToBGRTest, "cv/image_process/rgb_to_bgr");
316
317 class ImageProcessRGBAToBGRATest : public MNNTestCase {
318 public:
319 virtual ~ImageProcessRGBAToBGRATest() = default;
run(int precision)320 virtual bool run(int precision) {
321 int w = 27, h = 1, size = w * h;
322 auto integers = genSourceData(h, w, 4);
323 std::vector<uint8_t> floats(size * 4);
324 std::shared_ptr<MNN::Tensor> tensor(
325 MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 4}, floats.data(), Tensor::TENSORFLOW));
326 ImageProcess::Config config;
327 config.sourceFormat = RGBA;
328 config.destFormat = BGRA;
329
330 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
331 process->convert(integers.data(), w, h, 0, tensor.get());
332 for (int i = 0; i < floats.size() / 4; ++i) {
333 int r = floats[4 * i + 2];
334 int g = floats[4 * i + 1];
335 int b = floats[4 * i + 0];
336 if (r != integers[4 * i + 0] || g != integers[4 * i + 1] || b != integers[4 * i + 2]) {
337 MNN_ERROR("Error for turn rgba to bgra:\n %d,%d,%d->%d, %d, %d, %d\n", integers[4 * i + 0],
338 integers[4 * i + 1], integers[4 * i + 2], floats[4 * i + 0], floats[4 * i + 1],
339 floats[4 * i + 2], floats[4 * i + 3]);
340 return false;
341 }
342 }
343 return true;
344 }
345 };
346 MNNTestSuiteRegister(ImageProcessRGBAToBGRATest, "cv/image_process/rgba_to_bgra");
347
348 class ImageProcessBGRToBGRTest : public MNNTestCase {
349 public:
350 virtual ~ImageProcessBGRToBGRTest() = default;
run(int precision)351 virtual bool run(int precision) {
352 int w = 27, h = 1, size = w * h;
353 auto integers = genSourceData(h, w, 3);
354 std::vector<float> floats(size * 4);
355 std::shared_ptr<MNN::Tensor> tensor(
356 MNN::Tensor::create<float>(std::vector<int>{1, 1, h, w}, floats.data(), Tensor::CAFFE_C4));
357 ImageProcess::Config config;
358 config.sourceFormat = BGR;
359 config.destFormat = BGR;
360
361 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
362 process->convert(integers.data(), w, h, 0, tensor.get());
363 for (int i = 0; i < floats.size() / 4; ++i) {
364 int r = floats[4 * i + 0];
365 int g = floats[4 * i + 1];
366 int b = floats[4 * i + 2];
367 if (r != integers[3 * i + 0] || g != integers[3 * i + 1] || b != integers[3 * i + 2]) {
368 MNN_ERROR("Error for turn rgb to float:\n %d,%d,%d->%f, %f, %f, %f\n", integers[3 * i + 0],
369 integers[3 * i + 1], integers[3 * i + 2], floats[4 * i + 0], floats[4 * i + 1],
370 floats[4 * i + 2], floats[4 * i + 3]);
371 return false;
372 }
373 }
374 return true;
375 }
376 };
377 MNNTestSuiteRegister(ImageProcessBGRToBGRTest, "cv/image_process/bgr_to_bgr");
378
379 class ImageProcessRGBToGrayTest : public MNNTestCase {
380 public:
381 virtual ~ImageProcessRGBToGrayTest() = default;
run(int precision)382 virtual bool run(int precision) {
383 int w = 15, h = 1, size = w * h;
384 auto rgb = genSourceData(h, w, 3);
385 std::vector<uint8_t> gray(size);
386 std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 1}, gray.data()));
387 ImageProcess::Config config;
388 config.sourceFormat = RGB;
389 config.destFormat = GRAY;
390
391 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
392 process->convert(rgb.data(), w, h, 0, tensor.get());
393 for (int i = 0; i < size; ++i) {
394 int s = gray[i];
395 int r = rgb[3 * i + 0];
396 int g = rgb[3 * i + 1];
397 int b = rgb[3 * i + 2];
398 int y = (19 * r + 38 * g + 7 * b) >> 6;
399 if (abs(y - s) >= 2) {
400 MNN_ERROR("Error: Turn RGB to gray:%d, %d,%d,%d -> %d\n", i, r, g, b, s);
401 return false;
402 }
403 }
404 return true;
405 }
406 };
407 MNNTestSuiteRegister(ImageProcessRGBToGrayTest, "cv/image_process/rgb_to_gray");
408
409 class ImageProcessRGBAToGrayTest : public MNNTestCase {
410 public:
411 virtual ~ImageProcessRGBAToGrayTest() = default;
run(int precision)412 virtual bool run(int precision) {
413 int w = 15, h = 1, size = w * h;
414 auto rgba = genSourceData(h, w, 4);
415 std::vector<uint8_t> gray(size);
416 std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 1}, gray.data()));
417
418 ImageProcess::Config config;
419 config.sourceFormat = RGBA;
420 config.destFormat = GRAY;
421 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
422 process->convert(rgba.data(), w, h, 0, tensor.get());
423 for (int i = 0; i < size; ++i) {
424 int s = gray[i];
425 int r = rgba[4 * i + 0];
426 int g = rgba[4 * i + 1];
427 int b = rgba[4 * i + 2];
428 int y = (19 * r + 38 * g + 7 * b) >> 6;
429 if (abs(y - s) >= 2) {
430 MNN_ERROR("Turn RGBA to gray:%d, %d,%d,%d -> %d\n", i, r, g, b, s);
431 return false;
432 }
433 }
434 return true;
435 }
436 };
437 MNNTestSuiteRegister(ImageProcessRGBAToGrayTest, "cv/image_process/rgba_to_gray");
438
439 class ImageProcessRGBAToGrayBilinearTransformTest : public MNNTestCase {
440 public:
441 virtual ~ImageProcessRGBAToGrayBilinearTransformTest() = default;
run(int precision)442 virtual bool run(int precision) {
443 ImageProcess::Config config;
444 config.sourceFormat = RGBA;
445 config.destFormat = GRAY;
446 config.filterType = BILINEAR;
447 config.wrap = CLAMP_TO_EDGE;
448 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
449
450 int sw = 1280;
451 int sh = 720;
452 int dw = 360;
453 int dh = 640;
454 Matrix tr;
455 tr.setScale(1.0 / sw, 1.0 / sh);
456 tr.postRotate(30, 0.5f, 0.5f);
457 tr.postScale(dw, dh);
458 tr.invert(&tr);
459 process->setMatrix(tr);
460
461 auto integers = genSourceData(sh, sw, 4);
462 std::shared_ptr<Tensor> tensor(
463 Tensor::create<float>(std::vector<int>{1, 1, dw, dh}, nullptr, Tensor::CAFFE_C4));
464 process->convert(integers.data(), sw, sh, 0, tensor.get());
465 auto floats = tensor->host<float>();
466 int expect[] = {19, 37, 15, 37, 19, 45, 31, 61, 51, 25};
467 for (int v = 0; v < 10; ++v) {
468 if (fabsf(floats[4 * v] - (float)expect[v]) >= 2) {
469 MNN_ERROR("Error for %d, %.f, correct=%d\n", v, floats[4 * v], expect[v]);
470 return false;
471 }
472 }
473 return true;
474 }
475 };
476 MNNTestSuiteRegister(ImageProcessRGBAToGrayBilinearTransformTest, "cv/image_process/rgba_to_gray_bilinear_transorm");
477
478 class ImageProcessRGBAToGrayNearestTransformTest : public MNNTestCase {
479 public:
480 virtual ~ImageProcessRGBAToGrayNearestTransformTest() = default;
run(int precision)481 virtual bool run(int precision) {
482 ImageProcess::Config config;
483 config.sourceFormat = RGBA;
484 config.destFormat = GRAY;
485 config.filterType = NEAREST;
486 config.wrap = CLAMP_TO_EDGE;
487 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
488
489 int sw = 1280;
490 int sh = 720;
491 int dw = 360;
492 int dh = 640;
493 Matrix tr;
494 tr.setScale(1.0 / sw, 1.0 / sh);
495 tr.postRotate(60, 0.5f, 0.5f);
496 tr.postScale(dw, dh);
497 tr.invert(&tr);
498 process->setMatrix(tr);
499
500 auto integers = genSourceData(sh, sw, 4);
501 std::shared_ptr<Tensor> tensor(
502 Tensor::create<float>(std::vector<int>{1, 1, dw, dh}, nullptr, Tensor::CAFFE_C4));
503 for (int i = 0; i < 10; ++i) {
504 process->convert(integers.data(), sw, sh, 0, tensor.get());
505 }
506 auto floats = tensor->host<float>();
507 int expect[] = {3, 50, 26, 17, 5, 1, 5, 10, 26, 50};
508 for (int v = 0; v < 10; ++v) {
509 if ((int)(floats[4 * v]) != expect[v]) {
510 MNN_ERROR("Error for %d, %.f, correct=%d\n", v, floats[4 * v], expect[v]);
511 return false;
512 }
513 }
514 return true;
515 }
516 };
517 MNNTestSuiteRegister(ImageProcessRGBAToGrayNearestTransformTest, "cv/image_process/rgba_to_gray_nearest_transorm");
518
519 class ImageProcessRGBAToBGRTest : public MNNTestCase {
520 public:
521 virtual ~ImageProcessRGBAToBGRTest() = default;
run(int precision)522 virtual bool run(int precision) {
523 int w = 15, h = 1, size = w * h;
524 auto rgba = genSourceData(h, w, 4);
525 std::vector<uint8_t> bgr(size * 3);
526 std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, h, w, 3}, bgr.data()));
527
528 ImageProcess::Config config;
529 config.sourceFormat = RGBA;
530 config.destFormat = BGR;
531 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
532 process->convert(rgba.data(), w, h, 0, tensor.get());
533 for (int i = 0; i < size; ++i) {
534 if (rgba[4 * i + 0] != bgr[3 * i + 2] || rgba[4 * i + 1] != bgr[3 * i + 1] ||
535 rgba[4 * i + 2] != bgr[3 * i + 0]) {
536 MNN_ERROR("Error: Turn RGBA to BGR:%d, %d,%d,%d,%d -> %d,%d,%d\n", i, rgba[4 * i + 0], rgba[4 * i + 1],
537 rgba[4 * i + 2], rgba[4 * i + 3], bgr[3 * i + 0], bgr[3 * i + 1], bgr[3 * i + 2]);
538 return false;
539 }
540 }
541 return true;
542 }
543 };
544 MNNTestSuiteRegister(ImageProcessRGBAToBGRTest, "cv/image_process/rgba_to_bgr");
545
546 // Test for _blitC3ToFloatC3
547 class ImageProcessBGRToBGRFloatBlitterTest : public MNNTestCase {
548 public:
549 virtual ~ImageProcessBGRToBGRFloatBlitterTest() = default;
run(int precision)550 virtual bool run(int precision) {
551 int w = 27, h = 27, size = w * h;
552 auto integers = genSourceData(h, w, 3);
553 std::vector<float> floats(size * 3);
554 std::shared_ptr<MNN::Tensor> tensor(
555 MNN::Tensor::create<float>(std::vector<int>{1, h, w, 3}, floats.data(), Tensor::TENSORFLOW));
556 ImageProcess::Config config;
557 config.sourceFormat = BGR;
558 config.destFormat = BGR;
559
560 const float means[3] = {127.5f, 127.5f, 127.5f};
561 const float normals[3] = {2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f};
562 memcpy(config.mean, means, sizeof(means));
563 memcpy(config.normal, normals, sizeof(normals));
564
565 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
566 process->convert(integers.data(), w, h, 0, tensor.get());
567 for (int i = 0; i < size; ++i) {
568 for (int j = 0; j < 3; ++j) {
569 float result = floats[3 * i + j];
570 float right = (integers[3 * i + j] - means[j]) * normals[j];
571 if (fabs(result - right) > 1e-6f) {
572 MNN_ERROR("Error for blitter bgr to bgr\n%d -> %f, right: %f\n", integers[3 * i + j], result,
573 right);
574 return false;
575 }
576 }
577 }
578 return true;
579 }
580 };
581 MNNTestSuiteRegister(ImageProcessBGRToBGRFloatBlitterTest, "cv/image_process/bgr_to_bgr_blitter");
582
583 // Test for _blitC1ToFloatC1
584 class ImageProcessGrayToGrayFloatBlitterTest : public MNNTestCase {
585 public:
586 virtual ~ImageProcessGrayToGrayFloatBlitterTest() = default;
run(int precision)587 virtual bool run(int precision) {
588 int w = 27, h = 27, size = w * h;
589 auto integers = genSourceData(h, w, 1);
590 std::vector<float> floats(size);
591 std::shared_ptr<MNN::Tensor> tensor(
592 MNN::Tensor::create<float>(std::vector<int>{1, h, w, 1}, floats.data(), Tensor::TENSORFLOW));
593 ImageProcess::Config config;
594 config.sourceFormat = GRAY;
595 config.destFormat = GRAY;
596
597 const float means[1] = {127.5f};
598 const float normals[1] = {2.0f / 255.0f};
599 memcpy(config.mean, means, sizeof(means));
600 memcpy(config.normal, normals, sizeof(normals));
601
602 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
603 process->convert(integers.data(), w, h, 0, tensor.get());
604 for (int i = 0; i < size; ++i) {
605 float result = floats[i];
606 float right = (integers[i] - means[0]) * normals[0];
607 if (fabs(result - right) > 1e-6f) {
608 MNN_PRINT("raw: %d, result: %f, right: %f\n", integers[i], result, right);
609 MNN_ERROR("Error for blitter gray to gray\n");
610 return false;
611 }
612 }
613 return true;
614 }
615 };
616 MNNTestSuiteRegister(ImageProcessGrayToGrayFloatBlitterTest, "cv/image_process/gray_to_gray_blitter");
617
618 class ImageProcessYUVTestCommmon : public MNNTestCase {
619 protected:
620 virtual ~ImageProcessYUVTestCommmon() = default;
test(ImageFormat sourceFormat,ImageFormat destFormat,int bpp,int sw,int sh)621 bool test(ImageFormat sourceFormat, ImageFormat destFormat, int bpp, int sw, int sh) {
622 std::map<ImageFormat, std::string> formatMap = {
623 {RGBA, "RGBA"}, {RGB, "RGB"}, {BGRA, "BGRA"}, {BGR, "BGR"}, {GRAY, "GRAY"},
624 {YUV_NV21, "NV21"}, {YUV_NV12, "NV12"}, {YUV_I420, "I420"}
625 };
626 auto sourceStr = formatMap[sourceFormat].c_str(), destStr = formatMap[destFormat].c_str();
627 //MNN_PRINT("%s_to_%s\n", sourceStr, destStr);
628
629 ImageProcess::Config config;
630 config.sourceFormat = sourceFormat;
631 config.destFormat = destFormat;
632 //config.filterType = NEAREST;
633 //config.wrap = CLAMP_TO_EDGE;
634 std::shared_ptr<ImageProcess> process(ImageProcess::create(config));
635
636 //Matrix tr;
637 //process->setMatrix(tr);
638 std::vector<uint8_t> src, dst;
639 genYUVData(sh, sw, sourceFormat, destFormat, src, dst);
640
641 std::shared_ptr<Tensor> tensor(
642 Tensor::create<uint8_t>(std::vector<int>{1, sh, sw, bpp}, nullptr, Tensor::TENSORFLOW));
643 process->convert(src.data(), sw, sh, 0, tensor.get());
644 for (int y = 0; y < sh; ++y) {
645 auto srcY_Y = src.data() + y * sw;
646 auto srcY_UV = src.data() + (y / 2) * (sw / 2) * 2 + sw * sh;
647 for (int x = 0; x < sw; ++x) {
648 auto rightData = dst.data() + (y * sw + x) * bpp;
649 auto testData = tensor->host<uint8_t>() + (y * sw + x) * bpp;
650
651 bool wrong = false;
652 for (int i = 0; i < bpp && !wrong; ++i) {
653 if (abs(rightData[i] - testData[i]) > 5) {
654 wrong = true;
655 }
656 }
657 if (wrong) {
658 int Y = srcY_Y[x], U = srcY_UV[(x / 2) * 2], V = srcY_UV[(x / 2) * 2 + 1];
659 MNN_ERROR("Error for %s to %s (%d, %d): %d, %d, %d -> ", sourceStr, destStr, y, x, Y, U, V);
660 for (int i = 0; i < bpp; ++i) {
661 MNN_ERROR("%d, ", rightData[i]);
662 }
663 MNN_ERROR("wrong:");
664 for (int i = 0; i < bpp; ++i) {
665 MNN_ERROR(" %d%s", testData[i], (i < bpp ? ",": ""));
666 }
667 MNN_ERROR("\n");
668 return false;
669 }
670 }
671 }
672 return true;
673 }
674 };
675
676 class ImageProcessYUVBlitterTest : public ImageProcessYUVTestCommmon {
677 public:
678 virtual ~ImageProcessYUVBlitterTest() = default;
run(int precision)679 virtual bool run(int precision) {
680 std::vector<ImageFormat> srcFromats = {YUV_NV21, YUV_NV12, YUV_I420};
681 std::vector<ImageFormat> dstFormats = {RGBA, RGB, BGRA, BGR, GRAY};
682 std::vector<int> bpps = {4, 3, 4, 3, 1};
683 bool succ = true;
684 for (auto srcFormat : srcFromats) {
685 for (int i = 0; i < dstFormats.size(); ++i) {
686 succ = succ && test(srcFormat, dstFormats[i], bpps[i], 1920, 1080);
687 }
688 }
689 return succ;
690 }
691 };
692 // {YUV_NV21, YUV_NV12, YUV_I420} -> {RGBA, RGB, BGRA, BGR, GRAY} unit test
693 MNNTestSuiteRegister(ImageProcessYUVBlitterTest, "cv/image_process/yuv_blitter");
694