1 /******************************************************************************
2 * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Texas Instruments Incorporated nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28 #include <signal.h>
29 #include <getopt.h>
30 #include <iostream>
31 #include <iomanip>
32 #include <fstream>
33 #include <cassert>
34 #include <string>
35 #include <functional>
36 #include <queue>
37 #include <algorithm>
38 #include <time.h>
39 #include <memory.h>
40 #include <string.h>
42 #include "executor.h"
43 #include "execution_object.h"
44 #include "execution_object_pipeline.h"
45 #include "configuration.h"
46 #include "avg_fps_window.h"
47 #include "imgutil.h"
49 #include "opencv2/core.hpp"
50 #include "opencv2/imgproc.hpp"
51 #include "opencv2/highgui.hpp"
52 #include "opencv2/videoio.hpp"
55 //#define TWO_ROIs
56 #define LIVE_DISPLAY
57 #define PERF_VERBOSE
58 //#define RMT_GST_STREAMER
60 #define MAX_NUM_ROI 4
62 int live_input = 1;
63 char video_clip[320];
65 #ifdef TWO_ROIs
66 #define RES_X 400
67 #define RES_Y 300
68 #define NUM_ROI_X 2
69 #define NUM_ROI_Y 1
70 #define X_OFFSET 0
71 #define X_STEP 176
72 #define Y_OFFSET 52
73 #define Y_STEP 224
74 #else
75 #define RES_X 480
76 #define RES_Y 480
77 #define NUM_ROI_X 1
78 #define NUM_ROI_Y 1
79 #define X_OFFSET 10
80 #define X_STEP 460
81 #define Y_OFFSET 10
82 #define Y_STEP 460
83 #endif
85 #define NUM_ROI (NUM_ROI_X * NUM_ROI_Y)
87 //Temporal averaging
88 int TOP_CANDIDATES = 3;
90 using namespace tidl;
91 using namespace cv;
93 #ifdef LIVE_DISPLAY
94 char imagenet_win[160];
95 char tmp_classwindow_string[160];
96 Mat classlist_image;
98 void imagenetCallBackFunc(int event, int x, int y, int flags, void* userdata)
99 {
100 if ( event == EVENT_RBUTTONDOWN )
101 {
102 std::cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << " ... prepare to exit!" << std::endl;
103 exit(0);
104 }
105 }
106 #endif
108 Mat in_image, image, r_image, cnn_image, show_image, bgr_frames[3];
109 Mat to_stream;
110 Rect rectCrop[NUM_ROI];
111 // Report average FPS across a sliding window of 16 frames
112 AvgFPSWindow fps_window(16);
114 static int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id);
115 static int ShowRegion(int roi_history[]);
116 // from most recent to oldest at top indices
117 static int selclass_history[MAX_NUM_ROI][3];
119 bool RunConfiguration(const std::string& config_file, int num_layers_groups,
120 uint32_t num_dsps, uint32_t num_eves);
121 bool CreateExecutionObjectPipelines(uint32_t num_eves, uint32_t num_dsps,
122 Configuration& configuration,
123 uint32_t num_layers_groups,
124 Executor*& e_eve, Executor*& e_dsp,
125 std::vector<ExecutionObjectPipeline*>& eops);
126 void AllocateMemory(const std::vector<ExecutionObjectPipeline*>& eops);
127 void SetupLiveDisplay(uint32_t num_eves, uint32_t num_dsps);
128 bool SetupInput(VideoCapture& cap, VideoWriter& writer);
129 bool ReadFrame(ExecutionObjectPipeline* eop, const Configuration& c,
130 int frame_idx, VideoCapture &cap, VideoWriter& writer);
131 void DisplayFrame(const ExecutionObjectPipeline* eop, VideoWriter& writer,
132 uint32_t frame_idx, uint32_t num_eops,
133 uint32_t num_eves, uint32_t num_dsps);
134 static void ProcessArgs(int argc, char *argv[],
135 std::string& config_file,
136 uint32_t & num_dsps, uint32_t &num_eves,
137 int & num_layers_groups);
139 static void DisplayHelp();
140 extern std::string labels_classes[];
141 extern int IMAGE_CLASSES_NUM;
142 extern int selected_items_size;
143 extern int selected_items[];
144 extern int populate_selected_items (char *filename);
145 extern void populate_labels (char *filename);
147 bool verbose = false;
149 int main(int argc, char *argv[])
150 {
151 // Catch ctrl-c to ensure a clean exit
152 signal(SIGABRT, exit);
153 signal(SIGTERM, exit);
155 // If there are no devices capable of offloading TIDL on the SoC, exit
156 uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
157 uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
158 int num_layers_groups = 1;
160 if (num_eves == 0 && num_dsps == 0)
161 {
162 std::cout << "TI DL not supported on this SoC." << std::endl;
163 return EXIT_SUCCESS;
164 }
166 // Process arguments
167 std::string config_file;
168 ProcessArgs(argc, argv, config_file, num_dsps, num_eves, num_layers_groups);
170 bool status = false;
171 if (!config_file.empty()) {
172 std::cout << "Run single configuration: " << config_file << std::endl;
173 status = RunConfiguration(config_file, num_layers_groups, num_dsps, num_eves);
174 }
176 if (!status)
177 {
178 std::cout << "tidl FAILED" << std::endl;
179 return EXIT_FAILURE;
180 }
182 std::cout << "tidl PASSED" << std::endl;
183 return EXIT_SUCCESS;
184 }
186 bool RunConfiguration(const std::string& config_file, int num_layers_groups, uint32_t num_dsps, uint32_t num_eves)
187 {
189 // Read the TI DL configuration file
190 Configuration configuration;
191 if (!configuration.ReadFromFile(config_file))
192 return false;
194 if (verbose)
195 configuration.enableApiTrace = true;
197 try
198 {
199 // Create ExecutionObjectPipelines
200 Executor *e_eve = nullptr;
201 Executor *e_dsp = nullptr;
202 std::vector<ExecutionObjectPipeline *> eops;
203 if (! CreateExecutionObjectPipelines(num_eves, num_dsps, configuration,
204 num_layers_groups, e_eve, e_dsp, eops))
205 return false;
207 // Allocate input/output memory for each EOP
208 AllocateMemory(eops);
210 // Setup Live Display
211 SetupLiveDisplay(num_eves, num_dsps);
213 // Setup Input
214 VideoCapture cap;
215 VideoWriter writer; // gstreamer
216 if (! SetupInput(cap, writer)) return false;
218 // More initialization
219 for (int k = 0; k < NUM_ROI; k++)
220 for(int i = 0; i < 3; i ++)
221 selclass_history[k][i] = -1;
222 std::cout << "About to start ProcessFrame loop!!" << std::endl;
224 // Process frames with available EOPs in a pipelined manner
225 // additional num_eops iterations to flush the pipeline (epilogue)
226 int num_eops = eops.size();
227 for (int frame_idx = 0;
228 frame_idx < configuration.numFrames + num_eops; frame_idx++)
229 {
230 ExecutionObjectPipeline* eop = eops[frame_idx % num_eops];
232 // Wait for previous frame on the same eo to finish processing
233 if (eop->ProcessFrameWait())
234 {
235 DisplayFrame(eop, writer, frame_idx, num_eops,
236 num_eves, num_dsps);
237 }
238 fps_window.Tick();
240 if (ReadFrame(eop, configuration, frame_idx, cap, writer))
241 eop->ProcessFrameStartAsync();
242 }
244 // Cleanup
245 for (auto eop : eops)
246 {
247 free(eop->GetInputBufferPtr());
248 free(eop->GetOutputBufferPtr());
249 delete eop;
250 }
251 if (e_dsp) delete e_dsp;
252 if (e_eve) delete e_eve;
253 }
254 catch (tidl::Exception &e)
255 {
256 std::cerr << e.what() << std::endl;
257 return false;
258 }
260 return true;
261 }
264 bool CreateExecutionObjectPipelines(uint32_t num_eves, uint32_t num_dsps,
265 Configuration& configuration,
266 uint32_t num_layers_groups,
267 Executor*& e_eve, Executor*& e_dsp,
268 std::vector<ExecutionObjectPipeline*>& eops)
269 {
270 DeviceIds ids_eve, ids_dsp;
271 for (uint32_t i = 0; i < num_eves; i++)
272 ids_eve.insert(static_cast<DeviceId>(i));
273 for (uint32_t i = 0; i < num_dsps; i++)
274 ids_dsp.insert(static_cast<DeviceId>(i));
275 const uint32_t buffer_factor = 2;
277 switch(num_layers_groups)
278 {
279 case 1: // Single layers group
280 e_eve = num_eves == 0 ? nullptr :
281 new Executor(DeviceType::EVE, ids_eve, configuration);
282 e_dsp = num_dsps == 0 ? nullptr :
283 new Executor(DeviceType::DSP, ids_dsp, configuration);
285 configuration.runFullNet = true; //Force all layers to be in the same group
287 // Construct ExecutionObjectPipeline with single Execution Object to
288 // process each frame. This is parallel processing of frames with
289 // as many DSP and EVE cores that we have on hand.
290 // If buffer_factor == 2, duplicating EOPs for double buffering
291 // and overlapping host pre/post-processing with device processing
292 for (uint32_t j = 0; j < buffer_factor; j++)
293 {
294 for (uint32_t i = 0; i < num_eves; i++)
295 eops.push_back(new ExecutionObjectPipeline({(*e_eve)[i]}));
296 for (uint32_t i = 0; i < num_dsps; i++)
297 eops.push_back(new ExecutionObjectPipeline({(*e_dsp)[i]}));
298 }
299 break;
301 case 2: // Two layers group
302 // Create Executors with the approriate core type, number of cores
303 // and configuration specified
304 // EVE will run layersGroupId 1 in the network, while
305 // DSP will run layersGroupId 2 in the network
306 e_eve = num_eves == 0 ? nullptr :
307 new Executor(DeviceType::EVE, ids_eve, configuration, 1);
308 e_dsp = num_dsps == 0 ? nullptr :
309 new Executor(DeviceType::DSP, ids_dsp, configuration, 2);
311 // Construct ExecutionObjectPipeline that utilizes multiple
312 // ExecutionObjects to process a single frame, each ExecutionObject
313 // processes one layerGroup of the network
314 // If buffer_factor == 2, duplicating EOPs for pipelining at
315 // EO level rather than at EOP level, in addition to double buffering
316 // and overlapping host pre/post-processing with device processing
317 for (uint32_t j = 0; j < buffer_factor; j++)
318 {
319 for (uint32_t i = 0; i < std::max(num_eves, num_dsps); i++)
320 eops.push_back(new ExecutionObjectPipeline(
321 {(*e_eve)[i%num_eves], (*e_dsp)[i%num_dsps]}));
322 }
323 break;
325 default:
326 std::cout << "Layers groups can be either 1 or 2!" << std::endl;
327 return false;
328 break;
329 }
331 return true;
332 }
334 void AllocateMemory(const std::vector<ExecutionObjectPipeline*>& eops)
335 {
336 for (auto eop : eops)
337 {
338 size_t in_size = eop->GetInputBufferSizeInBytes();
339 size_t out_size = eop->GetOutputBufferSizeInBytes();
340 void* in_ptr = malloc(in_size);
341 void* out_ptr = malloc(out_size);
342 assert(in_ptr != nullptr && out_ptr != nullptr);
344 ArgInfo in(in_ptr, in_size);
345 ArgInfo out(out_ptr, out_size);
346 eop->SetInputOutputBuffer(in, out);
347 }
348 }
350 void SetupLiveDisplay(uint32_t num_eves, uint32_t num_dsps)
351 {
352 #ifdef LIVE_DISPLAY
353 sprintf(imagenet_win, "Imagenet_EVEx%d_DSPx%d", num_eves, num_dsps);
355 if(NUM_ROI > 1)
356 {
357 for(int i = 0; i < NUM_ROI; i ++) {
358 char tmp_string[80];
359 sprintf(tmp_string, "ROI[%02d]", i);
360 namedWindow(tmp_string, WINDOW_AUTOSIZE | CV_GUI_NORMAL);
361 }
362 }
363 Mat sw_stack_image = imread(
364 "/usr/share/ti/tidl/examples/classification/tidl-sw-stack-small.png",
365 IMREAD_COLOR); // Read the file
366 if( sw_stack_image.empty() ) // Check for invalid input
367 {
368 std::cout << "Could not open or find the tidl-sw-stack-small image"
369 << std::endl ;
370 } else {
371 // Create a window for display.
372 namedWindow( "TIDL SW Stack", WINDOW_AUTOSIZE | CV_GUI_NORMAL );
373 // Show our image inside it.
374 cv::imshow( "TIDL SW Stack", sw_stack_image );
375 }
377 namedWindow("ClassList", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
378 namedWindow(imagenet_win, WINDOW_AUTOSIZE | CV_GUI_NORMAL);
379 //set the callback function for any mouse event
380 setMouseCallback(imagenet_win, imagenetCallBackFunc, NULL);
382 classlist_image = cv::Mat::zeros(40 + selected_items_size * 20, 220,
383 CV_8UC3);
384 //Erase window
385 classlist_image.setTo(Scalar::all(0));
387 for (int i = 0; i < selected_items_size; i ++)
388 {
389 sprintf(tmp_classwindow_string, "%2d) %12s", 1+i,
390 labels_classes[selected_items[i]].c_str());
391 cv::putText(classlist_image, tmp_classwindow_string,
392 cv::Point(5, 40 + i * 20),
393 cv::FONT_HERSHEY_COMPLEX_SMALL,
394 0.75,
395 cv::Scalar(255,255,255), 1, 8);
396 }
397 cv::imshow("ClassList", classlist_image);
398 #endif
399 }
401 bool SetupInput(VideoCapture& cap, VideoWriter& writer)
402 {
403 if(live_input >= 0)
404 {
405 cap.open(live_input);
407 const double fps = cap.get(CAP_PROP_FPS);
408 const int width = cap.get(CAP_PROP_FRAME_WIDTH);
409 const int height = cap.get(CAP_PROP_FRAME_HEIGHT);
410 std::cout << "Capture camera with " << fps << " fps, " << width << "x"
411 << height << " px" << std::endl;
413 #ifdef RMT_GST_STREAMER
414 writer.open(" appsrc ! videoconvert ! video/x-raw, format=(string)NV12, width=(int)640, height=(int)480, framerate=(fraction)30/1 ! \
415 ducatih264enc bitrate=2000 ! queue ! h264parse config-interval=1 ! \
416 mpegtsmux ! udpsink host=192.168.1.2 sync=false port=5000",
417 0,fps,Size(640,480),true);
419 if (!writer.isOpened()) {
420 cap.release();
421 std::cerr << "Can't create gstreamer writer. "
422 << "Do you have the correct version installed?" << std::endl;
423 std::cerr << "Print out OpenCV build information" << std::endl;
424 std::cout << getBuildInformation() << std::endl;
425 return false;
426 }
427 #endif
428 } else {
429 std::cout << "Video input clip: " << video_clip << std::endl;
430 cap.open(std::string(video_clip));
431 const double fps = cap.get(CAP_PROP_FPS);
432 const int width = cap.get(CAP_PROP_FRAME_WIDTH);
433 const int height = cap.get(CAP_PROP_FRAME_HEIGHT);
434 std::cout << "Clip with " << fps << " fps, " << width << "x"
435 << height << " px" << std::endl;
436 }
438 if (!cap.isOpened()) {
439 std::cout << "Video input not opened!" << std::endl;
440 return false;
441 }
443 for (int y = 0; y < NUM_ROI_Y; y ++) {
444 for (int x = 0; x < NUM_ROI_X; x ++) {
445 rectCrop[y * NUM_ROI_X + x] = Rect(X_OFFSET + x * X_STEP,
446 Y_OFFSET + y * Y_STEP, X_STEP, Y_STEP);
447 std::cout << "Rect[" << X_OFFSET + x * X_STEP << ", "
448 << Y_OFFSET + y * Y_STEP << "]" << std::endl;
449 }
450 }
452 return true;
453 }
455 bool ReadFrame(ExecutionObjectPipeline* eop, const Configuration& c,
456 int frame_idx, VideoCapture &cap, VideoWriter& writer)
457 {
459 if (cap.grab() && frame_idx < c.numFrames)
460 {
461 if (cap.retrieve(in_image))
462 {
463 if(live_input >= 0)
464 { //Crop central square portion
465 int loc_xmin = (in_image.size().width - in_image.size().height) / 2; //Central position
466 int loc_ymin = 0;
467 int loc_w = in_image.size().height;
468 int loc_h = in_image.size().height;
470 cv::resize(in_image(Rect(loc_xmin, loc_ymin, loc_w, loc_h)), image, Size(RES_X, RES_Y));
471 } else {
472 if((in_image.size().width != RES_X) || (in_image.size().height != RES_Y))
473 {
474 cv::resize(in_image, image, Size(RES_X,RES_Y));
475 }
476 }
478 r_image = Mat(image, rectCrop[frame_idx % NUM_ROI]);
480 #ifdef LIVE_DISPLAY
481 if(NUM_ROI > 1)
482 {
483 char tmp_string[80];
484 sprintf(tmp_string, "ROI[%02d]", frame_idx % NUM_ROI);
485 cv::imshow(tmp_string, r_image);
486 }
487 #endif
488 imgutil::PreprocessImage(r_image, eop->GetInputBufferPtr(), c);
489 eop->SetFrameIndex(frame_idx);
491 #ifdef RMT_GST_STREAMER
492 cv::resize(Mat(image, Rect(0,32,640,448)), to_stream,
493 Size(640,480));
494 writer << to_stream;
495 #endif
497 #ifdef LIVE_DISPLAY
498 //waitKey(2);
499 image.copyTo(show_image);
500 #endif
501 return true;
502 }
503 } else {
504 if(live_input == -1) {
505 //Rewind!
506 cap.release();
507 cap.open(std::string(video_clip));
508 }
509 }
511 return false;
512 }
515 void DisplayFrame(const ExecutionObjectPipeline* eop, VideoWriter& writer,
516 uint32_t frame_idx, uint32_t num_eops,
517 uint32_t num_eves, uint32_t num_dsps)
518 {
519 int f_id = eop->GetFrameIndex();
520 int curr_roi = f_id % NUM_ROI;
521 int is_object = tf_postprocess((uchar*) eop->GetOutputBufferPtr(),
522 IMAGE_CLASSES_NUM, curr_roi, frame_idx, f_id);
523 selclass_history[curr_roi][2] = selclass_history[curr_roi][1];
524 selclass_history[curr_roi][1] = selclass_history[curr_roi][0];
525 selclass_history[curr_roi][0] = is_object;
526 for (int r = 0; r < NUM_ROI; r ++)
527 {
528 int rpt_id = ShowRegion(selclass_history[r]);
529 if(rpt_id >= 0)
530 {
531 // overlay the display window, if ball seen during last two times
532 cv::putText(show_image, labels_classes[rpt_id].c_str(),
533 cv::Point(rectCrop[r].x + 5,rectCrop[r].y + 20), // Coordinates
534 cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
535 1.0, // Scale. 2.0 = 2x bigger
536 cv::Scalar(0,0,255), // Color
537 1, // Thickness
538 8); // Line type
539 cv::rectangle(show_image, rectCrop[r], Scalar(255,0,0), 3);
540 std::cout << "ROI(" << r << ")(" << rpt_id << ")="
541 << labels_classes[rpt_id].c_str() << std::endl;
543 classlist_image.setTo(Scalar::all(0));
544 for (int k = 0; k < selected_items_size; k ++)
545 {
546 sprintf(tmp_classwindow_string, "%2d) %12s", 1+k,
547 labels_classes[selected_items[k]].c_str());
548 cv::putText(classlist_image, tmp_classwindow_string,
549 cv::Point(5, 40 + k * 20),
550 cv::FONT_HERSHEY_COMPLEX_SMALL,
551 0.75,
552 selected_items[k] == rpt_id ? cv::Scalar(0,0,255) :
553 cv::Scalar(255,255,255), 1, 8);
554 }
556 double avg_fps = fps_window.UpdateAvgFPS();
557 sprintf(tmp_classwindow_string, "FPS:%5.2lf", avg_fps );
559 #ifdef PERF_VERBOSE
560 std::cout << "Device:" << eop->GetDeviceName() << " eops("
561 << num_eops << "), EVES(" << num_eves << ") DSPS("
562 << num_dsps << ") FPS:" << avg_fps << std::endl;
563 #endif
564 cv::putText(classlist_image, tmp_classwindow_string,
565 cv::Point(5, 20),
566 cv::FONT_HERSHEY_COMPLEX_SMALL,
567 0.75,
568 cv::Scalar(0,255,0), 1, 8);
569 cv::imshow("ClassList", classlist_image);
570 }
571 }
573 #ifdef LIVE_DISPLAY
574 cv::imshow(imagenet_win, show_image);
575 #endif
577 #ifdef RMT_GST_STREAMER
578 cv::resize(show_image, to_stream, cv::Size(640,480));
579 writer << to_stream;
580 #endif
582 #ifdef LIVE_DISPLAY
583 waitKey(2);
584 #endif
585 }
587 // Function to process all command line arguments
588 void ProcessArgs(int argc, char *argv[], std::string& config_file,
589 uint32_t & num_dsps, uint32_t & num_eves, int & num_layers_groups )
590 {
591 const struct option long_options[] =
592 {
593 {"labels_classes_file", required_argument, 0, 'l'},
594 {"selected_classes_file", required_argument, 0, 's'},
595 {"config_file", required_argument, 0, 'c'},
596 {"num_dsps", required_argument, 0, 'd'},
597 {"num_eves", required_argument, 0, 'e'},
598 {"num_layers_groups", required_argument, 0, 'g'},
599 {"help", no_argument, 0, 'h'},
600 {"verbose", no_argument, 0, 'v'},
601 {0, 0, 0, 0}
602 };
604 int option_index = 0;
606 while (true)
607 {
608 int c = getopt_long(argc, argv, "l:c:s:i:d:e:g:hv", long_options, &option_index);
610 if (c == -1)
611 break;
613 switch (c)
614 {
615 case 'l': populate_labels(optarg);
616 break;
618 case 's': populate_selected_items(optarg);
619 break;
621 case 'i': if(strlen(optarg) == 1)
622 {
623 live_input = atoi(optarg);
624 } else {
625 live_input = -1;
626 strcpy(video_clip, optarg);
627 }
628 break;
630 case 'c': config_file = optarg;
631 break;
633 case 'g': num_layers_groups = atoi(optarg);
634 assert(num_layers_groups >= 1 && num_layers_groups <= 2);
635 break;
637 case 'd': num_dsps = atoi(optarg);
638 assert (num_dsps >= 0 && num_dsps <= 2);
639 break;
641 case 'e': num_eves = atoi(optarg);
642 assert (num_eves >= 0 && num_eves <= 4);
643 break;
645 case 'v': verbose = true;
646 break;
648 case 'h': DisplayHelp();
649 exit(EXIT_SUCCESS);
650 break;
652 case '?': // Error in getopt_long
653 exit(EXIT_FAILURE);
654 break;
656 default:
657 std::cerr << "Unsupported option: " << c << std::endl;
658 break;
659 }
660 }
662 // if no eves available, we can only run full net as one layer group
663 if (num_eves == 0) num_layers_groups = 1;
664 }
666 void DisplayHelp()
667 {
668 std::cout << "Usage: tidl_classification\n"
669 " Will run all available networks if tidl is invoked without"
670 " any arguments.\n Use -c to run a single network.\n"
671 "Optional arguments:\n"
672 " -c Path to the configuration file\n"
673 " -d <number of DSP cores> Number of DSP cores to use (0 - 2)\n"
674 " -e <number of EVE cores> Number of EVE cores to use (0 - 2)\n"
675 " -g <1|2> Number of layer groups\n"
676 " -l List of label strings (of all classes in model)\n"
677 " -s List of strings with selected classes\n"
678 " -i Video input (for camera:0,1 or video clip)\n"
679 " -v Verbose output during execution\n"
680 " -h Help\n";
681 }
683 // Function to filter all the reported decisions
684 bool tf_expected_id(int id)
685 {
686 // Filter out unexpected IDs
687 for (int i = 0; i < selected_items_size; i ++)
688 {
689 if(id == selected_items[i]) return true;
690 }
691 return false;
692 }
694 int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id)
695 {
696 //prob_i = exp(TIDL_Lib_output_i) / sum(exp(TIDL_Lib_output))
697 // sort and get k largest values and corresponding indices
698 const int k = TOP_CANDIDATES;
699 int rpt_id = -1;
701 typedef std::pair<uchar, int> val_index;
702 auto constexpr cmp = [](val_index &left, val_index &right) { return left.first > right.first; };
703 std::priority_queue<val_index, std::vector<val_index>, decltype(cmp)> queue(cmp);
704 // initialize priority queue with smallest value on top
705 for (int i = 0; i < k; i++) {
706 queue.push(val_index(in[i], i));
707 }
708 // for rest input, if larger than current minimum, pop mininum, push new val
709 for (int i = k; i < size; i++)
710 {
711 if (in[i] > queue.top().first)
712 {
713 queue.pop();
714 queue.push(val_index(in[i], i));
715 }
716 }
718 // output top k values in reverse order: largest val first
719 std::vector<val_index> sorted;
720 while (! queue.empty())
721 {
722 sorted.push_back(queue.top());
723 queue.pop();
724 }
726 for (int i = 0; i < k; i++)
727 {
728 int id = sorted[i].second;
730 if (tf_expected_id(id))
731 {
732 std::cout << "Frame:" << frame_idx << "," << f_id << " ROI[" << roi_idx << "]: rank="
733 << k-i << ", outval=" << (float)sorted[i].first / 255 << ", "
734 << labels_classes[sorted[i].second] << std::endl;
735 rpt_id = id;
736 }
737 }
738 return rpt_id;
739 }
741 int ShowRegion(int roi_history[])
742 {
743 if((roi_history[0] >= 0) && (roi_history[0] == roi_history[1])) return roi_history[0];
744 if((roi_history[0] >= 0) && (roi_history[0] == roi_history[2])) return roi_history[0];
745 if((roi_history[1] >= 0) && (roi_history[1] == roi_history[2])) return roi_history[1];
746 return -1;
747 }