blob: 80b47d7675f60340ea98ff38095dfe0b85d9da27 [file] [log] [blame]
Anthony Barbier871448e2017-03-24 14:54:29 +00001/*
Jenkins975dfe12019-09-02 11:47:54 +01002 * Copyright (c) 2017-2019 ARM Limited.
Anthony Barbier871448e2017-03-24 14:54:29 +00003 *
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
Jenkins975dfe12019-09-02 11:47:54 +010026#ifdef ARM_COMPUTE_CL
Jenkins0e205f72019-11-28 16:53:35 +000027#include "arm_compute/core/CL/CLKernelLibrary.h"
Jenkins975dfe12019-09-02 11:47:54 +010028#include "arm_compute/runtime/CL/CLScheduler.h"
29#endif /* ARM_COMPUTE_CL */
30
Anthony Barbier871448e2017-03-24 14:54:29 +000031#include <cctype>
Anthony Barbiera4376382017-04-12 15:12:46 +010032#include <cerrno>
Anthony Barbier871448e2017-03-24 14:54:29 +000033#include <iomanip>
34#include <string>
Anthony Barbier46d59272017-05-04 09:15:15 +010035
Jenkinsb9abeae2018-11-22 11:58:08 +000036#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wswitch-default"
Jenkins0e205f72019-11-28 16:53:35 +000038#pragma GCC diagnostic ignored "-Wunused-parameter"
39#pragma GCC diagnostic ignored "-Wstrict-overflow"
Jenkinsb9abeae2018-11-22 11:58:08 +000040#define STB_IMAGE_IMPLEMENTATION
41#include "stb/stb_image.h"
42#pragma GCC diagnostic pop
43
Anthony Barbierdbdab852017-06-23 15:42:00 +010044namespace arm_compute
45{
46namespace utils
47{
Anthony Barbier871448e2017-03-24 14:54:29 +000048namespace
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 Barbierf45d5a92018-01-24 16:23:15 +000082#ifndef BENCHMARK_EXAMPLES
Jenkinsb3a371b2018-05-23 11:36:53 +010083int run_example(int argc, char **argv, std::unique_ptr<Example> example)
Anthony Barbier871448e2017-03-24 14:54:29 +000084{
85 std::cout << "\n"
86 << argv[0] << "\n\n";
87
88 try
89 {
Jenkins52ba29e2018-08-29 15:32:11 +000090 bool status = example->do_setup(argc, argv);
91 if(!status)
92 {
93 return 1;
94 }
Jenkinsb3a371b2018-05-23 11:36:53 +010095 example->do_run();
96 example->do_teardown();
Anthony Barbier871448e2017-03-24 14:54:29 +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}
Anthony Barbierf45d5a92018-01-24 16:23:15 +0000122#endif /* BENCHMARK_EXAMPLES */
Anthony Barbier871448e2017-03-24 14:54:29 +0000123
Anthony Barbierdbdab852017-06-23 15:42:00 +0100124void draw_detection_rectangle(ITensor *tensor, const DetectionWindow &rect, uint8_t r, uint8_t g, uint8_t b)
Anthony Barbier871448e2017-03-24 14:54:29 +0000125{
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
Jenkins52ba29e2018-08-29 15:32:11 +0000161ImageType 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 {
Jenkins0e205f72019-11-28 16:53:35 +0000190 ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", filename.c_str(), e.what());
Jenkins52ba29e2018-08-29 15:32:11 +0000191 }
192
193 return type;
194}
195
Anthony Barbierdbdab852017-06-23 15:42:00 +0100196std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs)
Anthony Barbier871448e2017-03-24 14:54:29 +0000197{
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);
Yuxin Wub3640812017-03-31 14:54:34 -0700225}
Anthony Barbier8140e1e2017-12-14 23:48:46 +0000226
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
231 // Read header
232 std::string header = npy::read_header(fs);
233
234 // Parse header
235 bool fortran_order = false;
236 std::string typestr;
237 npy::parse_header(header, typestr, fortran_order, shape);
238
Jenkins52ba29e2018-08-29 15:32:11 +0000239 std::reverse(shape.begin(), shape.end());
Anthony Barbier8140e1e2017-12-14 23:48:46 +0000240
241 return std::make_tuple(shape, fortran_order, typestr);
242}
Anthony Barbier06ea0482018-02-22 15:45:35 +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}
Jenkins975dfe12019-09-02 11:47:54 +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{
284#ifdef ARM_COMPUTE_CL
285 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 }
Jenkins0e205f72019-11-28 16:53:35 +0000319#else /* ARM_COMPUTE_CL */
320 ARM_COMPUTE_UNUSED(filename);
Jenkins975dfe12019-09-02 11:47:54 +0100321#endif /* ARM_COMPUTE_CL */
322}
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{
330#ifdef ARM_COMPUTE_CL
331 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 }
Jenkins0e205f72019-11-28 16:53:35 +0000355#else /* ARM_COMPUTE_CL */
356 ARM_COMPUTE_UNUSED(filename);
Jenkins975dfe12019-09-02 11:47:54 +0100357#endif /* ARM_COMPUTE_CL */
358}
Anthony Barbierdbdab852017-06-23 15:42:00 +0100359} // namespace utils
360} // namespace arm_compute