xref: /aosp_15_r20/external/armnn/samples/ObjectDetection/src/Main.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "CvVideoFrameReader.hpp"
7 #include "CvWindowOutput.hpp"
8 #include "CvVideoFileWriter.hpp"
9 #include "ObjectDetectionPipeline.hpp"
10 #include "CmdArgsParser.hpp"
11 
12 #include <fstream>
13 #include <iostream>
14 #include <map>
15 #include <random>
16 
17 const std::string MODEL_NAME = "--model-name";
18 const std::string VIDEO_FILE_PATH = "--video-file-path";
19 const std::string MODEL_FILE_PATH = "--model-file-path";
20 const std::string OUTPUT_VIDEO_FILE_PATH = "--output-video-file-path";
21 const std::string LABEL_PATH = "--label-path";
22 const std::string PREFERRED_BACKENDS = "--preferred-backends";
23 const std::string PROFILING_ENABLED = "--profiling_enabled";
24 const std::string HELP = "--help";
25 
26 /*
27  * The accepted options for this Object detection executable
28  */
29 static std::map<std::string, std::string> CMD_OPTIONS = {
30         {VIDEO_FILE_PATH, "[REQUIRED] Path to the video file to run object detection on"},
31         {MODEL_FILE_PATH, "[REQUIRED] Path to the Object Detection model to use"},
32         {LABEL_PATH, "[REQUIRED] Path to the label set for the provided model file. "
33                      "Label file  should be an ordered list, separated by a new line."},
34         {MODEL_NAME, "[REQUIRED] The name of the model being used. Accepted options: YOLO_V3_TINY, SSD_MOBILE"},
35         {OUTPUT_VIDEO_FILE_PATH, "[OPTIONAL] Path to the output video file with detections added in. "
36                                  "If specified will save file to disk, else displays the output to screen"},
37         {PREFERRED_BACKENDS, "[OPTIONAL] Takes the preferred backends in preference order, separated by comma."
38                              " For example: CpuAcc,GpuAcc,CpuRef. Accepted options: [CpuAcc, CpuRef, GpuAcc]."
39                              " Defaults to CpuAcc,CpuRef"},
40         {PROFILING_ENABLED, "[OPTIONAL] Enabling this option will print important ML related milestones timing"
41                             "information in micro-seconds. By default, this option is disabled."
42                             "Accepted options are true/false."}
43 };
44 
45 /*
46  * Reads the user supplied backend preference, splits it by comma, and returns an ordered vector
47  */
GetPreferredBackendList(const std::string & preferredBackends)48 std::vector<armnn::BackendId> GetPreferredBackendList(const std::string& preferredBackends)
49 {
50     std::vector<armnn::BackendId> backends;
51     std::stringstream ss(preferredBackends);
52 
53     while(ss.good())
54     {
55         std::string backend;
56         std::getline( ss, backend, ',' );
57         backends.emplace_back(backend);
58     }
59     return backends;
60 }
61 
62 /*
63  * Assigns a color to each label in the label set
64  */
AssignColourToLabel(const std::string & pathToLabelFile)65 std::vector<std::tuple<std::string, common::BBoxColor>> AssignColourToLabel(const std::string& pathToLabelFile)
66 {
67     std::ifstream in(pathToLabelFile);
68     std::vector<std::tuple<std::string, common::BBoxColor>> labels;
69 
70     std::string str;
71     std::default_random_engine generator;
72     std::uniform_int_distribution<int> distribution(0,255);
73 
74     while (std::getline(in, str))
75     {
76         if(!str.empty())
77         {
78             common::BBoxColor c{
79                 .colorCode = std::make_tuple(distribution(generator),
80                                              distribution(generator),
81                                              distribution(generator))
82             };
83             auto bboxInfo = std::make_tuple (str, c);
84 
85             labels.emplace_back(bboxInfo);
86         }
87     }
88     return labels;
89 }
90 
91 std::tuple<std::unique_ptr<common::IFrameReader<cv::Mat>>,
92            std::unique_ptr<common::IFrameOutput<cv::Mat>>>
GetFrameSourceAndSink(const std::map<std::string,std::string> & options)93            GetFrameSourceAndSink(const std::map<std::string, std::string>& options) {
94 
95     std::unique_ptr<common::IFrameReader<cv::Mat>> readerPtr;
96 
97     std::unique_ptr<common::CvVideoFrameReader> reader = std::make_unique<common::CvVideoFrameReader>();
98     reader->Init(GetSpecifiedOption(options, VIDEO_FILE_PATH));
99 
100     auto enc = reader->GetSourceEncodingInt();
101     auto fps = reader->GetSourceFps();
102     auto w = reader->GetSourceWidth();
103     auto h = reader->GetSourceHeight();
104     if (!reader->ConvertToRGB())
105     {
106         readerPtr = std::move(std::make_unique<common::CvVideoFrameReaderRgbWrapper>(std::move(reader)));
107     }
108     else
109     {
110         readerPtr = std::move(reader);
111     }
112 
113     if(CheckOptionSpecified(options, OUTPUT_VIDEO_FILE_PATH))
114     {
115         std::string outputVideo = GetSpecifiedOption(options, OUTPUT_VIDEO_FILE_PATH);
116         auto writer = std::make_unique<common::CvVideoFileWriter>();
117         writer->Init(outputVideo, enc, fps, w, h);
118 
119         return std::make_tuple<>(std::move(readerPtr), std::move(writer));
120     }
121     else
122     {
123         auto writer = std::make_unique<common::CvWindowOutput>();
124         writer->Init("Processed Video");
125         return std::make_tuple<>(std::move(readerPtr), std::move(writer));
126     }
127 }
128 
main(int argc,char * argv[])129 int main(int argc, char *argv[])
130 {
131     std::map<std::string, std::string> options;
132 
133     int result = ParseOptions(options, CMD_OPTIONS, argv, argc);
134     if (result != 0)
135     {
136         return result;
137     }
138 
139     // Create the network options
140     common::PipelineOptions pipelineOptions;
141     pipelineOptions.m_ModelFilePath = GetSpecifiedOption(options, MODEL_FILE_PATH);
142     pipelineOptions.m_ModelName = GetSpecifiedOption(options, MODEL_NAME);
143 
144     if (CheckOptionSpecified(options, PROFILING_ENABLED))
145     {
146         pipelineOptions.m_ProfilingEnabled = GetSpecifiedOption(options, PROFILING_ENABLED) == "true";
147     }
148     if(CheckOptionSpecified(options, PREFERRED_BACKENDS))
149     {
150         pipelineOptions.m_backends = GetPreferredBackendList((GetSpecifiedOption(options, PREFERRED_BACKENDS)));
151     }
152     else
153     {
154         pipelineOptions.m_backends = {"CpuAcc", "CpuRef"};
155     }
156 
157     auto labels = AssignColourToLabel(GetSpecifiedOption(options, LABEL_PATH));
158 
159     common::Profiling profiling(pipelineOptions.m_ProfilingEnabled);
160     profiling.ProfilingStart();
161     od::IPipelinePtr objectDetectionPipeline = od::CreatePipeline(pipelineOptions);
162 
163     auto inputAndOutput = GetFrameSourceAndSink(options);
164     std::unique_ptr<common::IFrameReader<cv::Mat>> reader = std::move(std::get<0>(inputAndOutput));
165     std::unique_ptr<common::IFrameOutput<cv::Mat>> sink = std::move(std::get<1>(inputAndOutput));
166 
167     if (!sink->IsReady())
168     {
169         std::cerr << "Failed to open video writer.";
170         return 1;
171     }
172 
173     common::InferenceResults<float> results;
174 
175     std::shared_ptr<cv::Mat> frame = reader->ReadFrame();
176 
177     //pre-allocate frames
178     cv::Mat processed;
179 
180     while(!reader->IsExhausted(frame))
181     {
182         objectDetectionPipeline->PreProcessing(*frame, processed);
183         objectDetectionPipeline->Inference(processed, results);
184         objectDetectionPipeline->PostProcessing(results,
185                                                 [&frame, &labels](od::DetectedObjects detects) -> void {
186             AddInferenceOutputToFrame(detects, *frame, labels);
187         });
188 
189         sink->WriteFrame(frame);
190         frame = reader->ReadFrame();
191     }
192     sink->Close();
193     profiling.ProfilingStopAndPrintUs("Overall compute time");
194     return 0;
195 }
196