10 #include <stb/stb_image.h> 17 #include <cxxopts/cxxopts.hpp> 18 #include <ghc/filesystem.hpp> 26 using namespace armnn;
28 static const int OPEN_FILE_ERROR = -2;
29 static const int OPTIMIZE_NETWORK_ERROR = -3;
30 static const int LOAD_NETWORK_ERROR = -4;
31 static const int LOAD_IMAGE_ERROR = -5;
32 static const int GENERAL_ERROR = -100;
38 if (r_local != 0) { return r_local;} \ 40 catch (const armnn::Exception& e) \ 42 ARMNN_LOG(error) << "Oops: " << e.what(); \ 43 return GENERAL_ERROR; \ 49 template<
typename TContainer>
51 const std::vector<std::reference_wrapper<TContainer>>& inputDataContainers)
55 const size_t numInputs = inputBindings.size();
56 if (numInputs != inputDataContainers.size())
61 for (
size_t i = 0; i < numInputs; i++)
64 const TContainer& inputData = inputDataContainers[i].get();
67 inputTensors.push_back(std::make_pair(inputBinding.first, inputTensor));
73 template<
typename TContainer>
75 const std::vector<armnn::BindingPointInfo>& outputBindings,
76 const std::vector<std::reference_wrapper<TContainer>>& outputDataContainers)
80 const size_t numOutputs = outputBindings.size();
81 if (numOutputs != outputDataContainers.size())
86 outputTensors.reserve(numOutputs);
88 for (
size_t i = 0; i < numOutputs; i++)
91 const TContainer& outputData = outputDataContainers[i].get();
93 armnn::Tensor outputTensor(outputBinding.second, const_cast<float*>(outputData.data()));
94 outputTensors.push_back(std::make_pair(outputBinding.first, outputTensor));
100 #define S_BOOL(name) enum class name {False=0, True=1}; 115 std::ifstream stream(filename, std::ios::in | std::ios::binary);
116 if (!stream.is_open())
119 return OPEN_FILE_ERROR;
122 std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
125 auto model = parser.CreateNetworkFromBinary(contents);
132 auto optimizedModel =
Optimize(*model, backendPreferences, runtime.GetDeviceSpec(), options);
136 return OPTIMIZE_NETWORK_ERROR;
141 std::stringstream ss;
142 ss << filename <<
".dot";
143 std::ofstream dotStream(ss.str().c_str(), std::ofstream::out);
144 optimizedModel->SerializeToDot(dotStream);
149 std::string errorMessage;
154 Status status = runtime.LoadNetwork(networkId, std::move(optimizedModel), errorMessage, modelProps);
155 if (status != Status::Success)
157 ARMNN_LOG(
fatal) <<
"Could not load " << filename <<
" model into runtime: " << errorMessage;
158 return LOAD_NETWORK_ERROR;
167 if (strlen(filename) == 0)
169 return std::vector<float>(1920*10180*3, 0.0f);
173 ~Memory() {stbi_image_free(m_Data);}
174 bool IsLoaded()
const {
return m_Data !=
nullptr;}
176 unsigned char* m_Data;
179 std::vector<float> image;
185 Memory mem = {stbi_load(filename, &width, &height, &channels, 3)};
188 ARMNN_LOG(
error) <<
"Could not load input image file: " << filename;
192 if (width != 1920 || height != 1080 || channels != 3)
194 ARMNN_LOG(
error) <<
"Input image has wong dimension: " << width <<
"x" << height <<
"x" << channels <<
". " 195 " Expected 1920x1080x3.";
199 image.resize(1920*1080*3);
202 for (
unsigned int idx=0; idx <= 1920*1080*3; idx++)
204 image[idx] =
static_cast<float>(mem.m_Data[idx]) /255.0f;
213 if (!ghc::filesystem::exists(file))
215 std::cerr <<
"Given file path " << file <<
" does not exist" << std::endl;
218 if (!ghc::filesystem::is_regular_file(file) && expectFile ==
ExpectFile::True)
220 std::cerr <<
"Given file path " << file <<
" is not a regular file" << std::endl;
226 void CheckAccuracy(std::vector<float>* toDetector0, std::vector<float>* toDetector1,
227 std::vector<float>* toDetector2, std::vector<float>* detectorOutput,
228 const std::vector<yolov3::Detection>& nmsOut,
const std::vector<std::string>& filePaths)
230 std::ifstream pathStream;
231 std::vector<float> expected;
232 std::vector<std::vector<float>*> outputs;
234 unsigned int count = 0;
237 outputs.push_back(toDetector0);
238 outputs.push_back(toDetector1);
239 outputs.push_back(toDetector2);
240 outputs.push_back(detectorOutput);
242 for (
unsigned int i = 0; i < outputs.size(); ++i)
245 pathStream.open(filePaths[i]);
246 if (!pathStream.is_open())
248 ARMNN_LOG(
error) <<
"Expected output file can not be opened: " << filePaths[i];
252 expected.assign(std::istream_iterator<float>(pathStream), {});
257 if (expected.size() != outputs[i]->size())
259 ARMNN_LOG(
error) <<
"Expected output size does not match actual output size: " << filePaths[i];
266 for (
unsigned int j = 0; j < outputs[i]->size(); ++j)
268 compare = std::abs(expected[j] - outputs[i]->at(j));
269 if (compare > 0.001f)
276 ARMNN_LOG(
error) << count <<
" output(s) do not match expected values in: " << filePaths[i];
281 pathStream.open(filePaths[4]);
282 if (!pathStream.is_open())
284 ARMNN_LOG(
error) <<
"Expected output file can not be opened: " << filePaths[4];
288 expected.assign(std::istream_iterator<float>(pathStream), {});
292 unsigned int numOfMember = 6;
293 std::vector<float> intermediate;
295 for (
auto& detection: nmsOut)
297 for (
unsigned int x = y * numOfMember; x < ((y * numOfMember) + numOfMember); ++x)
299 intermediate.push_back(expected[x]);
303 ARMNN_LOG(
error) <<
"Expected NMS output does not match: Detection " << y + 1;
305 intermediate.clear();
313 ParseArgs(
int ac,
char *av[]) : options{
"TfLiteYoloV3Big-Armnn",
314 "Executes YoloV3Big using ArmNN. YoloV3Big consists " 315 "of 3 parts: A backbone TfLite model, a detector TfLite " 316 "model, and None Maximum Suppression. All parts are " 317 "executed successively."}
319 options.add_options()
321 "File path where the TfLite model for the yoloV3big backbone " 322 "can be found e.g. mydir/yoloV3big_backbone.tflite",
323 cxxopts::value<std::string>())
325 (
"c,comparison-files",
326 "Defines the expected outputs for the model " 327 "of yoloV3big e.g. 'mydir/file1.txt,mydir/file2.txt,mydir/file3.txt,mydir/file4.txt'->InputToDetector1" 328 " will be tried first then InputToDetector2 then InputToDetector3 then the Detector Output and finally" 329 " the NMS output. NOTE: Files are passed as comma separated list without whitespaces.",
330 cxxopts::value<std::vector<std::string>>()->default_value({}))
333 "File path where the TfLite model for the yoloV3big " 334 "detector can be found e.g.'mydir/yoloV3big_detector.tflite'",
335 cxxopts::value<std::string>())
337 (
"h,help",
"Produce help message")
340 "File path to a 1080x1920 jpg image that should be " 341 "processed e.g. 'mydir/example_img_180_1920.jpg'",
342 cxxopts::value<std::string>())
344 (
"B,preferred-backends-backbone",
345 "Defines the preferred backends to run the backbone model " 346 "of yoloV3big e.g. 'GpuAcc,CpuRef' -> GpuAcc will be tried " 347 "first before falling back to CpuRef. NOTE: Backends are passed " 348 "as comma separated list without whitespaces.",
349 cxxopts::value<std::vector<std::string>>()->default_value(
"GpuAcc,CpuRef"))
351 (
"D,preferred-backends-detector",
352 "Defines the preferred backends to run the detector model " 353 "of yoloV3big e.g. 'CpuAcc,CpuRef' -> CpuAcc will be tried " 354 "first before falling back to CpuRef. NOTE: Backends are passed " 355 "as comma separated list without whitespaces.",
356 cxxopts::value<std::vector<std::string>>()->default_value(
"CpuAcc,CpuRef"))
359 "Dump the optimized model to a dot file for debugging/analysis",
360 cxxopts::value<bool>()->default_value(
"false"))
362 (
"Y, dynamic-backends-path",
363 "Define a path from which to load any dynamic backends.",
364 cxxopts::value<std::string>());
366 auto result = options.parse(ac, av);
368 if (result.count(
"help"))
370 std::cout << options.help() <<
"\n";
377 comparisonFiles = GetPathArgument(result[
"comparison-files"].as<std::vector<std::string>>(),
OptionalArg::True);
385 prefBackendsBackbone =
GetBackendIDs(result[
"preferred-backends-backbone"].as<std::vector<std::string>>());
386 LogBackendsInfo(prefBackendsBackbone,
"Backbone");
387 prefBackendsDetector =
GetBackendIDs(result[
"preferred-backends-detector"].as<std::vector<std::string>>());
388 LogBackendsInfo(prefBackendsDetector,
"detector");
394 std::vector<BackendId>
GetBackendIDs(
const std::vector<std::string>& backendStrings)
396 std::vector<BackendId> backendIDs;
397 for (
const auto& b : backendStrings)
407 std::string GetPathArgument(cxxopts::ParseResult& result,
408 std::string&& argName,
412 if (result.count(argName))
414 std::string path = result[argName].as<std::string>();
417 std::stringstream ss;
418 ss <<
"Argument given to" << argName <<
"is not a valid file path";
419 throw cxxopts::option_syntax_exception(ss.str().c_str());
430 throw cxxopts::missing_argument_exception(argName);
435 std::vector<std::string> GetPathArgument(
const std::vector<std::string>& pathStrings,
OptionalArg isOptional)
437 if (pathStrings.size() < 5){
440 return std::vector<std::string>();
442 throw cxxopts::option_syntax_exception(
"Comparison files requires 5 file paths.");
445 std::vector<std::string> filePaths;
446 for (
auto& path : pathStrings)
448 filePaths.push_back(path);
451 throw cxxopts::option_syntax_exception(
"Argument given to Comparison Files is not a valid file path");
458 void LogBackendsInfo(std::vector<BackendId>& backends, std::string&& modelName)
461 info =
"Preferred backends for " + modelName +
" set to [ ";
462 for (
auto const &backend : backends)
464 info = info + std::string(backend) +
" ";
470 std::string backboneDir;
471 std::vector<std::string> comparisonFiles;
472 std::string detectorDir;
473 std::string imageDir;
474 std::string dynamicBackendPath;
476 std::vector<BackendId> prefBackendsBackbone;
477 std::vector<BackendId> prefBackendsDetector;
479 cxxopts::Options options;
484 int main(
int argc,
char* argv[])
491 ParseArgs progArgs = ParseArgs(argc, argv);
496 if (!progArgs.dynamicBackendPath.empty())
498 std::cout <<
"Loading backends from" << progArgs.dynamicBackendPath <<
"\n";
502 auto runtime = IRuntime::Create(runtimeOptions);
516 const DumpToDot dumpToDot = progArgs.dumpToDot;
521 progArgs.prefBackendsBackbone,
524 auto inputId = parser->GetNetworkInputBindingInfo(0,
"inputs");
525 auto bbOut0Id = parser->GetNetworkOutputBindingInfo(0,
"input_to_detector_1");
526 auto bbOut1Id = parser->GetNetworkOutputBindingInfo(0,
"input_to_detector_2");
527 auto bbOut2Id = parser->GetNetworkOutputBindingInfo(0,
"input_to_detector_3");
528 auto backboneProfile = runtime->GetProfiler(backboneId);
529 backboneProfile->EnableProfiling(
true);
539 progArgs.prefBackendsDetector,
542 auto detectIn0Id = parser->GetNetworkInputBindingInfo(0,
"input_to_detector_1");
543 auto detectIn1Id = parser->GetNetworkInputBindingInfo(0,
"input_to_detector_2");
544 auto detectIn2Id = parser->GetNetworkInputBindingInfo(0,
"input_to_detector_3");
545 auto outputBoxesId = parser->GetNetworkOutputBindingInfo(0,
"output_boxes");
546 auto detectorProfile = runtime->GetProfiler(detectorId);
550 auto image =
LoadImage(progArgs.imageDir.c_str());
553 return LOAD_IMAGE_ERROR;
557 std::vector<float> intermediateMem0(bbOut0Id.second.GetNumElements());
558 std::vector<float> intermediateMem1(bbOut1Id.second.GetNumElements());
559 std::vector<float> intermediateMem2(bbOut2Id.second.GetNumElements());
560 std::vector<float> intermediateMem3(outputBoxesId.second.GetNumElements());
563 using BindingInfos = std::vector<armnn::BindingPointInfo>;
564 using FloatTensors = std::vector<std::reference_wrapper<std::vector<float>>>;
567 FloatTensors{ image });
569 FloatTensors{ intermediateMem0,
575 FloatTensors{ intermediateMem0,
579 FloatTensors{ intermediateMem3 });
581 static const int numIterations=2;
582 using DurationUS = std::chrono::duration<double, std::micro>;
583 std::vector<DurationUS> nmsDurations(0);
584 std::vector<yolov3::Detection> filtered_boxes;
585 nmsDurations.reserve(numIterations);
586 for (
int i=0; i < numIterations; i++)
590 runtime->EnqueueWorkload(backboneId, bbInputTensors, bbOutputTensors);
594 runtime->EnqueueWorkload(detectorId, detectInputTensors, detectOutputTensors);
598 using clock = std::chrono::steady_clock;
599 auto nmsStartTime = clock::now();
605 filtered_boxes =
yolov3::nms(config, intermediateMem3);
606 auto nmsEndTime = clock::now();
613 const auto nmsDuration = DurationUS(nmsStartTime - nmsEndTime);
614 nmsDurations.push_back(nmsDuration);
616 backboneProfile->EnableProfiling(
true);
617 detectorProfile->EnableProfiling(
true);
620 std::ofstream backboneProfileStream(
"backbone.json");
621 backboneProfile->Print(backboneProfileStream);
622 backboneProfileStream.close();
624 std::ofstream detectorProfileStream(
"detector.json");
625 detectorProfile->Print(detectorProfileStream);
626 detectorProfileStream.close();
629 std::ofstream nmsProfileStream(
"nms.json");
630 nmsProfileStream <<
"{" <<
"\n";
631 nmsProfileStream << R
"( "NmsTimings": {)" << "\n";
632 nmsProfileStream << R
"( "raw": [)" << "\n";
634 for (
auto duration : nmsDurations)
638 nmsProfileStream <<
",\n";
641 nmsProfileStream <<
" " << duration.count();
644 nmsProfileStream <<
"\n";
645 nmsProfileStream << R
"( "units": "us")" << "\n";
646 nmsProfileStream <<
" ]" <<
"\n";
647 nmsProfileStream <<
" }" <<
"\n";
648 nmsProfileStream <<
"}" <<
"\n";
649 nmsProfileStream.close();
651 if (progArgs.comparisonFiles.size() > 0)
658 progArgs.comparisonFiles);
void CheckAccuracy(std::vector< float > *toDetector0, std::vector< float > *toDetector1, std::vector< float > *toDetector2, std::vector< float > *detectorOutput, const std::vector< yolov3::Detection > &nmsOut, const std::vector< std::string > &filePaths)
void SetAllLoggingSinks(bool standardOut, bool debugOut, bool coloured)
int LoadModel(const char *filename, ITfLiteParser &parser, IRuntime &runtime, NetworkId &networkId, const std::vector< BackendId > &backendPreferences, ImportMemory enableImport, DumpToDot dumpToDot)
armnn::InputTensors MakeInputTensors(const std::vector< armnn::BindingPointInfo > &inputBindings, const std::vector< std::reference_wrapper< TContainer >> &inputDataContainers)
int main(int argc, char *argv[])
#define ARMNN_LOG(severity)
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
std::vector< armnn::BackendId > GetBackendIDs(const std::vector< std::string > &backendStringsVec)
Takes a vector of backend strings and returns a vector of backendIDs.
Copyright (c) 2021 ARM Limited and Contributors.
unsigned int num_boxes
Number of detected boxes.
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
std::vector< float > LoadImage(const char *filename)
void SetLogFilter(LogSeverity level)
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
void print_detection(std::ostream &os, const std::vector< Detection > &detections)
Print identified yolo detections.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
ArmNN performs an optimization on each model/network before it gets loaded for execution.
float iou_threshold
Inclusion threshold for Intersection-Over-Union.
std::string m_DynamicBackendsPath
Setting this value will override the paths set by the DYNAMIC_BACKEND_PATHS compiler directive Only a...
std::pair< armnn::LayerBindingId, armnn::TensorInfo > BindingPointInfo
Base class for all ArmNN exceptions so that users can filter to just those.
std::vector< Detection > nms(const NMSConfig &config, const std::vector< float > &detected_boxes)
Perform Non-Maxima Supression on a list of given detections.
mapbox::util::variant< std::vector< float >, std::vector< int >, std::vector< unsigned char >, std::vector< int8_t > > TContainer
MemorySource
Define the Memory Source to reduce copies.
armnn::OutputTensors MakeOutputTensors(const std::vector< armnn::BindingPointInfo > &outputBindings, const std::vector< std::reference_wrapper< TContainer >> &outputDataContainers)
Non Maxima Suprresion configuration meta-data.
float confidence_threshold
Inclusion confidence threshold for a box.
bool compare_detection(const yolov3::Detection &detection, const std::vector< float > &expected)
Compare a detection object with a vector of float values.
bool ValidateFilePath(std::string &file, ExpectFile expectFile)
unsigned int num_classes
Number of classes in the detected boxes.