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