blob: 355fa25ad348c2c885bb12f900caf650839c27bf [file] [log] [blame]
Derek Lambertid6cb30e2020-04-28 13:31:29 +01001//
Jim Flynn357add22023-04-10 23:26:40 +01002// Copyright © 2020, 2023 Arm Ltd and Contributors. All rights reserved.
Derek Lambertid6cb30e2020-04-28 13:31:29 +01003// SPDX-License-Identifier: MIT
4//
Jan Eilerse7cc7c32020-06-25 13:18:47 +01005
Derek Lambertid6cb30e2020-04-28 13:31:29 +01006#include "armnnTfLiteParser/ITfLiteParser.hpp"
7
8#include "NMS.hpp"
9
10#include <stb/stb_image.h>
11
12#include <armnn/INetwork.hpp>
13#include <armnn/IRuntime.hpp>
14#include <armnn/Logging.hpp>
15#include <armnn/utility/IgnoreUnused.hpp>
16
Jan Eilerse7cc7c32020-06-25 13:18:47 +010017#include <cxxopts/cxxopts.hpp>
18#include <ghc/filesystem.hpp>
19
Derek Lambertid6cb30e2020-04-28 13:31:29 +010020#include <chrono>
Derek Lambertid6cb30e2020-04-28 13:31:29 +010021#include <fstream>
Jan Eilerse7cc7c32020-06-25 13:18:47 +010022#include <iostream>
Pablo Marquez Tellod71ae442022-12-05 14:14:31 +000023#include <iterator>
Keith Mok16fb1a22021-03-19 08:58:44 -070024#include <cmath>
Derek Lambertid6cb30e2020-04-28 13:31:29 +010025
26using namespace armnnTfLiteParser;
27using namespace armnn;
28
29static const int OPEN_FILE_ERROR = -2;
30static const int OPTIMIZE_NETWORK_ERROR = -3;
31static const int LOAD_NETWORK_ERROR = -4;
32static const int LOAD_IMAGE_ERROR = -5;
33static const int GENERAL_ERROR = -100;
34
Pavel Macenauer855a47b2020-05-26 10:54:22 +000035#define CHECK_OK(v) \
36 do { \
37 try { \
38 auto r_local = v; \
39 if (r_local != 0) { return r_local;} \
40 } \
41 catch (const armnn::Exception& e) \
42 { \
43 ARMNN_LOG(error) << "Oops: " << e.what(); \
44 return GENERAL_ERROR; \
45 } \
Derek Lambertid6cb30e2020-04-28 13:31:29 +010046 } while(0)
47
48
49
50template<typename TContainer>
51inline armnn::InputTensors MakeInputTensors(const std::vector<armnn::BindingPointInfo>& inputBindings,
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +010052 const std::vector<std::reference_wrapper<TContainer>>& inputDataContainers)
Derek Lambertid6cb30e2020-04-28 13:31:29 +010053{
54 armnn::InputTensors inputTensors;
55
56 const size_t numInputs = inputBindings.size();
57 if (numInputs != inputDataContainers.size())
58 {
59 throw armnn::Exception("Mismatching vectors");
60 }
61
62 for (size_t i = 0; i < numInputs; i++)
63 {
64 const armnn::BindingPointInfo& inputBinding = inputBindings[i];
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +010065 const TContainer& inputData = inputDataContainers[i].get();
Derek Lambertid6cb30e2020-04-28 13:31:29 +010066
67 armnn::ConstTensor inputTensor(inputBinding.second, inputData.data());
68 inputTensors.push_back(std::make_pair(inputBinding.first, inputTensor));
69 }
70
71 return inputTensors;
72}
73
74template<typename TContainer>
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +010075inline armnn::OutputTensors MakeOutputTensors(
76 const std::vector<armnn::BindingPointInfo>& outputBindings,
77 const std::vector<std::reference_wrapper<TContainer>>& outputDataContainers)
Derek Lambertid6cb30e2020-04-28 13:31:29 +010078{
79 armnn::OutputTensors outputTensors;
80
81 const size_t numOutputs = outputBindings.size();
82 if (numOutputs != outputDataContainers.size())
83 {
84 throw armnn::Exception("Mismatching vectors");
85 }
86
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +010087 outputTensors.reserve(numOutputs);
88
Derek Lambertid6cb30e2020-04-28 13:31:29 +010089 for (size_t i = 0; i < numOutputs; i++)
90 {
91 const armnn::BindingPointInfo& outputBinding = outputBindings[i];
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +010092 const TContainer& outputData = outputDataContainers[i].get();
Derek Lambertid6cb30e2020-04-28 13:31:29 +010093
94 armnn::Tensor outputTensor(outputBinding.second, const_cast<float*>(outputData.data()));
95 outputTensors.push_back(std::make_pair(outputBinding.first, outputTensor));
96 }
97
98 return outputTensors;
99}
100
Derek Lambertifbab30b2020-09-17 13:58:18 +0100101#define S_BOOL(name) enum class name {False=0, True=1};
102
103S_BOOL(ImportMemory)
104S_BOOL(DumpToDot)
105S_BOOL(ExpectFile)
106S_BOOL(OptionalArg)
107
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100108int LoadModel(const char* filename,
109 ITfLiteParser& parser,
110 IRuntime& runtime,
111 NetworkId& networkId,
Narumol Prangnawaratef6f3002020-08-17 17:02:12 +0100112 const std::vector<BackendId>& backendPreferences,
Derek Lambertifbab30b2020-09-17 13:58:18 +0100113 ImportMemory enableImport,
114 DumpToDot dumpToDot)
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100115{
116 std::ifstream stream(filename, std::ios::in | std::ios::binary);
117 if (!stream.is_open())
118 {
119 ARMNN_LOG(error) << "Could not open model: " << filename;
120 return OPEN_FILE_ERROR;
121 }
122
123 std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
124 stream.close();
125
126 auto model = parser.CreateNetworkFromBinary(contents);
127 contents.clear();
128 ARMNN_LOG(debug) << "Model loaded ok: " << filename;
129
130 // Optimize backbone model
John Mcloughlinc5ee0d72023-03-24 12:07:25 +0000131 OptimizerOptionsOpaque options;
132 options.SetImportEnabled(enableImport != ImportMemory::False);
Narumol Prangnawarata2493a02020-08-19 14:39:07 +0100133 auto optimizedModel = Optimize(*model, backendPreferences, runtime.GetDeviceSpec(), options);
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100134 if (!optimizedModel)
135 {
136 ARMNN_LOG(fatal) << "Could not optimize the model:" << filename;
137 return OPTIMIZE_NETWORK_ERROR;
138 }
139
Derek Lambertifbab30b2020-09-17 13:58:18 +0100140 if (dumpToDot != DumpToDot::False)
141 {
142 std::stringstream ss;
143 ss << filename << ".dot";
144 std::ofstream dotStream(ss.str().c_str(), std::ofstream::out);
145 optimizedModel->SerializeToDot(dotStream);
146 dotStream.close();
147 }
Narumol Prangnawaratef6f3002020-08-17 17:02:12 +0100148 // Load model into runtime
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100149 {
150 std::string errorMessage;
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100151
John Mcloughlinc5ee0d72023-03-24 12:07:25 +0000152 armnn::MemorySource memSource = options.GetImportEnabled() ? armnn::MemorySource::Malloc
Francis Murtagh73d3e2e2021-04-29 14:23:04 +0100153 : armnn::MemorySource::Undefined;
154 INetworkProperties modelProps(false, memSource, memSource);
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100155 Status status = runtime.LoadNetwork(networkId, std::move(optimizedModel), errorMessage, modelProps);
156 if (status != Status::Success)
157 {
158 ARMNN_LOG(fatal) << "Could not load " << filename << " model into runtime: " << errorMessage;
159 return LOAD_NETWORK_ERROR;
160 }
161 }
162
163 return 0;
164}
165
166std::vector<float> LoadImage(const char* filename)
167{
Derek Lambertifbab30b2020-09-17 13:58:18 +0100168 if (strlen(filename) == 0)
169 {
170 return std::vector<float>(1920*10180*3, 0.0f);
171 }
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100172 struct Memory
173 {
174 ~Memory() {stbi_image_free(m_Data);}
175 bool IsLoaded() const { return m_Data != nullptr;}
176
177 unsigned char* m_Data;
178 };
179
180 std::vector<float> image;
181
182 int width;
183 int height;
184 int channels;
185
186 Memory mem = {stbi_load(filename, &width, &height, &channels, 3)};
187 if (!mem.IsLoaded())
188 {
189 ARMNN_LOG(error) << "Could not load input image file: " << filename;
190 return image;
191 }
192
193 if (width != 1920 || height != 1080 || channels != 3)
194 {
195 ARMNN_LOG(error) << "Input image has wong dimension: " << width << "x" << height << "x" << channels << ". "
196 " Expected 1920x1080x3.";
197 return image;
198 }
199
200 image.resize(1920*1080*3);
201
202 // Expand to float. Does this need de-gamma?
203 for (unsigned int idx=0; idx <= 1920*1080*3; idx++)
204 {
205 image[idx] = static_cast<float>(mem.m_Data[idx]) /255.0f;
206 }
207
208 return image;
209}
210
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100211
Derek Lambertifbab30b2020-09-17 13:58:18 +0100212bool ValidateFilePath(std::string& file, ExpectFile expectFile)
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100213{
214 if (!ghc::filesystem::exists(file))
215 {
216 std::cerr << "Given file path " << file << " does not exist" << std::endl;
217 return false;
218 }
Derek Lambertifbab30b2020-09-17 13:58:18 +0100219 if (!ghc::filesystem::is_regular_file(file) && expectFile == ExpectFile::True)
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100220 {
221 std::cerr << "Given file path " << file << " is not a regular file" << std::endl;
222 return false;
223 }
224 return true;
225}
226
Ryan OShea74af0932020-08-07 16:27:34 +0100227void CheckAccuracy(std::vector<float>* toDetector0, std::vector<float>* toDetector1,
228 std::vector<float>* toDetector2, std::vector<float>* detectorOutput,
229 const std::vector<yolov3::Detection>& nmsOut, const std::vector<std::string>& filePaths)
230{
231 std::ifstream pathStream;
232 std::vector<float> expected;
233 std::vector<std::vector<float>*> outputs;
234 float compare = 0;
235 unsigned int count = 0;
236
237 //Push back output vectors from inference for use in loop
238 outputs.push_back(toDetector0);
239 outputs.push_back(toDetector1);
240 outputs.push_back(toDetector2);
241 outputs.push_back(detectorOutput);
242
243 for (unsigned int i = 0; i < outputs.size(); ++i)
244 {
245 // Reading expected output files and assigning them to @expected. Close and Clear to reuse stream and clean RAM
246 pathStream.open(filePaths[i]);
247 if (!pathStream.is_open())
248 {
249 ARMNN_LOG(error) << "Expected output file can not be opened: " << filePaths[i];
250 continue;
251 }
252
253 expected.assign(std::istream_iterator<float>(pathStream), {});
254 pathStream.close();
255 pathStream.clear();
256
257 // Ensure each vector is the same length
258 if (expected.size() != outputs[i]->size())
259 {
260 ARMNN_LOG(error) << "Expected output size does not match actual output size: " << filePaths[i];
261 }
262 else
263 {
264 count = 0;
265
266 // Compare abs(difference) with tolerance to check for value by value equality
267 for (unsigned int j = 0; j < outputs[i]->size(); ++j)
268 {
Keith Mok16fb1a22021-03-19 08:58:44 -0700269 compare = std::abs(expected[j] - outputs[i]->at(j));
Ryan OShea74af0932020-08-07 16:27:34 +0100270 if (compare > 0.001f)
271 {
272 count++;
273 }
274 }
275 if (count > 0)
276 {
277 ARMNN_LOG(error) << count << " output(s) do not match expected values in: " << filePaths[i];
278 }
279 }
280 }
281
282 pathStream.open(filePaths[4]);
283 if (!pathStream.is_open())
284 {
285 ARMNN_LOG(error) << "Expected output file can not be opened: " << filePaths[4];
286 }
287 else
288 {
289 expected.assign(std::istream_iterator<float>(pathStream), {});
290 pathStream.close();
291 pathStream.clear();
292 unsigned int y = 0;
293 unsigned int numOfMember = 6;
294 std::vector<float> intermediate;
295
296 for (auto& detection: nmsOut)
297 {
298 for (unsigned int x = y * numOfMember; x < ((y * numOfMember) + numOfMember); ++x)
299 {
300 intermediate.push_back(expected[x]);
301 }
302 if (!yolov3::compare_detection(detection, intermediate))
303 {
304 ARMNN_LOG(error) << "Expected NMS output does not match: Detection " << y + 1;
305 }
306 intermediate.clear();
307 y++;
308 }
309 }
310}
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100311
312struct ParseArgs
313{
314 ParseArgs(int ac, char *av[]) : options{"TfLiteYoloV3Big-Armnn",
315 "Executes YoloV3Big using ArmNN. YoloV3Big consists "
316 "of 3 parts: A backbone TfLite model, a detector TfLite "
317 "model, and None Maximum Suppression. All parts are "
318 "executed successively."}
319 {
320 options.add_options()
321 ("b,backbone-path",
322 "File path where the TfLite model for the yoloV3big backbone "
323 "can be found e.g. mydir/yoloV3big_backbone.tflite",
324 cxxopts::value<std::string>())
325
Ryan OShea74af0932020-08-07 16:27:34 +0100326 ("c,comparison-files",
327 "Defines the expected outputs for the model "
328 "of yoloV3big e.g. 'mydir/file1.txt,mydir/file2.txt,mydir/file3.txt,mydir/file4.txt'->InputToDetector1"
329 " will be tried first then InputToDetector2 then InputToDetector3 then the Detector Output and finally"
330 " the NMS output. NOTE: Files are passed as comma separated list without whitespaces.",
Derek Lambertifa4c1d12020-12-07 13:56:40 +0000331 cxxopts::value<std::vector<std::string>>()->default_value({}))
Ryan OShea74af0932020-08-07 16:27:34 +0100332
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100333 ("d,detector-path",
334 "File path where the TfLite model for the yoloV3big "
335 "detector can be found e.g.'mydir/yoloV3big_detector.tflite'",
336 cxxopts::value<std::string>())
337
338 ("h,help", "Produce help message")
339
340 ("i,image-path",
341 "File path to a 1080x1920 jpg image that should be "
342 "processed e.g. 'mydir/example_img_180_1920.jpg'",
343 cxxopts::value<std::string>())
344
345 ("B,preferred-backends-backbone",
346 "Defines the preferred backends to run the backbone model "
347 "of yoloV3big e.g. 'GpuAcc,CpuRef' -> GpuAcc will be tried "
348 "first before falling back to CpuRef. NOTE: Backends are passed "
349 "as comma separated list without whitespaces.",
350 cxxopts::value<std::vector<std::string>>()->default_value("GpuAcc,CpuRef"))
351
352 ("D,preferred-backends-detector",
353 "Defines the preferred backends to run the detector model "
354 "of yoloV3big e.g. 'CpuAcc,CpuRef' -> CpuAcc will be tried "
355 "first before falling back to CpuRef. NOTE: Backends are passed "
356 "as comma separated list without whitespaces.",
Derek Lambertifbab30b2020-09-17 13:58:18 +0100357 cxxopts::value<std::vector<std::string>>()->default_value("CpuAcc,CpuRef"))
358
359 ("M, model-to-dot",
360 "Dump the optimized model to a dot file for debugging/analysis",
361 cxxopts::value<bool>()->default_value("false"))
362
363 ("Y, dynamic-backends-path",
364 "Define a path from which to load any dynamic backends.",
365 cxxopts::value<std::string>());
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100366
367 auto result = options.parse(ac, av);
368
369 if (result.count("help"))
370 {
371 std::cout << options.help() << "\n";
372 exit(EXIT_SUCCESS);
373 }
374
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100375
Derek Lambertifbab30b2020-09-17 13:58:18 +0100376 backboneDir = GetPathArgument(result, "backbone-path", ExpectFile::True, OptionalArg::False);
Ryan OShea74af0932020-08-07 16:27:34 +0100377
Derek Lambertifbab30b2020-09-17 13:58:18 +0100378 comparisonFiles = GetPathArgument(result["comparison-files"].as<std::vector<std::string>>(), OptionalArg::True);
379
380 detectorDir = GetPathArgument(result, "detector-path", ExpectFile::True, OptionalArg::False);
381
382 imageDir = GetPathArgument(result, "image-path", ExpectFile::True, OptionalArg::True);
383
384 dynamicBackendPath = GetPathArgument(result, "dynamic-backends-path", ExpectFile::False, OptionalArg::True);
Ryan OShea74af0932020-08-07 16:27:34 +0100385
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100386 prefBackendsBackbone = GetBackendIDs(result["preferred-backends-backbone"].as<std::vector<std::string>>());
387 LogBackendsInfo(prefBackendsBackbone, "Backbone");
388 prefBackendsDetector = GetBackendIDs(result["preferred-backends-detector"].as<std::vector<std::string>>());
389 LogBackendsInfo(prefBackendsDetector, "detector");
Derek Lambertifbab30b2020-09-17 13:58:18 +0100390
391 dumpToDot = result["model-to-dot"].as<bool>() ? DumpToDot::True : DumpToDot::False;
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100392 }
393
394 /// Takes a vector of backend strings and returns a vector of backendIDs
395 std::vector<BackendId> GetBackendIDs(const std::vector<std::string>& backendStrings)
396 {
397 std::vector<BackendId> backendIDs;
398 for (const auto& b : backendStrings)
399 {
400 backendIDs.push_back(BackendId(b));
401 }
402 return backendIDs;
403 }
404
405 /// Verifies if the program argument with the name argName contains a valid file path.
406 /// Returns the valid file path string if given argument is associated a valid file path.
407 /// Otherwise throws an exception.
Derek Lambertifbab30b2020-09-17 13:58:18 +0100408 std::string GetPathArgument(cxxopts::ParseResult& result,
409 std::string&& argName,
410 ExpectFile expectFile,
411 OptionalArg isOptionalArg)
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100412 {
413 if (result.count(argName))
414 {
Derek Lambertifbab30b2020-09-17 13:58:18 +0100415 std::string path = result[argName].as<std::string>();
416 if (!ValidateFilePath(path, expectFile))
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100417 {
Derek Lambertifbab30b2020-09-17 13:58:18 +0100418 std::stringstream ss;
419 ss << "Argument given to" << argName << "is not a valid file path";
Jim Flynn357add22023-04-10 23:26:40 +0100420 throw cxxopts::exceptions::invalid_option_syntax(ss.str().c_str());
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100421 }
Derek Lambertifbab30b2020-09-17 13:58:18 +0100422 return path;
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100423 }
424 else
425 {
Derek Lambertifbab30b2020-09-17 13:58:18 +0100426 if (isOptionalArg == OptionalArg::True)
427 {
428 return "";
429 }
430
Jim Flynn357add22023-04-10 23:26:40 +0100431 throw cxxopts::exceptions::missing_argument(argName);
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100432 }
433 }
434
Ryan OShea74af0932020-08-07 16:27:34 +0100435 /// Assigns vector of strings to struct member variable
Derek Lambertifbab30b2020-09-17 13:58:18 +0100436 std::vector<std::string> GetPathArgument(const std::vector<std::string>& pathStrings, OptionalArg isOptional)
Ryan OShea74af0932020-08-07 16:27:34 +0100437 {
438 if (pathStrings.size() < 5){
Derek Lambertifbab30b2020-09-17 13:58:18 +0100439 if (isOptional == OptionalArg::True)
440 {
441 return std::vector<std::string>();
442 }
Jim Flynn357add22023-04-10 23:26:40 +0100443 throw cxxopts::exceptions::invalid_option_syntax("Comparison files requires 5 file paths.");
Ryan OShea74af0932020-08-07 16:27:34 +0100444 }
445
446 std::vector<std::string> filePaths;
447 for (auto& path : pathStrings)
448 {
449 filePaths.push_back(path);
Derek Lambertifbab30b2020-09-17 13:58:18 +0100450 if (!ValidateFilePath(filePaths.back(), ExpectFile::True))
Ryan OShea74af0932020-08-07 16:27:34 +0100451 {
Jim Flynn357add22023-04-10 23:26:40 +0100452 throw cxxopts::exceptions::invalid_option_syntax(
453 "Argument given to Comparison Files is not a valid file path");
Ryan OShea74af0932020-08-07 16:27:34 +0100454 }
455 }
456 return filePaths;
457 }
458
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100459 /// Log info about assigned backends
460 void LogBackendsInfo(std::vector<BackendId>& backends, std::string&& modelName)
461 {
462 std::string info;
463 info = "Preferred backends for " + modelName + " set to [ ";
464 for (auto const &backend : backends)
465 {
466 info = info + std::string(backend) + " ";
467 }
468 ARMNN_LOG(info) << info << "]";
469 }
470
471 // Member variables
472 std::string backboneDir;
Ryan OShea74af0932020-08-07 16:27:34 +0100473 std::vector<std::string> comparisonFiles;
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100474 std::string detectorDir;
475 std::string imageDir;
Derek Lambertifbab30b2020-09-17 13:58:18 +0100476 std::string dynamicBackendPath;
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100477
478 std::vector<BackendId> prefBackendsBackbone;
479 std::vector<BackendId> prefBackendsDetector;
480
481 cxxopts::Options options;
Derek Lambertifbab30b2020-09-17 13:58:18 +0100482
483 DumpToDot dumpToDot;
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100484};
485
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100486int main(int argc, char* argv[])
487{
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100488 // Configure logging
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100489 SetAllLoggingSinks(true, true, true);
490 SetLogFilter(LogSeverity::Trace);
491
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100492 // Check and get given program arguments
493 ParseArgs progArgs = ParseArgs(argc, argv);
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100494
495 // Create runtime
496 IRuntime::CreationOptions runtimeOptions; // default
Derek Lambertifbab30b2020-09-17 13:58:18 +0100497
498 if (!progArgs.dynamicBackendPath.empty())
499 {
500 std::cout << "Loading backends from" << progArgs.dynamicBackendPath << "\n";
501 runtimeOptions.m_DynamicBackendsPath = progArgs.dynamicBackendPath;
502 }
503
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100504 auto runtime = IRuntime::Create(runtimeOptions);
505 if (!runtime)
506 {
507 ARMNN_LOG(fatal) << "Could not create runtime.";
508 return -1;
509 }
510
511 // Create TfLite Parsers
512 ITfLiteParser::TfLiteParserOptions parserOptions;
513 auto parser = ITfLiteParser::Create(parserOptions);
514
515 // Load backbone model
516 ARMNN_LOG(info) << "Loading backbone...";
517 NetworkId backboneId;
Derek Lambertifbab30b2020-09-17 13:58:18 +0100518 const DumpToDot dumpToDot = progArgs.dumpToDot;
519 CHECK_OK(LoadModel(progArgs.backboneDir.c_str(),
520 *parser,
521 *runtime,
522 backboneId,
523 progArgs.prefBackendsBackbone,
524 ImportMemory::False,
525 dumpToDot));
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100526 auto inputId = parser->GetNetworkInputBindingInfo(0, "inputs");
527 auto bbOut0Id = parser->GetNetworkOutputBindingInfo(0, "input_to_detector_1");
528 auto bbOut1Id = parser->GetNetworkOutputBindingInfo(0, "input_to_detector_2");
529 auto bbOut2Id = parser->GetNetworkOutputBindingInfo(0, "input_to_detector_3");
530 auto backboneProfile = runtime->GetProfiler(backboneId);
531 backboneProfile->EnableProfiling(true);
532
Derek Lambertifbab30b2020-09-17 13:58:18 +0100533
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100534 // Load detector model
535 ARMNN_LOG(info) << "Loading detector...";
536 NetworkId detectorId;
Derek Lambertifbab30b2020-09-17 13:58:18 +0100537 CHECK_OK(LoadModel(progArgs.detectorDir.c_str(),
538 *parser,
539 *runtime,
540 detectorId,
541 progArgs.prefBackendsDetector,
542 ImportMemory::True,
543 dumpToDot));
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100544 auto detectIn0Id = parser->GetNetworkInputBindingInfo(0, "input_to_detector_1");
545 auto detectIn1Id = parser->GetNetworkInputBindingInfo(0, "input_to_detector_2");
546 auto detectIn2Id = parser->GetNetworkInputBindingInfo(0, "input_to_detector_3");
547 auto outputBoxesId = parser->GetNetworkOutputBindingInfo(0, "output_boxes");
548 auto detectorProfile = runtime->GetProfiler(detectorId);
549
550 // Load input from file
551 ARMNN_LOG(info) << "Loading test image...";
Jan Eilerse7cc7c32020-06-25 13:18:47 +0100552 auto image = LoadImage(progArgs.imageDir.c_str());
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100553 if (image.empty())
554 {
555 return LOAD_IMAGE_ERROR;
556 }
557
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100558 // Allocate the intermediate tensors
559 std::vector<float> intermediateMem0(bbOut0Id.second.GetNumElements());
560 std::vector<float> intermediateMem1(bbOut1Id.second.GetNumElements());
561 std::vector<float> intermediateMem2(bbOut2Id.second.GetNumElements());
562 std::vector<float> intermediateMem3(outputBoxesId.second.GetNumElements());
563
564 // Setup inputs and outputs
565 using BindingInfos = std::vector<armnn::BindingPointInfo>;
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +0100566 using FloatTensors = std::vector<std::reference_wrapper<std::vector<float>>>;
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100567
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +0100568 InputTensors bbInputTensors = MakeInputTensors(BindingInfos{ inputId },
569 FloatTensors{ image });
570 OutputTensors bbOutputTensors = MakeOutputTensors(BindingInfos{ bbOut0Id, bbOut1Id, bbOut2Id },
571 FloatTensors{ intermediateMem0,
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100572 intermediateMem1,
Narumol Prangnawarat2adf5f02020-07-21 10:21:19 +0100573 intermediateMem2 });
574 InputTensors detectInputTensors = MakeInputTensors(BindingInfos{ detectIn0Id,
575 detectIn1Id,
576 detectIn2Id } ,
577 FloatTensors{ intermediateMem0,
578 intermediateMem1,
579 intermediateMem2 });
580 OutputTensors detectOutputTensors = MakeOutputTensors(BindingInfos{ outputBoxesId },
581 FloatTensors{ intermediateMem3 });
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100582
583 static const int numIterations=2;
584 using DurationUS = std::chrono::duration<double, std::micro>;
585 std::vector<DurationUS> nmsDurations(0);
Ryan OShea74af0932020-08-07 16:27:34 +0100586 std::vector<yolov3::Detection> filtered_boxes;
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100587 nmsDurations.reserve(numIterations);
588 for (int i=0; i < numIterations; i++)
589 {
590 // Execute backbone
591 ARMNN_LOG(info) << "Running backbone...";
592 runtime->EnqueueWorkload(backboneId, bbInputTensors, bbOutputTensors);
593
594 // Execute detector
595 ARMNN_LOG(info) << "Running detector...";
596 runtime->EnqueueWorkload(detectorId, detectInputTensors, detectOutputTensors);
597
598 // Execute NMS
599 ARMNN_LOG(info) << "Running nms...";
600 using clock = std::chrono::steady_clock;
601 auto nmsStartTime = clock::now();
602 yolov3::NMSConfig config;
603 config.num_boxes = 127800;
604 config.num_classes = 80;
605 config.confidence_threshold = 0.9f;
606 config.iou_threshold = 0.5f;
Ryan OShea74af0932020-08-07 16:27:34 +0100607 filtered_boxes = yolov3::nms(config, intermediateMem3);
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100608 auto nmsEndTime = clock::now();
609
610 // Enable the profiling after the warm-up run
611 if (i>0)
612 {
613 print_detection(std::cout, filtered_boxes);
614
615 const auto nmsDuration = DurationUS(nmsStartTime - nmsEndTime);
616 nmsDurations.push_back(nmsDuration);
617 }
618 backboneProfile->EnableProfiling(true);
619 detectorProfile->EnableProfiling(true);
620 }
621 // Log timings to file
622 std::ofstream backboneProfileStream("backbone.json");
623 backboneProfile->Print(backboneProfileStream);
624 backboneProfileStream.close();
625
626 std::ofstream detectorProfileStream("detector.json");
627 detectorProfile->Print(detectorProfileStream);
628 detectorProfileStream.close();
629
630 // Manually construct the json output
631 std::ofstream nmsProfileStream("nms.json");
632 nmsProfileStream << "{" << "\n";
633 nmsProfileStream << R"( "NmsTimings": {)" << "\n";
634 nmsProfileStream << R"( "raw": [)" << "\n";
635 bool isFirst = true;
636 for (auto duration : nmsDurations)
637 {
638 if (!isFirst)
639 {
640 nmsProfileStream << ",\n";
641 }
642
643 nmsProfileStream << " " << duration.count();
644 isFirst = false;
645 }
646 nmsProfileStream << "\n";
647 nmsProfileStream << R"( "units": "us")" << "\n";
648 nmsProfileStream << " ]" << "\n";
649 nmsProfileStream << " }" << "\n";
650 nmsProfileStream << "}" << "\n";
651 nmsProfileStream.close();
652
Derek Lambertifbab30b2020-09-17 13:58:18 +0100653 if (progArgs.comparisonFiles.size() > 0)
654 {
655 CheckAccuracy(&intermediateMem0,
656 &intermediateMem1,
657 &intermediateMem2,
658 &intermediateMem3,
659 filtered_boxes,
660 progArgs.comparisonFiles);
661 }
Ryan OShea74af0932020-08-07 16:27:34 +0100662
Derek Lambertid6cb30e2020-04-28 13:31:29 +0100663 ARMNN_LOG(info) << "Run completed";
664 return 0;
Keith Mok16fb1a22021-03-19 08:58:44 -0700665}