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