blob: 4e5bc815abf6d2265cb69f98e86ecd21f0dc2cc0 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2016, 2017 ARM Limited.
3 *
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"
38#include "arm_compute/runtime/CL/CLTensor.h"
39#endif /* ARM_COMPUTE_CL */
Anthony Barbier7068f992017-10-26 15:23:08 +010040#ifdef ARM_COMPUTE_GC
41#include "arm_compute/runtime/GLES_COMPUTE/GCTensor.h"
42#endif /* ARM_COMPUTE_GC */
Anthony Barbier6ff3b192017-09-04 18:44:23 +010043
44#include <cstdlib>
45#include <cstring>
46#include <fstream>
47#include <iostream>
Giorgio Arenacf3935f2017-10-26 17:14:13 +010048#include <random>
49#include <string>
50#include <tuple>
51#include <vector>
Anthony Barbier6ff3b192017-09-04 18:44:23 +010052
53namespace arm_compute
54{
55namespace utils
56{
57/** Signature of an example to run
58 *
59 * @param[in] argc Number of command line arguments
60 * @param[in] argv Command line arguments
61 */
62using example = void(int argc, const char **argv);
63
64/** Run an example and handle the potential exceptions it throws
65 *
66 * @param[in] argc Number of command line arguments
67 * @param[in] argv Command line arguments
68 * @param[in] func Pointer to the function containing the code to run
69 */
70int run_example(int argc, const char **argv, example &func);
71
72/** Draw a RGB rectangular window for the detected object
73 *
74 * @param[in, out] tensor Input tensor where the rectangle will be drawn on. Format supported: RGB888
75 * @param[in] rect Geometry of the rectangular window
76 * @param[in] r Red colour to use
77 * @param[in] g Green colour to use
78 * @param[in] b Blue colour to use
79 */
80void draw_detection_rectangle(arm_compute::ITensor *tensor, const arm_compute::DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b);
81
82/** Parse the ppm header from an input file stream. At the end of the execution,
83 * the file position pointer will be located at the first pixel stored in the ppm file
84 *
85 * @param[in] fs Input file stream to parse
86 *
87 * @return The width, height and max value stored in the header of the PPM file
88 */
89std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs);
90
Giorgio Arenacf3935f2017-10-26 17:14:13 +010091/** Parse the npy header from an input file stream. At the end of the execution,
92 * the file position pointer will be located at the first pixel stored in the npy file //TODO
93 *
94 * @param[in] fs Input file stream to parse
95 *
96 * @return The width and height stored in the header of the NPY file
97 */
98std::tuple<std::vector<unsigned long>, bool, std::string> parse_npy_header(std::ifstream &fs);
99
100/** Obtain numpy type string from DataType.
101 *
102 * @param[in] data_type Data type.
103 *
104 * @return numpy type string.
105 */
106inline std::string get_typestring(DataType data_type)
107{
108 // Check endianness
109 const unsigned int i = 1;
110 const char *c = reinterpret_cast<const char *>(&i);
111 std::string endianness;
112 if(*c == 1)
113 {
114 endianness = std::string("<");
115 }
116 else
117 {
118 endianness = std::string(">");
119 }
120 const std::string no_endianness("|");
121
122 switch(data_type)
123 {
124 case DataType::U8:
125 return no_endianness + "u" + support::cpp11::to_string(sizeof(uint8_t));
126 case DataType::S8:
127 return no_endianness + "i" + support::cpp11::to_string(sizeof(int8_t));
128 case DataType::U16:
129 return endianness + "u" + support::cpp11::to_string(sizeof(uint16_t));
130 case DataType::S16:
131 return endianness + "i" + support::cpp11::to_string(sizeof(int16_t));
132 case DataType::U32:
133 return endianness + "u" + support::cpp11::to_string(sizeof(uint32_t));
134 case DataType::S32:
135 return endianness + "i" + support::cpp11::to_string(sizeof(int32_t));
136 case DataType::U64:
137 return endianness + "u" + support::cpp11::to_string(sizeof(uint64_t));
138 case DataType::S64:
139 return endianness + "i" + support::cpp11::to_string(sizeof(int64_t));
140 case DataType::F32:
141 return endianness + "f" + support::cpp11::to_string(sizeof(float));
142 case DataType::F64:
143 return endianness + "f" + support::cpp11::to_string(sizeof(double));
144 case DataType::SIZET:
145 return endianness + "u" + support::cpp11::to_string(sizeof(size_t));
146 default:
147 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
148 }
149}
150
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100151/** Maps a tensor if needed
152 *
153 * @param[in] tensor Tensor to be mapped
154 * @param[in] blocking Specified if map is blocking or not
155 */
156template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100157inline void map(T &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100158{
159 ARM_COMPUTE_UNUSED(tensor);
160 ARM_COMPUTE_UNUSED(blocking);
161}
162
163/** Unmaps a tensor if needed
164 *
165 * @param tensor Tensor to be unmapped
166 */
167template <typename T>
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100168inline void unmap(T &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100169{
170 ARM_COMPUTE_UNUSED(tensor);
171}
172
173#ifdef ARM_COMPUTE_CL
174/** Maps a tensor if needed
175 *
176 * @param[in] tensor Tensor to be mapped
177 * @param[in] blocking Specified if map is blocking or not
178 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100179inline void map(CLTensor &tensor, bool blocking)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100180{
181 tensor.map(blocking);
182}
183
184/** Unmaps a tensor if needed
185 *
186 * @param tensor Tensor to be unmapped
187 */
Gian Marco Iodiceae27e942017-09-28 18:31:26 +0100188inline void unmap(CLTensor &tensor)
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100189{
190 tensor.unmap();
191}
192#endif /* ARM_COMPUTE_CL */
193
Anthony Barbier7068f992017-10-26 15:23:08 +0100194#ifdef ARM_COMPUTE_GC
195/** Maps a tensor if needed
196 *
197 * @param[in] tensor Tensor to be mapped
198 * @param[in] blocking Specified if map is blocking or not
199 */
200inline void map(GCTensor &tensor, bool blocking)
201{
202 tensor.map(blocking);
203}
204
205/** Unmaps a tensor if needed
206 *
207 * @param tensor Tensor to be unmapped
208 */
209inline void unmap(GCTensor &tensor)
210{
211 tensor.unmap();
212}
213#endif /* ARM_COMPUTE_GC */
214
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100215/** Class to load the content of a PPM file into an Image
216 */
217class PPMLoader
218{
219public:
220 PPMLoader()
221 : _fs(), _width(0), _height(0)
222 {
223 }
224 /** Open a PPM file and reads its metadata (Width, height)
225 *
226 * @param[in] ppm_filename File to open
227 */
228 void open(const std::string &ppm_filename)
229 {
230 ARM_COMPUTE_ERROR_ON(is_open());
231 try
232 {
233 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
234 _fs.open(ppm_filename, std::ios::in | std::ios::binary);
235
236 unsigned int max_val = 0;
237 std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
238
239 ARM_COMPUTE_ERROR_ON_MSG(max_val >= 256, "2 bytes per colour channel not supported in file %s", ppm_filename.c_str());
240 }
241 catch(const std::ifstream::failure &e)
242 {
243 ARM_COMPUTE_ERROR("Accessing %s: %s", ppm_filename.c_str(), e.what());
244 }
245 }
246 /** Return true if a PPM file is currently open
247 */
248 bool is_open()
249 {
250 return _fs.is_open();
251 }
252
253 /** Initialise an image's metadata with the dimensions of the PPM file currently open
254 *
255 * @param[out] image Image to initialise
256 * @param[in] format Format to use for the image (Must be RGB888 or U8)
257 */
258 template <typename T>
259 void init_image(T &image, arm_compute::Format format)
260 {
261 ARM_COMPUTE_ERROR_ON(!is_open());
262 ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::RGB888 && format != arm_compute::Format::U8);
263
264 // Use the size of the input PPM image
265 arm_compute::TensorInfo image_info(_width, _height, format);
266 image.allocator()->init(image_info);
267 }
268
269 /** Fill an image with the content of the currently open PPM file.
270 *
271 * @note If the image is a CLImage, the function maps and unmaps the image
272 *
273 * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened PPM).
274 */
275 template <typename T>
276 void fill_image(T &image)
277 {
278 ARM_COMPUTE_ERROR_ON(!is_open());
279 ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
280 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&image, arm_compute::Format::U8, arm_compute::Format::RGB888);
281 try
282 {
Anthony Barbier7068f992017-10-26 15:23:08 +0100283 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100284 map(image, true);
285
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100286 // Check if the file is large enough to fill the image
287 const size_t current_position = _fs.tellg();
288 _fs.seekg(0, std::ios_base::end);
289 const size_t end_position = _fs.tellg();
290 _fs.seekg(current_position, std::ios_base::beg);
291
292 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < image.info()->tensor_shape().total_size() * image.info()->element_size(),
293 "Not enough data in file");
294 ARM_COMPUTE_UNUSED(end_position);
295
296 switch(image.info()->format())
297 {
298 case arm_compute::Format::U8:
299 {
300 // We need to convert the data from RGB to grayscale:
301 // Iterate through every pixel of the image
302 arm_compute::Window window;
303 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
304 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
305
306 arm_compute::Iterator out(&image, window);
307
308 unsigned char red = 0;
309 unsigned char green = 0;
310 unsigned char blue = 0;
311
312 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
313 {
314 red = _fs.get();
315 green = _fs.get();
316 blue = _fs.get();
317
318 *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
319 },
320 out);
321
322 break;
323 }
324 case arm_compute::Format::RGB888:
325 {
326 // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
327 // Create a vertical window to iterate through the image's rows:
328 arm_compute::Window window;
329 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
330
331 arm_compute::Iterator out(&image, window);
332
333 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
334 {
335 // Copy one row from the input file to the current row of the image:
336 _fs.read(reinterpret_cast<std::fstream::char_type *>(out.ptr()), _width * image.info()->element_size());
337 },
338 out);
339
340 break;
341 }
342 default:
343 ARM_COMPUTE_ERROR("Unsupported format");
344 }
345
Anthony Barbier7068f992017-10-26 15:23:08 +0100346 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100347 unmap(image);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100348 }
349 catch(const std::ifstream::failure &e)
350 {
351 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
352 }
353 }
354
Gian Marco44ec2e72017-10-19 14:13:38 +0100355 /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open PPM file.
356 *
357 * @note If the image is a CLImage, the function maps and unmaps the image
358 *
359 * @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
360 * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
361 */
362 template <typename T>
363 void fill_planar_tensor(T &tensor, bool bgr = false)
364 {
365 ARM_COMPUTE_ERROR_ON(!is_open());
366 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::U8, DataType::F32);
367 ARM_COMPUTE_ERROR_ON(tensor.info()->dimension(0) != _width || tensor.info()->dimension(1) != _height || tensor.info()->dimension(2) != 3);
368
369 try
370 {
371 // Map buffer if creating a CLTensor
372 map(tensor, true);
373
374 // Check if the file is large enough to fill the image
375 const size_t current_position = _fs.tellg();
376 _fs.seekg(0, std::ios_base::end);
377 const size_t end_position = _fs.tellg();
378 _fs.seekg(current_position, std::ios_base::beg);
379
380 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size(),
381 "Not enough data in file");
382 ARM_COMPUTE_UNUSED(end_position);
383
384 // Iterate through every pixel of the image
385 arm_compute::Window window;
386 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
387 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
388 window.set(arm_compute::Window::DimZ, arm_compute::Window::Dimension(0, 1, 1));
389
390 arm_compute::Iterator out(&tensor, window);
391
392 unsigned char red = 0;
393 unsigned char green = 0;
394 unsigned char blue = 0;
395
396 size_t stride_z = tensor.info()->strides_in_bytes()[2];
397
398 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
399 {
400 red = _fs.get();
401 green = _fs.get();
402 blue = _fs.get();
403
404 switch(tensor.info()->data_type())
405 {
406 case arm_compute::DataType::U8:
407 {
408 *(out.ptr() + 0 * stride_z) = bgr ? blue : red;
409 *(out.ptr() + 1 * stride_z) = green;
410 *(out.ptr() + 2 * stride_z) = bgr ? red : blue;
411 break;
412 }
413 case arm_compute::DataType::F32:
414 {
415 *reinterpret_cast<float *>(out.ptr() + 0 * stride_z) = static_cast<float>(bgr ? blue : red);
416 *reinterpret_cast<float *>(out.ptr() + 1 * stride_z) = static_cast<float>(green);
417 *reinterpret_cast<float *>(out.ptr() + 2 * stride_z) = static_cast<float>(bgr ? red : blue);
418 break;
419 }
420 default:
421 {
422 ARM_COMPUTE_ERROR("Unsupported data type");
423 }
424 }
425 },
426 out);
427
428 // Unmap buffer if creating a CLTensor
429 unmap(tensor);
430 }
431 catch(const std::ifstream::failure &e)
432 {
433 ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
434 }
435 }
436
Isabella Gottardia4c61882017-11-03 12:11:55 +0000437 /** Return the width of the currently open PPM file.
438 */
439 unsigned int width() const
440 {
441 return _width;
442 }
443
444 /** Return the height of the currently open PPM file.
445 */
446 unsigned int height() const
447 {
448 return _height;
449 }
450
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100451private:
452 std::ifstream _fs;
453 unsigned int _width, _height;
454};
455
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100456class NPYLoader
457{
458public:
459 NPYLoader()
460 : _fs(), _shape(), _fortran_order(false), _typestring()
461 {
462 }
463
464 /** Open a NPY file and reads its metadata
465 *
466 * @param[in] npy_filename File to open
467 */
468 void open(const std::string &npy_filename)
469 {
470 ARM_COMPUTE_ERROR_ON(is_open());
471 try
472 {
473 _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
474 _fs.open(npy_filename, std::ios::in | std::ios::binary);
475
476 std::tie(_shape, _fortran_order, _typestring) = parse_npy_header(_fs);
477 }
478 catch(const std::ifstream::failure &e)
479 {
480 ARM_COMPUTE_ERROR("Accessing %s: %s", npy_filename.c_str(), e.what());
481 }
482 }
483 /** Return true if a NPY file is currently open */
484 bool is_open()
485 {
486 return _fs.is_open();
487 }
488
489 /** Return true if a NPY file is in fortran order */
490 bool is_fortran()
491 {
492 return _fortran_order;
493 }
494
495 /** Initialise an image's metadata with the dimensions of the NPY file currently open
496 *
497 * @param[out] tensor Tensor to initialise
498 * @param[in] format Format to use for the image
499 */
500 template <typename T>
501 void init_tensor(T &tensor, arm_compute::Format format)
502 {
503 ARM_COMPUTE_ERROR_ON(!is_open());
504 ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::F32);
505
506 // Use the size of the input NPY tensor
507 TensorShape shape;
508 shape.set_num_dimensions(_shape.size());
509 for(size_t i = 0; i < _shape.size(); ++i)
510 {
511 shape.set(i, _shape.at(i));
512 }
513
514 arm_compute::TensorInfo tensor_info(shape, format);
515 tensor.allocator()->init(tensor_info);
516 }
517
518 /** Fill a tensor with the content of the currently open NPY file.
519 *
520 * @note If the tensor is a CLTensor, the function maps and unmaps the tensor
521 *
522 * @param[in,out] tensor Tensor to fill (Must be allocated, and of matching dimensions with the opened NPY).
523 */
524 template <typename T>
525 void fill_tensor(T &tensor)
526 {
527 ARM_COMPUTE_ERROR_ON(!is_open());
528 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::F32);
529 try
530 {
531 // Map buffer if creating a CLTensor
532 map(tensor, true);
533
534 // Check if the file is large enough to fill the tensor
535 const size_t current_position = _fs.tellg();
536 _fs.seekg(0, std::ios_base::end);
537 const size_t end_position = _fs.tellg();
538 _fs.seekg(current_position, std::ios_base::beg);
539
540 ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor.info()->tensor_shape().total_size() * tensor.info()->element_size(),
541 "Not enough data in file");
542 ARM_COMPUTE_UNUSED(end_position);
543
544 // Check if the typestring matches the given one
545 std::string expect_typestr = get_typestring(tensor.info()->data_type());
546 ARM_COMPUTE_ERROR_ON_MSG(_typestring != expect_typestr, "Typestrings mismatch");
547
548 // Validate tensor shape
549 ARM_COMPUTE_ERROR_ON_MSG(_shape.size() != tensor.shape().num_dimensions(), "Tensor ranks mismatch");
550 if(_fortran_order)
551 {
552 for(size_t i = 0; i < _shape.size(); ++i)
553 {
554 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != _shape[i], "Tensor dimensions mismatch");
555 }
556 }
557 else
558 {
559 for(size_t i = 0; i < _shape.size(); ++i)
560 {
561 ARM_COMPUTE_ERROR_ON_MSG(tensor.shape()[i] != _shape[_shape.size() - i - 1], "Tensor dimensions mismatch");
562 }
563 }
564
565 switch(tensor.info()->format())
566 {
567 case arm_compute::Format::F32:
568 {
569 // Read data
570 if(tensor.info()->padding().empty())
571 {
572 // If tensor has no padding read directly from stream.
573 _fs.read(reinterpret_cast<char *>(tensor.buffer()), tensor.info()->total_size());
574 }
575 else
576 {
577 // If tensor has padding accessing tensor elements through execution window.
578 Window window;
579 window.use_tensor_dimensions(tensor.info()->tensor_shape());
580
581 execute_window_loop(window, [&](const Coordinates & id)
582 {
583 _fs.read(reinterpret_cast<char *>(tensor.ptr_to_element(id)), tensor.info()->element_size());
584 });
585 }
586
587 break;
588 }
589 default:
590 ARM_COMPUTE_ERROR("Unsupported format");
591 }
592
593 // Unmap buffer if creating a CLTensor
594 unmap(tensor);
595 }
596 catch(const std::ifstream::failure &e)
597 {
598 ARM_COMPUTE_ERROR("Loading NPY file: %s", e.what());
599 }
600 }
601
602private:
603 std::ifstream _fs;
604 std::vector<unsigned long> _shape;
605 bool _fortran_order;
606 std::string _typestring;
607};
608
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100609/** Template helper function to save a tensor image to a PPM file.
610 *
611 * @note Only U8 and RGB888 formats supported.
612 * @note Only works with 2D tensors.
613 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
614 *
615 * @param[in] tensor The tensor to save as PPM file
616 * @param[in] ppm_filename Filename of the file to create.
617 */
618template <typename T>
619void save_to_ppm(T &tensor, const std::string &ppm_filename)
620{
621 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::RGB888, arm_compute::Format::U8);
622 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
623
624 std::ofstream fs;
625
626 try
627 {
628 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
629 fs.open(ppm_filename, std::ios::out | std::ios::binary);
630
631 const unsigned int width = tensor.info()->tensor_shape()[0];
632 const unsigned int height = tensor.info()->tensor_shape()[1];
633
634 fs << "P6\n"
635 << width << " " << height << " 255\n";
636
Anthony Barbier7068f992017-10-26 15:23:08 +0100637 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100638 map(tensor, true);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100639
640 switch(tensor.info()->format())
641 {
642 case arm_compute::Format::U8:
643 {
644 arm_compute::Window window;
645 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
646 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
647
648 arm_compute::Iterator in(&tensor, window);
649
650 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
651 {
652 const unsigned char value = *in.ptr();
653
654 fs << value << value << value;
655 },
656 in);
657
658 break;
659 }
660 case arm_compute::Format::RGB888:
661 {
662 arm_compute::Window window;
663 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, width));
664 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
665
666 arm_compute::Iterator in(&tensor, window);
667
668 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
669 {
670 fs.write(reinterpret_cast<std::fstream::char_type *>(in.ptr()), width * tensor.info()->element_size());
671 },
672 in);
673
674 break;
675 }
676 default:
677 ARM_COMPUTE_ERROR("Unsupported format");
678 }
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100679
Anthony Barbier7068f992017-10-26 15:23:08 +0100680 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100681 unmap(tensor);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100682 }
683 catch(const std::ofstream::failure &e)
684 {
685 ARM_COMPUTE_ERROR("Writing %s: (%s)", ppm_filename.c_str(), e.what());
686 }
687}
steniu01bee466b2017-06-21 16:45:41 +0100688
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100689/** Template helper function to save a tensor image to a NPY file.
690 *
691 * @note Only F32 format supported.
692 * @note Only works with 2D tensors.
693 * @note If the input tensor is a CLTensor, the function maps and unmaps the image
694 *
695 * @param[in] tensor The tensor to save as NPY file
696 * @param[in] npy_filename Filename of the file to create.
697 * @param[in] fortran_order If true, save matrix in fortran order.
698 */
699template <typename T>
700void save_to_npy(T &tensor, const std::string &npy_filename, bool fortran_order)
701{
702 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::F32);
703 ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
704
705 std::ofstream fs;
706
707 try
708 {
709 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
710 fs.open(npy_filename, std::ios::out | std::ios::binary);
711
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000712 const unsigned int width = tensor.info()->tensor_shape()[0];
713 const unsigned int height = tensor.info()->tensor_shape()[1];
714 std::vector<npy::ndarray_len_t> shape(2);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100715
716 if(!fortran_order)
717 {
718 shape[0] = height, shape[1] = width;
719 }
720 else
721 {
722 shape[0] = width, shape[1] = height;
723 }
724
725 // Map buffer if creating a CLTensor
726 map(tensor, true);
727
728 switch(tensor.info()->format())
729 {
730 case arm_compute::Format::F32:
731 {
732 std::vector<float> tmp; /* Used only to get the typestring */
733 npy::Typestring typestring_o{ tmp };
734 std::string typestring = typestring_o.str();
735
736 std::ofstream stream(npy_filename, std::ofstream::binary);
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000737 npy::write_header(stream, typestring, fortran_order, shape);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100738
739 arm_compute::Window window;
740 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
741 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
742
743 arm_compute::Iterator in(&tensor, window);
744
745 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
746 {
747 stream.write(reinterpret_cast<const char *>(in.ptr()), sizeof(float));
748 },
749 in);
750
751 break;
752 }
753 default:
754 ARM_COMPUTE_ERROR("Unsupported format");
755 }
756
757 // Unmap buffer if creating a CLTensor
758 unmap(tensor);
759 }
760 catch(const std::ofstream::failure &e)
761 {
762 ARM_COMPUTE_ERROR("Writing %s: (%s)", npy_filename.c_str(), e.what());
763 }
764}
765
steniu01bee466b2017-06-21 16:45:41 +0100766/** Load the tensor with pre-trained data from a binary file
767 *
768 * @param[in] tensor The tensor to be filled. Data type supported: F32.
769 * @param[in] filename Filename of the binary file to load from.
770 */
771template <typename T>
772void load_trained_data(T &tensor, const std::string &filename)
773{
774 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
775
776 std::ifstream fs;
777
778 try
779 {
780 fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
781 // Open file
782 fs.open(filename, std::ios::in | std::ios::binary);
783
784 if(!fs.good())
785 {
786 throw std::runtime_error("Could not load binary data: " + filename);
787 }
788
Anthony Barbier7068f992017-10-26 15:23:08 +0100789 // Map buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100790 map(tensor, true);
791
steniu01bee466b2017-06-21 16:45:41 +0100792 Window window;
793
794 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, 1, 1));
795
796 for(unsigned int d = 1; d < tensor.info()->num_dimensions(); ++d)
797 {
798 window.set(d, Window::Dimension(0, tensor.info()->tensor_shape()[d], 1));
799 }
800
801 arm_compute::Iterator in(&tensor, window);
802
803 execute_window_loop(window, [&](const Coordinates & id)
804 {
805 fs.read(reinterpret_cast<std::fstream::char_type *>(in.ptr()), tensor.info()->tensor_shape()[0] * tensor.info()->element_size());
806 },
807 in);
808
Anthony Barbier7068f992017-10-26 15:23:08 +0100809 // Unmap buffer if creating a CLTensor/GCTensor
Georgios Pinitasdc836b62017-09-20 14:02:37 +0100810 unmap(tensor);
steniu01bee466b2017-06-21 16:45:41 +0100811 }
812 catch(const std::ofstream::failure &e)
813 {
814 ARM_COMPUTE_ERROR("Writing %s: (%s)", filename.c_str(), e.what());
815 }
816}
817
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100818template <typename T>
819void fill_random_tensor(T &tensor, float lower_bound, float upper_bound)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100820{
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100821 std::random_device rd;
822 std::mt19937 gen(rd());
Anthony Barbier2a07e182017-08-04 18:20:27 +0100823
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100824 TensorShape shape(tensor.info()->dimension(0), tensor.info()->dimension(1));
825
826 Window window;
827 window.set(Window::DimX, Window::Dimension(0, shape.x(), 1));
828 window.set(Window::DimY, Window::Dimension(0, shape.y(), 1));
829
830 map(tensor, true);
831
832 Iterator it(&tensor, window);
833
834 switch(tensor.info()->format())
Anthony Barbier2a07e182017-08-04 18:20:27 +0100835 {
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100836 case arm_compute::Format::F32:
837 {
838 std::uniform_real_distribution<float> dist(lower_bound, upper_bound);
839
840 execute_window_loop(window, [&](const Coordinates & id)
841 {
842 *reinterpret_cast<float *>(it.ptr()) = dist(gen);
843 },
844 it);
845
846 break;
847 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100848 default:
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100849 {
850 ARM_COMPUTE_ERROR("Unsupported format");
851 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100852 }
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100853
854 unmap(tensor);
Anthony Barbier2a07e182017-08-04 18:20:27 +0100855}
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100856
857template <typename T>
858void init_sgemm_output(T &dst, T &src0, T &src1, arm_compute::Format format)
859{
860 dst.allocator()->init(TensorInfo(src1.info()->dimension(0), src0.info()->dimension(1), format));
861}
862
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100863} // namespace utils
864} // namespace arm_compute
865#endif /* __UTILS_UTILS_H__*/