91d68f48615abaef9156af5f1c8b0c22bd32c0b0
[tidl/tidl-api.git] / examples / segmentation / main.cpp
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 <iostream>
30 #include <iomanip>
31 #include <fstream>
32 #include <cassert>
33 #include <string>
34 #include <functional>
35 #include <algorithm>
36 #include <time.h>
37 #include <unistd.h>
39 #include <queue>
40 #include <vector>
41 #include <cstdio>
42 #include <chrono>
44 #include "executor.h"
45 #include "execution_object.h"
46 #include "configuration.h"
47 #include "object_classes.h"
48 #include "../common/utils.h"
49 #include "../common/video_utils.h"
51 using namespace std;
52 using namespace tidl;
53 using namespace cv;
56 #define NUM_VIDEO_FRAMES  300
57 #define DEFAULT_CONFIG    "jseg21_tiscapes"
58 #define DEFAULT_INPUT     "../test/testvecs/input/000100_1024x512_bgr.y"
59 #define DEFAULT_INPUT_FRAMES  (9)
61 object_class_table_t *object_class_table;
62 uint32_t orig_width;
63 uint32_t orig_height;
66 bool RunConfiguration(const cmdline_opts_t& opts);
67 Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c);
68 bool ReadFrame(ExecutionObject& eo, int frame_idx, const Configuration& c,
69                const cmdline_opts_t& opts, VideoCapture &cap);
70 bool WriteFrameOutput(const ExecutionObject &eo, const Configuration& c,
71                       const cmdline_opts_t& opts);
72 void DisplayHelp();
75 int main(int argc, char *argv[])
76 {
77     // Catch ctrl-c to ensure a clean exit
78     signal(SIGABRT, exit);
79     signal(SIGTERM, exit);
81     // If there are no devices capable of offloading TIDL on the SoC, exit
82     uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
83     uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
84     if (num_eves == 0 && num_dsps == 0)
85     {
86         cout << "TI DL not supported on this SoC." << endl;
87         return EXIT_SUCCESS;
88     }
90     // Process arguments
91     cmdline_opts_t opts;
92     opts.config = DEFAULT_CONFIG;
93     if (num_eves != 0) { opts.num_eves = 1;  opts.num_dsps = 0; }
94     else               { opts.num_eves = 0;  opts.num_dsps = 1; }
95     if (! ProcessArgs(argc, argv, opts))
96     {
97         DisplayHelp();
98         exit(EXIT_SUCCESS);
99     }
100     assert(opts.num_dsps != 0 || opts.num_eves != 0);
101     if (opts.num_frames == 0)
102         opts.num_frames = (opts.is_camera_input || opts.is_video_input) ?
103                           NUM_VIDEO_FRAMES :
104                           (opts.input_file.empty() ? DEFAULT_INPUT_FRAMES : 1);
105     if (opts.input_file.empty())
106         cout << "Input: " << DEFAULT_INPUT << endl;
107     else
108         cout << "Input: " << opts.input_file << endl;
110     // Get object class table
111     if ((object_class_table = GetObjectClassTable(opts.config)) == nullptr)
112     {
113         cout << "No object classes defined for this config." << endl;
114         return EXIT_FAILURE;
115     }
117     // Run network
118     bool status = RunConfiguration(opts);
119     if (!status)
120     {
121         cout << "segmentation FAILED" << endl;
122         return EXIT_FAILURE;
123     }
125     cout << "segmentation PASSED" << endl;
126     return EXIT_SUCCESS;
129 bool RunConfiguration(const cmdline_opts_t& opts)
131     // Read the TI DL configuration file
132     Configuration c;
133     std::string config_file = "../test/testvecs/config/infer/tidl_config_"
134                               + opts.config + ".txt";
135     bool status = c.ReadFromFile(config_file);
136     if (!status)
137     {
138         cerr << "Error in configuration file: " << config_file << endl;
139         return false;
140     }
141     c.enableApiTrace = opts.verbose;
143     // setup camera/video input/output
144     VideoCapture cap;
145     if (! SetVideoInputOutput(cap, opts, "Segmentation"))  return false;
147     try
148     {
149         // Create Executors with the approriate core type, number of cores
150         // and configuration specified
151         Executor* e_eve = CreateExecutor(DeviceType::EVE, opts.num_eves, c);
152         Executor* e_dsp = CreateExecutor(DeviceType::DSP, opts.num_dsps, c);
154         // Get ExecutionObjects from Executors
155         vector<ExecutionObject*> eos;
156         for (uint32_t i = 0; i < opts.num_eves; i++) eos.push_back((*e_eve)[i]);
157         for (uint32_t i = 0; i < opts.num_dsps; i++) eos.push_back((*e_dsp)[i]);
158         uint32_t num_eos = eos.size();
160         // Allocate input and output buffers for each execution object
161         AllocateMemory(eos);
163         chrono::time_point<chrono::steady_clock> tloop0, tloop1;
164         tloop0 = chrono::steady_clock::now();
166         // Process frames with available eos in a pipelined manner
167         // additional num_eos iterations to flush the pipeline (epilogue)
168         for (uint32_t frame_idx = 0;
169              frame_idx < opts.num_frames + num_eos; frame_idx++)
170         {
171             ExecutionObject* eo = eos[frame_idx % num_eos];
173             // Wait for previous frame on the same eo to finish processing
174             if (eo->ProcessFrameWait())
175             {
176                 ReportTime(eo);
177                 WriteFrameOutput(*eo, c, opts);
178             }
180             // Read a frame and start processing it with current eo
181             if (ReadFrame(*eo, frame_idx, c, opts, cap))
182                 eo->ProcessFrameStartAsync();
183         }
185         tloop1 = chrono::steady_clock::now();
186         chrono::duration<float> elapsed = tloop1 - tloop0;
187         cout << "Loop total time (including read/write/opencv/print/etc): "
188                   << setw(6) << setprecision(4)
189                   << (elapsed.count() * 1000) << "ms" << endl;
191         FreeMemory(eos);
192         delete e_eve;
193         delete e_dsp;
194     }
195     catch (tidl::Exception &e)
196     {
197         cerr << e.what() << endl;
198         status = false;
199     }
201     return status;
204 // Create an Executor with the specified type and number of EOs
205 Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c)
207     if (num == 0) return nullptr;
209     DeviceIds ids;
210     for (uint32_t i = 0; i < num; i++)
211         ids.insert(static_cast<DeviceId>(i));
213     return new Executor(dt, ids, c);
216 bool ReadFrame(ExecutionObject &eo, int frame_idx, const Configuration& c, 
217                const cmdline_opts_t& opts, VideoCapture &cap)
219     if (frame_idx >= opts.num_frames)
220         return false;
221     eo.SetFrameIndex(frame_idx);
223     char*  frame_buffer = eo.GetInputBufferPtr();
224     assert (frame_buffer != nullptr);
225     int channel_size = c.inWidth * c.inHeight;
227     Mat image;
228     if (! opts.is_camera_input && ! opts.is_video_input)
229     {
230         if (opts.input_file.empty())
231         {
232             ifstream ifs(DEFAULT_INPUT, ios::binary);
233             ifs.seekg((frame_idx % DEFAULT_INPUT_FRAMES) * channel_size * 3);
234             ifs.read(frame_buffer, channel_size * 3);
235             bool ifs_status = ifs.good();
236             ifs.close();
237             orig_width  = c.inWidth;
238             orig_height = c.inHeight;
239             return ifs_status;  // already PreProc-ed
240         }
241         else
242         {
243             image = cv::imread(opts.input_file, CV_LOAD_IMAGE_COLOR);
244             if (image.empty())
245             {
246                 cerr << "Unable to read from: " << opts.input_file << endl;
247                 return false;
248             }
249         }
250     }
251     else
252     {
253         // 640x480 camera input, process one in every 5 frames,
254         // can adjust number of skipped frames to match real time processing
255         if (! cap.grab())  return false;
256         if (! cap.grab())  return false;
257         if (! cap.grab())  return false;
258         if (! cap.grab())  return false;
259         if (! cap.grab())  return false;
260         if (! cap.retrieve(image)) return false;
261     }
263     // scale to network input size 1024 x 512
264     Mat s_image, bgr_frames[3];
265     orig_width  = image.cols;
266     orig_height = image.rows;
267     cv::resize(image, s_image, Size(c.inWidth, c.inHeight),
268                0, 0, cv::INTER_AREA);
269     cv::split(s_image, bgr_frames);
270     memcpy(frame_buffer,                bgr_frames[0].ptr(), channel_size);
271     memcpy(frame_buffer+1*channel_size, bgr_frames[1].ptr(), channel_size);
272     memcpy(frame_buffer+2*channel_size, bgr_frames[2].ptr(), channel_size);
273     return true;
276 // Create Overlay mask for pixel-level segmentation
277 void CreateMask(uchar *classes, uchar *mb, uchar *mg, uchar* mr,
278                 int channel_size)
280     for (int i = 0; i < channel_size; i++)
281     {
282         object_class_t *object_class = GetObjectClass(object_class_table,
283                                                       classes[i]);
284         mb[i] = object_class->color.blue;
285         mg[i] = object_class->color.green;
286         mr[i] = object_class->color.red;
287     }
290 // Create frame overlayed with pixel-level segmentation
291 bool WriteFrameOutput(const ExecutionObject &eo, const Configuration& c,
292                       const cmdline_opts_t& opts)
294     unsigned char *out = (unsigned char *) eo.GetOutputBufferPtr();
295     int width          = c.inWidth;
296     int height         = c.inHeight;
297     int channel_size   = width * height;
299     Mat mask, frame, blend, r_blend, bgr[3];
300     // Create overlay mask
301     bgr[0] = Mat(height, width, CV_8UC(1));
302     bgr[1] = Mat(height, width, CV_8UC(1));
303     bgr[2] = Mat(height, width, CV_8UC(1));
304     CreateMask(out, bgr[0].ptr(), bgr[1].ptr(), bgr[2].ptr(), channel_size);
305     cv::merge(bgr, 3, mask);
307     // Asseembly original frame
308     unsigned char *in = (unsigned char *) eo.GetInputBufferPtr();
309     bgr[0] = Mat(height, width, CV_8UC(1), in);
310     bgr[1] = Mat(height, width, CV_8UC(1), in + channel_size);
311     bgr[2] = Mat(height, width, CV_8UC(1), in + channel_size*2);
312     cv::merge(bgr, 3, frame);
314     // Create overlayed frame
315     cv::addWeighted(frame, 0.7, mask, 0.3, 0.0, blend);
317     // Resize to output width/height, keep aspect ratio
318     uint32_t output_width = opts.output_width;
319     if (output_width == 0)  output_width = orig_width;
320     uint32_t output_height = (output_width*1.0f) / orig_width * orig_height;
321     cv::resize(blend, r_blend, Size(output_width, output_height));
323     if (opts.is_camera_input || opts.is_video_input)
324     {
325         cv::imshow("Segmentation", r_blend);
326         waitKey(1);
327     }
328     else
329     {
330         int frame_index = eo.GetFrameIndex();
331         char outfile_name[64];
332         if (opts.input_file.empty())
333         {
334             snprintf(outfile_name, 64, "frame_%d.png", frame_index);
335             cv::imwrite(outfile_name, frame);
336             printf("Saving frame %d to: %s\n", frame_index, outfile_name);
337         }
339         snprintf(outfile_name, 64, "overlay_%d.png", frame_index);
340         cv::imwrite(outfile_name, r_blend);
341         printf("Saving frame %d overlayed with segmentation to: %s\n",
342                frame_index, outfile_name);
343     }
345     return true;
348 void DisplayHelp()
350     std::cout <<
351     "Usage: segmentation\n"
352     "  Will run segmentation network to perform pixel-level"
353     " classification.\n  Use -c to run a different"
354     "  segmentation network. Default is jseg21_tiscapes.\n"
355     "Optional arguments:\n"
356     " -c <config>          Valid configs: jseg21_tiscapes, jseg21\n"
357     " -d <number>          Number of dsp cores to use\n"
358     " -e <number>          Number of eve cores to use\n"
359     " -i <image>           Path to the image file as input\n"
360     "                      Default are 9 frames in testvecs\n"
361     " -i camera<number>    Use camera as input\n"
362     "                      video input port: /dev/video<number>\n"
363     " -i <name>.{mp4,mov,avi}  Use video file as input\n"
364     " -f <number>          Number of frames to process\n"
365     " -w <number>          Output image/video width\n"
366     " -v                   Verbose output during execution\n"
367     " -h                   Help\n";