arm_compute v17.06
[platform/upstream/armcl.git] / utils / Utils.h
1 /*
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"
31 #include "arm_compute/runtime/Tensor.h"
32
33 #ifdef ARM_COMPUTE_CL
34 #include "arm_compute/core/CL/OpenCL.h"
35 #include "arm_compute/runtime/CL/CLTensor.h"
36 #endif /* ARM_COMPUTE_CL */
37
38 #include <cstdlib>
39 #include <cstring>
40 #include <fstream>
41 #include <iostream>
42
43 namespace arm_compute
44 {
45 namespace utils
46 {
47 /** Signature of an example to run
48  *
49  * @param[in] argc Number of command line arguments
50  * @param[in] argv Command line arguments
51  */
52 using example = void(int argc, const char **argv);
53
54 /** Run an example and handle the potential exceptions it throws
55  *
56  * @param[in] argc Number of command line arguments
57  * @param[in] argv Command line arguments
58  * @param[in] func Pointer to the function containing the code to run
59  */
60 int run_example(int argc, const char **argv, example &func);
61
62 /** Draw a RGB rectangular window for the detected object
63  *
64  * @param[in, out] tensor Input tensor where the rectangle will be drawn on. Format supported: RGB888
65  * @param[in]      rect   Geometry of the rectangular window
66  * @param[in]      r      Red colour to use
67  * @param[in]      g      Green colour to use
68  * @param[in]      b      Blue colour to use
69  */
70 void draw_detection_rectangle(arm_compute::ITensor *tensor, const arm_compute::DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b);
71
72 /** Parse the ppm header from an input file stream. At the end of the execution,
73  *  the file position pointer will be located at the first pixel stored in the ppm file
74  *
75  * @param[in] fs Input file stream to parse
76  *
77  * @return The width, height and max value stored in the header of the PPM file
78  */
79 std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs);
80
81 /** Class to load the content of a PPM file into an Image
82  */
83 class PPMLoader
84 {
85 public:
86     PPMLoader()
87         : _fs(), _width(0), _height(0)
88     {
89     }
90     /** Open a PPM file and reads its metadata (Width, height)
91      *
92      * @param[in] ppm_filename File to open
93      */
94     void open(const std::string &ppm_filename)
95     {
96         ARM_COMPUTE_ERROR_ON(is_open());
97         try
98         {
99             _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
100             _fs.open(ppm_filename, std::ios::in | std::ios::binary);
101
102             unsigned int max_val = 0;
103             std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
104
105             ARM_COMPUTE_ERROR_ON_MSG(max_val >= 256, "2 bytes per colour channel not supported in file %s", ppm_filename.c_str());
106         }
107         catch(const std::ifstream::failure &e)
108         {
109             ARM_COMPUTE_ERROR("Accessing %s: %s", ppm_filename.c_str(), e.what());
110         }
111     }
112     /** Return true if a PPM file is currently open
113          */
114     bool is_open()
115     {
116         return _fs.is_open();
117     }
118
119     /** Initialise an image's metadata with the dimensions of the PPM file currently open
120      *
121      * @param[out] image  Image to initialise
122      * @param[in]  format Format to use for the image (Must be RGB888 or U8)
123      */
124     template <typename T>
125     void init_image(T &image, arm_compute::Format format)
126     {
127         ARM_COMPUTE_ERROR_ON(!is_open());
128         ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::RGB888 && format != arm_compute::Format::U8);
129
130         // Use the size of the input PPM image
131         arm_compute::TensorInfo image_info(_width, _height, format);
132         image.allocator()->init(image_info);
133     }
134
135     /** Fill an image with the content of the currently open PPM file.
136      *
137      * @note If the image is a CLImage, the function maps and unmaps the image
138      *
139      * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened PPM).
140      */
141     template <typename T>
142     void fill_image(T &image)
143     {
144         ARM_COMPUTE_ERROR_ON(!is_open());
145         ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
146         ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&image, arm_compute::Format::U8, arm_compute::Format::RGB888);
147         try
148         {
149 #ifdef ARM_COMPUTE_CL
150             // Map buffer if creating a CLTensor
151             if(std::is_same<typename std::decay<T>::type, arm_compute::CLImage>::value)
152             {
153                 image.map();
154             }
155 #endif
156             // Check if the file is large enough to fill the image
157             const size_t current_position = _fs.tellg();
158             _fs.seekg(0, std::ios_base::end);
159             const size_t end_position = _fs.tellg();
160             _fs.seekg(current_position, std::ios_base::beg);
161
162             ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < image.info()->tensor_shape().total_size() * image.info()->element_size(),
163                                      "Not enough data in file");
164             ARM_COMPUTE_UNUSED(end_position);
165
166             switch(image.info()->format())
167             {
168                 case arm_compute::Format::U8:
169                 {
170                     // We need to convert the data from RGB to grayscale:
171                     // Iterate through every pixel of the image
172                     arm_compute::Window window;
173                     window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
174                     window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
175
176                     arm_compute::Iterator out(&image, window);
177
178                     unsigned char red   = 0;
179                     unsigned char green = 0;
180                     unsigned char blue  = 0;
181
182                     arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
183                     {
184                         red   = _fs.get();
185                         green = _fs.get();
186                         blue  = _fs.get();
187
188                         *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
189                     },
190                     out);
191
192                     break;
193                 }
194                 case arm_compute::Format::RGB888:
195                 {
196                     // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
197                     // Create a vertical window to iterate through the image's rows:
198                     arm_compute::Window window;
199                     window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
200
201                     arm_compute::Iterator out(&image, window);
202
203                     arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
204                     {
205                         // Copy one row from the input file to the current row of the image:
206                         _fs.read(reinterpret_cast<std::fstream::char_type *>(out.ptr()), _width * image.info()->element_size());
207                     },
208                     out);
209
210                     break;
211                 }
212                 default:
213                     ARM_COMPUTE_ERROR("Unsupported format");
214             }
215
216 #ifdef ARM_COMPUTE_CL
217             // Unmap buffer if creating a CLTensor
218             if(std::is_same<typename std::decay<T>::type, arm_compute::CLTensor>::value)
219             {
220                 image.unmap();
221             }
222 #endif
223         }
224         catch(const std::ifstream::failure &e)
225         {
226             ARM_COMPUTE_ERROR("Loading PPM file: %s", e.what());
227         }
228     }
229
230 private:
231     std::ifstream _fs;
232     unsigned int  _width, _height;
233 };
234
235 /** Template helper function to save a tensor image to a PPM file.
236  *
237  * @note Only U8 and RGB888 formats supported.
238  * @note Only works with 2D tensors.
239  * @note If the input tensor is a CLTensor, the function maps and unmaps the image
240  *
241  * @param[in] tensor       The tensor to save as PPM file
242  * @param[in] ppm_filename Filename of the file to create.
243  */
244 template <typename T>
245 void save_to_ppm(T &tensor, const std::string &ppm_filename)
246 {
247     ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&tensor, arm_compute::Format::RGB888, arm_compute::Format::U8);
248     ARM_COMPUTE_ERROR_ON(tensor.info()->num_dimensions() > 2);
249
250     std::ofstream fs;
251
252     try
253     {
254         fs.exceptions(std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
255         fs.open(ppm_filename, std::ios::out | std::ios::binary);
256
257         const unsigned int width  = tensor.info()->tensor_shape()[0];
258         const unsigned int height = tensor.info()->tensor_shape()[1];
259
260         fs << "P6\n"
261            << width << " " << height << " 255\n";
262
263 #ifdef ARM_COMPUTE_CL
264         // Map buffer if creating a CLTensor
265         if(std::is_same<typename std::decay<T>::type, arm_compute::CLTensor>::value)
266         {
267             tensor.map();
268         }
269 #endif
270
271         switch(tensor.info()->format())
272         {
273             case arm_compute::Format::U8:
274             {
275                 arm_compute::Window window;
276                 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, 1));
277                 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
278
279                 arm_compute::Iterator in(&tensor, window);
280
281                 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
282                 {
283                     const unsigned char value = *in.ptr();
284
285                     fs << value << value << value;
286                 },
287                 in);
288
289                 break;
290             }
291             case arm_compute::Format::RGB888:
292             {
293                 arm_compute::Window window;
294                 window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, width, width));
295                 window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, height, 1));
296
297                 arm_compute::Iterator in(&tensor, window);
298
299                 arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
300                 {
301                     fs.write(reinterpret_cast<std::fstream::char_type *>(in.ptr()), width * tensor.info()->element_size());
302                 },
303                 in);
304
305                 break;
306             }
307             default:
308                 ARM_COMPUTE_ERROR("Unsupported format");
309         }
310 #ifdef ARM_COMPUTE_CL
311         // Unmap buffer if creating a CLTensor
312         if(std::is_same<typename std::decay<T>::type, arm_compute::CLTensor>::value)
313         {
314             tensor.unmap();
315         }
316 #endif
317     }
318     catch(const std::ofstream::failure &e)
319     {
320         ARM_COMPUTE_ERROR("Writing %s: (%s)", ppm_filename.c_str(), e.what());
321     }
322 }
323 } // namespace utils
324 } // namespace arm_compute
325 #endif /* __UTILS_UTILS_H__*/