blob: 73b6d4f83233df7a805f7a85c41e62999100d3d9 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Giorgio Arenaa66eaa22017-12-21 19:50:06 +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/** Abstract Example class.
59 *
60 * All examples have to inherit from this class.
61 */
62class Example
63{
64public:
Alex Gildayc357c472018-03-21 13:54:09 +000065 /** Setup the example.
66 *
67 * @param[in] argc Argument count.
68 * @param[in] argv Argument values.
69 */
Anthony Barbier6db0ff52018-01-05 10:59:12 +000070 virtual void do_setup(int argc, char **argv) {};
Alex Gildayc357c472018-03-21 13:54:09 +000071 /** Run the example. */
Anthony Barbier6db0ff52018-01-05 10:59:12 +000072 virtual void do_run() {};
Alex Gildayc357c472018-03-21 13:54:09 +000073 /** Teardown the example. */
Anthony Barbier6db0ff52018-01-05 10:59:12 +000074 virtual void do_teardown() {};
75
76 /** Default destructor. */
77 virtual ~Example() = default;
78};
79
80/** Run an example and handle the potential exceptions it throws
81 *
82 * @param[in] argc Number of command line arguments
83 * @param[in] argv Command line arguments
84 * @param[in] example Example to run
85 */
86int run_example(int argc, char **argv, Example &example);
87
88template <typename T>
89int run_example(int argc, char **argv)
90{
91 T example;
92 return run_example(argc, argv, example);
93}
Anthony Barbier6ff3b192017-09-04 18:44:23 +010094
95/** Draw a RGB rectangular window for the detected object
96 *
97 * @param[in, out] tensor Input tensor where the rectangle will be drawn on. Format supported: RGB888
98 * @param[in] rect Geometry of the rectangular window
99 * @param[in] r Red colour to use
100 * @param[in] g Green colour to use
101 * @param[in] b Blue colour to use
102 */
103void draw_detection_rectangle(arm_compute::ITensor *tensor, const arm_compute::DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b);
104
105/** Parse the ppm header from an input file stream. At the end of the execution,
106 * the file position pointer will be located at the first pixel stored in the ppm file
107 *
108 * @param[in] fs Input file stream to parse
109 *
110 * @return The width, height and max value stored in the header of the PPM file
111 */
112std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs);
113
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100114/** Parse the npy header from an input file stream. At the end of the execution,
115 * the file position pointer will be located at the first pixel stored in the npy file //TODO
116 *
117 * @param[in] fs Input file stream to parse
118 *
119 * @return The width and height stored in the header of the NPY file
120 */
121std::tuple<std::vector<unsigned long>, bool, std::string> parse_npy_header(std::ifstream &fs);
122
123/** Obtain numpy type string from DataType.
124 *
125 * @param[in] data_type Data type.
126 *
127 * @return numpy type string.
128 */
129inline std::string get_typestring(DataType data_type)
130{
131 // Check endianness
132 const unsigned int i = 1;
133 const char *c = reinterpret_cast<const char *>(&i);
134 std::string endianness;
135 if(*c == 1)
136 {
137 endianness = std::string("<");
138 }
139 else
140 {
141 endianness = std::string(">");
142 }
143 const std::string no_endianness("|");
144
145 switch(data_type)
146 {
147 case DataType::U8:
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000148 case DataType::QASYMM8:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100149 return no_endianness + "u" + support::cpp11::to_string(sizeof(uint8_t));
150 case DataType::S8:
151 return no_endianness + "i" + support::cpp11::to_string(sizeof(int8_t));
152 case DataType::U16:
153 return endianness + "u" + support::cpp11::to_string(sizeof(uint16_t));
154 case DataType::S16:
155 return endianness + "i" + support::cpp11::to_string(sizeof(int16_t));
156 case DataType::U32:
157 return endianness + "u" + support::cpp11::to_string(sizeof(uint32_t));
158 case DataType::S32:
159 return endianness + "i" + support::cpp11::to_string(sizeof(int32_t));
160 case DataType::U64:
161 return endianness + "u" + support::cpp11::to_string(sizeof(uint64_t));
162 case DataType::S64:
163 return endianness + "i" + support::cpp11::to_string(sizeof(int64_t));
164 case DataType::F32:
165 return endianness + "f" + support::cpp11::to_string(sizeof(float));
166 case DataType::F64:
167 return endianness + "f" + support::cpp11::to_string(sizeof(double));
168 case DataType::SIZET:
169 return endianness + "u" + support::cpp11::to_string(sizeof(size_t));
170 default:
171 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
172 }
173}
174
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100175/** Maps a tensor if needed
176 *
177 * @param[in] tensor Tensor to be mapped
178 * @param[in] blocking Specified if map is blocking or not
179 */
180template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100181inline void map(T &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100182{
183 ARM_COMPUTE_UNUSED(tensor);
184 ARM_COMPUTE_UNUSED(blocking);
185}
186
187/** Unmaps a tensor if needed
188 *
189 * @param tensor Tensor to be unmapped
190 */
191template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100192inline void unmap(T &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100193{
194 ARM_COMPUTE_UNUSED(tensor);
195}
196
197#ifdef ARM_COMPUTE_CL
198/** Maps a tensor if needed
199 *
200 * @param[in] tensor Tensor to be mapped
201 * @param[in] blocking Specified if map is blocking or not
202 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100203inline void map(CLTensor &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100204{
205 tensor.map(blocking);
206}
207
208/** Unmaps a tensor if needed
209 *
210 * @param tensor Tensor to be unmapped
211 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100212inline void unmap(CLTensor &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100213{
214 tensor.unmap();
215}
Isabella Gottardi02aabcc2017-10-12 17:28:51 +0100216
217/** Maps a distribution if needed
218 *
219 * @param[in] distribution Distribution to be mapped
220 * @param[in] blocking Specified if map is blocking or not
221 */
222inline void map(CLDistribution1D &distribution, bool blocking)
223{
224 distribution.map(blocking);
225}
226
227/** Unmaps a distribution if needed
228 *
229 * @param distribution Distribution to be unmapped
230 */
231inline void unmap(CLDistribution1D &distribution)
232{
233 distribution.unmap();
234}
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100235#endif /* ARM_COMPUTE_CL */
236
Anthony Barbier7068f992017-10-26 15:23:08 +0100237#ifdef ARM_COMPUTE_GC
238/** Maps a tensor if needed
239 *
240 * @param[in] tensor Tensor to be mapped
241 * @param[in] blocking Specified if map is blocking or not
242 */
243inline void map(GCTensor &tensor, bool blocking)
244{
245 tensor.map(blocking);
246}
247
248/** Unmaps a tensor if needed
249 *
250 * @param tensor Tensor to be unmapped
251 */
252inline void unmap(GCTensor &tensor)
253{
254 tensor.unmap();
255}
256#endif /* ARM_COMPUTE_GC */
257
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100258/** Class to load the content of a PPM file into an Image
259 */
260class PPMLoader
261{
262public:
263 PPMLoader()
264 : _fs(), _width(0), _height(0)
265 {
266 }
267 /** Open a PPM file and reads its metadata (Width, height)
268 *
269 * @param[in] ppm_filename File to open
270 */
271 void open(const std::string &ppm_filename)
272 {
273 ARM_COMPUTE_ERROR_ON(is_open());
274 try
275 {
276 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
277 _fs.open(ppm_filename, std::ios::in | std::ios::binary);
278
279 unsigned int max_val = 0;
280 std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
281
282 ARM_COMPUTE_ERROR_ON_MSG(max_val >= 256, "2 bytes per colour channel not supported in file %s", ppm_filename.c_str());
283 }
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000284 catch(std::runtime_error &e)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100285 {
286 ARM_COMPUTE_ERROR("Accessing %s: %s", ppm_filename.c_str(), e.what());
287 }
288 }
289 /** Return true if a PPM file is currently open
290 */
291 bool is_open()
292 {
293 return _fs.is_open();
294 }
295
296 /** Initialise an image's metadata with the dimensions of the PPM file currently open
297 *
298 * @param[out] image Image to initialise
299 * @param[in] format Format to use for the image (Must be RGB888 or U8)
300 */
301 template <typename T>
302 void init_image(T &image, arm_compute::Format format)
303 {
304 ARM_COMPUTE_ERROR_ON(!is_open());
305 ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::RGB888 && format != arm_compute::Format::U8);
306
307 // Use the size of the input PPM image
308 arm_compute::TensorInfo image_info(_width, _height, format);
309 image.allocator()->init(image_info);
310 }
311
312 /** Fill an image with the content of the currently open PPM file.
313 *
314 * @note If the image is a CLImage, the function maps and unmaps the image
315 *
316 * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened PPM).
317 */
318 template <typename T>
319 void fill_image(T &image)
320 {
321 ARM_COMPUTE_ERROR_ON(!is_open());
322 ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
323 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&image, arm_compute::Format::U8, arm_compute::Format::RGB888);
324 try
325 {
Anthony Barbier7068f992017-10-26 15:23:08 +0100326 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100327 map(image, true);
328
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100329 // Check if the file is large enough to fill the image
330 const size_t current_position = _fs.tellg();
331 _fs.seekg(0, std::ios_base::end);
332 const size_t end_position = _fs.tellg();
333 _fs.seekg(current_position, std::ios_base::beg);
334
335 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < image.info()->tensor_shape().total_size() * image.info()->element_size(),
336 "Not enough data in file");
337 ARM_COMPUTE_UNUSED(end_position);
338
339 switch(image.info()->format())
340 {
341 case arm_compute::Format::U8:
342 {
343 // We need to convert the data from RGB to grayscale:
344 // Iterate through every pixel of the image
345 arm_compute::Window window;
346 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
347 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
348
349 arm_compute::Iterator out(&image, window);
350
351 unsigned char red = 0;
352 unsigned char green = 0;
353 unsigned char blue = 0;
354
355 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
356 {
357 red = _fs.get();
358 green = _fs.get();
359 blue = _fs.get();
360
361 *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
362 },
363 out);
364
365 break;
366 }
367 case arm_compute::Format::RGB888:
368 {
369 // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
370 // Create a vertical window to iterate through the image's rows:
371 arm_compute::Window window;
372 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
373
374 arm_compute::Iterator out(&image, window);
375
376 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
377 {
378 // Copy one row from the input file to the current row of the image:
379 _fs.read(reinterpret_cast<std::fstream::char_type *>(out.ptr()), _width * image.info()->element_size());
380 },
381 out);
382
383 break;
384 }
385 default:
386 ARM_COMPUTE_ERROR("Unsupported format");
387 }
388
Anthony Barbier7068f992017-10-26 15:23:08 +0100389 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100390 unmap(image);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100391 }
392 catch(const std::ifstream::failure &e)
393 {
394 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
395 }
396 }
397
Gian Marco44ec2e72017-10-19 14:13:38 +0100398 /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open PPM file.
399 *
400 * @note If the image is a CLImage, the function maps and unmaps the image
401 *
402 * @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
403 * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
404 */
405 template <typename T>
406 void fill_planar_tensor(T &tensor, bool bgr = false)
407 {
408 ARM_COMPUTE_ERROR_ON(!is_open());
409 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::U8, DataType::F32);
410 ARM_COMPUTE_ERROR_ON(tensor.info()->dimension(0) != _width || tensor.info()->dimension(1) != _height || tensor.info()->dimension(2) != 3);
411
412 try
413 {
414 // Map buffer if creating a CLTensor
415 map(tensor, true);
416
417 // Check if the file is large enough to fill the image
418 const size_t current_position = _fs.tellg();
419 _fs.seekg(0, std::ios_base::end);
420 const size_t end_position = _fs.tellg();
421 _fs.seekg(current_position, std::ios_base::beg);
422
423 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size(),
424 "Not enough data in file");
425 ARM_COMPUTE_UNUSED(end_position);
426
427 // Iterate through every pixel of the image
428 arm_compute::Window window;
429 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
430 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
431 window.set(arm_compute::Window::DimZ, arm_compute::Window::Dimension(0, 1, 1));
432
433 arm_compute::Iterator out(&tensor, window);
434
435 unsigned char red = 0;
436 unsigned char green = 0;
437 unsigned char blue = 0;
438
439 size_t stride_z = tensor.info()->strides_in_bytes()[2];
440
441 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
442 {
443 red = _fs.get();
444 green = _fs.get();
445 blue = _fs.get();
446
447 switch(tensor.info()->data_type())
448 {
449 case arm_compute::DataType::U8:
450 {
451 *(out.ptr() + 0 * stride_z) = bgr ? blue : red;
452 *(out.ptr() + 1 * stride_z) = green;
453 *(out.ptr() + 2 * stride_z) = bgr ? red : blue;
454 break;
455 }
456 case arm_compute::DataType::F32:
457 {
458 *reinterpret_cast<float *>(out.ptr() + 0 * stride_z) = static_cast<float>(bgr ? blue : red);
459 *reinterpret_cast<float *>(out.ptr() + 1 * stride_z) = static_cast<float>(green);
460 *reinterpret_cast<float *>(out.ptr() + 2 * stride_z) = static_cast<float>(bgr ? red : blue);
461 break;
462 }
463 default:
464 {
465 ARM_COMPUTE_ERROR("Unsupported data type");
466 }
467 }
468 },
469 out);
470
471 // Unmap buffer if creating a CLTensor
472 unmap(tensor);
473 }
474 catch(const std::ifstream::failure &e)
475 {
476 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
477 }
478 }
479
Isabella Gottardia4c61882017-11-03 12:11:55 +0000480 /** Return the width of the currently open PPM file.
481 */
482 unsigned int width() const
483 {
484 return _width;
485 }
486
487 /** Return the height of the currently open PPM file.
488 */
489 unsigned int height() const
490 {
491 return _height;
492 }
493
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100494private:
495 std::ifstream _fs;
496 unsigned int _width, _height;
497};
498
Alex Gildayc357c472018-03-21 13:54:09 +0000499/** Numpy data loader */
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100500class NPYLoader
501{
502public:
Alex Gildayc357c472018-03-21 13:54:09 +0000503 /** Default constructor */
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100504 NPYLoader()
505 : _fs(), _shape(), _fortran_order(false), _typestring()
506 {
507 }
508
509 /** Open a NPY file and reads its metadata
510 *
511 * @param[in] npy_filename File to open
512 */
513 void open(const std::string &npy_filename)
514 {
515 ARM_COMPUTE_ERROR_ON(is_open());
516 try
517 {
518 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
519 _fs.open(npy_filename, std::ios::in | std::ios::binary);
520
521 std::tie(_shape, _fortran_order, _typestring) = parse_npy_header(_fs);
522 }
523 catch(const std::ifstream::failure &e)
524 {
525 ARM_COMPUTE_ERROR("Accessing %s: %s", npy_filename.c_str(), e.what());
526 }
527 }
528 /** Return true if a NPY file is currently open */
529 bool is_open()
530 {
531 return _fs.is_open();
532 }
533
534 /** Return true if a NPY file is in fortran order */
535 bool is_fortran()
536 {
537 return _fortran_order;
538 }
539
Gian Marco0bc5a252017-12-04 13:55:08 +0000540 /** Initialise the tensor's metadata with the dimensions of the NPY file currently open
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100541 *
542 * @param[out] tensor Tensor to initialise
Gian Marco0bc5a252017-12-04 13:55:08 +0000543 * @param[in] dt Data type to use for the tensor
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100544 */
545 template <typename T>
Gian Marco0bc5a252017-12-04 13:55:08 +0000546 void init_tensor(T &tensor, arm_compute::DataType dt)
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100547 {
548 ARM_COMPUTE_ERROR_ON(!is_open());
Gian Marco0bc5a252017-12-04 13:55:08 +0000549 ARM_COMPUTE_ERROR_ON(dt != arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100550
551 // Use the size of the input NPY tensor
552 TensorShape shape;
553 shape.set_num_dimensions(_shape.size());
554 for(size_t i = 0; i < _shape.size(); ++i)
555 {
556 shape.set(i, _shape.at(i));
557 }
558
Gian Marco0bc5a252017-12-04 13:55:08 +0000559 arm_compute::TensorInfo tensor_info(shape, 1, dt);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100560 tensor.allocator()->init(tensor_info);
561 }
562
563 /** Fill a tensor with the content of the currently open NPY file.
564 *
565 * @note If the tensor is a CLTensor, the function maps and unmaps the tensor
566 *
567 * @param[in,out] tensor Tensor to fill (Must be allocated, and of matching dimensions with the opened NPY).
568 */
569 template <typename T>
570 void fill_tensor(T &tensor)
571 {
572 ARM_COMPUTE_ERROR_ON(!is_open());
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000573 ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100574 try
575 {
576 // Map buffer if creating a CLTensor
577 map(tensor, true);
578
579 // Check if the file is large enough to fill the tensor
580 const size_t current_position = _fs.tellg();
581 _fs.seekg(0, std::ios_base::end);
582 const size_t end_position = _fs.tellg();
583 _fs.seekg(current_position, std::ios_base::beg);
584
585 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size() * tensor.info()->element_size(),
586 "Not enough data in file");
587 ARM_COMPUTE_UNUSED(end_position);
588
589 // Check if the typestring matches the given one
590 std::string expect_typestr = get_typestring(tensor.info()->data_type());
591 ARM_COMPUTE_ERROR_ON_MSG(_typestring != expect_typestr, "Typestrings mismatch");
592
593 // Validate tensor shape
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000594 ARM_COMPUTE_ERROR_ON_MSG(_shape.size() != tensor.info()->tensor_shape().num_dimensions(), "Tensor ranks mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100595 if(_fortran_order)
596 {
597 for(size_t i = 0; i < _shape.size(); ++i)
598 {
Anthony Barbier6db0ff52018-01-05 10:59:12 +0000599 ARM_COMPUTE_ERROR_ON_MSG(tensor.info()->tensor_shape()[i] != _shape[i], "Tensor dimensions mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100600 }
601 }
602 else
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[_shape.size() - i - 1], "Tensor dimensions mismatch");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100607 }
608 }
609
Gian Marco0bc5a252017-12-04 13:55:08 +0000610 switch(tensor.info()->data_type())
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100611 {
Gian Marco0bc5a252017-12-04 13:55:08 +0000612 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100613 {
614 // Read data
615 if(tensor.info()->padding().empty())
616 {
617 // If tensor has no padding read directly from stream.
618 _fs.read(reinterpret_cast<char *>(tensor.buffer()), tensor.info()->total_size());
619 }
620 else
621 {
622 // If tensor has padding accessing tensor elements through execution window.
623 Window window;
624 window.use_tensor_dimensions(tensor.info()->tensor_shape());
625
626 execute_window_loop(window, [&](const Coordinates & id)
627 {
628 _fs.read(reinterpret_cast<char *>(tensor.ptr_to_element(id)), tensor.info()->element_size());
629 });
630 }
631
632 break;
633 }
634 default:
Gian Marco0bc5a252017-12-04 13:55:08 +0000635 ARM_COMPUTE_ERROR("Unsupported data type");
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100636 }
637
638 // Unmap buffer if creating a CLTensor
639 unmap(tensor);
640 }
641 catch(const std::ifstream::failure &e)
642 {
643 ARM_COMPUTE_ERROR("Loading NPY file: %s", e.what());
644 }
645 }
646
647private:
648 std::ifstream _fs;
649 std::vector<unsigned long> _shape;
650 bool _fortran_order;
651 std::string _typestring;
652};
653
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100654/** Template helper function to save a tensor image to a PPM file.
655 *
656 * @note Only U8 and RGB888 formats supported.
657 * @note Only works with 2D tensors.
658 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
659 *
660 * @param[in] tensor The tensor to save as PPM file
661 * @param[in] ppm_filename Filename of the file to create.
662 */
663template <typename T>
664void save_to_ppm(T &tensor, const std::string &ppm_filename)
665{
666 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::RGB888, arm_compute::Format::U8);
667 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
668
669 std::ofstream fs;
670
671 try
672 {
673 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
674 fs.open(ppm_filename, std::ios::out | std::ios::binary);
675
676 const unsigned int width = tensor.info()->tensor_shape()[0];
677 const unsigned int height = tensor.info()->tensor_shape()[1];
678
679 fs << "P6\n"
680 << width << " " << height << " 255\n";
681
Anthony Barbier7068f992017-10-26 15:23:08 +0100682 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100683 map(tensor, true);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100684
685 switch(tensor.info()->format())
686 {
687 case arm_compute::Format::U8:
688 {
689 arm_compute::Window window;
690 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
691 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
692
693 arm_compute::Iterator in(&tensor, window);
694
695 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
696 {
697 const unsigned char value = *in.ptr();
698
699 fs << value << value << value;
700 },
701 in);
702
703 break;
704 }
705 case arm_compute::Format::RGB888:
706 {
707 arm_compute::Window window;
708 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, width));
709 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
710
711 arm_compute::Iterator in(&tensor, window);
712
713 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
714 {
715 fs.write(reinterpret_cast<std::fstream::char_type *>(in.ptr()), width * tensor.info()->element_size());
716 },
717 in);
718
719 break;
720 }
721 default:
722 ARM_COMPUTE_ERROR("Unsupported format");
723 }
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100724
Anthony Barbier7068f992017-10-26 15:23:08 +0100725 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100726 unmap(tensor);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100727 }
728 catch(const std::ofstream::failure &e)
729 {
730 ARM_COMPUTE_ERROR("Writing %s: (%s)", ppm_filename.c_str(), e.what());
731 }
732}
steniu01bee466b2017-06-21 16:45:41 +0100733
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100734/** Template helper function to save a tensor image to a NPY file.
735 *
Gian Marcobfa3b522017-12-12 10:08:38 +0000736 * @note Only F32 data type supported.
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100737 * @note Only works with 2D tensors.
738 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
739 *
740 * @param[in] tensor The tensor to save as NPY file
741 * @param[in] npy_filename Filename of the file to create.
742 * @param[in] fortran_order If true, save matrix in fortran order.
743 */
744template <typename T>
745void save_to_npy(T &tensor, const std::string &npy_filename, bool fortran_order)
746{
Gian Marcobfa3b522017-12-12 10:08:38 +0000747 ARM_COMPUTE_ERROR_ON_DATA_TYPE_NOT_IN(&tensor, arm_compute::DataType::F32);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100748 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
749
750 std::ofstream fs;
751
752 try
753 {
754 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
755 fs.open(npy_filename, std::ios::out | std::ios::binary);
756
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000757 const unsigned int width = tensor.info()->tensor_shape()[0];
758 const unsigned int height = tensor.info()->tensor_shape()[1];
759 std::vector<npy::ndarray_len_t> shape(2);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100760
761 if(!fortran_order)
762 {
763 shape[0] = height, shape[1] = width;
764 }
765 else
766 {
767 shape[0] = width, shape[1] = height;
768 }
769
770 // Map buffer if creating a CLTensor
771 map(tensor, true);
772
Gian Marcobfa3b522017-12-12 10:08:38 +0000773 switch(tensor.info()->data_type())
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100774 {
Gian Marcobfa3b522017-12-12 10:08:38 +0000775 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100776 {
777 std::vector<float> tmp; /* Used only to get the typestring */
778 npy::Typestring typestring_o{ tmp };
779 std::string typestring = typestring_o.str();
780
781 std::ofstream stream(npy_filename, std::ofstream::binary);
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000782 npy::write_header(stream, typestring, fortran_order, shape);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100783
784 arm_compute::Window window;
785 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
786 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
787
788 arm_compute::Iterator in(&tensor, window);
789
790 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
791 {
792 stream.write(reinterpret_cast<const char *>(in.ptr()), sizeof(float));
793 },
794 in);
795
796 break;
797 }
798 default:
799 ARM_COMPUTE_ERROR("Unsupported format");
800 }
801
802 // Unmap buffer if creating a CLTensor
803 unmap(tensor);
804 }
805 catch(const std::ofstream::failure &e)
806 {
807 ARM_COMPUTE_ERROR("Writing %s: (%s)", npy_filename.c_str(), e.what());
808 }
809}
810
steniu01bee466b2017-06-21 16:45:41 +0100811/** Load the tensor with pre-trained data from a binary file
812 *
813 * @param[in] tensor The tensor to be filled. Data type supported: F32.
814 * @param[in] filename Filename of the binary file to load from.
815 */
816template <typename T>
817void load_trained_data(T &tensor, const std::string &filename)
818{
819 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
820
821 std::ifstream fs;
822
823 try
824 {
825 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
826 // Open file
827 fs.open(filename, std::ios::in | std::ios::binary);
828
829 if(!fs.good())
830 {
831 throw std::runtime_error("Could not load binary data: " + filename);
832 }
833
Anthony Barbier7068f992017-10-26 15:23:08 +0100834 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100835 map(tensor, true);
836
steniu01bee466b2017-06-21 16:45:41 +0100837 Window window;
838
839 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, 1, 1));
840
841 for(unsigned int d = 1; d < tensor.info()->num_dimensions(); ++d)
842 {
843 window.set(d, Window::Dimension(0, tensor.info()->tensor_shape()[d], 1));
844 }
845
846 arm_compute::Iterator in(&tensor, window);
847
848 execute_window_loop(window, [&](const Coordinates & id)
849 {
850 fs.read(reinterpret_cast<std::fstream::char_type *>(in.ptr()), tensor.info()->tensor_shape()[0] * tensor.info()->element_size());
851 },
852 in);
853
Anthony Barbier7068f992017-10-26 15:23:08 +0100854 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100855 unmap(tensor);
steniu01bee466b2017-06-21 16:45:41 +0100856 }
857 catch(const std::ofstream::failure &e)
858 {
859 ARM_COMPUTE_ERROR("Writing %s: (%s)", filename.c_str(), e.what());
860 }
861}
862
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100863template <typename T>
864void fill_random_tensor(T &tensor, float lower_bound, float upper_bound)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100865{
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100866 std::random_device rd;
867 std::mt19937 gen(rd());
Anthony Barbier2a07e182017-08-04 18:20:27 +0100868
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100869 Window window;
Michalis Spyrou5e69bb42018-03-09 16:36:00 +0000870 window.use_tensor_dimensions(tensor.info()->tensor_shape());
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100871
872 map(tensor, true);
873
874 Iterator it(&tensor, window);
875
Gian Marcobfa3b522017-12-12 10:08:38 +0000876 switch(tensor.info()->data_type())
Anthony Barbier2a07e182017-08-04 18:20:27 +0100877 {
Gian Marcobfa3b522017-12-12 10:08:38 +0000878 case arm_compute::DataType::F32:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100879 {
880 std::uniform_real_distribution<float> dist(lower_bound, upper_bound);
881
882 execute_window_loop(window, [&](const Coordinates & id)
883 {
884 *reinterpret_cast<float *>(it.ptr()) = dist(gen);
885 },
886 it);
887
888 break;
889 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100890 default:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100891 {
892 ARM_COMPUTE_ERROR("Unsupported format");
893 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100894 }
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100895
896 unmap(tensor);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100897}
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100898
899template <typename T>
Gian Marco0bc5a252017-12-04 13:55:08 +0000900void init_sgemm_output(T &dst, T &src0, T &src1, arm_compute::DataType dt)
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100901{
Gian Marco0bc5a252017-12-04 13:55:08 +0000902 dst.allocator()->init(TensorInfo(TensorShape(src1.info()->dimension(0), src0.info()->dimension(1)), 1, dt));
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100903}
Gian Marco5ca74092018-02-08 16:21:54 +0000904/** This function returns the amount of memory free reading from /proc/meminfo
905 *
906 * @return The free memory in kB
907 */
908uint64_t get_mem_free_from_meminfo();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100909} // namespace utils
910} // namespace arm_compute
911#endif /* __UTILS_UTILS_H__*/