Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
catchNPZ.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Test visp::cnpy::npz_load() / visp::cnpy::npy_save() functions.
32 */
33
37#include <iostream>
38#include <visp3/core/vpConfig.h>
39#include <visp3/core/vpEndian.h>
40
41#if defined(VISP_HAVE_CATCH2) && \
42 (defined(_WIN32) || (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))) && \
43 defined(VISP_LITTLE_ENDIAN) && defined(VISP_HAVE_MINIZ) && defined(VISP_HAVE_WORKING_REGEX)
44
45#include <catch_amalgamated.hpp>
46
47#include <type_traits>
48#include <complex>
49#include <visp3/core/vpIoTools.h>
50#include <visp3/core/vpImage.h>
51
52#ifdef ENABLE_VISP_NAMESPACE
53using namespace VISP_NAMESPACE_NAME;
54#endif
55
56namespace
57{
58std::string createTmpDir()
59{
60 std::string directory_filename = vpIoTools::getTempPath() + "/testNPZ";
61
62 vpIoTools::makeDirectory(directory_filename);
63 return directory_filename;
64}
65}
66
67TEST_CASE("Test visp::cnpy::npy_load/npz_save", "[visp::cnpy I/O]")
68{
69 std::string directory_filename = createTmpDir();
70 REQUIRE(vpIoTools::checkDirectory(directory_filename));
71 std::string npz_filename = directory_filename + "/test_npz_read_write.npz";
72
73 SECTION("Read/Save string data")
74 {
75 const std::string save_string = "Open Source Visual Servoing Platform";
76 std::vector<char> vec_save_string(save_string.begin(), save_string.end());
77 const std::string identifier = "String";
78 visp::cnpy::npz_save(npz_filename, identifier, &vec_save_string[0], { vec_save_string.size() }, "w");
79
80 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
81 REQUIRE(npz_data.find(identifier) != npz_data.end());
82
83 visp::cnpy::NpyArray arr_string_data = npz_data[identifier];
84 std::vector<char> vec_arr_string_data = arr_string_data.as_vec<char>();
85 // For null-terminated character handling, see:
86 // https://stackoverflow.com/a/8247804
87 // https://stackoverflow.com/a/45491652
88 const std::string read_string(vec_arr_string_data.begin(), vec_arr_string_data.end());
89 CHECK(save_string == read_string);
90
91 // Direct variable access
92 visp::cnpy::NpyArray arr_string_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
93 std::vector<char> vec_arr_string_data_direct = arr_string_data_direct.as_vec<char>();
94
95 const std::string read_string_direct(vec_arr_string_data_direct.begin(), vec_arr_string_data_direct.end());
96 CHECK(read_string_direct == read_string);
97 }
98
99 SECTION("Read/Save multi-dimensional array")
100 {
101 const std::string identifier = "Array";
102 size_t height = 5, width = 7, channels = 3;
103 std::vector<int> save_vec_copy;
104 {
105 std::vector<int> save_vec;
106 save_vec.reserve(height*width*channels);
107 for (int i = 0; i < static_cast<int>(height*width*channels); ++i) {
108 save_vec.push_back(i);
109 }
110
111 visp::cnpy::npz_save(npz_filename, identifier, &save_vec[0], { height, width, channels }, "a"); // append
112 save_vec_copy = save_vec;
113 }
114
115 {
116 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
117 REQUIRE(npz_data.find(identifier) != npz_data.end());
118
119 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
120 std::vector<int> read_vec = arr_vec_data.as_vec<int>();
121
122 REQUIRE(save_vec_copy.size() == read_vec.size());
123 for (size_t i = 0; i < read_vec.size(); ++i) {
124 CHECK(save_vec_copy[i] == read_vec[i]);
125 }
126
127 // Direct variable access
128 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
129 std::vector<int> read_vec_direct = arr_vec_data_direct.as_vec<int>();
130
131 REQUIRE(read_vec_direct.size() == read_vec.size());
132 for (size_t i = 0; i < read_vec.size(); ++i) {
133 CHECK(read_vec_direct[i] == read_vec[i]);
134 }
135 }
136 }
137
138 SECTION("Read/Save vpImage<vpRGBa>")
139 {
140 // CHECK(std::is_trivially_copyable<vpRGBa>::value == true); // false
141 // CHECK(std::is_trivial<vpRGBa>::value == true); // false
142 CHECK(sizeof(vpRGBa) == (4 * sizeof(unsigned char)));
143
144 const std::string identifier = "vpImage<vpRGBa>";
145 vpImage<vpRGBa> I_save_copy;
146 {
147 vpImage<vpRGBa> I_save(11, 17);
148 for (unsigned int i = 0; i < I_save.getRows(); i++) {
149 for (unsigned int j = 0; j < I_save.getCols(); j++) {
150 I_save[i][j].R = 4 * (i*I_save.getCols() + j) + 0;
151 I_save[i][j].G = 4 * (i*I_save.getCols() + j) + 1;
152 I_save[i][j].B = 4 * (i*I_save.getCols() + j) + 2;
153 I_save[i][j].A = 4 * (i*I_save.getCols() + j) + 3;
154 }
155 }
156
157 visp::cnpy::npz_save(npz_filename, identifier, &I_save.bitmap[0], { I_save.getRows(), I_save.getCols() }, "a"); // append
158 I_save_copy = I_save;
159 }
160
161 {
162 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
163 REQUIRE(npz_data.find(identifier) != npz_data.end());
164
165 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
166 const bool copy_data = false;
167 vpImage<vpRGBa> I_read(arr_vec_data.data<vpRGBa>(), static_cast<unsigned int>(arr_vec_data.shape[0]),
168 static_cast<unsigned int>(arr_vec_data.shape[1]), copy_data);
169
170 CHECK(I_save_copy.getSize() == I_read.getSize());
171 CHECK(I_save_copy == I_read);
172
173 // Direct variable access
174 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
175 vpImage<vpRGBa> I_read_direct(arr_vec_data_direct.data<vpRGBa>(), static_cast<unsigned int>(arr_vec_data_direct.shape[0]),
176 static_cast<unsigned int>(arr_vec_data_direct.shape[1]), copy_data);
177
178 CHECK(I_read.getSize() == I_read_direct.getSize());
179 CHECK(I_read == I_read_direct);
180 }
181 }
182
183 SECTION("Read/Save std::complex<double>")
184 {
185 // Handling of std::complex<>?
186 // - https://github.com/rogersce/cnpy/blob/4e8810b1a8637695171ed346ce68f6984e585ef4/cnpy.cpp#L40-L42
187 // - https://github.com/rogersce/cnpy/blob/4e8810b1a8637695171ed346ce68f6984e585ef4/cnpy.h#L129
188 // https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable
189
190 // Next CHECK() call may fail when g++ < 5 (case on centos-7-2 ci). That's why we ensure that c++ standard is > 11
191 // See https://stackoverflow.com/questions/25123458/is-trivially-copyable-is-not-a-member-of-std
192#if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_11)
193 CHECK(std::is_trivially_copyable<std::complex<double>>::value == true);
194#endif
195 // https://en.cppreference.com/w/cpp/types/is_trivial
196 // CHECK(std::is_trivial<std::complex<double>>::value == true); // false
197
198 const std::string identifier = "std::complex<double>";
199 std::complex<double> complex_data_copy;
200 {
201 std::complex<double> complex_data(99, 3.14);
202 visp::cnpy::npz_save(npz_filename, identifier, &complex_data, { 1 }, "a"); // append
203 complex_data_copy = complex_data;
204 }
205
206 {
207 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
208 REQUIRE(npz_data.find(identifier) != npz_data.end());
209
210 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
211 std::complex<double> complex_data_read = *arr_vec_data.data<std::complex<double>>();
212
213 CHECK(complex_data_copy.real() == complex_data_read.real());
214 CHECK(complex_data_copy.imag() == complex_data_read.imag());
215
216 // Direct variable access
217 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
218 std::complex<double> complex_data_read_direct = *arr_vec_data.data<std::complex<double>>();
219
220 CHECK(complex_data_read_direct.real() == complex_data_read.real());
221 CHECK(complex_data_read_direct.imag() == complex_data_read.imag());
222 }
223 }
224
225 SECTION("Read/Save std::vector<std::complex<double>>")
226 {
227 const std::string identifier = "std::vector<std::complex<double>>";
228 std::vector<std::complex<double>> vec_complex_data_copy;
229 {
230 std::vector<std::complex<double>> vec_complex_data;
231 std::complex<double> complex_data(99, 3.14);
232 vec_complex_data.push_back(complex_data);
233
234 complex_data.real(-77.12);
235 complex_data.imag(-100.95);
236 vec_complex_data.push_back(complex_data);
237
238 visp::cnpy::npz_save(npz_filename, identifier, &vec_complex_data[0], { vec_complex_data.size() }, "a"); // append
239 vec_complex_data_copy = vec_complex_data;
240 }
241
242 {
243 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
244 REQUIRE(npz_data.find(identifier) != npz_data.end());
245
246 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
247 std::vector<std::complex<double>> vec_complex_data_read = arr_vec_data.as_vec<std::complex<double>>();
248
249 REQUIRE(vec_complex_data_copy.size() == vec_complex_data_read.size());
250 for (size_t i = 0; i < vec_complex_data_copy.size(); i++) {
251 CHECK(vec_complex_data_copy[i].real() == vec_complex_data_read[i].real());
252 CHECK(vec_complex_data_copy[i].imag() == vec_complex_data_read[i].imag());
253 }
254
255 // Direct variable access
256 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
257 std::vector<std::complex<double>> vec_complex_data_read_direct = arr_vec_data_direct.as_vec<std::complex<double>>();
258
259 REQUIRE(vec_complex_data_read_direct.size() == vec_complex_data_read.size());
260 for (size_t i = 0; i < vec_complex_data_read_direct.size(); i++) {
261 CHECK(vec_complex_data_read_direct[i].real() == vec_complex_data_read[i].real());
262 CHECK(vec_complex_data_read_direct[i].imag() == vec_complex_data_read[i].imag());
263 }
264 }
265 }
266
267 SECTION("Read/Save vpHomogeneousMatrix")
268 {
269 const std::string identifier = "vpHomogeneousMatrix";
270 vpHomogeneousMatrix cMo_save_copy;
271 {
272 vpHomogeneousMatrix cMo_save(vpTranslationVector(10, 20, 30), vpThetaUVector(0.1, 0.2, 0.3));
273 // std::cout << "cMo_save:\n" << cMo_save << std::endl;
274
275 visp::cnpy::npz_save(npz_filename, identifier, &cMo_save.data[0], { cMo_save.getRows(), cMo_save.getCols() }, "a"); // append
276 cMo_save_copy = cMo_save;
277 }
278
279 {
280 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
281 REQUIRE(npz_data.find(identifier) != npz_data.end());
282
283 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
284 vpHomogeneousMatrix cMo_read(arr_vec_data.as_vec<double>());
285 // std::cout << "cMo_read:\n" << cMo_read << std::endl;
286
287 CHECK(cMo_save_copy == cMo_read);
288
289 // Direct variable access
290 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
291 vpHomogeneousMatrix cMo_read_direct(arr_vec_data_direct.as_vec<double>());
292
293 CHECK(cMo_read_direct == cMo_read);
294 }
295 }
296
297 SECTION("Read/Save std::vector<vpHomogeneousMatrix>")
298 {
299 const std::string identifier = "std::vector<vpHomogeneousMatrix>";
300 std::vector<vpHomogeneousMatrix> vec_cMo_save_copy;
301 {
302 std::vector<double> vec_cMo_save;
303 for (size_t i = 0; i < 5; i++) {
304 vpHomogeneousMatrix cMo_save(vpTranslationVector(1. +10.*i, 2. +20.*i, 3. +30.*i), vpThetaUVector(0.1+i, 0.2+i, 0.3+i));
305 vec_cMo_save_copy.push_back(cMo_save);
306 vec_cMo_save.insert(vec_cMo_save.end(), cMo_save.data, cMo_save.data+cMo_save.size());
307 // std::cout << "cMo_save:\n" << cMo_save << std::endl;
308 }
309
310 visp::cnpy::npz_save(npz_filename, identifier, &vec_cMo_save[0], { vec_cMo_save.size()/16, 16 }, "a"); // append
311 }
312
313 {
314 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
315 REQUIRE(npz_data.find(identifier) != npz_data.end());
316
317 visp::cnpy::NpyArray arr_vec_data = npz_data[identifier];
318 std::vector<double> vec_cMo_read = arr_vec_data.as_vec<double>();
319 REQUIRE(vec_cMo_save_copy.size() == arr_vec_data.shape[0]);
320
321 for (size_t i = 0; i < arr_vec_data.shape[0]; i++) {
322 std::vector<double>::const_iterator first = vec_cMo_read.begin() + i*arr_vec_data.shape[1];
323 std::vector<double>::const_iterator last = first + arr_vec_data.shape[1];
324 std::vector<double> subvec_cMo_read(first, last);
325 vpHomogeneousMatrix cMo_read(subvec_cMo_read);
326 // std::cout << "cMo_read:\n" << cMo_read << std::endl;
327 CHECK(vec_cMo_save_copy[i] == cMo_read);
328 }
329
330 // Direct variable access
331 visp::cnpy::NpyArray arr_vec_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
332 std::vector<double> vec_cMo_read_direct = arr_vec_data_direct.as_vec<double>();
333 REQUIRE(arr_vec_data_direct.shape.size() == arr_vec_data.shape.size());
334 REQUIRE(arr_vec_data_direct.shape[0] == arr_vec_data.shape[0]);
335
336 for (size_t i = 0; i < arr_vec_data_direct.shape[0]; i++) {
337 std::vector<double>::const_iterator first = vec_cMo_read_direct.begin() + i*arr_vec_data_direct.shape[1];
338 std::vector<double>::const_iterator last = first + arr_vec_data_direct.shape[1];
339 std::vector<double> subvec_cMo_read_direct(first, last);
340 vpHomogeneousMatrix cMo_read_direct(subvec_cMo_read_direct);
341 // std::cout << "cMo_read:\n" << cMo_read << std::endl;
342 CHECK(vec_cMo_save_copy[i] == cMo_read_direct);
343 }
344 }
345 }
346 REQUIRE(vpIoTools::remove(directory_filename));
347 REQUIRE(!vpIoTools::checkDirectory(directory_filename));
348}
349
350// https://en.cppreference.com/w/cpp/types/integer
351// https://github.com/catchorg/Catch2/blob/devel/docs/test-cases-and-sections.md#type-parametrised-test-cases
352using BasicTypes = std::tuple<uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, float, double>;
353TEMPLATE_LIST_TEST_CASE("Test visp::cnpy::npy_load/npz_save", "[BasicTypes][list]", BasicTypes)
354{
355 std::string directory_filename = createTmpDir();
356 REQUIRE(vpIoTools::checkDirectory(directory_filename));
357 std::string npz_filename = directory_filename + "/test_npz_read_write.npz";
358
359 std::string identifier = "data";
360 TestType save_data_copy;
361 {
362 TestType save_data = std::numeric_limits<TestType>::min();
363 visp::cnpy::npz_save(npz_filename, identifier, &save_data, { 1 }, "w");
364 save_data_copy = save_data;
365 }
366 {
367 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
368 REQUIRE(npz_data.find(identifier) != npz_data.end());
369 visp::cnpy::NpyArray arr_data = npz_data[identifier];
370 TestType read_data = *arr_data.data<TestType>();
371 CHECK(save_data_copy == read_data);
372
373 // Direct variable access
374 visp::cnpy::NpyArray arr_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
375 TestType read_data_direct = *arr_data_direct.data<TestType>();
376 CHECK(read_data_direct == read_data);
377 }
378
379 identifier = "data2";
380 {
381 TestType save_data = std::numeric_limits<TestType>::max();
382 visp::cnpy::npz_save(npz_filename, identifier, &save_data, { 1 }, "a"); // append
383 save_data_copy = save_data;
384 }
385 {
386 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
387 REQUIRE(npz_data.find(identifier) != npz_data.end());
388 visp::cnpy::NpyArray arr_data = npz_data[identifier];
389 TestType read_data = *arr_data.data<TestType>();
390 CHECK(save_data_copy == read_data);
391
392 // Direct variable access
393 visp::cnpy::NpyArray arr_data_direct = visp::cnpy::npz_load(npz_filename, identifier);
394 TestType read_data_direct = *arr_data_direct.data<TestType>();
395 CHECK(read_data_direct == read_data);
396 }
397 {
398 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
399 REQUIRE(npz_data.find(identifier) != npz_data.end());
400 visp::cnpy::NpyArray arr_data = npz_data[identifier];
401 TestType read_data = *arr_data.data<TestType>();
402 CHECK(save_data_copy == read_data);
403 }
404
405 REQUIRE(vpIoTools::remove(directory_filename));
406 REQUIRE(!vpIoTools::checkDirectory(directory_filename));
407}
408
409#if defined(VISP_HAVE_DATASET) && (VISP_HAVE_DATASET_VERSION >= 0x030703)
410namespace
411{
412void loadData(const std::string &npz_filename,
413 bool &false_data, bool &true_data, uint32_t &uint32_data, int64_t &int64_data,
414 float &float_data, double &double_data, std::string &string_data, std::complex<double> &complex_data,
415 std::vector<int> &vec_int, std::vector<float> &vec_flt, std::vector<std::string> &vec_string,
416 std::vector<std::complex<float>> &vec_complex, bool has_complex)
417{
418 const std::string bool_false_identifier = "My bool false data";
419 const std::string bool_true_identifier = "My bool true data";
420 const std::string uint32_identifier = "My uint32 data";
421 const std::string int64_identifier = "My int64 data";
422 const std::string float_identifier = "My float data";
423 const std::string double_identifier = "My double data";
424 const std::string string_identifier = "My string data";
425 const std::string complex_identifier = "My complex data";
426 const std::string matrix_int_identifier = "My int matrix data";
427 const std::string matrix_flt_identifier = "My float matrix data";
428 const std::string matrix_string_identifier = "My string matrix data";
429 const std::string vec_complex_identifier = "My complex vector data";
430
431 visp::cnpy::npz_t npz_data = visp::cnpy::npz_load(npz_filename);
432 visp::cnpy::npz_t::iterator it_bool_false = npz_data.find(bool_false_identifier);
433 visp::cnpy::npz_t::iterator it_bool_true = npz_data.find(bool_true_identifier);
434 visp::cnpy::npz_t::iterator it_uint32 = npz_data.find(uint32_identifier);
435 visp::cnpy::npz_t::iterator it_int64 = npz_data.find(int64_identifier);
436 visp::cnpy::npz_t::iterator it_float = npz_data.find(float_identifier);
437 visp::cnpy::npz_t::iterator it_double = npz_data.find(double_identifier);
438 visp::cnpy::npz_t::iterator it_string = npz_data.find(string_identifier);
439 visp::cnpy::npz_t::iterator it_matrix_int = npz_data.find(matrix_int_identifier);
440 visp::cnpy::npz_t::iterator it_matrix_flt = npz_data.find(matrix_flt_identifier);
441 visp::cnpy::npz_t::iterator it_matrix_string = npz_data.find(matrix_string_identifier);
442
443 REQUIRE(it_bool_false != npz_data.end());
444 REQUIRE(it_bool_true != npz_data.end());
445 REQUIRE(it_uint32 != npz_data.end());
446 REQUIRE(it_int64 != npz_data.end());
447 REQUIRE(it_float != npz_data.end());
448 REQUIRE(it_double != npz_data.end());
449 REQUIRE(it_string != npz_data.end());
450 REQUIRE(it_matrix_int != npz_data.end());
451 REQUIRE(it_matrix_flt != npz_data.end());
452 REQUIRE(it_matrix_string != npz_data.end());
453
454 visp::cnpy::NpyArray arr_data_bool_false = it_bool_false->second;
455 visp::cnpy::NpyArray arr_data_bool_true = it_bool_true->second;
456 visp::cnpy::NpyArray arr_data_uint32 = it_uint32->second;
457 visp::cnpy::NpyArray arr_data_int64 = it_int64->second;
458 visp::cnpy::NpyArray arr_data_float = it_float->second;
459 visp::cnpy::NpyArray arr_data_double = it_double->second;
460 visp::cnpy::NpyArray arr_data_string = it_string->second;
461 visp::cnpy::NpyArray arr_data_matrix_int = it_matrix_int->second;
462 visp::cnpy::NpyArray arr_data_matrix_flt = it_matrix_flt->second;
463 visp::cnpy::NpyArray arr_data_matrix_string = it_matrix_string->second;
464
465 false_data = *arr_data_bool_false.data<bool>();
466 true_data = *arr_data_bool_true.data<bool>();
467 uint32_data = *arr_data_uint32.data<uint32_t>();
468 int64_data = *arr_data_int64.data<int64_t>();
469 float_data = *arr_data_float.data<float>();
470 double_data = *arr_data_double.data<double>();
471 assert(!arr_data_string.as_utf8_string_vec().empty());
472 string_data = arr_data_string.as_utf8_string_vec()[0];
473 vec_int = arr_data_matrix_int.as_vec<int>();
474 vec_flt = arr_data_matrix_flt.as_vec<float>();
475 vec_string = arr_data_matrix_string.as_utf8_string_vec();
476
477 if (has_complex) {
478 visp::cnpy::npz_t::iterator it_complex = npz_data.find(complex_identifier);
479 REQUIRE(it_complex != npz_data.end());
480 visp::cnpy::NpyArray arr_data_complex = it_complex->second;
481 complex_data = *arr_data_complex.data<std::complex<double>>();
482
483 visp::cnpy::npz_t::iterator it_vec_complex = npz_data.find(vec_complex_identifier);
484 REQUIRE(it_vec_complex != npz_data.end());
485 visp::cnpy::NpyArray arr_data_vec_complex = it_vec_complex->second;
486 vec_complex = arr_data_vec_complex.as_vec<std::complex<float>>();
487 }
488}
489
490void getNpzGroundTruth(bool &gt_bool_false, bool &gt_bool_true, uint32_t &gt_uint32_data, int64_t &gt_int64_data,
491 float &gt_float_data, double &gt_double_data, std::string &gt_string_data, std::complex<double> &gt_complex_data,
492 std::vector<int> &gt_vec_int, std::vector<float> &gt_vec_flt, std::vector<std::string> &gt_vec_string,
493 std::vector<std::complex<float>> &gt_vec_complex_data)
494{
495 // ground-truth values
496 gt_bool_false = false;
497 gt_bool_true = true;
498 gt_uint32_data = 99;
499 gt_int64_data = -123456;
500 gt_float_data = -456.51f;
501 gt_double_data = 3.14;
502 gt_string_data = "ViSP: Open source Visual Servoing Platform";
503
504 gt_complex_data = std::complex<double>(gt_float_data, gt_double_data);
505 const size_t height = 5, width = 7, channels = 3;
506 for (int i = 0; i < static_cast<int>(height*width*channels); ++i) {
507 gt_vec_int.push_back(i);
508 gt_vec_flt.push_back(i);
509 }
510
511 gt_vec_string.push_back("ViSP ");
512 gt_vec_string.push_back("for ");
513 gt_vec_string.push_back("visual servoing: ");
514 gt_vec_string.push_back("a generic software platform ");
515 gt_vec_string.push_back("with a wide class of ");
516 gt_vec_string.push_back("robot control skills");
517
518 for (int i = 0; i < 3; i++) {
519 gt_vec_complex_data.push_back(std::complex<float>(gt_complex_data.real()*(i+1), gt_complex_data.imag()*2*(i+1)));
520 }
521}
522}
523
524TEST_CASE("Test little-endian / big-endian npz loading", "[visp::cnpy I/O]")
525{
526 const bool has_complex = true;
527 SECTION("Check little-endian correctness for npz loading")
528 {
529 const std::string npz_filename = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(),
530 "npz/visp_cnpy/visp_npz_test_data_cnpy_LE.npz");
531
532 // Ground-truth data
533 bool gt_bool_false, gt_bool_true;
534 uint32_t gt_uint32_data;
535 int64_t gt_int64_data;
536 float gt_float_data;
537 double gt_double_data;
538 std::string gt_string_data;
539 std::complex<double> gt_complex_data;
540 std::vector<int> gt_vec_int;
541 std::vector<float> gt_vec_flt;
542 std::vector<std::string> gt_vec_string;
543 std::vector<std::complex<float>> gt_vec_complex_data;
544 getNpzGroundTruth(gt_bool_false, gt_bool_true, gt_uint32_data, gt_int64_data, gt_float_data, gt_double_data,
545 gt_string_data, gt_complex_data, gt_vec_int, gt_vec_flt, gt_vec_string, gt_vec_complex_data);
546
547 // Load data
548 bool b_false = false, b_true = false;
549 uint32_t uint32_data = 0;
550 int64_t int64_data = 0;
551 float float_data = 0;
552 double double_data = 0;
553 std::string string_data = "";
554 std::complex<double> complex_data;
555 std::vector<int> vec_int;
556 std::vector<float> vec_flt;
557 std::vector<std::string> vec_string;
558 std::vector<std::complex<float>> vec_complex_data;
559 loadData(npz_filename, b_false, b_true, uint32_data, int64_data, float_data, double_data, string_data,
560 complex_data, vec_int, vec_flt, vec_string, vec_complex_data, has_complex);
561
562 CHECK(b_false == gt_bool_false);
563 CHECK(b_true == gt_bool_true);
564 CHECK(uint32_data == gt_uint32_data);
565 CHECK(int64_data == gt_int64_data);
566 CHECK(float_data == gt_float_data);
567 CHECK(double_data == gt_double_data);
568 CHECK(string_data == gt_string_data);
569 CHECK(complex_data.real() == gt_complex_data.real());
570 CHECK(complex_data.imag() == gt_complex_data.imag());
571
572 REQUIRE(gt_vec_int.size() == gt_vec_flt.size());
573 REQUIRE(gt_vec_int.size() == vec_int.size());
574 REQUIRE(gt_vec_int.size() == vec_flt.size());
575 for (size_t i = 0; i < gt_vec_int.size(); i++) {
576 REQUIRE(gt_vec_int[i] == vec_int[i]);
577 REQUIRE(gt_vec_flt[i] == vec_flt[i]);
578 }
579
580 REQUIRE(gt_vec_string.size() == vec_string.size());
581 for (size_t i = 0; i < gt_vec_string.size(); i++) {
582 REQUIRE(gt_vec_string[i] == vec_string[i]);
583 }
584
585 REQUIRE(gt_vec_complex_data.size() == vec_complex_data.size());
586 for (size_t i = 0; i < gt_vec_complex_data.size(); i++) {
587 REQUIRE(vec_complex_data[i].real() == gt_vec_complex_data[i].real());
588 REQUIRE(vec_complex_data[i].imag() == gt_vec_complex_data[i].imag());
589 }
590 }
591
592 SECTION("Check little-endian vs big_endian correctness for npz loading")
593 {
594 const std::string npz_filename_LE = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(),
595 "npz/visp_cnpy/visp_npz_test_data_cnpy_LE.npz");
596 const std::string npz_filename_BE = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(),
597 "npz/visp_cnpy/visp_npz_test_data_cnpy_BE.npz");
598
599 visp::cnpy::npz_t npz_data_LE = visp::cnpy::npz_load(npz_filename_LE);
600 visp::cnpy::npz_t npz_data_BE = visp::cnpy::npz_load(npz_filename_BE);
601
602 bool b_data_false_LE, b_data_false_BE;
603 bool b_data_true_LE, b_data_true_BE;
604 uint32_t uint32_data_LE, uint32_data_BE;
605 int64_t int64_data_LE, int64_data_BE;
606 float float_data_LE, float_data_BE;
607 double double_data_LE, double_data_BE;
608 std::string string_data_LE, string_data_BE;
609 std::complex<double> complex_data_LE, complex_data_BE;
610 std::vector<int> vec_int_LE, vec_int_BE;
611 std::vector<float> vec_flt_LE, vec_flt_BE;
612 std::vector<std::string> vec_string_LE, vec_string_BE;
613 std::vector<std::complex<float>> vec_complex_data_LE, vec_complex_data_BE;
614
615 loadData(npz_filename_LE, b_data_false_LE, b_data_true_LE, uint32_data_LE, int64_data_LE, float_data_LE,
616 double_data_LE, string_data_LE, complex_data_LE, vec_int_LE, vec_flt_LE, vec_string_LE, vec_complex_data_LE, has_complex);
617
618 loadData(npz_filename_BE, b_data_false_BE, b_data_true_BE, uint32_data_BE, int64_data_BE, float_data_BE,
619 double_data_BE, string_data_BE, complex_data_BE, vec_int_BE, vec_flt_BE, vec_string_BE, vec_complex_data_BE, has_complex);
620
621 CHECK(b_data_false_LE == b_data_false_BE);
622 CHECK(b_data_true_LE == b_data_true_BE);
623 CHECK(uint32_data_LE == uint32_data_BE);
624 CHECK(int64_data_LE == int64_data_BE);
625 CHECK(float_data_LE == float_data_BE);
626 CHECK(double_data_LE == double_data_BE);
627 CHECK(string_data_LE == string_data_BE);
628 CHECK(complex_data_LE.real() == complex_data_BE.real());
629 CHECK(complex_data_LE.imag() == complex_data_BE.imag());
630
631 REQUIRE(vec_int_LE.size() == vec_flt_LE.size());
632 REQUIRE(vec_int_LE.size() == vec_int_BE.size());
633 REQUIRE(vec_int_LE.size() == vec_flt_BE.size());
634 for (size_t i = 0; i < vec_int_LE.size(); i++) {
635 CHECK(vec_int_LE[i] == vec_int_BE[i]);
636 CHECK(vec_flt_LE[i] == vec_flt_BE[i]);
637 }
638
639 REQUIRE(vec_string_LE.size() == vec_string_BE.size());
640 for (size_t i = 0; i < vec_string_LE.size(); i++) {
641 CHECK(vec_string_LE[i] == vec_string_BE[i]);
642 }
643
644 REQUIRE(vec_complex_data_LE.size() == vec_complex_data_BE.size());
645 for (size_t i = 0; i < vec_complex_data_LE.size(); i++) {
646 CHECK(vec_complex_data_LE[i].real() == vec_complex_data_LE[i].real());
647 CHECK(vec_complex_data_LE[i].imag() == vec_complex_data_LE[i].imag());
648 }
649 }
650}
651
652TEST_CASE("Test loading correctness wrt. NumPy generated npz", "[visp::cnpy I/O]")
653{
654 const bool has_complex = false;
655
656 // Ground-truth data
657 bool gt_bool_false, gt_bool_true;
658 uint32_t gt_uint32_data;
659 int64_t gt_int64_data;
660 float gt_float_data;
661 double gt_double_data;
662 std::string gt_string_data;
663 std::complex<double> gt_complex_data;
664 std::vector<int> gt_vec_int;
665 std::vector<float> gt_vec_flt;
666 std::vector<std::string> gt_vec_string;
667 std::vector<std::complex<float>> gt_vec_complex_data;
668 getNpzGroundTruth(gt_bool_false, gt_bool_true, gt_uint32_data, gt_int64_data, gt_float_data, gt_double_data,
669 gt_string_data, gt_complex_data, gt_vec_int, gt_vec_flt, gt_vec_string, gt_vec_complex_data);
670
671 SECTION("Check little-endian correctness")
672 {
673 const std::string npz_filename = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(),
674 "npz/numpy/visp_npz_test_data_numpy_LE.npz");
675
676 bool bool_false = false, bool_true = false;
677 uint32_t uint32_data = 0;
678 int64_t int64_data = 0;
679 float float_data;
680 double double_data;
681 std::string string_data;
682 std::complex<double> complex_data;
683 std::vector<int> vec_int;
684 std::vector<float> vec_flt;
685 std::vector<std::string> vec_string;
686 std::vector<std::complex<float>> vec_complex_data;
687
688 loadData(npz_filename, bool_false, bool_true, uint32_data, int64_data, float_data, double_data, string_data,
689 complex_data, vec_int, vec_flt, vec_string, vec_complex_data, has_complex);
690
691 CHECK(bool_false == gt_bool_false);
692 CHECK(bool_true == gt_bool_true);
693 CHECK(uint32_data == gt_uint32_data);
694 CHECK(int64_data == gt_int64_data);
695 CHECK(float_data == gt_float_data);
696 CHECK(double_data == gt_double_data);
697 CHECK(string_data == gt_string_data);
698
699 REQUIRE(gt_vec_int.size() == gt_vec_flt.size());
700 REQUIRE(gt_vec_int.size() == vec_int.size());
701 REQUIRE(gt_vec_int.size() == vec_flt.size());
702 for (size_t i = 0; i < gt_vec_int.size(); i++) {
703 CHECK(gt_vec_int[i] == vec_int[i]);
704 CHECK(gt_vec_flt[i] == vec_flt[i]);
705 }
706 REQUIRE(gt_vec_string.size() == vec_string.size());
707 for (size_t i = 0; i < gt_vec_string.size(); i++) {
708 CHECK(gt_vec_string[i] == vec_string[i]);
709 }
710 }
711
712 SECTION("Check big-endian correctness")
713 {
714 const std::string npz_filename = vpIoTools::createFilePath(vpIoTools::getViSPImagesDataPath(),
715 "npz/numpy/visp_npz_test_data_numpy_BE.npz");
716
717 bool bool_false = false, bool_true = false;
718 uint32_t uint32_data = 0;
719 int64_t int64_data = 0;
720 float float_data;
721 double double_data;
722 std::string string_data;
723 std::complex<double> complex_data;
724 std::vector<int> vec_int;
725 std::vector<float> vec_flt;
726 std::vector<std::string> vec_string;
727 std::vector<std::complex<float>> vec_complex_data;
728
729 loadData(npz_filename, bool_false, bool_true, uint32_data, int64_data, float_data, double_data, string_data,
730 complex_data, vec_int, vec_flt, vec_string, vec_complex_data, has_complex);
731
732 CHECK(bool_false == gt_bool_false);
733 CHECK(bool_true == gt_bool_true);
734 CHECK(uint32_data == gt_uint32_data);
735 CHECK(int64_data == gt_int64_data);
736 CHECK(float_data == gt_float_data);
737 CHECK(double_data == gt_double_data);
738 CHECK(string_data == gt_string_data);
739
740 REQUIRE(gt_vec_int.size() == gt_vec_flt.size());
741 REQUIRE(gt_vec_int.size() == vec_int.size());
742 REQUIRE(gt_vec_int.size() == vec_flt.size());
743 for (size_t i = 0; i < gt_vec_int.size(); i++) {
744 CHECK(gt_vec_int[i] == vec_int[i]);
745 CHECK(gt_vec_flt[i] == vec_flt[i]);
746 }
747
748 REQUIRE(gt_vec_string.size() == vec_string.size());
749 for (size_t i = 0; i < gt_vec_string.size(); i++) {
750 CHECK(gt_vec_string[i] == vec_string[i]);
751 }
752 }
753}
754#endif
755
756int main(int argc, char *argv[])
757{
758 Catch::Session session;
759 session.applyCommandLine(argc, argv);
760 int numFailed = session.run();
761 return numFailed;
762}
763
764#else
765int main() { return EXIT_SUCCESS; }
766#endif
Implementation of an homogeneous matrix and operations on such kind of matrices.
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getSize() const
Definition vpImage.h:221
static std::string getViSPImagesDataPath()
static std::string getTempPath()
static bool checkDirectory(const std::string &dirname)
static std::string createFilePath(const std::string &parent, const std::string &child)
static void makeDirectory(const std::string &dirname)
static bool remove(const std::string &filename)
Implementation of a rotation vector as axis-angle minimal representation.
Class that consider the case of a translation vector.
read_data(CameraParameters|None cam_depth, ImageGray I, rs.pipeline pipe)
VISP_EXPORT npz_t npz_load(const std::string &fname)
std::map< std::string, NpyArray > npz_t
Definition vpIoTools.h:177
VISP_EXPORT void npz_save(const std::string &zipname, std::string fname, const std::vector< std::string > &data_vec, const std::vector< size_t > &shape, const std::string &mode="w")
std::vector< size_t > shape
Definition vpIoTools.h:170
std::vector< T > as_vec() const
Definition vpIoTools.h:124
std::vector< std::string > as_utf8_string_vec() const
Definition vpIoTools.h:138