blob: 9b5d0c4aa9f94e1635dd5de4fab27fd213e391a4 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Anthony Barbier6db0ff52018-01-05 10:59:12 +00002 * Copyright (c) 2016, 2018 ARM Limited.
Anthony Barbier6ff3b192017-09-04 18:44:23 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef __UTILS_UTILS_H__
25#define __UTILS_UTILS_H__
26
27#include "arm_compute/core/Helpers.h"
28#include "arm_compute/core/ITensor.h"
29#include "arm_compute/core/Types.h"
30#include "arm_compute/core/Validate.h"
steniu01bee466b2017-06-21 16:45:41 +010031#include "arm_compute/core/Window.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010032#include "arm_compute/runtime/Tensor.h"
Giorgio Arenacf3935f2017-10-26 17:14:13 +010033#include "libnpy/npy.hpp"
Anthony Barbier2a07e182017-08-04 18:20:27 +010034#include "support/ToolchainSupport.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010035
36#ifdef ARM_COMPUTE_CL
37#include "arm_compute/core/CL/OpenCL.h"
Isabella Gottardi02aabcc2017-10-12 17:28:51 +010038#include "arm_compute/runtime/CL/CLDistribution1D.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010039#include "arm_compute/runtime/CL/CLTensor.h"
40#endif /* ARM_COMPUTE_CL */
Anthony Barbier7068f992017-10-26 15:23:08 +010041#ifdef ARM_COMPUTE_GC
42#include "arm_compute/runtime/GLES_COMPUTE/GCTensor.h"
43#endif /* ARM_COMPUTE_GC */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010044
45#include <cstdlib>
46#include <cstring>
47#include <fstream>
48#include <iostream>
Giorgio Arenacf3935f2017-10-26 17:14:13 +010049#include <random>
50#include <string>
51#include <tuple>
52#include <vector>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010053
54namespace arm_compute
55{
56namespace utils
57{
Anthony Barbier6db0ff52018-01-05 10:59:12 +000058//FIXME: Delete once tests have been ported (COMPMID-782)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010059/** Signature of an example to run
60 *
61 * @param[in] argc Number of command line arguments
62 * @param[in] argv Command line arguments
63 */
Anthony Barbier6db0ff52018-01-05 10:59:12 +000064using example = void(int argc, char **argv);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010065
Anthony Barbier6db0ff52018-01-05 10:59:12 +000066//FIXME: Delete once tests have been ported (COMPMID-782)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010067/** Run an example and handle the potential exceptions it throws
68 *
69 * @param[in] argc Number of command line arguments
70 * @param[in] argv Command line arguments
71 * @param[in] func Pointer to the function containing the code to run
72 */
Anthony Barbier6db0ff52018-01-05 10:59:12 +000073int run_example(int argc, char **argv, example &func);
74
75/** Abstract Example class.
76 *
77 * All examples have to inherit from this class.
78 */
79class Example
80{
81public:
82 virtual void do_setup(int argc, char **argv) {};
83 virtual void do_run() {};
84 virtual void do_teardown() {};
85
86 /** Default destructor. */
87 virtual ~Example() = default;
88};
89
90/** Run an example and handle the potential exceptions it throws
91 *
92 * @param[in] argc Number of command line arguments
93 * @param[in] argv Command line arguments
94 * @param[in] example Example to run
95 */
96int run_example(int argc, char **argv, Example &example);
97
98template <typename T>
99int run_example(int argc, char **argv)
100{
101 T example;
102 return run_example(argc, argv, example);
103}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100104
105/** Draw a RGB rectangular window for the detected object
106 *
107 * @param[in, out] tensor Input tensor where the rectangle will be drawn on. Format supported: RGB888
108 * @param[in] rect Geometry of the rectangular window
109 * @param[in] r Red colour to use
110 * @param[in] g Green colour to use
111 * @param[in] b Blue colour to use
112 */
113void draw_detection_rectangle(arm_compute::ITensor *tensor, const arm_compute::DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b);
114
115/** Parse the ppm header from an input file stream. At the end of the execution,
116 * the file position pointer will be located at the first pixel stored in the ppm file
117 *
118 * @param[in] fs Input file stream to parse
119 *
120 * @return The width, height and max value stored in the header of the PPM file
121 */
122std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs);
123
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100124/** Parse the npy header from an input file stream. At the end of the execution,
125 * the file position pointer will be located at the first pixel stored in the npy file //TODO
126 *
127 * @param[in] fs Input file stream to parse
128 *
129 * @return The width and height stored in the header of the NPY file
130 */
131std::tuple<std::vector<unsigned long>, bool, std::string> parse_npy_header(std::ifstream &fs);
132
133/** Obtain numpy type string from DataType.
134 *
135 * @param[in] data_type Data type.
136 *
137 * @return numpy type string.
138 */
139inline std::string get_typestring(DataType data_type)
140{
141 // Check endianness
142 const unsigned int i = 1;
143 const char *c = reinterpret_cast<const char *>(&i);
144 std::string endianness;
145 if(*c == 1)
146 {
147 endianness = std::string("<");
148 }
149 else
150 {
151 endianness = std::string(">");
152 }
153 const std::string no_endianness("|");
154
155 switch(data_type)
156 {
157 case DataType::U8:
158 return no_endianness + "u" + support::cpp11::to_string(sizeof(uint8_t));
159 case DataType::S8:
160 return no_endianness + "i" + support::cpp11::to_string(sizeof(int8_t));
161 case DataType::U16:
162 return endianness + "u" + support::cpp11::to_string(sizeof(uint16_t));
163 case DataType::S16:
164 return endianness + "i" + support::cpp11::to_string(sizeof(int16_t));
165 case DataType::U32:
166 return endianness + "u" + support::cpp11::to_string(sizeof(uint32_t));
167 case DataType::S32:
168 return endianness + "i" + support::cpp11::to_string(sizeof(int32_t));
169 case DataType::U64:
170 return endianness + "u" + support::cpp11::to_string(sizeof(uint64_t));
171 case DataType::S64:
172 return endianness + "i" + support::cpp11::to_string(sizeof(int64_t));
173 case DataType::F32:
174 return endianness + "f" + support::cpp11::to_string(sizeof(float));
175 case DataType::F64:
176 return endianness + "f" + support::cpp11::to_string(sizeof(double));
177 case DataType::SIZET:
178 return endianness + "u" + support::cpp11::to_string(sizeof(size_t));
179 default:
180 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
181 }
182}
183
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100184/** Maps a tensor if needed
185 *
186 * @param[in] tensor Tensor to be mapped
187 * @param[in] blocking Specified if map is blocking or not
188 */
189template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100190inline void map(T &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100191{
192 ARM_COMPUTE_UNUSED(tensor);
193 ARM_COMPUTE_UNUSED(blocking);
194}
195
196/** Unmaps a tensor if needed
197 *
198 * @param tensor Tensor to be unmapped
199 */
200template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100201inline void unmap(T &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100202{
203 ARM_COMPUTE_UNUSED(tensor);
204}
205
206#ifdef ARM_COMPUTE_CL
207/** Maps a tensor if needed
208 *
209 * @param[in] tensor Tensor to be mapped
210 * @param[in] blocking Specified if map is blocking or not
211 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100212inline void map(CLTensor &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100213{
214 tensor.map(blocking);
215}
216
217/** Unmaps a tensor if needed
218 *
219 * @param tensor Tensor to be unmapped
220 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100221inline void unmap(CLTensor &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100222{
223 tensor.unmap();
224}
Isabella Gottardi02aabcc2017-10-12 17:28:51 +0100225
226/** Maps a distribution if needed
227 *
228 * @param[in] distribution Distribution to be mapped
229 * @param[in] blocking Specified if map is blocking or not
230 */
231inline void map(CLDistribution1D &distribution, bool blocking)
232{
233 distribution.map(blocking);
234}
235
236/** Unmaps a distribution if needed
237 *
238 * @param distribution Distribution to be unmapped
239 */
240inline void unmap(CLDistribution1D &distribution)
241{
242 distribution.unmap();
243}
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100244#endif /* ARM_COMPUTE_CL */
245
Anthony Barbier7068f992017-10-26 15:23:08 +0100246#ifdef ARM_COMPUTE_GC
247/** Maps a tensor if needed
248 *
249 * @param[in] tensor Tensor to be mapped
250 * @param[in] blocking Specified if map is blocking or not
251 */
252inline void map(GCTensor &tensor, bool blocking)
253{
254 tensor.map(blocking);
255}
256
257/** Unmaps a tensor if needed
258 *
259 * @param tensor Tensor to be unmapped
260 */
261inline void unmap(GCTensor &tensor)
262{
263 tensor.unmap();
264}
265#endif /* ARM_COMPUTE_GC */
266
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100267/** Class to load the content of a PPM file into an Image
268 */
269class PPMLoader
270{
271public:
272 PPMLoader()
273 : _fs(), _width(0), _height(0)
274 {
275 }
276 /** Open a PPM file and reads its metadata (Width, height)
277 *
278 * @param[in] ppm_filename File to open
279 */
280 void open(const std::string &ppm_filename)
281 {
282 ARM_COMPUTE_ERROR_ON(is_open());
283 try
284 {
285 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
286 _fs.open(ppm_filename, std::ios::in | std::ios::binary);
287
288 unsigned int max_val = 0;
289 std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
290
291 ARM_COMPUTE_ERROR_ON_MSG(max_val >= 256, "2 bytes per colour channel not supported in file %s", ppm_filename.c_str());
292 }
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000293 catch(std::runtime_error &e)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100294 {
295 ARM_COMPUTE_ERROR("Accessing %s: %s", ppm_filename.c_str(), e.what());
296 }
297 }
298 /** Return true if a PPM file is currently open
299 */
300 bool is_open()
301 {
302 return _fs.is_open();
303 }
304
305 /** Initialise an image's metadata with the dimensions of the PPM file currently open
306 *
307 * @param[out] image Image to initialise
308 * @param[in] format Format to use for the image (Must be RGB888 or U8)
309 */
310 template <typename T>
311 void init_image(T &image, arm_compute::Format format)
312 {
313 ARM_COMPUTE_ERROR_ON(!is_open());
314 ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::RGB888 && format != arm_compute::Format::U8);
315
316 // Use the size of the input PPM image
317 arm_compute::TensorInfo image_info(_width, _height, format);
318 image.allocator()->init(image_info);
319 }
320
321 /** Fill an image with the content of the currently open PPM file.
322 *
323 * @note If the image is a CLImage, the function maps and unmaps the image
324 *
325 * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened PPM).
326 */
327 template <typename T>
328 void fill_image(T &image)
329 {
330 ARM_COMPUTE_ERROR_ON(!is_open());
331 ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
332 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&image, arm_compute::Format::U8, arm_compute::Format::RGB888);
333 try
334 {
Anthony Barbier7068f992017-10-26 15:23:08 +0100335 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100336 map(image, true);
337
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100338 // Check if the file is large enough to fill the image
339 const size_t current_position = _fs.tellg();
340 _fs.seekg(0, std::ios_base::end);
341 const size_t end_position = _fs.tellg();
342 _fs.seekg(current_position, std::ios_base::beg);
343
344 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < image.info()->tensor_shape().total_size() * image.info()->element_size(),
345 "Not enough data in file");
346 ARM_COMPUTE_UNUSED(end_position);
347
348 switch(image.info()->format())
349 {
350 case arm_compute::Format::U8:
351 {
352 // We need to convert the data from RGB to grayscale:
353 // Iterate through every pixel of the image
354 arm_compute::Window window;
355 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
356 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
357
358 arm_compute::Iterator out(&image, window);
359
360 unsigned char red = 0;
361 unsigned char green = 0;
362 unsigned char blue = 0;
363
364 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
365 {
366 red = _fs.get();
367 green = _fs.get();
368 blue = _fs.get();
369
370 *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
371 },
372 out);
373
374 break;
375 }
376 case arm_compute::Format::RGB888:
377 {
378 // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
379 // Create a vertical window to iterate through the image's rows:
380 arm_compute::Window window;
381 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
382
383 arm_compute::Iterator out(&image, window);
384
385 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
386 {
387 // Copy one row from the input file to the current row of the image:
388 _fs.read(reinterpret_cast<std::fstream::char_type *>(out.ptr()), _width * image.info()->element_size());
389 },
390 out);
391
392 break;
393 }
394 default:
395 ARM_COMPUTE_ERROR("Unsupported format");
396 }
397
Anthony Barbier7068f992017-10-26 15:23:08 +0100398 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100399 unmap(image);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100400 }
401 catch(const std::ifstream::failure &e)
402 {
403 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
404 }
405 }
406
Gian Marco44ec2e72017-10-19 14:13:38 +0100407 /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open PPM file.
408 *
409 * @note If the image is a CLImage, the function maps and unmaps the image
410 *
411 * @param[in,out] tensor Tensor with 3 planes to fill (Must be allocated, and of matching dimensions with the opened PPM). Data types supported: U8/F32
412 * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
413 */
414 template <typename T>
415 void fill_planar_tensor(T &tensor, bool bgr = false)
416 {
417 ARM_COMPUTE_ERROR_ON(!is_open());
418 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::U8, DataType::F32);
419 ARM_COMPUTE_ERROR_ON(tensor.info()->dimension(0) != _width || tensor.info()->dimension(1) != _height || tensor.info()->dimension(2) != 3);
420
421 try
422 {
423 // Map buffer if creating a CLTensor
424 map(tensor, true);
425
426 // Check if the file is large enough to fill the image
427 const size_t current_position = _fs.tellg();
428 _fs.seekg(0, std::ios_base::end);
429 const size_t end_position = _fs.tellg();
430 _fs.seekg(current_position, std::ios_base::beg);
431
432 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size(),
433 "Not enough data in file");
434 ARM_COMPUTE_UNUSED(end_position);
435
436 // Iterate through every pixel of the image
437 arm_compute::Window window;
438 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
439 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
440 window.set(arm_compute::Window::DimZ, arm_compute::Window::Dimension(0, 1, 1));
441
442 arm_compute::Iterator out(&tensor, window);
443
444 unsigned char red = 0;
445 unsigned char green = 0;
446 unsigned char blue = 0;
447
448 size_t stride_z = tensor.info()->strides_in_bytes()[2];
449
450 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
451 {
452 red = _fs.get();
453 green = _fs.get();
454 blue = _fs.get();
455
456 switch(tensor.info()->data_type())
457 {
458 case arm_compute::DataType::U8:
459 {
460 *(out.ptr() + 0 * stride_z) = bgr ? blue : red;
461 *(out.ptr() + 1 * stride_z) = green;
462 *(out.ptr() + 2 * stride_z) = bgr ? red : blue;
463 break;
464 }
465 case arm_compute::DataType::F32:
466 {
467 *reinterpret_cast<float *>(out.ptr() + 0 * stride_z) = static_cast<float>(bgr ? blue : red);
468 *reinterpret_cast<float *>(out.ptr() + 1 * stride_z) = static_cast<float>(green);
469 *reinterpret_cast<float *>(out.ptr() + 2 * stride_z) = static_cast<float>(bgr ? red : blue);
470 break;
471 }
472 default:
473 {
474 ARM_COMPUTE_ERROR("Unsupported data type");
475 }
476 }
477 },
478 out);
479
480 // Unmap buffer if creating a CLTensor
481 unmap(tensor);
482 }
483 catch(const std::ifstream::failure &e)
484 {
485 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
486 }
487 }
488
Isabella Gottardia4c61882017-11-03 12:11:55 +0000489 /** Return the width of the currently open PPM file.
490 */
491 unsigned int width() const
492 {
493 return _width;
494 }
495
496 /** Return the height of the currently open PPM file.
497 */
498 unsigned int height() const
499 {
500 return _height;
501 }
502
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100503private:
504 std::ifstream _fs;
505 unsigned int _width, _height;
506};
507
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100508class NPYLoader
509{
510public:
511 NPYLoader()
512 : _fs(), _shape(), _fortran_order(false), _typestring()
513 {
514 }
515
516 /** Open a NPY file and reads its metadata
517 *
518 * @param[in] npy_filename File to open
519 */
520 void open(const std::string &npy_filename)
521 {
522 ARM_COMPUTE_ERROR_ON(is_open());
523 try
524 {
525 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
526 _fs.open(npy_filename, std::ios::in | std::ios::binary);
527
528 std::tie(_shape, _fortran_order, _typestring) = parse_npy_header(_fs);
529 }
530 catch(const std::ifstream::failure &e)
531 {
532 ARM_COMPUTE_ERROR("Accessing %s: %s", npy_filename.c_str(), e.what());
533 }
534 }
535 /** Return true if a NPY file is currently open */
536 bool is_open()
537 {
538 return _fs.is_open();
539 }
540
541 /** Return true if a NPY file is in fortran order */
542 bool is_fortran()
543 {
544 return _fortran_order;
545 }
546
Gian Marco0bc5a252017-12-04 13:55:08 +0000547 /** Initialise the tensor's metadata with the dimensions of the NPY file currently open
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100548 *
549 * @param[out] tensor Tensor to initialise
Gian Marco0bc5a252017-12-04 13:55:08 +0000550 * @param[in] dt Data type to use for the tensor
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100551 */
552 template <typename T>
Gian Marco0bc5a252017-12-04 13:55:08 +0000553 void init_tensor(T &tensor, arm_compute::DataType dt)
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100554 {
555 ARM_COMPUTE_ERROR_ON(!is_open());
Gian Marco0bc5a252017-12-04 13:55:08 +0000556 ARM_COMPUTE_ERROR_ON(dt != arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100557
558 // Use the size of the input NPY tensor
559 TensorShape shape;
560 shape.set_num_dimensions(_shape.size());
561 for(size_t i = 0; i < _shape.size(); ++i)
562 {
563 shape.set(i, _shape.at(i));
564 }
565
Gian Marco0bc5a252017-12-04 13:55:08 +0000566 arm_compute::TensorInfo tensor_info(shape, 1, dt);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100567 tensor.allocator()->init(tensor_info);
568 }
569
570 /** Fill a tensor with the content of the currently open NPY file.
571 *
572 * @note If the tensor is a CLTensor, the function maps and unmaps the tensor
573 *
574 * @param[in,out] tensor Tensor to fill (Must be allocated, and of matching dimensions with the opened NPY).
575 */
576 template <typename T>
577 void fill_tensor(T &tensor)
578 {
579 ARM_COMPUTE_ERROR_ON(!is_open());
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000580 ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100581 try
582 {
583 // Map buffer if creating a CLTensor
584 map(tensor, true);
585
586 // Check if the file is large enough to fill the tensor
587 const size_t current_position = _fs.tellg();
588 _fs.seekg(0, std::ios_base::end);
589 const size_t end_position = _fs.tellg();
590 _fs.seekg(current_position, std::ios_base::beg);
591
592 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size() * tensor.info()->element_size(),
593 "Not enough data in file");
594 ARM_COMPUTE_UNUSED(end_position);
595
596 // Check if the typestring matches the given one
597 std::string expect_typestr = get_typestring(tensor.info()->data_type());
598 ARM_COMPUTE_ERROR_ON_MSG(_typestring != expect_typestr, "Typestrings mismatch");
599
600 // Validate tensor shape
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000601 ARM_COMPUTE_ERROR_ON_MSG(_shape.size() != tensor.info()->tensor_shape().num_dimensions(), "Tensor ranks mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100602 if(_fortran_order)
603 {
604 for(size_t i = 0; i < _shape.size(); ++i)
605 {
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000606 ARM_COMPUTE_ERROR_ON_MSG(tensor.info()->tensor_shape()[i] != _shape[i], "Tensor dimensions mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100607 }
608 }
609 else
610 {
611 for(size_t i = 0; i < _shape.size(); ++i)
612 {
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000613 ARM_COMPUTE_ERROR_ON_MSG(tensor.info()->tensor_shape()[i] != _shape[_shape.size() - i - 1], "Tensor dimensions mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100614 }
615 }
616
Gian Marco0bc5a252017-12-04 13:55:08 +0000617 switch(tensor.info()->data_type())
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100618 {
Gian Marco0bc5a252017-12-04 13:55:08 +0000619 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100620 {
621 // Read data
622 if(tensor.info()->padding().empty())
623 {
624 // If tensor has no padding read directly from stream.
625 _fs.read(reinterpret_cast<char *>(tensor.buffer()), tensor.info()->total_size());
626 }
627 else
628 {
629 // If tensor has padding accessing tensor elements through execution window.
630 Window window;
631 window.use_tensor_dimensions(tensor.info()->tensor_shape());
632
633 execute_window_loop(window, [&](const Coordinates & id)
634 {
635 _fs.read(reinterpret_cast<char *>(tensor.ptr_to_element(id)), tensor.info()->element_size());
636 });
637 }
638
639 break;
640 }
641 default:
Gian Marco0bc5a252017-12-04 13:55:08 +0000642 ARM_COMPUTE_ERROR("Unsupported data type");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100643 }
644
645 // Unmap buffer if creating a CLTensor
646 unmap(tensor);
647 }
648 catch(const std::ifstream::failure &e)
649 {
650 ARM_COMPUTE_ERROR("Loading NPY file: %s", e.what());
651 }
652 }
653
654private:
655 std::ifstream _fs;
656 std::vector<unsigned long> _shape;
657 bool _fortran_order;
658 std::string _typestring;
659};
660
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100661/** Template helper function to save a tensor image to a PPM file.
662 *
663 * @note Only U8 and RGB888 formats supported.
664 * @note Only works with 2D tensors.
665 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
666 *
667 * @param[in] tensor The tensor to save as PPM file
668 * @param[in] ppm_filename Filename of the file to create.
669 */
670template <typename T>
671void save_to_ppm(T &tensor, const std::string &ppm_filename)
672{
673 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::RGB888, arm_compute::Format::U8);
674 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
675
676 std::ofstream fs;
677
678 try
679 {
680 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
681 fs.open(ppm_filename, std::ios::out | std::ios::binary);
682
683 const unsigned int width = tensor.info()->tensor_shape()[0];
684 const unsigned int height = tensor.info()->tensor_shape()[1];
685
686 fs << "P6\n"
687 << width << " " << height << " 255\n";
688
Anthony Barbier7068f992017-10-26 15:23:08 +0100689 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100690 map(tensor, true);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100691
692 switch(tensor.info()->format())
693 {
694 case arm_compute::Format::U8:
695 {
696 arm_compute::Window window;
697 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
698 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
699
700 arm_compute::Iterator in(&tensor, window);
701
702 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
703 {
704 const unsigned char value = *in.ptr();
705
706 fs << value << value << value;
707 },
708 in);
709
710 break;
711 }
712 case arm_compute::Format::RGB888:
713 {
714 arm_compute::Window window;
715 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, width));
716 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
717
718 arm_compute::Iterator in(&tensor, window);
719
720 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
721 {
722 fs.write(reinterpret_cast<std::fstream::char_type *>(in.ptr()), width * tensor.info()->element_size());
723 },
724 in);
725
726 break;
727 }
728 default:
729 ARM_COMPUTE_ERROR("Unsupported format");
730 }
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100731
Anthony Barbier7068f992017-10-26 15:23:08 +0100732 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100733 unmap(tensor);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100734 }
735 catch(const std::ofstream::failure &e)
736 {
737 ARM_COMPUTE_ERROR("Writing %s: (%s)", ppm_filename.c_str(), e.what());
738 }
739}
steniu01bee466b2017-06-21 16:45:41 +0100740
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100741/** Template helper function to save a tensor image to a NPY file.
742 *
Gian Marcobfa3b522017-12-12 10:08:38 +0000743 * @note Only F32 data type supported.
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100744 * @note Only works with 2D tensors.
745 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
746 *
747 * @param[in] tensor The tensor to save as NPY file
748 * @param[in] npy_filename Filename of the file to create.
749 * @param[in] fortran_order If true, save matrix in fortran order.
750 */
751template <typename T>
752void save_to_npy(T &tensor, const std::string &npy_filename, bool fortran_order)
753{
Gian Marcobfa3b522017-12-12 10:08:38 +0000754 ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100755 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
756
757 std::ofstream fs;
758
759 try
760 {
761 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
762 fs.open(npy_filename, std::ios::out | std::ios::binary);
763
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000764 const unsigned int width = tensor.info()->tensor_shape()[0];
765 const unsigned int height = tensor.info()->tensor_shape()[1];
766 std::vector<npy::ndarray_len_t> shape(2);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100767
768 if(!fortran_order)
769 {
770 shape[0] = height, shape[1] = width;
771 }
772 else
773 {
774 shape[0] = width, shape[1] = height;
775 }
776
777 // Map buffer if creating a CLTensor
778 map(tensor, true);
779
Gian Marcobfa3b522017-12-12 10:08:38 +0000780 switch(tensor.info()->data_type())
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100781 {
Gian Marcobfa3b522017-12-12 10:08:38 +0000782 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100783 {
784 std::vector<float> tmp; /* Used only to get the typestring */
785 npy::Typestring typestring_o{ tmp };
786 std::string typestring = typestring_o.str();
787
788 std::ofstream stream(npy_filename, std::ofstream::binary);
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000789 npy::write_header(stream, typestring, fortran_order, shape);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100790
791 arm_compute::Window window;
792 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
793 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
794
795 arm_compute::Iterator in(&tensor, window);
796
797 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
798 {
799 stream.write(reinterpret_cast<const char *>(in.ptr()), sizeof(float));
800 },
801 in);
802
803 break;
804 }
805 default:
806 ARM_COMPUTE_ERROR("Unsupported format");
807 }
808
809 // Unmap buffer if creating a CLTensor
810 unmap(tensor);
811 }
812 catch(const std::ofstream::failure &e)
813 {
814 ARM_COMPUTE_ERROR("Writing %s: (%s)", npy_filename.c_str(), e.what());
815 }
816}
817
steniu01bee466b2017-06-21 16:45:41 +0100818/** Load the tensor with pre-trained data from a binary file
819 *
820 * @param[in] tensor The tensor to be filled. Data type supported: F32.
821 * @param[in] filename Filename of the binary file to load from.
822 */
823template <typename T>
824void load_trained_data(T &tensor, const std::string &filename)
825{
826 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
827
828 std::ifstream fs;
829
830 try
831 {
832 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
833 // Open file
834 fs.open(filename, std::ios::in | std::ios::binary);
835
836 if(!fs.good())
837 {
838 throw std::runtime_error("Could not load binary data: " + filename);
839 }
840
Anthony Barbier7068f992017-10-26 15:23:08 +0100841 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100842 map(tensor, true);
843
steniu01bee466b2017-06-21 16:45:41 +0100844 Window window;
845
846 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, 1, 1));
847
848 for(unsigned int d = 1; d < tensor.info()->num_dimensions(); ++d)
849 {
850 window.set(d, Window::Dimension(0, tensor.info()->tensor_shape()[d], 1));
851 }
852
853 arm_compute::Iterator in(&tensor, window);
854
855 execute_window_loop(window, [&](const Coordinates & id)
856 {
857 fs.read(reinterpret_cast<std::fstream::char_type *>(in.ptr()), tensor.info()->tensor_shape()[0] * tensor.info()->element_size());
858 },
859 in);
860
Anthony Barbier7068f992017-10-26 15:23:08 +0100861 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100862 unmap(tensor);
steniu01bee466b2017-06-21 16:45:41 +0100863 }
864 catch(const std::ofstream::failure &e)
865 {
866 ARM_COMPUTE_ERROR("Writing %s: (%s)", filename.c_str(), e.what());
867 }
868}
869
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100870template <typename T>
871void fill_random_tensor(T &tensor, float lower_bound, float upper_bound)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100872{
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100873 std::random_device rd;
874 std::mt19937 gen(rd());
Anthony Barbier2a07e182017-08-04 18:20:27 +0100875
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100876 TensorShape shape(tensor.info()->dimension(0), tensor.info()->dimension(1));
877
878 Window window;
879 window.set(Window::DimX, Window::Dimension(0, shape.x(), 1));
880 window.set(Window::DimY, Window::Dimension(0, shape.y(), 1));
881
882 map(tensor, true);
883
884 Iterator it(&tensor, window);
885
Gian Marcobfa3b522017-12-12 10:08:38 +0000886 switch(tensor.info()->data_type())
Anthony Barbier2a07e182017-08-04 18:20:27 +0100887 {
Gian Marcobfa3b522017-12-12 10:08:38 +0000888 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100889 {
890 std::uniform_real_distribution<float> dist(lower_bound, upper_bound);
891
892 execute_window_loop(window, [&](const Coordinates & id)
893 {
894 *reinterpret_cast<float *>(it.ptr()) = dist(gen);
895 },
896 it);
897
898 break;
899 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100900 default:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100901 {
902 ARM_COMPUTE_ERROR("Unsupported format");
903 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100904 }
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100905
906 unmap(tensor);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100907}
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100908
909template <typename T>
Gian Marco0bc5a252017-12-04 13:55:08 +0000910void init_sgemm_output(T &dst, T &src0, T &src1, arm_compute::DataType dt)
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100911{
Gian Marco0bc5a252017-12-04 13:55:08 +0000912 dst.allocator()->init(TensorInfo(TensorShape(src1.info()->dimension(0), src0.info()->dimension(1)), 1, dt));
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100913}
914
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100915} // namespace utils
916} // namespace arm_compute
917#endif /* __UTILS_UTILS_H__*/