blob: 8cc3fcf9b09149cbe0bdaa27cf1516adfcc793af [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Pablo Tellodb9116f2019-07-11 16:50:37 +01002 * Copyright (c) 2017-2019 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#include "Utils.h"
25
Pablo Tellocf9c8432019-07-22 17:36:03 +010026#ifdef ARM_COMPUTE_CL
Pablo Tellodb8485a2019-09-24 11:03:47 +010027#include "arm_compute/core/CL/CLKernelLibrary.h"
Pablo Tellodb9116f2019-07-11 16:50:37 +010028#include "arm_compute/runtime/CL/CLScheduler.h"
Pablo Tellocf9c8432019-07-22 17:36:03 +010029#endif /* ARM_COMPUTE_CL */
Pablo Tellodb9116f2019-07-11 16:50:37 +010030
Anthony Barbier6ff3b192017-09-04 18:44:23 +010031#include <cctype>
32#include <cerrno>
33#include <iomanip>
34#include <string>
35
Anthony Barbier1c28ab42018-08-29 11:22:33 +010036#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wswitch-default"
Michalis Spyrou6bff1952019-10-02 17:22:11 +010038#pragma GCC diagnostic ignored "-Wunused-parameter"
Anthony Barbier1c28ab42018-08-29 11:22:33 +010039#define STB_IMAGE_IMPLEMENTATION
40#include "stb/stb_image.h"
41#pragma GCC diagnostic pop
42
Anthony Barbier6ff3b192017-09-04 18:44:23 +010043namespace arm_compute
44{
45namespace utils
46{
47namespace
48{
49/* Advance the iterator to the first character which is not a comment
50 *
51 * @param[in,out] fs Stream to drop comments from
52 */
53void discard_comments(std::ifstream &fs)
54{
55 while(fs.peek() == '#')
56 {
57 fs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
58 }
59}
60
61/* Advance the string iterator to the next character which is neither a space or a comment
62 *
63 * @param[in,out] fs Stream to drop comments from
64 */
65void discard_comments_and_spaces(std::ifstream &fs)
66{
67 while(true)
68 {
69 discard_comments(fs);
70
71 if(isspace(fs.peek()) == 0)
72 {
73 break;
74 }
75
76 fs.ignore(1);
77 }
78}
79} // namespace
80
Anthony Barbier6db0ff52018-01-05 10:59:12 +000081#ifndef BENCHMARK_EXAMPLES
Anthony Barbier9fb0cac2018-04-20 15:46:21 +010082int run_example(int argc, char **argv, std::unique_ptr<Example> example)
Anthony Barbier6db0ff52018-01-05 10:59:12 +000083{
84 std::cout << "\n"
85 << argv[0] << "\n\n";
86
87 try
88 {
Georgios Pinitas12be7ab2018-07-03 12:06:23 +010089 bool status = example->do_setup(argc, argv);
90 if(!status)
91 {
92 return 1;
93 }
Anthony Barbier9fb0cac2018-04-20 15:46:21 +010094 example->do_run();
95 example->do_teardown();
Anthony Barbier6db0ff52018-01-05 10:59:12 +000096
97 std::cout << "\nTest passed\n";
98 return 0;
99 }
100#ifdef ARM_COMPUTE_CL
101 catch(cl::Error &err)
102 {
103 std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
104 std::cerr << std::endl
105 << "ERROR " << err.what() << "(" << err.err() << ")" << std::endl;
106 std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
107 }
108#endif /* ARM_COMPUTE_CL */
109 catch(std::runtime_error &err)
110 {
111 std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
112 std::cerr << std::endl
113 << "ERROR " << err.what() << " " << (errno ? strerror(errno) : "") << std::endl;
114 std::cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl;
115 }
116
117 std::cout << "\nTest FAILED\n";
118
119 return -1;
120}
121#endif /* BENCHMARK_EXAMPLES */
122
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100123void draw_detection_rectangle(ITensor *tensor, const DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b)
124{
125 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(tensor, Format::RGB888);
126
127 uint8_t *top = tensor->info()->offset_element_in_bytes(Coordinates(rect.x, rect.y)) + tensor->buffer();
128 uint8_t *bottom = tensor->info()->offset_element_in_bytes(Coordinates(rect.x, rect.y + rect.height)) + tensor->buffer();
129 uint8_t *left = top;
130 uint8_t *right = tensor->info()->offset_element_in_bytes(Coordinates(rect.x + rect.width, rect.y)) + tensor->buffer();
131 size_t stride = tensor->info()->strides_in_bytes()[Window::DimY];
132
133 for(size_t x = 0; x < rect.width; ++x)
134 {
135 top[0] = r;
136 top[1] = g;
137 top[2] = b;
138 bottom[0] = r;
139 bottom[1] = g;
140 bottom[2] = b;
141
142 top += 3;
143 bottom += 3;
144 }
145
146 for(size_t y = 0; y < rect.height; ++y)
147 {
148 left[0] = r;
149 left[1] = g;
150 left[2] = b;
151 right[0] = r;
152 right[1] = g;
153 right[2] = b;
154
155 left += stride;
156 right += stride;
157 }
158}
159
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100160ImageType get_image_type_from_file(const std::string &filename)
161{
162 ImageType type = ImageType::UNKNOWN;
163
164 try
165 {
166 // Open file
167 std::ifstream fs;
168 fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
169 fs.open(filename, std::ios::in | std::ios::binary);
170
171 // Identify type from magic number
172 std::array<unsigned char, 2> magic_number{ { 0 } };
173 fs >> magic_number[0] >> magic_number[1];
174
175 // PPM check
176 if(static_cast<char>(magic_number[0]) == 'P' && static_cast<char>(magic_number[1]) == '6')
177 {
178 type = ImageType::PPM;
179 }
180 else if(magic_number[0] == 0xFF && magic_number[1] == 0xD8)
181 {
182 type = ImageType::JPEG;
183 }
184
185 fs.close();
186 }
187 catch(std::runtime_error &e)
188 {
189 ARM_COMPUTE_ERROR("Accessing %s: %s", filename.c_str(), e.what());
190 }
191
192 return type;
193}
194
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100195std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs)
196{
197 // Check the PPM magic number is valid
198 std::array<char, 2> magic_number{ { 0 } };
199 fs >> magic_number[0] >> magic_number[1];
200 ARM_COMPUTE_ERROR_ON_MSG(magic_number[0] != 'P' || magic_number[1] != '6', "Invalid file type");
201 ARM_COMPUTE_UNUSED(magic_number);
202
203 discard_comments_and_spaces(fs);
204
205 unsigned int width = 0;
206 fs >> width;
207
208 discard_comments_and_spaces(fs);
209
210 unsigned int height = 0;
211 fs >> height;
212
213 discard_comments_and_spaces(fs);
214
215 int max_val = 0;
216 fs >> max_val;
217
218 discard_comments(fs);
219
220 ARM_COMPUTE_ERROR_ON_MSG(isspace(fs.peek()) == 0, "Invalid PPM header");
221 fs.ignore(1);
222
223 return std::make_tuple(width, height, max_val);
224}
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100225
226std::tuple<std::vector<unsigned long>, bool, std::string> parse_npy_header(std::ifstream &fs) //NOLINT
227{
228 std::vector<unsigned long> shape; // NOLINT
229
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100230 // Read header
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000231 std::string header = npy::read_header(fs);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100232
233 // Parse header
234 bool fortran_order = false;
235 std::string typestr;
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000236 npy::parse_header(header, typestr, fortran_order, shape);
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100237
Michalis Spyrou39412952018-08-14 17:06:16 +0100238 std::reverse(shape.begin(), shape.end());
Giorgio Arenacf3935f2017-10-26 17:14:13 +0100239
240 return std::make_tuple(shape, fortran_order, typestr);
241}
Gian Marco5ca74092018-02-08 16:21:54 +0000242
243/** This function returns the amount of memory free reading from /proc/meminfo
244 *
245 * @return The free memory in kB
246 */
247uint64_t get_mem_free_from_meminfo()
248{
249 std::string line_attribute;
250 std::ifstream file_meminfo("/proc/meminfo");
251
252 if(file_meminfo.is_open())
253 {
254 while(!(file_meminfo >> line_attribute).fail())
255 {
256 //Test if is the line containing MemFree
257 if(line_attribute == "MemFree:")
258 {
259 uint64_t mem_available;
260 if(!(file_meminfo >> mem_available).fail())
261 {
262 return mem_available;
263 }
264 else
265 {
266 return 0;
267 }
268 }
269 // if it's not MemFree ignore rest of the line
270 file_meminfo.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
271 }
272 }
273 // Nothing found or an error during opening the file
274 return 0;
275}
Pablo Tellodb9116f2019-07-11 16:50:37 +0100276
277/** This function loads prebuilt opencl kernels from a file
278 *
279 * @param[in] filename Name of the file to be used to load the kernels
280 */
281void restore_program_cache_from_file(const std::string &filename)
282{
Pablo Tellocf9c8432019-07-22 17:36:03 +0100283#ifdef ARM_COMPUTE_CL
Pablo Tellodb9116f2019-07-11 16:50:37 +0100284 std::ifstream cache_file(filename, std::ios::binary);
285 if(cache_file.is_open())
286 {
287 if(!CLScheduler::get().is_initialised())
288 {
289 arm_compute::CLScheduler::get().default_init();
290 }
291
292 while(!cache_file.eof())
293 {
294 size_t name_len = 0;
295 size_t binary_len = 0;
296 cache_file.read(reinterpret_cast<char *>(&name_len), sizeof(size_t));
297 cache_file.read(reinterpret_cast<char *>(&binary_len), sizeof(size_t));
298 if(name_len == 0 || binary_len == 0)
299 {
300 break;
301 }
302 std::vector<char> tmp(name_len);
303 std::vector<unsigned char> binary(binary_len);
304 std::string name;
305 cache_file.read(tmp.data(), name_len);
306 name.assign(tmp.data(), name_len);
307 tmp.resize(binary_len);
308 cache_file.read(reinterpret_cast<char *>(binary.data()), binary_len);
309 cl::Context context = arm_compute::CLScheduler::get().context();
310 cl::Program::Binaries binaries{ binary };
311 std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
312 cl::Program program(context, devices, binaries);
313 program.build();
314 CLKernelLibrary::get().add_built_program(name, program);
315 }
316 cache_file.close();
317 }
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100318#else /* ARM_COMPUTE_CL */
319 ARM_COMPUTE_UNUSED(filename);
Pablo Tellocf9c8432019-07-22 17:36:03 +0100320#endif /* ARM_COMPUTE_CL */
Pablo Tellodb9116f2019-07-11 16:50:37 +0100321}
322
323/** This function saves opencl kernels library to a file
324 *
325 * @param[in] filename Name of the file to be used to save the library
326 */
327void save_program_cache_to_file(const std::string &filename)
328{
Pablo Tellocf9c8432019-07-22 17:36:03 +0100329#ifdef ARM_COMPUTE_CL
Pablo Tellodb9116f2019-07-11 16:50:37 +0100330 if(CLScheduler::get().is_initialised())
331 {
332 std::ofstream cache_file(filename, std::ios::binary);
333 if(cache_file.is_open())
334 {
335 for(const auto &it : CLKernelLibrary::get().get_built_programs())
336 {
337 std::vector<std::vector<unsigned char>> binaries = it.second.getInfo<CL_PROGRAM_BINARIES>();
338 ARM_COMPUTE_ERROR_ON(binaries.size() != 1);
339 const std::string kernel_name = it.first;
340 size_t kernel_name_size = kernel_name.length();
341 size_t binary_size = binaries[0].size();
342 cache_file.write(reinterpret_cast<char *>(&kernel_name_size), sizeof(size_t));
343 cache_file.write(reinterpret_cast<char *>(&binary_size), sizeof(size_t));
344 cache_file.write(kernel_name.c_str(), kernel_name_size);
345 cache_file.write(reinterpret_cast<const char *>(binaries[0].data()), binaries[0].size());
346 }
347 cache_file.close();
348 }
349 else
350 {
351 ARM_COMPUTE_ERROR("Cannot open cache file");
352 }
353 }
Michalis Spyrou6bff1952019-10-02 17:22:11 +0100354#else /* ARM_COMPUTE_CL */
355 ARM_COMPUTE_UNUSED(filename);
Pablo Tellocf9c8432019-07-22 17:36:03 +0100356#endif /* ARM_COMPUTE_CL */
Pablo Tellodb9116f2019-07-11 16:50:37 +0100357}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100358} // namespace utils
359} // namespace arm_compute