blob: 7e380fa8f3c6b21533c1ee169d22b79cfb91d8ea [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
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100194std::random_device::result_type AssetsLibrary::seed() const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100195{
196 return _seed;
197}
198
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100199void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100200{
201 //FIXME: Should be done by swapping cached buffers
202 const RawTensor &src = get(name, format);
203 std::copy_n(src.data(), raw.size(), raw.data());
204}
205
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100206void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100207{
208 fill(raw, name, get_format_for_channel(channel), channel);
209}
210
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100211void AssetsLibrary::fill(RawTensor &raw, const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100212{
213 const RawTensor &src = get(name, format, channel);
214 std::copy_n(src.data(), raw.size(), raw.data());
215}
216
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100217const AssetsLibrary::Loader &AssetsLibrary::get_loader(const std::string &extension) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100218{
219 static std::unordered_map<std::string, Loader> loaders =
220 {
221 { "ppm", load_ppm }
222 };
223
224 const auto it = loaders.find(extension);
225
226 if(it != loaders.end())
227 {
228 return it->second;
229 }
230 else
231 {
232 throw std::invalid_argument("Cannot load image with extension '" + extension + "'");
233 }
234}
235
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100236const AssetsLibrary::Converter &AssetsLibrary::get_converter(Format src, Format dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100237{
238 static std::map<std::pair<Format, Format>, Converter> converters =
239 {
Giorgio Arenafda46182017-06-16 13:57:33 +0100240 { std::make_pair(Format::RGB888, Format::U8), rgb_to_luminance<uint8_t> },
241 { std::make_pair(Format::RGB888, Format::U16), rgb_to_luminance<uint16_t> },
242 { std::make_pair(Format::RGB888, Format::S16), rgb_to_luminance<int16_t> },
243 { std::make_pair(Format::RGB888, Format::U32), rgb_to_luminance<uint32_t> }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100244 };
245
246 const auto it = converters.find(std::make_pair(src, dst));
247
248 if(it != converters.end())
249 {
250 return it->second;
251 }
252 else
253 {
254 std::stringstream msg;
255 msg << "Cannot convert from format '" << src << "' to format '" << dst << "'\n";
256 throw std::invalid_argument(msg.str());
257 }
258}
259
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100260const AssetsLibrary::Converter &AssetsLibrary::get_converter(DataType src, Format dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100261{
262 static std::map<std::pair<DataType, Format>, Converter> converters = {};
263
264 const auto it = converters.find(std::make_pair(src, dst));
265
266 if(it != converters.end())
267 {
268 return it->second;
269 }
270 else
271 {
272 std::stringstream msg;
273 msg << "Cannot convert from data type '" << src << "' to format '" << dst << "'\n";
274 throw std::invalid_argument(msg.str());
275 }
276}
277
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100278const AssetsLibrary::Converter &AssetsLibrary::get_converter(DataType src, DataType dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100279{
280 static std::map<std::pair<DataType, DataType>, Converter> converters = {};
281
282 const auto it = converters.find(std::make_pair(src, dst));
283
284 if(it != converters.end())
285 {
286 return it->second;
287 }
288 else
289 {
290 std::stringstream msg;
291 msg << "Cannot convert from data type '" << src << "' to data type '" << dst << "'\n";
292 throw std::invalid_argument(msg.str());
293 }
294}
295
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100296const AssetsLibrary::Converter &AssetsLibrary::get_converter(Format src, DataType dst) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100297{
298 static std::map<std::pair<Format, DataType>, Converter> converters = {};
299
300 const auto it = converters.find(std::make_pair(src, dst));
301
302 if(it != converters.end())
303 {
304 return it->second;
305 }
306 else
307 {
308 std::stringstream msg;
309 msg << "Cannot convert from format '" << src << "' to data type '" << dst << "'\n";
310 throw std::invalid_argument(msg.str());
311 }
312}
313
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100314const AssetsLibrary::Extractor &AssetsLibrary::get_extractor(Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100315{
316 static std::map<std::pair<Format, Channel>, Extractor> extractors =
317 {
318 { std::make_pair(Format::RGB888, Channel::R), extract_r_from_rgb },
319 { std::make_pair(Format::RGB888, Channel::G), extract_g_from_rgb }
320 };
321
322 const auto it = extractors.find(std::make_pair(format, channel));
323
324 if(it != extractors.end())
325 {
326 return it->second;
327 }
328 else
329 {
330 std::stringstream msg;
331 msg << "Cannot extract channel '" << channel << "' from format '" << format << "'\n";
332 throw std::invalid_argument(msg.str());
333 }
334}
335
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100336RawTensor AssetsLibrary::load_image(const std::string &name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100337{
338#ifdef _WIN32
339 const std::string image_path = ("\\images\\");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100340#else /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100341 const std::string image_path = ("/images/");
Anthony Barbierac69aa12017-07-03 17:39:37 +0100342#endif /* _WIN32 */
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100343
344 const std::string path = _library_path + image_path + name;
345 const std::string extension = path.substr(path.find_last_of('.') + 1);
346 return (*get_loader(extension))(path);
347}
348
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100349const RawTensor &AssetsLibrary::find_or_create_raw_tensor(const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100350{
351 std::lock_guard<std::mutex> guard(_format_lock);
352
353 const RawTensor *ptr = _cache.find(std::make_tuple(name, format));
354
355 if(ptr != nullptr)
356 {
357 return *ptr;
358 }
359
360 RawTensor raw = load_image(name);
361
362 if(raw.format() != format)
363 {
364 //FIXME: Remove unnecessary copy
365 RawTensor dst(raw.shape(), format);
366 (*get_converter(raw.format(), format))(raw, dst);
367 raw = std::move(dst);
368 }
369
370 return _cache.add(std::make_tuple(name, format), std::move(raw));
371}
372
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100373const RawTensor &AssetsLibrary::find_or_create_raw_tensor(const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100374{
375 std::lock_guard<std::mutex> guard(_channel_lock);
376
377 const RawTensor *ptr = _cache.find(std::make_tuple(name, format, channel));
378
379 if(ptr != nullptr)
380 {
381 return *ptr;
382 }
383
384 const RawTensor &src = get(name, format);
385 //FIXME: Need to change shape to match channel
386 RawTensor dst(src.shape(), get_channel_format(channel));
387
388 (*get_extractor(format, channel))(src, dst);
389
390 return _cache.add(std::make_tuple(name, format, channel), std::move(dst));
391}
392
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100393TensorShape AssetsLibrary::get_image_shape(const std::string &name)
Giorgio Arenafda46182017-06-16 13:57:33 +0100394{
395 return load_image(name).shape();
396}
397
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100398const RawTensor &AssetsLibrary::get(const std::string &name) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100399{
400 //FIXME: Format should be derived from the image name. Not be fixed to RGB.
401 return find_or_create_raw_tensor(name, Format::RGB888);
402}
403
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100404RawTensor AssetsLibrary::get(const std::string &name)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100405{
406 //FIXME: Format should be derived from the image name. Not be fixed to RGB.
407 return RawTensor(find_or_create_raw_tensor(name, Format::RGB888));
408}
409
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100410RawTensor AssetsLibrary::get(const std::string &name, DataType data_type, int num_channels) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100411{
412 const RawTensor &raw = get(name);
413
414 return RawTensor(raw.shape(), data_type, num_channels);
415}
416
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100417const RawTensor &AssetsLibrary::get(const std::string &name, Format format) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100418{
419 return find_or_create_raw_tensor(name, format);
420}
421
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100422RawTensor AssetsLibrary::get(const std::string &name, Format format)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100423{
424 return RawTensor(find_or_create_raw_tensor(name, format));
425}
426
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100427const RawTensor &AssetsLibrary::get(const std::string &name, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100428{
429 return get(name, get_format_for_channel(channel), channel);
430}
431
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100432RawTensor AssetsLibrary::get(const std::string &name, Channel channel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100433{
434 return RawTensor(get(name, get_format_for_channel(channel), channel));
435}
436
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100437const RawTensor &AssetsLibrary::get(const std::string &name, Format format, Channel channel) const
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100438{
439 return find_or_create_raw_tensor(name, format, channel);
440}
441
Moritz Pflanzerfb5aabb2017-07-18 14:39:55 +0100442RawTensor AssetsLibrary::get(const std::string &name, Format format, Channel channel)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100443{
444 return RawTensor(find_or_create_raw_tensor(name, format, channel));
445}
446} // namespace test
447} // namespace arm_compute