blob: 7e618c9de5169e40709652949176cf829366e849 [file] [log] [blame]
Anthony Barbier2a07e182017-08-04 18:20:27 +01001/*
Pablo Marquez Tello37d84452024-03-25 16:31:26 +00002 * Copyright (c) 2017-2021, 2024 Arm Limited.
Anthony Barbier2a07e182017-08-04 18:20:27 +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
25#include "utils/GraphUtils.h"
hakanardof36ac352018-02-16 10:06:34 +010026
Georgios Pinitascac13b12018-04-27 19:07:19 +010027#include "arm_compute/core/Helpers.h"
28#include "arm_compute/core/Types.h"
Georgios Pinitas12be7ab2018-07-03 12:06:23 +010029#include "arm_compute/graph/Logger.h"
hakanardof36ac352018-02-16 10:06:34 +010030#include "arm_compute/runtime/SubTensor.h"
Michalis Spyrou6bff1952019-10-02 17:22:11 +010031
32#pragma GCC diagnostic push
33#pragma GCC diagnostic ignored "-Wunused-parameter"
Georgios Pinitas7c3b9242018-06-21 19:01:25 +010034#include "utils/ImageLoader.h"
Michalis Spyrou6bff1952019-10-02 17:22:11 +010035#pragma GCC diagnostic pop
Anthony Barbier2a07e182017-08-04 18:20:27 +010036#include "utils/Utils.h"
37
Michalis Spyrou7c60c992019-10-10 14:33:47 +010038#include <inttypes.h>
Gian Marco44ec2e72017-10-19 14:13:38 +010039#include <iomanip>
Georgios Pinitas12be7ab2018-07-03 12:06:23 +010040#include <limits>
Anthony Barbier2a07e182017-08-04 18:20:27 +010041
42using namespace arm_compute::graph_utils;
43
Georgios Pinitascac13b12018-04-27 19:07:19 +010044namespace
45{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010046std::pair<arm_compute::TensorShape, arm_compute::PermutationVector>
47compute_permutation_parameters(const arm_compute::TensorShape &shape, arm_compute::DataLayout data_layout)
Georgios Pinitascac13b12018-04-27 19:07:19 +010048{
49 // Set permutation parameters if needed
50 arm_compute::TensorShape permuted_shape = shape;
51 arm_compute::PermutationVector perm;
52 // Permute only if num_dimensions greater than 2
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010053 if (shape.num_dimensions() > 2)
Georgios Pinitascac13b12018-04-27 19:07:19 +010054 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010055 perm = (data_layout == arm_compute::DataLayout::NHWC) ? arm_compute::PermutationVector(2U, 0U, 1U)
56 : arm_compute::PermutationVector(1U, 2U, 0U);
Georgios Pinitascac13b12018-04-27 19:07:19 +010057
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010058 arm_compute::PermutationVector perm_shape = (data_layout == arm_compute::DataLayout::NCHW)
59 ? arm_compute::PermutationVector(2U, 0U, 1U)
60 : arm_compute::PermutationVector(1U, 2U, 0U);
Georgios Pinitascac13b12018-04-27 19:07:19 +010061 arm_compute::permute(permuted_shape, perm_shape);
62 }
63
64 return std::make_pair(permuted_shape, perm);
65}
66} // namespace
67
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010068TFPreproccessor::TFPreproccessor(float min_range, float max_range) : _min_range(min_range), _max_range(max_range)
Georgios Pinitasbe2772a2018-08-17 15:33:39 +010069{
70}
Georgios Pinitas140fdc72018-02-16 11:42:38 +000071void TFPreproccessor::preprocess(ITensor &tensor)
72{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010073 if (tensor.info()->data_type() == DataType::F32)
giuros01351bd132019-08-23 14:27:30 +010074 {
75 preprocess_typed<float>(tensor);
76 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010077 else if (tensor.info()->data_type() == DataType::F16)
giuros01351bd132019-08-23 14:27:30 +010078 {
79 preprocess_typed<half>(tensor);
80 }
81 else
82 {
83 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
84 }
85}
86
87template <typename T>
88void TFPreproccessor::preprocess_typed(ITensor &tensor)
89{
Georgios Pinitas140fdc72018-02-16 11:42:38 +000090 Window window;
91 window.use_tensor_dimensions(tensor.info()->tensor_shape());
92
Georgios Pinitasbe2772a2018-08-17 15:33:39 +010093 const float range = _max_range - _min_range;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010094 execute_window_loop(window,
95 [&](const Coordinates &id)
96 {
97 const T value = *reinterpret_cast<T *>(tensor.ptr_to_element(id));
98 float res = value / 255.f; // Normalize to [0, 1]
99 res = res * range + _min_range; // Map to [min_range, max_range]
100 *reinterpret_cast<T *>(tensor.ptr_to_element(id)) = res;
101 });
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000102}
103
Georgios Pinitasb54c6442019-04-03 13:18:14 +0100104CaffePreproccessor::CaffePreproccessor(std::array<float, 3> mean, bool bgr, float scale)
105 : _mean(mean), _bgr(bgr), _scale(scale)
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000106{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100107 if (_bgr)
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000108 {
109 std::swap(_mean[0], _mean[2]);
110 }
111}
112
113void CaffePreproccessor::preprocess(ITensor &tensor)
114{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100115 if (tensor.info()->data_type() == DataType::F32)
giuros01351bd132019-08-23 14:27:30 +0100116 {
117 preprocess_typed<float>(tensor);
118 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100119 else if (tensor.info()->data_type() == DataType::F16)
giuros01351bd132019-08-23 14:27:30 +0100120 {
121 preprocess_typed<half>(tensor);
122 }
123 else
124 {
125 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
126 }
127}
128
129template <typename T>
130void CaffePreproccessor::preprocess_typed(ITensor &tensor)
131{
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000132 Window window;
133 window.use_tensor_dimensions(tensor.info()->tensor_shape());
Georgios Pinitas7d66a8e2018-07-17 12:28:42 +0100134 const int channel_idx = get_data_layout_dimension_index(tensor.info()->data_layout(), DataLayoutDimension::CHANNEL);
135
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100136 execute_window_loop(window,
137 [&](const Coordinates &id)
138 {
139 const T value =
140 *reinterpret_cast<T *>(tensor.ptr_to_element(id)) - T(_mean[id[channel_idx]]);
141 *reinterpret_cast<T *>(tensor.ptr_to_element(id)) = value * T(_scale);
142 });
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000143}
144
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100145PPMWriter::PPMWriter(std::string name, unsigned int maximum) : _name(std::move(name)), _iterator(0), _maximum(maximum)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100146{
147}
148
149bool PPMWriter::access_tensor(ITensor &tensor)
150{
151 std::stringstream ss;
152 ss << _name << _iterator << ".ppm";
Gian Marco44ec2e72017-10-19 14:13:38 +0100153
154 arm_compute::utils::save_to_ppm(tensor, ss.str());
Anthony Barbier2a07e182017-08-04 18:20:27 +0100155
156 _iterator++;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100157 if (_maximum == 0)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100158 {
159 return true;
160 }
161 return _iterator < _maximum;
162}
163
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100164DummyAccessor::DummyAccessor(unsigned int maximum) : _iterator(0), _maximum(maximum)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100165{
166}
167
Giorgio Arena9c67d382021-08-20 15:24:03 +0100168bool DummyAccessor::access_tensor_data()
169{
170 return false;
171}
172
Anthony Barbier2a07e182017-08-04 18:20:27 +0100173bool DummyAccessor::access_tensor(ITensor &tensor)
174{
175 ARM_COMPUTE_UNUSED(tensor);
Anthony Barbier8a042112018-08-21 18:16:53 +0100176 bool ret = _maximum == 0 || _iterator < _maximum;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100177 if (_iterator == _maximum)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100178 {
179 _iterator = 0;
180 }
181 else
182 {
183 _iterator++;
184 }
185 return ret;
186}
187
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100188NumPyAccessor::NumPyAccessor(
189 std::string npy_path, TensorShape shape, DataType data_type, DataLayout data_layout, std::ostream &output_stream)
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100190 : _npy_tensor(), _filename(std::move(npy_path)), _output_stream(output_stream)
191{
Isabella Gottardia7acb3c2019-01-08 13:48:44 +0000192 NumPyBinLoader loader(_filename, data_layout);
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100193
194 TensorInfo info(shape, 1, data_type);
Isabella Gottardia7acb3c2019-01-08 13:48:44 +0000195 info.set_data_layout(data_layout);
196
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100197 _npy_tensor.allocator()->init(info);
198 _npy_tensor.allocator()->allocate();
199
200 loader.access_tensor(_npy_tensor);
201}
202
203template <typename T>
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000204void NumPyAccessor::access_numpy_tensor(ITensor &tensor, T tolerance)
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100205{
Gian Marco Iodicead486e22018-08-07 17:17:06 +0100206 const int num_elements = tensor.info()->tensor_shape().total_size();
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000207 int num_mismatches = utils::compare_tensor<T>(tensor, _npy_tensor, tolerance);
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100208 float percentage_mismatches = static_cast<float>(num_mismatches) / num_elements;
209
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100210 _output_stream << "Results: " << 100.f - (percentage_mismatches * 100) << " % matches with the provided output["
211 << _filename << "]." << std::endl;
212 _output_stream << " " << num_elements - num_mismatches << " out of " << num_elements
213 << " matches with the provided output[" << _filename << "]." << std::endl
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000214 << std::endl;
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100215}
216
217bool NumPyAccessor::access_tensor(ITensor &tensor)
218{
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000219 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32, DataType::QASYMM8);
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100220 ARM_COMPUTE_ERROR_ON(_npy_tensor.info()->dimension(0) != tensor.info()->dimension(0));
221
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100222 switch (tensor.info()->data_type())
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100223 {
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000224 case DataType::QASYMM8:
225 access_numpy_tensor<qasymm8_t>(tensor, 0);
226 break;
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100227 case DataType::F32:
Isabella Gottardi0ae5de92019-03-14 10:32:11 +0000228 access_numpy_tensor<float>(tensor, 0.0001f);
Isabella Gottardi88d5b222018-04-06 12:24:55 +0100229 break;
230 default:
231 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
232 }
233
234 return false;
235}
236
Isabella Gottardicd4e9ab2019-11-05 17:50:27 +0000237#ifdef ARM_COMPUTE_ASSERTS_ENABLED
238PrintAccessor::PrintAccessor(std::ostream &output_stream, IOFormatInfo io_fmt)
239 : _output_stream(output_stream), _io_fmt(io_fmt)
240{
241}
242
243bool PrintAccessor::access_tensor(ITensor &tensor)
244{
245 tensor.print(_output_stream, _io_fmt);
246 return false;
247}
248#endif /* ARM_COMPUTE_ASSERTS_ENABLED */
249
Isabella Gottardi2ea37612019-07-16 11:48:51 +0100250SaveNumPyAccessor::SaveNumPyAccessor(std::string npy_name, const bool is_fortran)
251 : _npy_name(std::move(npy_name)), _is_fortran(is_fortran)
252{
253}
254
255bool SaveNumPyAccessor::access_tensor(ITensor &tensor)
256{
257 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
258
259 utils::save_to_npy(tensor, _npy_name, _is_fortran);
260
261 return false;
262}
263
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100264ImageAccessor::ImageAccessor(std::string filename, bool bgr, std::unique_ptr<IPreprocessor> preprocessor)
Anthony Barbier8a042112018-08-21 18:16:53 +0100265 : _already_loaded(false), _filename(std::move(filename)), _bgr(bgr), _preprocessor(std::move(preprocessor))
Gian Marco44ec2e72017-10-19 14:13:38 +0100266{
267}
268
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100269bool ImageAccessor::access_tensor(ITensor &tensor)
Gian Marco44ec2e72017-10-19 14:13:38 +0100270{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100271 if (!_already_loaded)
Georgios Pinitascac13b12018-04-27 19:07:19 +0100272 {
Anthony Barbier8a042112018-08-21 18:16:53 +0100273 auto image_loader = utils::ImageLoaderFactory::create(_filename);
274 ARM_COMPUTE_EXIT_ON_MSG(image_loader == nullptr, "Unsupported image type");
Isabella Gottardia4c61882017-11-03 12:11:55 +0000275
Anthony Barbier8a042112018-08-21 18:16:53 +0100276 // Open image file
277 image_loader->open(_filename);
Gian Marco44ec2e72017-10-19 14:13:38 +0100278
Anthony Barbier8a042112018-08-21 18:16:53 +0100279 // Get permutated shape and permutation parameters
280 TensorShape permuted_shape = tensor.info()->tensor_shape();
281 arm_compute::PermutationVector perm;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100282 if (tensor.info()->data_layout() != DataLayout::NCHW)
Anthony Barbier8a042112018-08-21 18:16:53 +0100283 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100284 std::tie(permuted_shape, perm) =
285 compute_permutation_parameters(tensor.info()->tensor_shape(), tensor.info()->data_layout());
Anthony Barbier8a042112018-08-21 18:16:53 +0100286 }
Sheri Zhangd80792a2020-11-05 10:43:37 +0000287
288#ifdef __arm__
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100289 ARM_COMPUTE_EXIT_ON_MSG_VAR(
290 image_loader->width() != permuted_shape.x() || image_loader->height() != permuted_shape.y(),
291 "Failed to load image file: dimensions [%d,%d] not correct, expected [%" PRIu32 ",%" PRIu32 "].",
292 image_loader->width(), image_loader->height(), permuted_shape.x(), permuted_shape.y());
Sheri Zhangd80792a2020-11-05 10:43:37 +0000293#else // __arm__
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100294 ARM_COMPUTE_EXIT_ON_MSG_VAR(
295 image_loader->width() != permuted_shape.x() || image_loader->height() != permuted_shape.y(),
296 "Failed to load image file: dimensions [%d,%d] not correct, expected [%" PRIu64 ",%" PRIu64 "].",
297 image_loader->width(), image_loader->height(), static_cast<uint64_t>(permuted_shape.x()),
298 static_cast<uint64_t>(permuted_shape.y()));
Sheri Zhangd80792a2020-11-05 10:43:37 +0000299#endif // __arm__
Anthony Barbier8a042112018-08-21 18:16:53 +0100300
301 // Fill the tensor with the PPM content (BGR)
302 image_loader->fill_planar_tensor(tensor, _bgr);
303
304 // Preprocess tensor
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100305 if (_preprocessor)
Anthony Barbier8a042112018-08-21 18:16:53 +0100306 {
307 _preprocessor->preprocess(tensor);
308 }
Georgios Pinitas140fdc72018-02-16 11:42:38 +0000309 }
Gian Marco44ec2e72017-10-19 14:13:38 +0100310
Anthony Barbier8a042112018-08-21 18:16:53 +0100311 _already_loaded = !_already_loaded;
312 return _already_loaded;
Gian Marco44ec2e72017-10-19 14:13:38 +0100313}
314
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100315ValidationInputAccessor::ValidationInputAccessor(const std::string &image_list,
316 std::string images_path,
317 std::unique_ptr<IPreprocessor> preprocessor,
318 bool bgr,
319 unsigned int start,
Anthony Barbier40606df2018-07-23 14:41:59 +0100320 unsigned int end,
321 std::ostream &output_stream)
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100322 : _path(std::move(images_path)),
323 _images(),
324 _preprocessor(std::move(preprocessor)),
325 _bgr(bgr),
326 _offset(0),
327 _output_stream(output_stream)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100328{
Anthony Barbier40606df2018-07-23 14:41:59 +0100329 ARM_COMPUTE_EXIT_ON_MSG(start > end, "Invalid validation range!");
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100330
331 std::ifstream ifs;
332 try
333 {
334 ifs.exceptions(std::ifstream::badbit);
335 ifs.open(image_list, std::ios::in | std::ios::binary);
336
337 // Parse image names
338 unsigned int counter = 0;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100339 for (std::string line; !std::getline(ifs, line).fail() && counter <= end; ++counter)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100340 {
341 // Add image to process if withing range
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100342 if (counter >= start)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100343 {
344 std::stringstream linestream(line);
345 std::string image_name;
346
347 linestream >> image_name;
348 _images.emplace_back(std::move(image_name));
349 }
350 }
351 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100352 catch (const std::ifstream::failure &e)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100353 {
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100354 ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", image_list.c_str(), e.what());
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100355 }
356}
357
358bool ValidationInputAccessor::access_tensor(arm_compute::ITensor &tensor)
359{
360 bool ret = _offset < _images.size();
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100361 if (ret)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100362 {
363 utils::JPEGLoader jpeg;
364
365 // Open JPEG file
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100366 std::string image_name = _path + _images[_offset++];
367 jpeg.open(image_name);
Anthony Barbier40606df2018-07-23 14:41:59 +0100368 _output_stream << "[" << _offset << "/" << _images.size() << "] Validating " << image_name << std::endl;
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100369
370 // Get permutated shape and permutation parameters
371 TensorShape permuted_shape = tensor.info()->tensor_shape();
372 arm_compute::PermutationVector perm;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100373 if (tensor.info()->data_layout() != DataLayout::NCHW)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100374 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100375 std::tie(permuted_shape, perm) =
376 compute_permutation_parameters(tensor.info()->tensor_shape(), tensor.info()->data_layout());
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100377 }
Sheri Zhangd80792a2020-11-05 10:43:37 +0000378
379#ifdef __arm__
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100380 ARM_COMPUTE_EXIT_ON_MSG_VAR(jpeg.width() != permuted_shape.x() || jpeg.height() != permuted_shape.y(),
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100381 "Failed to load image file: dimensions [%d,%d] not correct, expected [%" PRIu32
382 ",%" PRIu32 "].",
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100383 jpeg.width(), jpeg.height(), permuted_shape.x(), permuted_shape.y());
Sheri Zhangd80792a2020-11-05 10:43:37 +0000384#else // __arm__
385 ARM_COMPUTE_EXIT_ON_MSG_VAR(jpeg.width() != permuted_shape.x() || jpeg.height() != permuted_shape.y(),
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100386 "Failed to load image file: dimensions [%d,%d] not correct, expected [%" PRIu64
387 ",%" PRIu64 "].",
388 jpeg.width(), jpeg.height(), static_cast<uint64_t>(permuted_shape.x()),
389 static_cast<uint64_t>(permuted_shape.y()));
Sheri Zhangd80792a2020-11-05 10:43:37 +0000390#endif // __arm__
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100391
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100392 // Fill the tensor with the JPEG content (BGR)
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100393 jpeg.fill_planar_tensor(tensor, _bgr);
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100394
395 // Preprocess tensor
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100396 if (_preprocessor)
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100397 {
398 _preprocessor->preprocess(tensor);
399 }
Georgios Pinitas7c3b9242018-06-21 19:01:25 +0100400 }
401
402 return ret;
403}
404
Georgios Pinitas7908de72018-06-27 12:34:20 +0100405ValidationOutputAccessor::ValidationOutputAccessor(const std::string &image_list,
Georgios Pinitas7908de72018-06-27 12:34:20 +0100406 std::ostream &output_stream,
407 unsigned int start,
408 unsigned int end)
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100409 : _results(), _output_stream(output_stream), _offset(0), _positive_samples_top1(0), _positive_samples_top5(0)
Georgios Pinitas7908de72018-06-27 12:34:20 +0100410{
Anthony Barbier40606df2018-07-23 14:41:59 +0100411 ARM_COMPUTE_EXIT_ON_MSG(start > end, "Invalid validation range!");
Georgios Pinitas7908de72018-06-27 12:34:20 +0100412
413 std::ifstream ifs;
414 try
415 {
416 ifs.exceptions(std::ifstream::badbit);
417 ifs.open(image_list, std::ios::in | std::ios::binary);
418
419 // Parse image correctly classified labels
420 unsigned int counter = 0;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100421 for (std::string line; !std::getline(ifs, line).fail() && counter <= end; ++counter)
Georgios Pinitas7908de72018-06-27 12:34:20 +0100422 {
423 // Add label if within range
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100424 if (counter >= start)
Georgios Pinitas7908de72018-06-27 12:34:20 +0100425 {
426 std::stringstream linestream(line);
427 std::string image_name;
428 int result;
429
430 linestream >> image_name >> result;
431 _results.emplace_back(result);
432 }
433 }
434 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100435 catch (const std::ifstream::failure &e)
Georgios Pinitas7908de72018-06-27 12:34:20 +0100436 {
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100437 ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", image_list.c_str(), e.what());
Georgios Pinitas7908de72018-06-27 12:34:20 +0100438 }
439}
440
441void ValidationOutputAccessor::reset()
442{
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100443 _offset = 0;
444 _positive_samples_top1 = 0;
445 _positive_samples_top5 = 0;
Georgios Pinitas7908de72018-06-27 12:34:20 +0100446}
447
448bool ValidationOutputAccessor::access_tensor(arm_compute::ITensor &tensor)
449{
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100450 bool ret = _offset < _results.size();
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100451 if (ret)
Georgios Pinitas7908de72018-06-27 12:34:20 +0100452 {
453 // Get results
454 std::vector<size_t> tensor_results;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100455 switch (tensor.info()->data_type())
Georgios Pinitas7908de72018-06-27 12:34:20 +0100456 {
457 case DataType::QASYMM8:
458 tensor_results = access_predictions_tensor<uint8_t>(tensor);
459 break;
giuros01351bd132019-08-23 14:27:30 +0100460 case DataType::F16:
461 tensor_results = access_predictions_tensor<half>(tensor);
462 break;
Georgios Pinitas7908de72018-06-27 12:34:20 +0100463 case DataType::F32:
464 tensor_results = access_predictions_tensor<float>(tensor);
465 break;
466 default:
467 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
468 }
469
470 // Check if tensor results are within top-n accuracy
471 size_t correct_label = _results[_offset++];
Georgios Pinitas7908de72018-06-27 12:34:20 +0100472
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100473 aggregate_sample(tensor_results, _positive_samples_top1, 1, correct_label);
474 aggregate_sample(tensor_results, _positive_samples_top5, 5, correct_label);
Georgios Pinitas7908de72018-06-27 12:34:20 +0100475 }
476
477 // Report top_n accuracy
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100478 if (_offset >= _results.size())
Georgios Pinitas7908de72018-06-27 12:34:20 +0100479 {
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100480 report_top_n(1, _results.size(), _positive_samples_top1);
481 report_top_n(5, _results.size(), _positive_samples_top5);
Georgios Pinitas7908de72018-06-27 12:34:20 +0100482 }
483
484 return ret;
485}
486
487template <typename T>
488std::vector<size_t> ValidationOutputAccessor::access_predictions_tensor(arm_compute::ITensor &tensor)
489{
490 // Get the predicted class
491 std::vector<size_t> index;
492
493 const auto output_net = reinterpret_cast<T *>(tensor.buffer() + tensor.info()->offset_first_element_in_bytes());
494 const size_t num_classes = tensor.info()->dimension(0);
495
496 index.resize(num_classes);
497
498 // Sort results
499 std::iota(std::begin(index), std::end(index), static_cast<size_t>(0));
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100500 std::sort(std::begin(index), std::end(index), [&](size_t a, size_t b) { return output_net[a] > output_net[b]; });
Georgios Pinitas7908de72018-06-27 12:34:20 +0100501
502 return index;
503}
504
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100505void ValidationOutputAccessor::aggregate_sample(const std::vector<size_t> &res,
506 size_t &positive_samples,
507 size_t top_n,
508 size_t correct_label)
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100509{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100510 auto is_valid_label = [correct_label](size_t label) { return label == correct_label; };
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100511
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100512 if (std::any_of(std::begin(res), std::begin(res) + top_n, is_valid_label))
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100513 {
514 ++positive_samples;
515 }
516}
517
518void ValidationOutputAccessor::report_top_n(size_t top_n, size_t total_samples, size_t positive_samples)
519{
520 size_t negative_samples = total_samples - positive_samples;
521 float accuracy = positive_samples / static_cast<float>(total_samples);
522
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100523 _output_stream << "----------Top " << top_n << " accuracy ----------" << std::endl << std::endl;
Georgios Pinitas12be7ab2018-07-03 12:06:23 +0100524 _output_stream << "Positive samples : " << positive_samples << std::endl;
525 _output_stream << "Negative samples : " << negative_samples << std::endl;
526 _output_stream << "Accuracy : " << accuracy << std::endl;
527}
528
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100529DetectionOutputAccessor::DetectionOutputAccessor(const std::string &labels_path,
530 std::vector<TensorShape> &imgs_tensor_shapes,
531 std::ostream &output_stream)
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000532 : _labels(), _tensor_shapes(std::move(imgs_tensor_shapes)), _output_stream(output_stream)
533{
534 _labels.clear();
535
536 std::ifstream ifs;
537
538 try
539 {
540 ifs.exceptions(std::ifstream::badbit);
541 ifs.open(labels_path, std::ios::in | std::ios::binary);
542
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100543 for (std::string line; !std::getline(ifs, line).fail();)
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000544 {
545 _labels.emplace_back(line);
546 }
547 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100548 catch (const std::ifstream::failure &e)
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000549 {
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100550 ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", labels_path.c_str(), e.what());
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000551 }
552}
553
554template <typename T>
555void DetectionOutputAccessor::access_predictions_tensor(ITensor &tensor)
556{
557 const size_t num_detection = tensor.info()->valid_region().shape.y();
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100558 const auto output_prt = reinterpret_cast<T *>(tensor.buffer() + tensor.info()->offset_first_element_in_bytes());
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000559
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100560 if (num_detection > 0)
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000561 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100562 _output_stream << "---------------------- Detections ----------------------" << std::endl << std::endl;
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000563
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100564 _output_stream << std::left << std::setprecision(4) << std::setw(8) << "Image | " << std::setw(8) << "Label | "
565 << std::setw(12) << "Confidence | "
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000566 << "[ xmin, ymin, xmax, ymax ]" << std::endl;
567
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100568 for (size_t i = 0; i < num_detection; ++i)
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000569 {
570 auto im = static_cast<const int>(output_prt[i * 7]);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100571 _output_stream << std::setw(8) << im << std::setw(8) << _labels[output_prt[i * 7 + 1]] << std::setw(12)
572 << output_prt[i * 7 + 2] << " [" << (output_prt[i * 7 + 3] * _tensor_shapes[im].x()) << ", "
573 << (output_prt[i * 7 + 4] * _tensor_shapes[im].y()) << ", "
574 << (output_prt[i * 7 + 5] * _tensor_shapes[im].x()) << ", "
575 << (output_prt[i * 7 + 6] * _tensor_shapes[im].y()) << "]" << std::endl;
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000576 }
577 }
578 else
579 {
580 _output_stream << "No detection found." << std::endl;
581 }
582}
583
584bool DetectionOutputAccessor::access_tensor(ITensor &tensor)
585{
586 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32);
587
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100588 switch (tensor.info()->data_type())
Isabella Gottardi7234ed82018-11-27 08:51:10 +0000589 {
590 case DataType::F32:
591 access_predictions_tensor<float>(tensor);
592 break;
593 default:
594 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
595 }
596
597 return false;
598}
599
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100600TopNPredictionsAccessor::TopNPredictionsAccessor(const std::string &labels_path,
601 size_t top_n,
602 std::ostream &output_stream)
Gian Marco44ec2e72017-10-19 14:13:38 +0100603 : _labels(), _output_stream(output_stream), _top_n(top_n)
604{
605 _labels.clear();
606
607 std::ifstream ifs;
608
609 try
610 {
611 ifs.exceptions(std::ifstream::badbit);
612 ifs.open(labels_path, std::ios::in | std::ios::binary);
613
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100614 for (std::string line; !std::getline(ifs, line).fail();)
Gian Marco44ec2e72017-10-19 14:13:38 +0100615 {
616 _labels.emplace_back(line);
617 }
618 }
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100619 catch (const std::ifstream::failure &e)
Gian Marco44ec2e72017-10-19 14:13:38 +0100620 {
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100621 ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", labels_path.c_str(), e.what());
Gian Marco44ec2e72017-10-19 14:13:38 +0100622 }
623}
624
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000625template <typename T>
626void TopNPredictionsAccessor::access_predictions_tensor(ITensor &tensor)
Gian Marco44ec2e72017-10-19 14:13:38 +0100627{
Gian Marco44ec2e72017-10-19 14:13:38 +0100628 // Get the predicted class
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000629 std::vector<T> classes_prob;
Gian Marco44ec2e72017-10-19 14:13:38 +0100630 std::vector<size_t> index;
631
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000632 const auto output_net = reinterpret_cast<T *>(tensor.buffer() + tensor.info()->offset_first_element_in_bytes());
Gian Marco44ec2e72017-10-19 14:13:38 +0100633 const size_t num_classes = tensor.info()->dimension(0);
634
635 classes_prob.resize(num_classes);
636 index.resize(num_classes);
637
638 std::copy(output_net, output_net + num_classes, classes_prob.begin());
639
640 // Sort results
641 std::iota(std::begin(index), std::end(index), static_cast<size_t>(0));
642 std::sort(std::begin(index), std::end(index),
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100643 [&](size_t a, size_t b) { return classes_prob[a] > classes_prob[b]; });
Gian Marco44ec2e72017-10-19 14:13:38 +0100644
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100645 _output_stream << "---------- Top " << _top_n << " predictions ----------" << std::endl << std::endl;
646 for (size_t i = 0; i < _top_n; ++i)
Gian Marco44ec2e72017-10-19 14:13:38 +0100647 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100648 _output_stream << std::fixed << std::setprecision(4) << +classes_prob[index.at(i)] << " - [id = " << index.at(i)
649 << "]"
Gian Marco44ec2e72017-10-19 14:13:38 +0100650 << ", " << _labels[index.at(i)] << std::endl;
651 }
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000652}
653
654bool TopNPredictionsAccessor::access_tensor(ITensor &tensor)
655{
656 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::F32, DataType::QASYMM8);
657 ARM_COMPUTE_ERROR_ON(_labels.size() != tensor.info()->dimension(0));
658
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100659 switch (tensor.info()->data_type())
Giorgio Arenaa66eaa22017-12-21 19:50:06 +0000660 {
661 case DataType::QASYMM8:
662 access_predictions_tensor<uint8_t>(tensor);
663 break;
664 case DataType::F32:
665 access_predictions_tensor<float>(tensor);
666 break;
667 default:
668 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
669 }
Gian Marco44ec2e72017-10-19 14:13:38 +0100670
671 return false;
672}
673
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100674RandomAccessor::RandomAccessor(PixelValue lower, PixelValue upper, std::random_device::result_type seed)
675 : _lower(lower), _upper(upper), _seed(seed)
676{
677}
678
679template <typename T, typename D>
680void RandomAccessor::fill(ITensor &tensor, D &&distribution)
681{
682 std::mt19937 gen(_seed);
683
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100684 if (tensor.info()->padding().empty() && (dynamic_cast<SubTensor *>(&tensor) == nullptr))
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100685 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100686 for (size_t offset = 0; offset < tensor.info()->total_size(); offset += tensor.info()->element_size())
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100687 {
Michele Di Giorgio88731f02018-09-25 16:49:27 +0100688 const auto value = static_cast<T>(distribution(gen));
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100689 *reinterpret_cast<T *>(tensor.buffer() + offset) = value;
690 }
691 }
692 else
693 {
694 // If tensor has padding accessing tensor elements through execution window.
695 Window window;
696 window.use_tensor_dimensions(tensor.info()->tensor_shape());
697
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100698 execute_window_loop(window,
699 [&](const Coordinates &id)
700 {
701 const auto value = static_cast<T>(distribution(gen));
702 *reinterpret_cast<T *>(tensor.ptr_to_element(id)) = value;
703 });
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100704 }
705}
706
707bool RandomAccessor::access_tensor(ITensor &tensor)
708{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100709 switch (tensor.info()->data_type())
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100710 {
John Kesapidesfb68ca12019-01-21 14:13:27 +0000711 case DataType::QASYMM8:
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100712 case DataType::U8:
713 {
Pablo Marquez Tello37d84452024-03-25 16:31:26 +0000714 std::uniform_int_distribution<uint32_t> distribution_u8(_lower.get<uint8_t>(), _upper.get<uint8_t>());
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100715 fill<uint8_t>(tensor, distribution_u8);
716 break;
717 }
718 case DataType::S8:
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100719 {
Pablo Marquez Tello37d84452024-03-25 16:31:26 +0000720 std::uniform_int_distribution<int32_t> distribution_s8(_lower.get<int8_t>(), _upper.get<int8_t>());
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100721 fill<int8_t>(tensor, distribution_s8);
722 break;
723 }
724 case DataType::U16:
725 {
726 std::uniform_int_distribution<uint16_t> distribution_u16(_lower.get<uint16_t>(), _upper.get<uint16_t>());
727 fill<uint16_t>(tensor, distribution_u16);
728 break;
729 }
730 case DataType::S16:
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100731 {
732 std::uniform_int_distribution<int16_t> distribution_s16(_lower.get<int16_t>(), _upper.get<int16_t>());
733 fill<int16_t>(tensor, distribution_s16);
734 break;
735 }
736 case DataType::U32:
737 {
738 std::uniform_int_distribution<uint32_t> distribution_u32(_lower.get<uint32_t>(), _upper.get<uint32_t>());
739 fill<uint32_t>(tensor, distribution_u32);
740 break;
741 }
742 case DataType::S32:
743 {
744 std::uniform_int_distribution<int32_t> distribution_s32(_lower.get<int32_t>(), _upper.get<int32_t>());
745 fill<int32_t>(tensor, distribution_s32);
746 break;
747 }
748 case DataType::U64:
749 {
750 std::uniform_int_distribution<uint64_t> distribution_u64(_lower.get<uint64_t>(), _upper.get<uint64_t>());
751 fill<uint64_t>(tensor, distribution_u64);
752 break;
753 }
754 case DataType::S64:
755 {
756 std::uniform_int_distribution<int64_t> distribution_s64(_lower.get<int64_t>(), _upper.get<int64_t>());
757 fill<int64_t>(tensor, distribution_s64);
758 break;
759 }
760 case DataType::F16:
761 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100762 arm_compute::utils::uniform_real_distribution_16bit<half> distribution_f16(_lower.get<float>(),
763 _upper.get<float>());
Michele Di Giorgio88731f02018-09-25 16:49:27 +0100764 fill<half>(tensor, distribution_f16);
Michalis Spyrou53b405f2017-09-27 15:55:31 +0100765 break;
766 }
767 case DataType::F32:
768 {
769 std::uniform_real_distribution<float> distribution_f32(_lower.get<float>(), _upper.get<float>());
770 fill<float>(tensor, distribution_f32);
771 break;
772 }
773 case DataType::F64:
774 {
775 std::uniform_real_distribution<double> distribution_f64(_lower.get<double>(), _upper.get<double>());
776 fill<double>(tensor, distribution_f64);
777 break;
778 }
779 default:
780 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
781 }
782 return true;
783}
784
Georgios Pinitascac13b12018-04-27 19:07:19 +0100785NumPyBinLoader::NumPyBinLoader(std::string filename, DataLayout file_layout)
Anthony Barbier8a042112018-08-21 18:16:53 +0100786 : _already_loaded(false), _filename(std::move(filename)), _file_layout(file_layout)
Anthony Barbier2a07e182017-08-04 18:20:27 +0100787{
788}
789
790bool NumPyBinLoader::access_tensor(ITensor &tensor)
791{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100792 if (!_already_loaded)
Anthony Barbier8a042112018-08-21 18:16:53 +0100793 {
794 utils::NPYLoader loader;
795 loader.open(_filename, _file_layout);
796 loader.fill_tensor(tensor);
797 }
Anthony Barbier2a07e182017-08-04 18:20:27 +0100798
Anthony Barbier8a042112018-08-21 18:16:53 +0100799 _already_loaded = !_already_loaded;
800 return _already_loaded;
Anthony Barbier87f21cd2017-11-10 16:27:32 +0000801}