blob: 87b03890396de6d46c51e64c8f26d6966a62f69a [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 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 */
Moritz Pflanzera09de0c2017-09-01 20:41:12 +010024#include "tests/AssetsLibrary.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010025
Anthony Barbier6ff3b192017-09-04 18:44:23 +010026#include "Utils.h"
Anthony Barbier2a07e182017-08-04 18:20:27 +010027#include "utils/TypePrinter.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010028
29#include "arm_compute/core/ITensor.h"
30
31#include <cctype>
32#include <fstream>
33#include <limits>
34#include <map>
35#include <mutex>
36#include <sstream>
37#include <stdexcept>
38#include <tuple>
39#include <unordered_map>
40#include <utility>
41
42namespace arm_compute
43{
44namespace test
45{
46namespace
47{
Giorgio Arenafda46182017-06-16 13:57:33 +010048template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
49void rgb_to_luminance(const RawTensor &src, RawTensor &dst)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010050{
51 const size_t min_size = std::min(src.size(), dst.size());
52
53 for(size_t i = 0, j = 0; i < min_size; i += 3, ++j)
54 {
Giorgio Arenafda46182017-06-16 13:57:33 +010055 reinterpret_cast<T *>(dst.data())[j] = 0.2126f * src.data()[i + 0] + 0.7152f * src.data()[i + 1] + 0.0722f * src.data()[i + 2];
Anthony Barbier6ff3b192017-09-04 18:44:23 +010056 }
57}
58
59void extract_r_from_rgb(const RawTensor &src, RawTensor &dst)
60{
61 const size_t min_size = std::min(src.size(), dst.size());
62
63 for(size_t i = 0, j = 0; i < min_size; i += 3, ++j)
64 {
65 dst.data()[j] = src.data()[i];
66 }
67}
68
69void extract_g_from_rgb(const RawTensor &src, RawTensor &dst)
70{
71 const size_t min_size = std::min(src.size(), dst.size());
72
73 for(size_t i = 1, j = 0; i < min_size; i += 3, ++j)
74 {
75 dst.data()[j] = src.data()[i];
76 }
77}
78
79void discard_comments(std::ifstream &fs)
80{
81 while(fs.peek() == '#')
82 {
83 fs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
84 }
85}
86
87void discard_comments_and_spaces(std::ifstream &fs)
88{
89 while(true)
90 {
91 discard_comments(fs);
92
93 if(isspace(fs.peek()) == 0)
94 {
95 break;
96 }
97
98 fs.ignore(1);
99 }
100}
101
102std::tuple<unsigned int, unsigned int, int> parse_ppm_header(std::ifstream &fs)
103{
104 // Check the PPM magic number is valid
105 std::array<char, 2> magic_number{ { 0 } };
106 fs >> magic_number[0] >> magic_number[1];
107
108 if(magic_number[0] != 'P' || magic_number[1] != '6')
109 {
110 throw std::runtime_error("Only raw PPM format is suported");
111 }
112
113 discard_comments_and_spaces(fs);
114
115 unsigned int width = 0;
116 fs >> width;
117
118 discard_comments_and_spaces(fs);
119
120 unsigned int height = 0;
121 fs >> height;
122
123 discard_comments_and_spaces(fs);
124
125 int max_value = 0;
126 fs >> max_value;
127
128 if(!fs.good())
129 {
130 throw std::runtime_error("Cannot read image dimensions");
131 }
132
133 if(max_value != 255)
134 {
135 throw std::runtime_error("RawTensor doesn't have 8-bit values");
136 }
137
138 discard_comments(fs);
139
140 if(isspace(fs.peek()) == 0)
141 {
142 throw std::runtime_error("Invalid PPM header");
143 }
144
145 fs.ignore(1);
146
147 return std::make_tuple(width, height, max_value);
148}
149
150RawTensor load_ppm(const std::string &path)
151{
152 std::ifstream file(path, std::ios::in | std::ios::binary);
153
154 if(!file.good())
155 {
Anthony Barbierf6705ec2017-09-28 12:01:10 +0100156 throw framework::FileNotFound("Could not load PPM image: " + path);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100157 }
158
159 unsigned int width = 0;
160 unsigned int height = 0;
161
162 std::tie(width, height, std::ignore) = parse_ppm_header(file);
163
164 RawTensor raw(TensorShape(width, height), Format::RGB888);
165
166 // Check if the file is large enough to fill the image
167 const size_t current_position = file.tellg();
168 file.seekg(0, std::ios_base::end);
169 const size_t end_position = file.tellg();
170 file.seekg(current_position, std::ios_base::beg);
171
172 if((end_position - current_position) < raw.size())
173 {
174 throw std::runtime_error("Not enough data in file");
175 }
176
177 file.read(reinterpret_cast<std::fstream::char_type *>(raw.data()), raw.size());
178
179 if(!file.good())
180 {
181 throw std::runtime_error("Failure while reading image buffer");
182 }
183
184 return raw;
185}
186} // namespace
187
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100188AssetsLibrary::AssetsLibrary(std::string path, std::random_device::result_type seed) //NOLINT
Anthony Barbierac69aa12017-07-03 17:39:37 +0100189 : _library_path(std::move(path)),
190 _seed{ seed }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100191{
192}
193
John Richardson70f946b2017-10-02 16:52:16 +0100194std::string AssetsLibrary::path() const
195{
196 return _library_path;
197}
198
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100199std::random_device::result_type AssetsLibrary::seed() const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100200{
201 return _seed;
202}
203
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100204void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100205{
206 //FIXME: Should be done by swapping cached buffers
207 const RawTensor &src = get(name, format);
208 std::copy_n(src.data(), raw.size(), raw.data());
209}
210
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100211void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100212{
213 fill(raw, name, get_format_for_channel(channel), channel);
214}
215
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100216void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100217{
218 const RawTensor &src = get(name, format, channel);
219 std::copy_n(src.data(), raw.size(), raw.data());
220}
221
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100222const AssetsLibrary::Loader &AssetsLibrary::get_loader(const std::string &extension) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100223{
224 static std::unordered_map<std::string, Loader> loaders =
225 {
226 { "ppm", load_ppm }
227 };
228
229 const auto it = loaders.find(extension);
230
231 if(it != loaders.end())
232 {
233 return it->second;
234 }
235 else
236 {
237 throw std::invalid_argument("Cannot load image with extension '" + extension + "'");
238 }
239}
240
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100241const AssetsLibrary::Converter &AssetsLibrary::get_converter(Format src, Format dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100242{
243 static std::map<std::pair<Format, Format>, Converter> converters =
244 {
Giorgio Arenafda46182017-06-16 13:57:33 +0100245 { std::make_pair(Format::RGB888, Format::U8), rgb_to_luminance<uint8_t> },
246 { std::make_pair(Format::RGB888, Format::U16), rgb_to_luminance<uint16_t> },
247 { std::make_pair(Format::RGB888, Format::S16), rgb_to_luminance<int16_t> },
248 { std::make_pair(Format::RGB888, Format::U32), rgb_to_luminance<uint32_t> }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100249 };
250
251 const auto it = converters.find(std::make_pair(src, dst));
252
253 if(it != converters.end())
254 {
255 return it->second;
256 }
257 else
258 {
259 std::stringstream msg;
260 msg << "Cannot convert from format '" << src << "' to format '" << dst << "'\n";
261 throw std::invalid_argument(msg.str());
262 }
263}
264
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100265const AssetsLibrary::Converter &AssetsLibrary::get_converter(DataType src, Format dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100266{
267 static std::map<std::pair<DataType, Format>, Converter> converters = {};
268
269 const auto it = converters.find(std::make_pair(src, dst));
270
271 if(it != converters.end())
272 {
273 return it->second;
274 }
275 else
276 {
277 std::stringstream msg;
278 msg << "Cannot convert from data type '" << src << "' to format '" << dst << "'\n";
279 throw std::invalid_argument(msg.str());
280 }
281}
282
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100283const AssetsLibrary::Converter &AssetsLibrary::get_converter(DataType src, DataType dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100284{
285 static std::map<std::pair<DataType, DataType>, Converter> converters = {};
286
287 const auto it = converters.find(std::make_pair(src, dst));
288
289 if(it != converters.end())
290 {
291 return it->second;
292 }
293 else
294 {
295 std::stringstream msg;
296 msg << "Cannot convert from data type '" << src << "' to data type '" << dst << "'\n";
297 throw std::invalid_argument(msg.str());
298 }
299}
300
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100301const AssetsLibrary::Converter &AssetsLibrary::get_converter(Format src, DataType dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100302{
303 static std::map<std::pair<Format, DataType>, Converter> converters = {};
304
305 const auto it = converters.find(std::make_pair(src, dst));
306
307 if(it != converters.end())
308 {
309 return it->second;
310 }
311 else
312 {
313 std::stringstream msg;
314 msg << "Cannot convert from format '" << src << "' to data type '" << dst << "'\n";
315 throw std::invalid_argument(msg.str());
316 }
317}
318
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100319const AssetsLibrary::Extractor &AssetsLibrary::get_extractor(Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100320{
321 static std::map<std::pair<Format, Channel>, Extractor> extractors =
322 {
323 { std::make_pair(Format::RGB888, Channel::R), extract_r_from_rgb },
324 { std::make_pair(Format::RGB888, Channel::G), extract_g_from_rgb }
325 };
326
327 const auto it = extractors.find(std::make_pair(format, channel));
328
329 if(it != extractors.end())
330 {
331 return it->second;
332 }
333 else
334 {
335 std::stringstream msg;
336 msg << "Cannot extract channel '" << channel << "' from format '" << format << "'\n";
337 throw std::invalid_argument(msg.str());
338 }
339}
340
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100341RawTensor AssetsLibrary::load_image(const std::string &name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100342{
343#ifdef _WIN32
344 const std::string image_path = ("\\images\\");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100345#else /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100346 const std::string image_path = ("/images/");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100347#endif /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100348
349 const std::string path = _library_path + image_path + name;
350 const std::string extension = path.substr(path.find_last_of('.') + 1);
351 return (*get_loader(extension))(path);
352}
353
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100354const RawTensor &AssetsLibrary::find_or_create_raw_tensor(const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100355{
356 std::lock_guard<std::mutex> guard(_format_lock);
357
358 const RawTensor *ptr = _cache.find(std::make_tuple(name, format));
359
360 if(ptr != nullptr)
361 {
362 return *ptr;
363 }
364
365 RawTensor raw = load_image(name);
366
367 if(raw.format() != format)
368 {
369 //FIXME: Remove unnecessary copy
370 RawTensor dst(raw.shape(), format);
371 (*get_converter(raw.format(), format))(raw, dst);
372 raw = std::move(dst);
373 }
374
375 return _cache.add(std::make_tuple(name, format), std::move(raw));
376}
377
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100378const RawTensor &AssetsLibrary::find_or_create_raw_tensor(const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100379{
380 std::lock_guard<std::mutex> guard(_channel_lock);
381
382 const RawTensor *ptr = _cache.find(std::make_tuple(name, format, channel));
383
384 if(ptr != nullptr)
385 {
386 return *ptr;
387 }
388
389 const RawTensor &src = get(name, format);
390 //FIXME: Need to change shape to match channel
391 RawTensor dst(src.shape(), get_channel_format(channel));
392
393 (*get_extractor(format, channel))(src, dst);
394
395 return _cache.add(std::make_tuple(name, format, channel), std::move(dst));
396}
397
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100398TensorShape AssetsLibrary::get_image_shape(const std::string &name)
Giorgio Arenafda46182017-06-16 13:57:33 +0100399{
400 return load_image(name).shape();
401}
402
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100403const RawTensor &AssetsLibrary::get(const std::string &name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100404{
405 //FIXME: Format should be derived from the image name. Not be fixed to RGB.
406 return find_or_create_raw_tensor(name, Format::RGB888);
407}
408
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100409RawTensor AssetsLibrary::get(const std::string &name)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100410{
411 //FIXME: Format should be derived from the image name. Not be fixed to RGB.
412 return RawTensor(find_or_create_raw_tensor(name, Format::RGB888));
413}
414
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100415RawTensor AssetsLibrary::get(const std::string &name, DataType data_type, int num_channels) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100416{
417 const RawTensor &raw = get(name);
418
419 return RawTensor(raw.shape(), data_type, num_channels);
420}
421
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100422const RawTensor &AssetsLibrary::get(const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100423{
424 return find_or_create_raw_tensor(name, format);
425}
426
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100427RawTensor AssetsLibrary::get(const std::string &name, Format format)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100428{
429 return RawTensor(find_or_create_raw_tensor(name, format));
430}
431
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100432const RawTensor &AssetsLibrary::get(const std::string &name, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100433{
434 return get(name, get_format_for_channel(channel), channel);
435}
436
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100437RawTensor AssetsLibrary::get(const std::string &name, Channel channel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100438{
439 return RawTensor(get(name, get_format_for_channel(channel), channel));
440}
441
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100442const RawTensor &AssetsLibrary::get(const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100443{
444 return find_or_create_raw_tensor(name, format, channel);
445}
446
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100447RawTensor AssetsLibrary::get(const std::string &name, Format format, Channel channel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100448{
449 return RawTensor(find_or_create_raw_tensor(name, format, channel));
450}
451} // namespace test
452} // namespace arm_compute