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