Merge branch 'release/v01.01.00.00' v01.01.00.00
authorAjay Jayaraj <ajayj@ti.com>
Fri, 7 Sep 2018 19:45:44 +0000 (14:45 -0500)
committerAjay Jayaraj <ajayj@ti.com>
Fri, 7 Sep 2018 19:45:44 +0000 (14:45 -0500)
80 files changed:
docs/source/_templates/layout.html
docs/source/api.rst
docs/source/conf.py
docs/source/example.rst
docs/source/faq/building.rst [moved from docs/source/building.rst with 66% similarity]
docs/source/faq/index.rst
docs/source/faq/out_of_memory.rst
docs/source/images/tidl-api.png
docs/source/images/tidl-frame-across-eos-opt.png [new file with mode: 0755]
docs/source/images/tidl-frame-across-eos.png [new file with mode: 0755]
docs/source/images/tidl-one-eo-per-frame.png [new file with mode: 0755]
docs/source/index.rst
docs/source/intro.rst
docs/source/overview.rst [new file with mode: 0644]
docs/source/readme/index.rst [new file with mode: 0644]
docs/source/readme/v01.00.x.rst [new file with mode: 0644]
docs/source/readme/v01.01.x.rst [new file with mode: 0644]
docs/source/using_api.rst
examples/Makefile
examples/classification/Makefile
examples/classification/clips/test10.mp4 [new file with mode: 0644]
examples/classification/clips/test1a.mp4 [deleted file]
examples/classification/clips/test2.mp4
examples/classification/images/baseball.jpg
examples/classification/images/coffe.jpg
examples/classification/images/coffe_pot.jpg
examples/classification/images/plane.jpg [new file with mode: 0644]
examples/classification/images/scripts/img2clip.sh [moved from examples/classification/images/img2clip.sh with 52% similarity]
examples/classification/images/tennis_ball.jpg
examples/classification/main.cpp
examples/classification/multiple_executors.cpp [deleted file]
examples/classification/readme.md
examples/classification/stream_config_dogs.txt [deleted file]
examples/classification/stream_config_j11_v2.txt
examples/classification/tidl-sw-stack-small.png
examples/common/utils.cpp [new file with mode: 0644]
examples/common/utils.h [new file with mode: 0644]
examples/common/video_utils.cpp [new file with mode: 0644]
examples/common/video_utils.h [new file with mode: 0644]
examples/imagenet/Makefile
examples/imagenet/main.cpp
examples/layer_output/Makefile [new file with mode: 0644]
examples/layer_output/j11_v2_trace.txt [new file with mode: 0644]
examples/layer_output/main.cpp [new file with mode: 0644]
examples/make.common
examples/one_eo_per_frame/Makefile [new file with mode: 0644]
examples/one_eo_per_frame/main.cpp [new file with mode: 0644]
examples/segmentation/Makefile
examples/segmentation/main.cpp
examples/segmentation/object_classes.cpp
examples/ssd_multibox/Makefile
examples/ssd_multibox/main.cpp
examples/test/Makefile
examples/test/main.cpp
examples/test/multiple_executors.cpp
examples/two_eo_per_frame/Makefile [new file with mode: 0644]
examples/two_eo_per_frame/main.cpp [new file with mode: 0644]
examples/two_eo_per_frame_opt/Makefile [new file with mode: 0644]
examples/two_eo_per_frame_opt/main.cpp [new file with mode: 0644]
readme.md
tidl_api/Makefile
tidl_api/dsp/ocl_wrapper.cl
tidl_api/inc/configuration.h
tidl_api/inc/execution_object.h
tidl_api/inc/execution_object_internal.h [new file with mode: 0644]
tidl_api/inc/execution_object_pipeline.h [new file with mode: 0644]
tidl_api/inc/executor.h
tidl_api/make.buildid
tidl_api/src/configuration.cpp
tidl_api/src/configuration_parser.cpp
tidl_api/src/device_arginfo.h [new file with mode: 0644]
tidl_api/src/execution_object.cpp
tidl_api/src/execution_object_pipeline.cpp [new file with mode: 0644]
tidl_api/src/executor.cpp
tidl_api/src/executor_impl.h
tidl_api/src/ocl_device.cpp
tidl_api/src/ocl_device.h
tidl_api/src/trace.cpp
tidl_api/src/trace.h
tidl_api_manifest.html

index 5104df8d6ae02cc9210d08d89c85f1a51fb426c8..8527cfc6bcc00abd55ce716f626815d58ba8fae5 100644 (file)
             VERSION:'{{ release|e }}',
             COLLAPSE_INDEX:false,
             FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
-            HAS_SOURCE:  {{ has_source|lower }}
+            HAS_SOURCE:  {{ has_source|lower }},
+            SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
         };
     </script>
     {%- for scriptfile in script_files %}
index 7aa7849a9f542b8d1152cfc2184c7500d34cc0c0..d6033a8700b12d04905f77c23020a212873d37c7 100644 (file)
@@ -1,66 +1,90 @@
 .. _api-documentation:
 
-******************
-TIDL API Reference
-******************
+*************
+API Reference
+*************
+
+.. _api-ref-configuration:
 
 Configuration
--------------
-The ``Configuration`` object is used to specify various parameters required for network execution. Applications can directly initialize fields in an instance of ``Configuration`` or use the ``ReadFromFile`` method to read the configuration from a file. The following sections describe the commonly used fields available in the ``Configuration`` class:
++++++++++++++
+.. doxygenclass:: tidl::Configuration
+    :members:
 
-Input image description
-+++++++++++++++++++++++
-.. data::  std::string Configuration.inData
+Configuration file
+==================
 
-    Path to the input image file. This field is not used by the TIDL API itself. It can be used by applications to load an input image into a buffer. Can be empty if the application uses frameworks such as OpenCV to read images. See ``test/main.cpp`` for example usage.
+TIDL API allows the user to create a Configuration object by reading from a file or by initializing it directly. Configuration settings supported by ``Configuration::ReadFromFile``:
 
-.. data:: std::size_t Configuration.inHeight
+    * numFrames
+    * inWidth
+    * inHeight
+    * inNumChannels
+    * preProcType
+    * layerIndex2LayerGroupId
 
-    Height of the input image. Used by the API, must be specified.
+    * inData
+    * outData
 
-.. data:: std::size_t Configuration.inWidth
+    * netBinFile
+    * paramsBinFile
 
-    Width of the input image. Used by the API, must be specified.
+    * enableTrace
 
-.. data:: std::size_t Configuration.inNumChannels
+An example configuration file:
 
-    Number of channels in the input image. Used by the API, must be specified.
+.. literalinclude:: ../../examples/layer_output/j11_v2_trace.txt
+    :language: bash
 
-Output description
-++++++++++++++++++
 
-.. data::  std::string Configuration.outData
+.. _layer-group-override:
 
-    Path to the output image file. This field is not used by the TIDL API itself. It can be used by applications to write a buffer to file. Can be empty if the application uses frameworks such as OpenCV to read images. See ``test/main.cpp`` for example usage.
+Overriding layer group assignment
+=================================
+The `TIDL device translation tool`_ assigns layer group ids to layers during the translation process. TIDL API 1.1 and higher allows the user to override this assignment by specifying explicit mappings. There are two ways for the user to provide an updated mapping:
 
-Network
-+++++++
+1. Specify a mapping in the configuration file to indicate that layers 12, 13 and 14 are assigned to layer group 2:
 
-.. data:: std::string Configuration.netBinFile
+.. code-block:: c++
 
-    Path to the TIDL network binary file. Used by the API, must be specified.
+    layerIndex2LayerGroupId = { {12, 2}, {13, 2}, {14, 2} }
 
-.. data:: std::string Configuration.paramBinFile
 
-    Path to the TIDL parameter file. Used by the API, must be specified.
+2. User can also provide the layer index to group mapping in the code:
 
-Memory Management
-+++++++++++++++++
-The ``Configuration`` object specifies the sizes of 2 heaps. These heaps are allocated from OpenCL global memory that is shared across the host and device. Refer section :ref:`opencl-global-memory` for steps to increase the size of the OpenCL global memory heap.
+.. code-block:: c++
 
-.. data:: std::size_t Configuration.PARAM_HEAP_SIZE
+    Configuration c;
+    c.ReadFromFile("test.cfg");
+    c.layerIndex2LayerGroupId = { {12, 2}, {13, 2}, {14, 2} };
 
-    This field is used to specify the size of the device heap used for network parameters. The size depends on the size of the parameter binary file. For example, ``jsegnet21v2``'s parameter file, ``tidl_param_jsegnet21v2.bin`` is 2.6MB. Due to alignment reasons, the parameter heap must be 10% larger than the binary file size - in this case, 2.9MB. The constructor for ``Configuration`` sets PARAM_HEAP_SIZE to 9MB. There is one parameter heap for each instance of ``Executor`` .
 
-.. data:: std::size_t Configuration.EXTMEM_HEAP_SIZE
+.. role:: cpp(code)
+   :language: c++
 
-    This field is used to specify the size of the device heap used for all allocations other than network parameters. The constructor for ``Configuration`` sets EXTMEM_HEAP_SIZE to 64MB.  There is one external memory heap for each instance of ``ExecutionObject``
 
-API Reference
--------------
+.. _api-ref-executor:
 
-.. doxygennamespace:: tidl
-    :project: TIDL
+Executor
+++++++++
+.. doxygenclass:: tidl::Executor
     :members:
 
+.. _api-ref-eo:
+
+ExecutionObject
++++++++++++++++
+.. doxygenclass:: tidl::ExecutionObject
+    :members:
+
+.. _api-ref-eop:
+
+ExecutionObjectPipeline
++++++++++++++++++++++++
+.. doxygenclass:: tidl::ExecutionObjectPipeline
+    :members:
+
+
+.. refer https://breathe.readthedocs.io/en/latest/directives.html
 
+.. _TIDL device translation tool: http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components_TIDL.html#import-process
index 6a1cf0a9fd848dc77687316e019c39a56dc22490..f01f94295767c39df43401d50f311badda5c501d 100644 (file)
@@ -60,9 +60,9 @@ copyright = u'2018, Texas Instruments Incorporated'
 # built documents.
 #
 # The short X.Y version.
-version = '1.0'
+version = '1.1'
 # The full version, including alpha/beta/rc tags.
-release = '1.0.0'
+release = '1.1.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -277,7 +277,9 @@ def setup(app):
 
 
 # -- Breathe extension to integrate doxygen output --
-
+# https://breathe.readthedocs.io/en/latest/index.html
 breathe_projects = {
 "TIDL":"../../tidl_api/doxygen/xml/",
 }
+breathe_default_project = "TIDL"
+
index 7ba9bc7d3761368f0d9b5fae8b5b7a2eeb9472d6..21721c18d7bbec6699cb5c6325c1a09992e3bd12 100644 (file)
 Examples
 ********
 
-We ship three end-to-end examples within the tidl-api package
-to demonstrate three categories of deep learning networks.  The first
-two examples can run on AM57x SoCs with either EVE or DSP devices.  The last
-example requires AM57x SoCs with both EVE and DSP.  The performance
-numbers that we present here were obtained on an AM5729 EVM, which
-includes 2 ARM A15 cores running at 1.5GHz, 4 EVE cores at 535MHz, and
-2 DSP cores at 750MHz.
-
-For each example, we report device processing time, host processing time,
-and TIDL API overhead.  **Device processing time** is measured on the device,
-from the moment processing starts for a frame till processing finishes.
-**Host processing time** is measured on the host, from the moment
-``ProcessFrameStartAsync()`` is called till ``ProcessFrameWait()`` returns
-in user application.  It includes the TIDL API overhead, the OpenCL runtime
-overhead, and the time to copy user input data into padded TIDL internal
-buffers.
+.. list-table:: TIDL API Examples
+   :header-rows: 1
+   :widths: 12 43 20 25
+
+   * - Example
+     - Description
+     - Compute cores
+     - Input image
+   * - one_eo_per_frame
+     - Processes a single frame with one :term:`EO` using the j11_v2 network. Throughput is increased by distributing frame processing across EOs. Refer :ref:`use-case-1`.
+     - EVE or C66x
+     - Pre-processed image read from file.
+   * - two_eo_per_frame
+     - Processes a single frame with an :term:`EOP` using the j11_v2 network to reduce per-frame processing latency. Also increases throughput by distributing frame processing across EOPs. The EOP consists of two EOs. Refer :ref:`use-case-2`.
+     - EVE and C66x (network is split across both EVE and C66x)
+     - Pre-processed image read from file.
+   * - two_eo_per_frame_opt
+     - Builds on ``two_eo_per_frame``. Adds double buffering to improve performance. Refer :ref:`use-case-3`.
+     - EVE and C66x (network is split across both EVE and C66x)
+     - Pre-processed image read from file.
+
+   * - imagenet
+     - Classification example
+     - EVE or C66x
+     - OpenCV used to read input image from file or capture from camera.
+   * - segmentation
+     - Pixel level segmentation example
+     - EVE or C66x
+     - OpenCV used to read input image from file or capture from camera.
+   * - ssd_multibox
+     - Object detection
+     - EVE and C66x (network is split across both EVE and C66x)
+     - OpenCV used to read input image from file or capture from camera.
+   * - classification
+     - Classification example, called from the Matrix GUI.
+     -
+     - OpenCV used to read input image from file or capture from camera.
+   * - layer_output
+     - Illustrates using TIDL APIs to access output buffers of intermediate :term:`layers<Layer>` in the network.
+     - EVE or C66x
+     - Pre-processed image read from file.
+   * - test
+     - This example is used to test pre-converted networks included in the TIDL API package (``test/testvecs/config/tidl_models``). When run without any arguments, the program ``test_tidl`` will run all available networks on the C66x DSPs and EVEs available on the SoC. Use the ``-c`` option to specify a single network. Run ``test_tidl -h``  for details.
+     - C66x and EVEs (if available)
+     - Pre-processed image read from file.
+
+The included examples demonstrate three categories of deep learning networks: classification, segmentation and object detection.  ``imagenet`` and ``segmentation`` can run on AM57x processors with either EVE or C66x cores.  ``ssd_multibox`` requires AM57x processors with both EVE and C66x. The examples are available at ``/usr/share/ti/tidl/examples`` on the EVM file system and in the linux devkit.
+
+The performance numbers were obtained using:
+
+* `AM574x IDK EVM`_ with the Sitara `AM5749`_ Processor - 2 Arm Cortex-A15 cores running at 1.0GHz, 2 EVE cores at 650MHz, and 2 C66x cores at 750MHz.
+* `Processor SDK Linux`_ v5.1 with TIDL API v1.1
+
+For each example, device processing time, host processing time,
+and TIDL API overhead is reported.
+
+* **Device processing time** is measured on the device, from the moment processing starts for a frame till processing finishes.
+* **Host processing time** is measured on the host, from the moment ``ProcessFrameStartAsync()`` is called till ``ProcessFrameWait()`` returns in user application.  It includes the TIDL API overhead, the OpenCL runtime overhead, and the time to copy user input data into padded TIDL internal buffers. ``Host processing time = Device processing time + TIDL API overhead``.
+
 
 Imagenet
 --------
 
 The imagenet example takes an image as input and outputs 1000 probabilities.
 Each probability corresponds to one object in the 1000 objects that the
-network is pre-trained with.  Our example outputs top 5 predictions
-as the most likely objects that the input image can be.
+network is pre-trained with.  The example outputs top 5 predictions for a given input image.
 
 The following figure and tables shows an input image, top 5 predicted
-objects as output, and the processing time on either EVE or DSP.
+objects as output, and the processing time on either EVE or C66x.
 
 .. image:: ../../examples/test/testvecs/input/objects/cat-pet-animal-domestic-104827.jpeg
    :width: 600
 
-.. table::
-
-    ==== ==============
-    Rank Object Classes
-    ==== ==============
-    1    tabby
-    2    Egyptian_cat
-    3    tiger_cat
-    4    lynx
-    5    Persian_cat
-    ==== ==============
-
-.. table::
-
-   ====================== ==================== ============
-   Device Processing Time Host Processing Time API Overhead
-   ====================== ==================== ============
-   EVE: 123.1 ms          124.7 ms             1.34 %
-   **OR**
-   DSP: 117.9 ms          119.3 ms             1.14 %
-   ====================== ==================== ============
-
-The particular network that we ran in this category, jacintonet11v2,
-has 14 layers.  User can specify whether to run the network on EVE or DSP
-for acceleration.  We can see that EVE time is slightly higher than DSP time.
-We can also see that the overall overhead is less than 1.5%.
+
+==== ==============
+Rank Object Classes
+==== ==============
+1    tabby
+2    Egyptian_cat
+3    tiger_cat
+4    lynx
+5    Persian_cat
+==== ==============
+
+=======   ====================== ==================== ============
+Device    Device Processing Time Host Processing Time API Overhead
+=======   ====================== ==================== ============
+EVE       106.5 ms               107.9 ms             1.37 %
+C66x      117.9 ms               118.7 ms             0.93 %
+=======   ====================== ==================== ============
+
+The :term:`network<Network>` used in the example is jacintonet11v2. It has
+14 layers. Input to the network is RGB image of 224x224. Users can specify whether to run the network on EVE or C66x.
+
+The example code sets ``buffer_factor`` to 2 to create duplicated
+ExecutionObjectPipelines with identical ExecutionObjects to
+perform double buffering, so that host pre/post-processing can be overlapped
+with device processing (see comments in the code for details).
+The following table shows the loop overall time over 10 frames
+with single buffering and double buffering,
+``./imagenet -f 10 -d <num> -e <num>``.
+
+.. list-table:: Loop overall time over 10 frames
+   :header-rows: 1
+
+   * - Device(s)
+     - Single Buffering (buffer_factor=1)
+     - Double Buffering (buffer_factor=2)
+   * - 1 EVE
+     - 1744 ms
+     - 1167 ms
+   * - 2 EVEs
+     - 966 ms
+     - 795 ms
+   * - 1 C66x
+     - 1879 ms
+     - 1281 ms
+   * - 2 C66xs
+     - 1021 ms
+     - 814 ms
 
 .. note::
     The predicitions reported here are based on the output of the softmax
@@ -79,19 +142,48 @@ in blue and background in gray.
 .. image:: images/pexels-photo-972355-seg.jpg
    :width: 600
 
-The network we ran in this category is jsegnet21v2, which has 26 layers.
+The :term:`network<Network>` used in the example is jsegnet21v2. It has
+26 layers.  Users can specify whether to run the network on EVE or C66x.
+Input to the network is RGB image of size 1024x512.  The output is 1024x512
+values, each value indicates which pre-trained category the current pixel
+belongs to.  The example will take the network output, create an overlay,
+and blend the overlay onto the original input image to create an output image.
 From the reported time in the following table, we can see that this network
-runs significantly faster on EVE than on DSP.
-
-.. table::
-
-   ====================== ==================== ============
-   Device Processing Time Host Processing Time API Overhead
-   ====================== ==================== ============
-   EVE: 296.5 ms          303.3 ms             2.26 %
-   **OR**
-   DSP: 812.0 ms          818.4 ms             0.79 %
-   ====================== ==================== ============
+runs significantly faster on EVE than on C66x.
+
+=======     ====================== ==================== ============
+Device      Device Processing Time Host Processing Time API Overhead
+=======     ====================== ==================== ============
+EVE         251.8 ms               254.2 ms             0.96 %
+C66x        812.7 ms               815.0 ms             0.27 %
+=======     ====================== ==================== ============
+
+The example code sets ``buffer_factor`` to 2 to create duplicated
+ExecutionObjectPipelines with identical ExecutionObjects to
+perform double buffering, so that host pre/post-processing can be overlapped
+with device processing (see comments in the code for details).
+The following table shows the loop overall time over 10 frames
+with single buffering and double buffering,
+``./segmentation -f 10 -d <num> -e <num>``.
+
+.. list-table:: Loop overall time over 10 frames
+   :header-rows: 1
+
+   * - Device(s)
+     - Single Buffering (buffer_factor=1)
+     - Double Buffering (buffer_factor=2)
+   * - 1 EVE
+     - 5233 ms
+     - 3017 ms
+   * - 2 EVEs
+     - 3032 ms
+     - 3015 ms
+   * - 1 C66x
+     - 10890 ms
+     - 8416 ms
+   * - 2 C66xs
+     - 5742 ms
+     - 4638 ms
 
 .. _ssd-example:
 
@@ -111,142 +203,161 @@ vehicles in blue and road signs in yellow.
 .. image:: images/pexels-photo-378570-ssd.jpg
    :width: 600
 
-The network can be run entirely on either EVE or DSP.  But the best
-performance comes with running the first 30 layers on EVE and the
-next 13 layers on DSP, for this particular jdetnet_ssd network.
-Note the **AND** in the following table for the reported time.
-Our end-to-end example shows how easy it is to assign a layers group id
-to an *Executor* and how easy it is to connect the output from one
-*ExecutionObject* to the input to another *ExecutionObject*.
-
-.. table::
-
-   ====================== ==================== ============
-   Device Processing Time Host Processing Time API Overhead
-   ====================== ==================== ============
-   EVE: 175.2 ms          179.1 ms             2.14 %
-   **AND**
-   DSP:  21.1 ms           22.3 ms             5.62 %
-   ====================== ==================== ============
-
-Test
-----
-This example is used to test pre-converted networks included in the TIDL API package (``test/testvecs/config/tidl_models``). When run without any arguments, the program ``test_tidl`` will run all available networks on the C66x DSPs and EVEs available on the SoC. Use the ``-c`` option to specify a single network. Run ``test_tidl -h``  for details.
+The network we ran in this category is jdenet_ssd, which has 43 layers.
+Input to the network is RGB image of size 768x320.  Output is a list of
+boxes (up to 20), each box has information about the box coordinates, and
+which pre-trained category that the object inside the box belongs to.
+The example will take the network output, draw boxes accordingly,
+and create an output image.
+The network can be run entirely on either EVE or C66x.  However, the best
+performance comes with running the first 30 layers as a group on EVE
+and the next 13 layers as another group on C66x.
+Our end-to-end example shows how easy it is to assign a :term:`Layer Group` id
+to an :term:`Executor` and how easy it is to construct an :term:`ExecutionObjectPipeline` to connect the output of one *Executor*'s :term:`ExecutionObject`
+to the input of another *Executor*'s *ExecutionObject*.
+
+========      ====================== ==================== ============
+Device        Device Processing Time Host Processing Time API Overhead
+========      ====================== ==================== ============
+EVE+C66x      169.5ms                172.0ms              1.68 %
+========      ====================== ==================== ============
+
+The example code sets ``pipeline_depth`` to 2 to create duplicated
+ExecutionObjectPipelines with identical ExecutionObjects to
+perform pipelined execution at the ExecutionObject level.
+The side effect is that it also overlaps host pre/post-processing
+with device processing (see comments in the code for details).
+The following table shows the loop overall time over 10 frames
+with pipelining at ExecutionObjectPipeline level
+versus ExecutionObject level.
+``./ssd_multibox -f 10 -d <num> -e <num>``.
+
+.. list-table:: Loop overall time over 10 frames
+   :header-rows: 1
+
+   * - Device(s)
+     - pipeline_depth=1
+     - pipeline_depth=2
+   * - 1 EVE + 1 C66x
+     - 2900 ms
+     - 1735 ms
+   * - 2 EVEs + 2 C66xs
+     - 1630 ms
+     - 1408 ms
 
 Running Examples
 ----------------
 
 The examples are located in ``/usr/share/ti/tidl/examples`` on
-the EVM file system.  Each example needs to be run its own directory.
+the EVM file system.  **Each example needs to be run in its own directory** due to relative paths to configuration files.
 Running an example with ``-h`` will show help message with option set.
-The following code section shows how to run the examples, and
-the test program that tests all supported TIDL network configs.
+The following listing illustrates how to build and run the examples.
 
-.. code:: shell
+.. code-block:: shell
 
-   root@am57xx-evm:~# cd /usr/share/ti/tidl-api/examples/imagenet/
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/imagenet# make -j4
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/imagenet# ./imagenet -t d
+   root@am57xx-evm:~/tidl-api/examples/imagenet# ./imagenet
    Input: ../test/testvecs/input/objects/cat-pet-animal-domestic-104827.jpeg
-   frame[0]: Time on device:  117.9ms, host:  119.3ms API overhead:   1.17 %
-   1: tabby, prob = 0.996
-   2: Egyptian_cat, prob = 0.977
-   3: tiger_cat, prob = 0.973
-   4: lynx, prob = 0.941
-   5: Persian_cat, prob = 0.922
+   frame[  0]: Time on EVE0: 106.50 ms, host: 107.96 ms API overhead: 1.35 %
+   1: tabby
+   2: Egyptian_cat
+   3: tiger_cat
+   4: lynx
+   5: Persian_cat
+   Loop total time (including read/write/opencv/print/etc):  202.6ms
    imagenet PASSED
 
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/imagenet# cd ../segmentation/; make -j4
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/segmentation# ./segmentation -i ../test/testvecs/input/roads/pexels-photo-972355.jpeg
-   Input: ../test/testvecs/input/roads/pexels-photo-972355.jpeg
-   frame[0]: Time on device:  296.5ms, host:  303.2ms API overhead:   2.21 %
+   root@am57xx-evm:~/tidl-api/examples/segmentation# ./segmentation
+   Input: ../test/testvecs/input/000100_1024x512_bgr.y
+   frame[  0]: Time on EVE0: 251.74 ms, host: 258.02 ms API overhead: 2.43 %
+   Saving frame 0 to: frame_0.png
    Saving frame 0 overlayed with segmentation to: overlay_0.png
+   frame[  1]: Time on EVE0: 251.76 ms, host: 255.79 ms API overhead: 1.58 %
+   Saving frame 1 to: frame_1.png
+   Saving frame 1 overlayed with segmentation to: overlay_1.png
+   ...
+   frame[  8]: Time on EVE0: 251.75 ms, host: 254.21 ms API overhead: 0.97 %
+   Saving frame 8 to: frame_8.png
+   Saving frame 8 overlayed with segmentation to: overlay_8.png
+   Loop total time (including read/write/opencv/print/etc):   4809ms
    segmentation PASSED
 
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/segmentation# cd ../ssd_multibox/; make -j4
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/ssd_multibox# ./ssd_multibox -i ../test/testvecs/input/roads/pexels-photo-378570.jpeg
-   Input: ../test/testvecs/input/roads/pexels-photo-378570.jpeg
-   frame[0]: Time on EVE:  175.2ms, host:    179ms API overhead:    2.1 %
-   frame[0]: Time on DSP:  21.06ms, host:  22.43ms API overhead:   6.08 %
+   root@am57xx-evm:~/tidl-api/examples/ssd_multibox# ./ssd_multibox
+   Input: ../test/testvecs/input/preproc_0_768x320.y
+   frame[  0]: Time on EVE0+DSP0: 169.44 ms, host: 173.56 ms API overhead: 2.37 %
+   Saving frame 0 to: frame_0.png
    Saving frame 0 with SSD multiboxes to: multibox_0.png
-   Loop total time (including read/write/print/etc):  423.8ms
+   Loop total time (including read/write/opencv/print/etc):  320.2ms
    ssd_multibox PASSED
 
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/ssd_multibox# cd ../test; make -j4
-   root@am57xx-evm:/usr/share/ti/tidl-api/examples/test# ./test_tidl
-   API Version: 01.00.00.d91e442
-   Running dense_1x1 on 2 devices, type EVE
-   frame[0]: Time on device:  134.3ms, host:  135.6ms API overhead:  0.994 %
-   dense_1x1 : PASSED
-   Running j11_bn on 2 devices, type EVE
-   frame[0]: Time on device:  176.2ms, host:  177.7ms API overhead:  0.835 %
-   j11_bn : PASSED
-   Running j11_cifar on 2 devices, type EVE
-   frame[0]: Time on device:  53.86ms, host:  54.88ms API overhead:   1.85 %
-   j11_cifar : PASSED
-   Running j11_controlLayers on 2 devices, type EVE
-   frame[0]: Time on device:  122.9ms, host:  123.9ms API overhead:  0.821 %
-   j11_controlLayers : PASSED
-   Running j11_prelu on 2 devices, type EVE
-   frame[0]: Time on device:  300.8ms, host:  302.1ms API overhead:  0.437 %
-   j11_prelu : PASSED
-   Running j11_v2 on 2 devices, type EVE
-   frame[0]: Time on device:  124.1ms, host:  125.6ms API overhead:   1.18 %
-   j11_v2 : PASSED
-   Running jseg21 on 2 devices, type EVE
-   frame[0]: Time on device:    367ms, host:    374ms API overhead:   1.88 %
-   jseg21 : PASSED
-   Running jseg21_tiscapes on 2 devices, type EVE
-   frame[0]: Time on device:  302.2ms, host:  308.5ms API overhead:   2.02 %
-   frame[1]: Time on device:  301.9ms, host:  312.5ms API overhead:   3.38 %
-   frame[2]: Time on device:  302.7ms, host:  305.9ms API overhead:   1.04 %
-   frame[3]: Time on device:  301.9ms, host:    305ms API overhead:   1.01 %
-   frame[4]: Time on device:  302.7ms, host:  305.9ms API overhead:   1.05 %
-   frame[5]: Time on device:  301.9ms, host:  305.5ms API overhead:   1.17 %
-   frame[6]: Time on device:  302.7ms, host:  305.9ms API overhead:   1.06 %
-   frame[7]: Time on device:  301.9ms, host:    305ms API overhead:   1.02 %
-   frame[8]: Time on device:    297ms, host:  300.3ms API overhead:   1.09 %
-   Comparing frame: 0
-   jseg21_tiscapes : PASSED
-   Running smallRoi on 2 devices, type EVE
-   frame[0]: Time on device:  2.548ms, host:  3.637ms API overhead:   29.9 %
-   smallRoi : PASSED
-   Running squeeze1_1 on 2 devices, type EVE
-   frame[0]: Time on device:  292.9ms, host:  294.6ms API overhead:  0.552 %
-   squeeze1_1 : PASSED
-
-   Multiple Executor...
-   Running network tidl_config_j11_v2.txt on EVEs: 1  in thread 0
-   Running network tidl_config_j11_cifar.txt on EVEs: 0  in thread 1
-   Multiple executors: PASSED
-   Running j11_bn on 2 devices, type DSP
-   frame[0]: Time on device:  170.5ms, host:  171.5ms API overhead:  0.568 %
-   j11_bn : PASSED
-   Running j11_controlLayers on 2 devices, type DSP
-   frame[0]: Time on device:  416.4ms, host:  417.1ms API overhead:  0.176 %
-   j11_controlLayers : PASSED
-   Running j11_v2 on 2 devices, type DSP
-   frame[0]: Time on device:    118ms, host:  119.2ms API overhead:   1.01 %
-   j11_v2 : PASSED
-   Running jseg21 on 2 devices, type DSP
-   frame[0]: Time on device:   1123ms, host:   1128ms API overhead:  0.443 %
-   jseg21 : PASSED
-   Running jseg21_tiscapes on 2 devices, type DSP
-   frame[0]: Time on device:  812.3ms, host:  817.3ms API overhead:  0.614 %
-   frame[1]: Time on device:  812.6ms, host:  818.6ms API overhead:  0.738 %
-   frame[2]: Time on device:  812.3ms, host:  815.1ms API overhead:  0.343 %
-   frame[3]: Time on device:  812.7ms, host:  815.2ms API overhead:  0.312 %
-   frame[4]: Time on device:  812.3ms, host:  815.1ms API overhead:  0.353 %
-   frame[5]: Time on device:  812.6ms, host:  815.1ms API overhead:  0.302 %
-   frame[6]: Time on device:  812.2ms, host:  815.1ms API overhead:  0.357 %
-   frame[7]: Time on device:  812.6ms, host:  815.2ms API overhead:  0.315 %
-   frame[8]: Time on device:    812ms, host:    815ms API overhead:  0.367 %
-   Comparing frame: 0
-   jseg21_tiscapes : PASSED
-   Running smallRoi on 2 devices, type DSP
-   frame[0]: Time on device:  14.21ms, host:  14.94ms API overhead:   4.89 %
-   smallRoi : PASSED
-   Running squeeze1_1 on 2 devices, type DSP
-   frame[0]: Time on device:    960ms, host:  961.1ms API overhead:  0.116 %
-   squeeze1_1 : PASSED
-   tidl PASSED
+
+Image input
+^^^^^^^^^^^
+
+The image input option, ``-i <image>``, takes an image file as input.
+You can supply an image file with format that OpenCV can read, since
+we use OpenCV for image pre/post-processing.  When ``-f <number>`` option
+is used, the same image will be processed repeatedly.
+
+Camera (live video) input
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The input option, ``-i camera<number>``, enables live frame inputs
+from camera.  ``<number>`` is the video input port number
+of your camera in Linux.  Use the following command to check video
+input ports.  The number defaults to ``1`` for TMDSCM572X camera module
+used on AM57x EVMs.  You can use ``-f <number>`` to specify the number
+of frames you want to process.
+
+.. code-block:: shell
+
+  root@am57xx-evm:~# v4l2-ctl --list-devices
+  omapwb-cap (platform:omapwb-cap):
+        /dev/video11
+
+  omapwb-m2m (platform:omapwb-m2m):
+        /dev/video10
+
+  vip (platform:vip):
+        /dev/video1
+
+  vpe (platform:vpe):
+        /dev/video0
+
+
+Pre-recorded video (mp4/mov/avi) input
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The input option, ``-i <name>.{mp4,mov,avi}``, enables frame inputs from
+pre-recorded video file in mp4, mov or avi format.  If you have a video in
+a different OpenCV-supported format/suffix, you can simply create a softlink
+with one of the mp4, mov or avi suffixes and feed it into the example.
+Again, use ``-f <number>`` to specify the number of frames you want to process.
+
+Displaying video output
+^^^^^^^^^^^^^^^^^^^^^^^
+
+When using video input, live or pre-recorded, the example will display
+the output in a window using OpenCV.  If you have a LCD screen attached
+to the EVM, you will need to kill the ``matrix-gui`` first in order to
+see the example display window, as shown in the following example.
+
+.. code-block:: shell
+
+  root@am57xx-evm:/usr/share/ti/tidl/examples/ssd_multibox# /etc/init.d/matrix-gui-2.0 stop
+  Stopping Matrix GUI application.
+  root@am57xx-evm:/usr/share/ti/tidl/examples/ssd_multibox# ./ssd_multibox -i camera -f 100
+  Input: camera
+  init done
+  Using Wayland-EGL
+  wlpvr: PVR Services Initialised
+  Using the 'xdg-shell-v5' shell integration
+  ... ...
+  root@am57xx-evm:/usr/share/ti/tidl/examples/ssd_multibox# /etc/init.d/matrix-gui-2.0 start
+  /usr/share/ti/tidl/examples/ssd_multibox
+  Removing stale PID file /var/run/matrix-gui-2.0.pid.
+  Starting Matrix GUI application.
+
+
+.. _AM574x IDK EVM:  http://www.ti.com/tool/tmdsidk574
+.. _AM5749: http://www.ti.com/product/AM5749/
+.. _Processor SDK Linux: http://software-dl.ti.com/processor-sdk-linux/esd/AM57X/latest/index_FDS.html
similarity index 66%
rename from docs/source/building.rst
rename to docs/source/faq/building.rst
index c50755037029698b07b4497b600059044946f10d..3e089ff564d8ce475a1956da365185b971085e67 100644 (file)
@@ -1,5 +1,5 @@
-*********************
-Building from sources
-*********************
+########################################
+How do I build the TIDL API from source?
+########################################
 
 Source for the TIDL API is available at https://git.ti.com/tidl/tidl-api. ``tidl-api/makefile`` contains targets to build the API, viewer and examples. The makefile supports native compilation on the EVM and cross-compilation on x86/Linux.
index c79723dfd6b94ce3c4ee1a87d6e4bc97443e488c..b87a92701121ea31e42967fa85db76750d7a7c73 100644 (file)
@@ -6,3 +6,4 @@ Frequently Asked Questions
    :maxdepth: 2
 
    out_of_memory
+   building
index cba902cc68f7dfbd95197e559b76df4d710a15b8..ee97e58eb00d5b66cc7c3afda189aafee94934a6 100644 (file)
@@ -26,7 +26,7 @@ One possible reason is that previous runs of the application were aborted (e.g.
 
 Insufficient OpenCL global memory
 +++++++++++++++++++++++++++++++++
-Another possible reason is that total memory requirement specified in the ``Configuration`` using EXTMEM_HEAP_SIZE and PARAM_HEAP_SIZE exceeds default memory available for OpenCL.  Follow the instructions below to increase the amount of CMEM (contiguous memory available for OpenCL)
+Another possible reason is that total memory requirement specified in the ``Configuration`` using NETWORK_HEAP_SIZE and PARAM_HEAP_SIZE exceeds default memory available for OpenCL.  Follow the instructions below to increase the amount of CMEM (contiguous memory available for OpenCL) from 192MB (0xc000000) to 384MB (0x18000000):
 
 .. code:: bash
 
index 59c52f2987a03ec017947c86bd83b5953c64b12b..ede3238a6c26644a7219ef08d8de64a83fa16e57 100755 (executable)
Binary files a/docs/source/images/tidl-api.png and b/docs/source/images/tidl-api.png differ
diff --git a/docs/source/images/tidl-frame-across-eos-opt.png b/docs/source/images/tidl-frame-across-eos-opt.png
new file mode 100755 (executable)
index 0000000..3d5bac1
Binary files /dev/null and b/docs/source/images/tidl-frame-across-eos-opt.png differ
diff --git a/docs/source/images/tidl-frame-across-eos.png b/docs/source/images/tidl-frame-across-eos.png
new file mode 100755 (executable)
index 0000000..9c6a098
Binary files /dev/null and b/docs/source/images/tidl-frame-across-eos.png differ
diff --git a/docs/source/images/tidl-one-eo-per-frame.png b/docs/source/images/tidl-one-eo-per-frame.png
new file mode 100755 (executable)
index 0000000..ff61781
Binary files /dev/null and b/docs/source/images/tidl-one-eo-per-frame.png differ
index ec32a6b2c40e41681ff1337169721ac96964176b..a32f4c15fba8650c47e55f1974915a45d523bfcf 100644 (file)
@@ -8,12 +8,13 @@ TI Deep Learning API User's Guide
    :maxdepth: 3
 
    intro
+   overview
    using_api
-   viewer
    example
    api
-   building
+   viewer
    faq/index
+   readme/index
    notice
    disclaimer
 
index 407a39e1ea387ca4ba030c3ce86118ab07237dab..224aca26595f8c353c9818a882ceb56c2e1a790b 100644 (file)
@@ -2,27 +2,27 @@
 Introduction
 ************
 
-TI Deep Learning (TIDL) API brings deep learning to the edge by enabling applications to leverage TI's proprietary, highly optimized CNN/DNN implementation on the EVE and C66x DSP compute engines. TIDL will initially target Vision/2D use cases on AM57x SoCs.
+TI Deep Learning (TIDL) API brings deep learning to the edge by enabling applications to leverage TI's proprietary, highly optimized CNN/DNN implementation on the EVE and C66x DSP compute engines. TIDL will initially target Vision/2D use cases on AM57x Sitara |(TM)| Processors.
 
-This User's Guide covers the TIDL API. For information on TIDL such as the overall development flow, techniques to optimize performance of CNN/DNN on TI's SoCs,performance/benchmarking data and list of supported layers, see the TIDL section in the `Processor SDK Linux Software Developer's Guide`_.
+The TIDL API leverages TI's `OpenCL`_ |(TM)| product to offload deep learning applications to both EVE(s) and DSP(s).  The TIDL API significantly improves the out-of-box deep learning experience for users and enables them to focus on their overall use case. They do not have to spend time on the mechanics of Arm |(R)| ↔ DSP/EVE communication or implementing optimized network layers on EVE(s) and/or DSP(s).  The API allows customers to easily integrate frameworks such as OpenCV and rapidly prototype deep learning applications.
 
 .. note::
-    TIDL API is available only on AM57x SoCs. It requires OpenCL version 1.1.15.1 or higher.
+
+    This User's Guide focuses on the TIDL API. For information on TIDL such as the overall development flow, techniques to optimize performance of CNN/DNN on TI's processors, performance/benchmarking data and list of supported layers, see the TIDL section in the `Processor SDK Linux Software Developer's Guide (TIDL chapter)`_.
 
 Key Features
 ------------
-Ease of use
-+++++++++++
+**Ease of use**
+
 * Easily integrate TIDL APIs into other frameworks such as `OpenCV`_
-* Provides a common host abstraction for user applications across multiple compute engines (EVEs and C66x DSPs)
+* Provides simple host abstractions for user applications to run a network across multiple compute cores (EVEs and C66x DSPs). Refer :ref:`use-case-1` and :ref:`use-case-2` for details.
+
+**Low overhead**
 
-Low overhead
-+++++++++++++
 The execution time of TIDL APIs on the host is a fairly small percentage of the overall per-frame execution time. For example, with jseg21 network, 1024x512 frame with 3 channels, the APIs account for ~1.5% of overall per-frame processing time.
 
-Software Architecture
----------------------
-The TIDL API leverages TI's `OpenCL`_ product to offload deep learning applications to both EVE(s) and DSP(s).  The TIDL API significantly improves the out-of-box deep learning experience for users and enables them to focus on their overall use case. They do not have to spend time on the mechanics of ARM ↔ DSP/EVE communication or implementing optimized network layers on EVE(s) and/or DSP(s).  The API allows customers to easily integrate frameworks such as OpenCV and rapidly prototype deep learning applications.
+Development Flow
+----------------
 
 .. _`TIDL Development flow`:
 
@@ -32,66 +32,15 @@ The TIDL API leverages TI's `OpenCL`_ product to offload deep learning applicati
 
     Development flow with TIDL APIs
 
-:numref:`TIDL Development flow` shows the overall development process. Deep learning consists to two stages: training at development stage and inference at deployment stage.  Training involves designing neural network model, running training data through the network to tune the model parameters.  Inference takes the pre-trained model including parameters, applies to new input and produces output.  Training is computationally intensive and is done using frameworks such as Caffe/TensorFlow. Once the network is trained, the TIDL converter tool can be used to translate the network and parameters to TIDL. The `Processor SDK Linux Software Developer's Guide`_ provides details on the development flow and and the converter tool. The converter tool generates a TIDL network binary file and model or parameter file. The network file specifies the network graph. The parameter file specifies the weights.
-
-:numref:`TIDL API Software Architecture` shows the TIDL API software architecture.
-
-.. _`TIDL API Software Architecture`:
-
-.. figure:: images/tidl-api.png
-    :align: center
-    :scale: 60
-
-    TIDL API Software Architecture
-
-TIDL APIs provide three intuitive C++ classes.  ``Configuration`` encapsulates a network configuration, including pointers to the network and parameter binary files.  ``Executor`` encapsulates on-device memory allocation, network setup and initialization.  ``ExecutionObject`` encapsulates TIDL processing on a single DSP or EVE core.  Implementation of these classes will call into OpenCL runtime to offload network processing onto EVE/DSP devices, abstracting these details from the user.
-
-:numref:`simple-example` illustrates how easy it is to use TIDL APIs to leverage deep learning application in user applications.  In this example, a configuration object is created from reading a TIDL network config file.  An executor object is created with two EVE devices.  It uses the configuration object to setup and initialize TIDL network on EVEs.  Each of the two execution objects dispatches TIDL processing to a different EVE core.  Because the OpenCL kernel execution is asynchronous, we can pipeline the frames across two EVEs.  When one frame is being processed by a EVE, the next frame can be processed by another EVE.
-
-
-.. code-block:: c++
-    :caption: Application using TIDL APIs
-    :name: simple-example
-
-    // Read a TI DL network configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(“./tidl_j11v2_net");
-
-    // Create an executor with 2 EVEs and configuration
-    DeviceIds ids = {DeviceId::ID0, DeviceId::ID1};
-    Executor executor(DeviceType::EVE, ids, configuration);
-
-    // Query Executor for set of ExecutionObjects created
-    const ExecutionObjects& eos = executor.GetExecutionObjects();
-    int num_eos = eos.size();  // 2 EVEs
-
-    // Allocate input and output buffers for each execution object
-    for (auto &eo : eos)
-    {
-         ArgInfo in(eo->GetInputBufferSizeInBytes());
-         ArgInfo out(eo->GetOutputBufferSizeInBytes());
-         eo->SetInputOutputBuffer(in, out);
-    }
-
-    // Pipelined processing with 2 EVE cores
-    for (int idx = 0; idx < configuration.numFrames + num_eos; idx++)
-    {
-        ExecutionObject* eo = eos[idx % num_eos].get();
-
-        // Wait for previous frame on the same eo to finish processing
-        if (eo->ProcessFrameWait())  WriteFrameOutput(*eo);
-
-        // Read a frame and start processing it with current eo
-        if (ReadFrameInput(*eo, idx))  eo->ProcessFrameStartAsync();
-    }
-
-
-``ReadFrameInput`` and ``WriteFrameOutput`` functions are used to read an input frame and write the result of processing. For example, with OpenCV, ``ReadFrameInput`` is implemented using OpenCV APIs to capture a frame. To execute the same network on DSPs, the only change to :numref:`simple-example` is to replace ``DeviceType::EVE`` with ``DeviceType::DSP``.
-
-Section :ref:`using-tidl-api` contains details on using the APIs. The APIs themselves are documented in section :ref:`api-documentation`.
-
-Sometimes it is beneficial to partition a network and run different parts on different cores because some types of layers could run faster on EVEs while other types could run faster on DSPs.  TIDL APIs provide the flexibility to run partitioned network across EVEs and DSPs. Refer the :ref:`ssd-example` example for details.
+:numref:`TIDL Development flow` shows the overall development process. Deep learning consists to two stages: training at development stage and inference at deployment stage.  Training involves designing neural network model, running training data through the network to tune the model parameters.  Inference takes the pre-trained model including parameters, applies to new input and produces output.  Training is computationally intensive and is done using frameworks such as Caffe/TensorFlow. Once the network is trained, the TIDL converter tool can be used to translate the network and parameters to TIDL. The `Processor SDK Linux Software Developer's Guide (TIDL chapter)`_ provides details on the development flow and and the converter tool.
 
 .. _Processor SDK Linux Software Developer's Guide: http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/index.html
+.. _Processor SDK Linux Software Developer's Guide (TIDL chapter): http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components_TIDL.html
 .. _OpenCV: http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components.html#opencv
 .. _OpenCL: http://software-dl.ti.com/mctools/esd/docs/opencl/index.html
+
+.. |(TM)| unicode:: U+2122
+    :ltrim:
+
+.. |(R)| unicode:: U+00AE
+    :ltrim:
diff --git a/docs/source/overview.rst b/docs/source/overview.rst
new file mode 100644 (file)
index 0000000..344ea63
--- /dev/null
@@ -0,0 +1,66 @@
+********
+Overview
+********
+
+Software Architecture
++++++++++++++++++++++
+
+:numref:`TIDL API Software Architecture` shows the TIDL API software architecture and how it fits into the software ecosystem on AM57x. The TIDL API leverages OpenCL APIs to deploy translated network models. It provides the following services:
+
+* Make the application's input data available in memories associated with the :term:`compute core`.
+* Initialize and run the :term:`layer groups<Layer group>` associated with the network on compute cores
+* Make the output data available to the application
+
+.. _`TIDL API Software Architecture`:
+
+.. figure:: images/tidl-api.png
+    :align: center
+
+    TIDL API Software Architecture
+
+The TIDL API consists of 4 C++ classes and associated methods: ``Configuration``, ``Executor``, ``ExecutionObject``, and ``ExecutionObjectPipeline``. Refer :ref:`using-tidl-api` and :ref:`api-documentation` for details.
+
+Terminology
++++++++++++
+.. glossary::
+    :sorted:
+
+    Network
+        A description of the layers used in a Deep Learning model and the connections between the layers. The network is generated by the TIDL import tool and used by the TIDL API. Refer `Processor SDK Linux Software Developer's Guide (TIDL chapter)`_ for creating TIDL network and parameter binary files from TensorFlow and Caffe. A network consists of one or more Layer Groups.
+
+    Parameter binary
+        A binary file with weights generated by the TIDL import tool and used by the TIDL API.
+
+    Layer
+        A layer consists of mathematical operations such as filters, rectification linear unit (ReLU) operations, downsampling operations (usually called average pooling, max pooling or striding), elementwise additions, concatenations, batch normalization and fully connected matrix multiplications. Refer `Processor SDK Linux Software Developer's Guide (TIDL chapter)`_ for a list of supported layers.
+
+    Layer Group
+        A collection of interconnected layers. Forms a unit of execution. The Execution Object "runs" a layer group on a compute core i.e. it performs the mathematical operations associated with the layers in the layer group on the input and generates one or more outputs.
+
+    Compute core
+        A single EVE or C66x DSP. An Execution Object manages execution on one compute core. Also referred to as a **device** in OpenCL. Sitara AM5749 has 4 compute cores: EVE1, EVE2, DSP1 and DSP2.
+
+    Executor
+        A TIDL API class. The executor is responsible for initializing Execution Objects with a Configuration. The Executor is also responsible for initialzing the OpenCL runtime. Refer :ref:`api-ref-executor` for available methods.
+
+    ExecutionObject
+    EO
+        A TIDL API class. Manages the execution of a layer group on a compute core. There is an EO associated with each compute core. The EO leverages the OpenCL runtime to manage execution. TIDL API implementation leverages the OpenCL runtime to offload network processing. Refer :ref:`api-ref-eo` for a description of the ExecutionObject class and methods.
+
+    ExecutionObjectPipeline
+    EOP
+        A TIDL API class. Two use cases:
+
+        * Pipeline execution of a single input frame across multiple Execution Objects (:ref:`use-case-2`).
+        * Double buffering using the input and output buffer associated with an instance of ExecutionObjectPipeline (:ref:`use-case-3`).
+
+        Refer :ref:`api-ref-eop` for a description of the ExecutionObjectPipeline class and methods.
+
+    Configuration
+        A TIDL API class. Used to specify a configuration for the Executor, including pointers to the network and parameter binary files. Refer :ref:`api-ref-configuration` for a description of the Configuration class and methods.
+
+    Frame
+        A buffer representing 2D data, typically an image.
+
+
+.. _Processor SDK Linux Software Developer's Guide (TIDL chapter): http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components_TIDL.html
diff --git a/docs/source/readme/index.rst b/docs/source/readme/index.rst
new file mode 100644 (file)
index 0000000..5247678
--- /dev/null
@@ -0,0 +1,25 @@
+#############
+Release Notes
+#############
+
+.. toctree::
+   :maxdepth: 1
+
+   v01.01.x
+   v01.00.x
+
+
+Processor SDK to TIDL API version map
+=====================================
+
++---------------+------------+--------------------------------------------+
+| Processor SDK | TIDL API   | Key features                               |
++---------------+------------+--------------------------------------------+
+| 05.01.00.x    | 01.01.x    | Added ExecutionObjectPipeline and          |
+|               |            | output trace capability.                   |
+|               |            | Refer :doc:`v01.01.x` for details.         |
++---------------+------------+--------------------------------------------+
+| 05.00.00.x    | 01.00.x    | Initial release. Refer                     |
+|               |            | Refer :doc:`v01.00.x` for details.         |
++---------------+------------+--------------------------------------------+
+
diff --git a/docs/source/readme/v01.00.x.rst b/docs/source/readme/v01.00.x.rst
new file mode 100644 (file)
index 0000000..278deb9
--- /dev/null
@@ -0,0 +1,25 @@
+******************
+TIDL API v01.00.x
+******************
+
+New Features
+=============
+First release of the TI Deep Learning API. TIDL API brings deep learning to the edge by enabling applications to leverage TI's proprietary, highly optimized CNN/DNN implementation on the EVE and C66x DSP compute engines. TIDL will initially target Vision/2D use cases and is available on the following AM57x Sitara Processors:
+
+ * `AM571x`_ (offload to C66x DSPs)
+ * `AM5728`_ (offload to C66x DSPs)
+ * `AM574x`_ (offload to EVEs and C66x DSPs)
+
+Supported Evaluation Modules (EVMs)
+===================================
+
+* `AM572x EVM`_
+* `AM571x IDK EVM`_
+* `AM574x IDK EVM`_
+
+.. _AM572x EVM:  http://www.ti.com/tool/tmdsevm572x
+.. _AM571x IDK EVM:  http://www.ti.com/tool/tmdxidk5718
+.. _AM574x IDK EVM:  http://www.ti.com/tool/tmdsidk574
+.. _AM571x:     http://www.ti.com/processors/sitara/arm-cortex-a15/am57x/products.html#p2098=1%20C66x&p809=2;2
+.. _AM5728:     http://www.ti.com/product/AM5728
+.. _AM574x:     http://www.ti.com/processors/sitara/arm-cortex-a15/am57x/products.html#p2098=2%20C66x&p815=ECC
diff --git a/docs/source/readme/v01.01.x.rst b/docs/source/readme/v01.01.x.rst
new file mode 100644 (file)
index 0000000..8a231cc
--- /dev/null
@@ -0,0 +1,28 @@
+******************
+TIDL API v01.01.x
+******************
+
+New Features
+=============
+
+* Enhance API to hide complexity of executing network across DSP/EVE
+* Add support for tracing outputs from intermediate network layers
+* Update layer group id assignment before execution
+* Provide feedback to the user on parameter and network heap size requirements
+
+Defect Fixes
+============
+N/A
+
+Supported Evaluation Modules (EVMs)
+===================================
+* `AM572x EVM`_
+* `AM571x IDK EVM`_
+* `AM574x IDK EVM`_
+
+.. _AM572x EVM:  http://www.ti.com/tool/tmdsevm572x
+.. _AM571x IDK EVM:  http://www.ti.com/tool/tmdxidk5718
+.. _AM574x IDK EVM:  http://www.ti.com/tool/tmdsidk574
+.. _AM571x:     http://www.ti.com/processors/sitara/arm-cortex-a15/am57x/products.html#p2098=1%20C66x&p809=2;2
+.. _AM5728:     http://www.ti.com/product/AM5728
+.. _AM574x:     http://www.ti.com/processors/sitara/arm-cortex-a15/am57x/products.html#p2098=2%20C66x&p815=ECC
index b2c909e03666bc4ae93877fe22b77c84a1b33125..9d216d00e9b5cda2e2ee2eae07684976bac73220 100644 (file)
 .. _using-tidl-api:
 
-******************
-Using the TIDL API
-******************
+*************
+Using the API
+*************
 
-This example illustrates using the TIDL API to offload deep learning network processing from a Linux application to the C66x DSPs or EVEs on AM57x devices. The API consists of three classes: ``Configuration``, ``Executor`` and ``ExecutionObject``.
+This section illustrates using TIDL APIs to leverage deep learning in user applications. The overall flow is as follows:
 
-Step 1
-======
+* Create a :term:`Configuration` object to specify the set of parameters required for network exectution.
+* Create :term:`Executor` objects - one to manage overall execution on the EVEs, the other for C66x DSPs.
+* Use the :term:`Execution Objects<EO>` (EO) created by the Executor to process :term:`frames<Frame>`. There are two approaches to processing frames using Execution Objects:
 
-Determine if there are any TIDL capable devices on the AM57x SoC:
+  #. Each EO processes a single frame.
+  #. Split processing frame across multiple EOs using an :term:`ExecutionObjectPipeline`.
 
-.. code-block:: c++
+Refer Section :ref:`api-documentation` for API documentation.
+
+Use Cases
++++++++++
+
+.. _use-case-1:
+
+Each EO processes a single frame
+================================
+
+In this approach, the :term:`network<Network>` is set up as a single :term:`Layer Group`. An :term:`EO` runs the entire layer group on a single frame. To increase throughput, frame processing can be pipelined across available EOs. For example, on AM5749, frames can be processed by 4 EOs: one each on EVE1, EVE2, DSP1, and DSP2.
+
+
+.. figure:: images/tidl-one-eo-per-frame.png
+    :align: center
+    :scale: 80
+
+    Processing a frame with one EO. Not to scale. Fn: Frame n, LG: Layer Group.
+
+#. Determine if there are any TIDL capable :term:`compute cores<Compute core>` on the AM57x Processor:
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 64-65
+        :linenos:
+
+#. Create a Configuration object by reading it from a file or by initializing it directly. The example below parses a configuration file and initializes the Configuration object. See ``examples/test/testvecs/config/infer`` for examples of configuration files.
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 92-94
+        :linenos:
+
+#. Create Executor on C66x and EVE. In this example, all available C66x and EVE  cores are used (lines 1-2 and :ref:`CreateExecutor`).
+#. Create a vector of available ExecutionObjects from both Executors (lines 7-8 and :ref:`CollectEOs`).
+#. Allocate input and output buffers for each ExecutionObject (:ref:`AllocateMemory`)
+#. Run the network on each input frame.  The frames are processed with available execution objects in a pipelined manner. The additional num_eos iterations are required to flush the pipeline (lines 15-26).
+
+   * Wait for the EO to finish processing. If the EO is not processing a frame (the first iteration on each EO), the call to ``ProcessFrameWait`` returns false. ``ReportTime`` is used to report host and device execution times.
+   * Read a frame and start running the network. ``ProcessFrameStartAsync`` is asynchronous and returns before processing is complete. ``ReadFrame`` is application specific and used to read an input frame for processing. For example, with OpenCV, ``ReadFrame`` is implemented using OpenCV APIs to capture a frame from the camera.
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 108-127,129,133-139
+        :linenos:
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 154-163
+        :linenos:
+        :caption: CreateExecutor
+        :name: CreateExecutor
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 166-172
+        :linenos:
+        :caption: CollectEOs
+        :name: CollectEOs
 
-    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
-    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
+    .. literalinclude:: ../../examples/common/utils.cpp
+        :language: c++
+        :lines: 197-212
+        :linenos:
+        :caption: AllocateMemory
+        :name: AllocateMemory
+
+The complete example is available at ``/usr/share/ti/tidl/examples/one_eo_per_frame/main.cpp``.
 
 .. note::
-    By default, the OpenCL runtime is configured with sufficient global memory 
-    (via CMEM) to offload TIDL networks to 2 OpenCL devices. On devices where
-    ``Executor::GetNumDevices`` returns 4 (E.g. AM5729 with 4 EVE OpenCL
-    devices) the amount of memory available to the runtime must be increased. 
-    Refer :ref:`opencl-global-memory` for details
+    The double buffering technique described in :ref:`use-case-3` can be used with a single :term:`ExecutionObject` to overlap reading a frame with the processing of the previous frame.
 
-Step 2
-======
-Create a Configuration object by reading it from a file or by initializing it directly. The example below parses a configuration file and initializes the Configuration object. See ``examples/test/testvecs/config/infer`` for examples of configuration files.
+.. _use-case-2:
 
-.. code::
+Frame split across EOs
+======================
+This approach is typically used to reduce the latency of processing a single frame. Certain network layers such as Softmax and Pooling run faster on the C66x vs. EVE. Running these layers on C66x can lower the per-frame latency.
 
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
+Time to process a single frame 224x224x3 frame on AM574x IDK EVM (Arm @ 1GHz, C66x @ 0.75GHz, EVE @ 0.65GHz) with JacintoNet11 (tidl_net_imagenet_jacintonet11v2.bin), TIDL API v1.1:
+
+======      =======     ===================
+EVE         C66x        EVE + C66x
+======      =======     ===================
+~112ms      ~120ms      ~64ms :sup:`1`
+======      =======     ===================
+
+:sup:`1` BatchNorm and Convolution layers run on EVE are placed in a :term:`Layer Group` and run on EVE. Pooling, InnerProduct, SoftMax layers are placed in a second :term:`Layer Group` and run on C66x. The EVE layer group takes ~57.5ms, C66x layer group takes ~6.5ms.
+
+.. _frame-across-eos:
+.. figure:: images/tidl-frame-across-eos.png
+    :align: center
+    :scale: 80
+
+    Processing a frame across EOs. Not to scale. Fn: Frame n, LG: Layer Group.
+
+The network consists of 2 :term:`Layer Groups<Layer Group>`. :term:`Execution Objects<EO>` are organized into :term:`Execution Object Pipelines<EOP>` (EOP). Each :term:`EOP` processes a frame. The API manages inter-EO synchronization.
+
+#. Determine if there are any TIDL capable :term:`compute cores<Compute core>` on the AM57x Processor:
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 64-65
+        :linenos:
+
+#. Create a Configuration object by reading it from a file or by initializing it directly. The example below parses a configuration file and initializes the Configuration object. See ``examples/test/testvecs/config/infer`` for examples of configuration files.
+
+    .. literalinclude:: ../../examples/one_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 92-94
+        :linenos:
+
+#. Update the default layer group index assignment. Pooling (layer 12), InnerProduct (layer 13) and SoftMax (layer 14) are added to a second layer group. Refer :ref:`layer-group-override` for details.
+
+    .. literalinclude:: ../../examples/two_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 101-102
+        :linenos:
+
+#. Create :term:`Executors<Executor>` on C66x and EVE. The EVE Executor runs layer group 1, the C66x executor runs layer group 2.
+
+#. Create two :term:`Execution Object Pipelines<EOP>`.  Each EOP contains one EVE and one C66x :term:`Execution Object<EO>` respectively.
+#. Allocate input and output buffers for each ExecutionObject in the EOP. (:ref:`AllocateMemory2`)
+#. Run the network on each input frame.  The frames are processed with available EOPs in a pipelined manner. For ease of use, EOP and EO present the same interface to the user.
+
+   * Wait for the EOP to finish processing. If the EOP is not processing a frame (the first iteration on each EOP), the call to ``ProcessFrameWait`` returns false. ``ReportTime`` is used to report host and device execution times.
+   * Read a frame and start running the network. ``ProcessFrameStartAsync`` is asynchronous and returns before processing is complete. ``ReadFrame`` is application specific and used to read an input frame for processing. For example, with OpenCV, ``ReadFrame`` is implemented using OpenCV APIs to capture a frame from the camera.
+
+
+    .. literalinclude:: ../../examples/two_eo_per_frame/main.cpp
+        :language: c++
+        :lines: 110-138,140,147-153
+        :linenos:
+
+    .. literalinclude:: ../../examples/common/utils.cpp
+        :language: c++
+        :lines: 225-240
+        :linenos:
+        :caption: AllocateMemory
+        :name: AllocateMemory2
+
+
+The complete example is available at ``/usr/share/ti/tidl/examples/two_eo_per_frame/main.cpp``. Another example of using the EOP is :ref:`ssd-example`.
+
+.. _use-case-3:
+
+Using EOPs for double buffering
+===============================
+
+The timeline shown in :numref:`frame-across-eos` indicates that EO-EVE1 waits for processing on E0-DSP1 to complete before it starts processing its next frame. It is possible to optimize the example further and overlap processing F :sub:`n-2` on EO-DSP1 and F :sub:`n` on E0-EVE1. This is illustrated in :numref:`frame-across-eos-opt`.
+
+.. _frame-across-eos-opt:
+.. figure:: images/tidl-frame-across-eos-opt.png
+    :align: center
+    :scale: 80
+
+    Optimizing using double buffered EOPs. Not to scale. Fn: Frame n, LG: Layer Group.
+
+EOP1 and EOP2 use the same :term:`EOs<EO>`: E0-EVE1 and E0-DSP1. Each :term:`EOP` has it's own input and output buffer. This enables EOP2 to read an input frame when EOP1 is processing its input frame. This in turn enables EOP2 to start processing on EO-EVE1 as soon as EOP1 completes processing on E0-EVE1.
+
+The only change in the code compared to :ref:`use-case-2` is to create an additional set of EOPs for double buffering:
+
+.. literalinclude:: ../../examples/two_eo_per_frame_opt/main.cpp
+    :language: c++
+    :lines: 117-129
+    :linenos:
+    :caption: Setting up EOPs for double buffering
+    :name: test-code
 
 .. note::
-    Refer `Processor SDK Linux Software Developer's Guide`_ for creating TIDL network and parameter binary files from TensorFlow and Caffe.
+    EOP1 in :numref:`frame-across-eos-opt` -> EOPs[0] in :numref:`test-code`.
+    EOP2 in :numref:`frame-across-eos-opt` -> EOPs[1] in :numref:`test-code`.
+    EOP3 in :numref:`frame-across-eos-opt` -> EOPs[2] in :numref:`test-code`.
+    EOP4 in :numref:`frame-across-eos-opt` -> EOPs[3] in :numref:`test-code`.
 
-Step 3
-======
-Create an Executor with the appropriate device type, set of devices and a configuration. In the snippet below, an Executor is created on 2 EVEs.
+The complete example is available at ``/usr/share/ti/tidl/examples/two_eo_per_frame_opt/main.cpp``.
 
-.. code-block:: c++
+Sizing device side heaps
+++++++++++++++++++++++++
+
+TIDL API allocates 2 heaps for device size allocations during network setup/initialization:
 
-        DeviceIds ids = {DeviceId::ID0, DeviceId::ID1};
-        Executor executor(DeviceType::EVE, ids, configuration);
++-----------+-----------------------------------+-----------------------------+
+| Heap Name | Configuration parameter           | Default size                |
++-----------+-----------------------------------+-----------------------------+
+| Parameter | Configuration::PARAM_HEAP_SIZE    | 9MB,  1 per Executor        |
++-----------+-----------------------------------+-----------------------------+
+| Network   | Configuration::NETWORK_HEAP_SIZE  | 64MB, 1 per ExecutionObject |
++-----------+-----------------------------------+-----------------------------+
 
-Step 4
-======
-Get the set of available ExecutionObjects and allocate input and output buffers for each ExecutionObject.
+Depending on the network being deployed, these defaults may be smaller or larger than required. In order to determine the exact sizes for the heaps, the following approach can be used:
+
+Start with the default heap sizes. The API displays heap usage statistics when Configuration::showHeapStats is set to true.
 
 .. code-block:: c++
 
-        const ExecutionObjects& execution_objects = executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
+    Configuration configuration;
+    bool status = configuration.ReadFromFile(config_file);
+    configuration.showHeapStats = true;
+
+If the heap size is larger than required by device side allocations, the API displays usage statistics. When ``Free`` > 0, the heaps are larger than required.
+
+.. code-block:: bash
 
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
-        {
-            ArgInfo in  = { ArgInfo(malloc(frame_sz), frame_sz)};
-            ArgInfo out = { ArgInfo(malloc(frame_sz), frame_sz)};
-            eo->SetInputOutputBuffer(in, out);
+    # ./test_tidl -n 1 -t e -c testvecs/config/infer/tidl_config_j11_v2.txt
+    API Version: 01.01.00.00.e4e45c8
+    [eve 0]         TIDL Device Trace: PARAM heap: Size 9437184, Free 6556180, Total requested 2881004
+    [eve 0]         TIDL Device Trace: NETWORK heap: Size 67108864, Free 47047680, Total requested 20061184
 
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-        }
 
-Step 5
-======
-Run the network on each input frame.  The frames are processed with available execution objects in a pipelined manner with additional num_eos iterations to flush the pipeline (epilogue).
+Update the application to set the heap sizes to the "Total requested size" displayed:
 
 .. code-block:: c++
 
-        for (int frame_idx = 0; frame_idx < configuration.numFrames + num_eos; frame_idx++)
-        {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
+    configuration.PARAM_HEAP_SIZE   = 2881004;
+    configuration.NETWORK_HEAP_SIZE = 20061184;
 
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
-                WriteFrame(*eo, output_data_file);
+.. code-block:: bash
 
-            // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
-                eo->ProcessFrameStartAsync();
-        }
+    # ./test_tidl -n 1 -t e -c testvecs/config/infer/tidl_config_j11_v2.txt
+    API Version: 01.01.00.00.e4e45c8
+    [eve 0]         TIDL Device Trace: PARAM heap: Size 2881004, Free 0, Total requested 2881004
+    [eve 0]         TIDL Device Trace: NETWORK heap: Size 20061184, Free 0, Total requested 20061184
 
-For a complete example of using the API, refer any of the examples available at ``/usr/share/ti/tidl/examples`` on the EVM file system.
+Now, the heaps are sized as required by network execution (i.e. ``Free`` is 0)
+and the ``configuration.showHeapStats = true`` line can be removed.
+
+.. note::
+
+    If the default heap sizes are smaller than required, the device will report an allocation failure and indicate the required minimum size. E.g.
+.. code-block:: bash
+
+    # ./test_tidl -n 1 -t e -c testvecs/config/infer/tidl_config_j11_v2.txt
+    API Version: 01.01.00.00.0ba86d4
+    [eve 0]         TIDL Device Error:  Allocation failure with NETWORK heap, request size 161472, avail 102512
+    [eve 0]         TIDL Device Error: Network heap must be >= 20061184 bytes, 19960944 not sufficient. Update Configuration::NETWORK_HEAP_SIZE
+    TIDL Error: [src/execution_object.cpp, Wait, 548]: Allocation failed on device
+
+.. note::
+
+    The memory for parameter and network heaps is itself allocated from OpenCL global memory (CMEM). Refer :ref:`opencl-global-memory` for details.
+
+
+Accessing outputs of network layers
++++++++++++++++++++++++++++++++++++
+
+TIDL API v1.1 and higher provides the following APIs to access the output buffers associated with network layers:
+
+* :cpp:`ExecutionObject::WriteLayerOutputsToFile` - write outputs from each layer into individual files. Files are named ``<filename_prefix>_<layer_index>.bin``.
+* :cpp:`ExecutionObject::GetOutputsFromAllLayers` - Get output buffers from all layers.
+* :cpp:`ExecutionObject::GetOutputFromLayer` - Get a single output buffer from a layer.
+
+See ``examples/layer_output/main.cpp, ProcessTrace()`` for examples of using these tracing APIs.
+
+.. note::
+    The :cpp:`ExecutionObject::GetOutputsFromAllLayers` method can be memory intensive if the network has a large number of layers. This method allocates sufficient host memory to hold all output buffers from all layers.
 
 .. _Processor SDK Linux Software Developer's Guide: http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/index.html
+.. _Processor SDK Linux Software Developer's Guide (TIDL chapter): http://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components_TIDL.html
index 69c708600066f1dbd22ac98d8b7a7552ddba0dda..52b3ff6508b233093e8ff3b83a20f9b473351146 100644 (file)
@@ -28,6 +28,9 @@
 MFS  = $(wildcard */Makefile)
 DIRS = $(patsubst %/Makefile,%,$(MFS))
 
+# classification cannot be run from command line without attached display
+RUN_DIRS := $(filter-out classification, $(DIRS))
+
 define make_in_dirs
        @for dir in $(1); do \
          echo "=============== " $$dir " =================" ; \
@@ -39,6 +42,10 @@ endef
 all:
        $(call make_in_dirs, $(DIRS), )
 
+.PHONY: run
+run:
+       $(call make_in_dirs, $(RUN_DIRS), run)
+
 .PHONY: clean
 clean:
        $(call make_in_dirs, $(DIRS), clean)
index 507ee00d937340656d05f39e2a1db15911c71985..441b8424167afe247dd5ef08a5d28de4b7e07a3b 100644 (file)
@@ -31,7 +31,7 @@ include ../make.common
 LIBS     += -lopencv_highgui -lopencv_imgcodecs -lopencv_videoio\
                        -lopencv_imgproc -lopencv_core
 
-SOURCES = main.cpp multiple_executors.cpp findclasses.cpp
+SOURCES = main.cpp findclasses.cpp
 
 $(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
        $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
diff --git a/examples/classification/clips/test10.mp4 b/examples/classification/clips/test10.mp4
new file mode 100644 (file)
index 0000000..e89f811
Binary files /dev/null and b/examples/classification/clips/test10.mp4 differ
diff --git a/examples/classification/clips/test1a.mp4 b/examples/classification/clips/test1a.mp4
deleted file mode 100644 (file)
index cb47958..0000000
Binary files a/examples/classification/clips/test1a.mp4 and /dev/null differ
index 6952fab389a27ca41a0a0d81bb6bf0f1e11ee547..82d6bfe219974eee4ea5cf117561bd4b26e55c1b 100644 (file)
Binary files a/examples/classification/clips/test2.mp4 and b/examples/classification/clips/test2.mp4 differ
index 836c5b4f0ed38e5593d46a19198334727ec266ec..a25f4c56a1c2bd13bdb863b16f5fe11b8473b906 100644 (file)
Binary files a/examples/classification/images/baseball.jpg and b/examples/classification/images/baseball.jpg differ
index 370d93c0e887dc004950522c39bc3cdbfc4b0017..cb451f3da042b2fe73dcd4436f8c05272d04d4ef 100644 (file)
Binary files a/examples/classification/images/coffe.jpg and b/examples/classification/images/coffe.jpg differ
index 5d1b2dce78734ea57ae1cdd53230ed50468d1cbd..d59242f55afdd8fc1b39e9490472e200b17b4b3b 100644 (file)
Binary files a/examples/classification/images/coffe_pot.jpg and b/examples/classification/images/coffe_pot.jpg differ
diff --git a/examples/classification/images/plane.jpg b/examples/classification/images/plane.jpg
new file mode 100644 (file)
index 0000000..f9d457a
Binary files /dev/null and b/examples/classification/images/plane.jpg differ
similarity index 52%
rename from examples/classification/images/img2clip.sh
rename to examples/classification/images/scripts/img2clip.sh
index f79f2d4292e258aee558e39b6208eb0523e0cd5f..8e579be5276c02fa5680f3a01f2677f69972cfa9 100644 (file)
@@ -1,2 +1,5 @@
+# Use this script on Linux x86
+# Make sure that ImageMagick and ffmpeg are installed first
+
 convert ../*.jpg -delay 500 -morph 300 -scale 320x320 %05d.jpg
-ffmpeg -i %05d.jpg -vcodec libx264 -profile:v main -pix_fmt yuv420p  -r 15 test.mp4
+ffmpeg -i %05d.jpg -vcodec libx264 -profile:v main -pix_fmt yuv420p  -r 15 ../test.mp4
index 9cdd085a019d90c55805579f54c53e3be34a17fc..550a5ff9ab0f55c5450a966407dfc35c500d032a 100644 (file)
Binary files a/examples/classification/images/tennis_ball.jpg and b/examples/classification/images/tennis_ball.jpg differ
index e38376a814908db9dc1c9d5d312c5f5e8c6d888d..21c05a74f4ac39d0a81703edc179b580a728433c 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "executor.h"
 #include "execution_object.h"
+#include "execution_object_pipeline.h"
 #include "configuration.h"
 
 #include "opencv2/core.hpp"
@@ -50,8 +51,7 @@
 
 //#define TWO_ROIs
 #define LIVE_DISPLAY
-//#define PERF_VERBOSE
-
+#define PERF_VERBOSE
 //#define RMT_GST_STREAMER
 
 #define MAX_NUM_ROI 4
@@ -60,34 +60,38 @@ int live_input = 1;
 char video_clip[320];
 
 #ifdef TWO_ROIs
-#define RES_X 400                                                              
-#define RES_Y 300                                                            
-#define NUM_ROI_X 2                                                     
-#define NUM_ROI_Y 1                                                      
-#define X_OFFSET 0                                                           
-#define X_STEP   176                                                        
-#define Y_OFFSET 52                                                         
+#define RES_X 400
+#define RES_Y 300
+#define NUM_ROI_X 2
+#define NUM_ROI_Y 1
+#define X_OFFSET 0
+#define X_STEP   176
+#define Y_OFFSET 52
 #define Y_STEP   224
 #else
-#define RES_X 244
-#define RES_Y 244                                                            
-#define NUM_ROI_X 1                                                     
-#define NUM_ROI_Y 1                                                      
-#define X_OFFSET 10                                                         
-#define X_STEP   224                                                     
-#define Y_OFFSET 10                                                    
-#define Y_STEP   224
+#define RES_X 480
+#define RES_Y 480
+#define NUM_ROI_X 1
+#define NUM_ROI_Y 1
+#define X_OFFSET 10
+#define X_STEP   460
+#define Y_OFFSET 10
+#define Y_STEP   460
 #endif
 
-int NUM_ROI = NUM_ROI_X * NUM_ROI_Y;
+#define NUM_ROI (NUM_ROI_X * NUM_ROI_Y)
 
 //Temporal averaging
-int TOP_CANDIDATES = 2;
+int TOP_CANDIDATES = 3;
 
 using namespace tidl;
 using namespace cv;
 
 #ifdef LIVE_DISPLAY
+char imagenet_win[160];
+char tmp_classwindow_string[160];
+Mat  classlist_image;
+
 void imagenetCallBackFunc(int event, int x, int y, int flags, void* userdata)
 {
     if  ( event == EVENT_RBUTTONDOWN )
@@ -98,33 +102,40 @@ void imagenetCallBackFunc(int event, int x, int y, int flags, void* userdata)
 }
 #endif
 
+Mat in_image, image, r_image, cnn_image, show_image, bgr_frames[3];
+Mat to_stream;
+Rect rectCrop[NUM_ROI];
+double avg_fps;
+
 static int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id);
 static void tf_preprocess(uchar *out, uchar *in, int size);
 static int ShowRegion(int roi_history[]);
-static int selclass_history[MAX_NUM_ROI][3];  // from most recent to oldest at top indices
+// from most recent to oldest at top indices
+static int selclass_history[MAX_NUM_ROI][3];
 
 bool __TI_show_debug_ = false;
 
-bool RunMultipleExecutors(const std::string& config_file_1,
-                          const std::string& config_file_2,
-                          uint32_t num_devices_available);
-
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type);
-bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
-
-bool ReadFrame(ExecutionObject&     eo,
-               int                  frame_idx,
-               const Configuration& configuration,
-               std::istream&        input_file);
-
-bool WriteFrame(const ExecutionObject &eo,
-                std::ostream& output_file);
-
+bool RunConfiguration(const std::string& config_file, int num_layers_groups,
+                      uint32_t num_dsps, uint32_t num_eves);
+bool CreateExecutionObjectPipelines(uint32_t num_eves, uint32_t num_dsps,
+                                    Configuration& configuration, 
+                                    uint32_t num_layers_groups,
+                                    Executor*& e_eve, Executor*& e_dsp,
+                                  std::vector<ExecutionObjectPipeline*>& eops);
+void AllocateMemory(const std::vector<ExecutionObjectPipeline*>& eops);
+void SetupLiveDisplay(uint32_t num_eves, uint32_t num_dsps);
+bool SetupInput(VideoCapture& cap, VideoWriter& writer);
+bool ReadFrame(ExecutionObjectPipeline* eop,
+               uint32_t frame_idx, uint32_t num_frames,
+               VideoCapture &cap, VideoWriter& writer);
+void DisplayFrame(const ExecutionObjectPipeline* eop, VideoWriter& writer,
+                  uint32_t frame_idx, uint32_t num_eops,
+                  uint32_t num_eves, uint32_t num_dsps);
 static void ProcessArgs(int argc, char *argv[],
                         std::string& config_file,
-                        int& num_devices,
-                        DeviceType& device_type);
+                        uint32_t & num_dsps, uint32_t &num_eves,
+                        int & num_layers_groups);
+void ReportTime(const ExecutionObjectPipeline* eop);
 
 static void DisplayHelp();
 extern std::string labels_classes[];
@@ -134,9 +145,6 @@ extern int selected_items[];
 extern int populate_selected_items (char *filename);
 extern void populate_labels (char *filename);
 
-static double ms_diff(struct timespec &t0, struct timespec &t1)
-{ return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
-
 
 int main(int argc, char *argv[])
 {
@@ -145,11 +153,11 @@ int main(int argc, char *argv[])
     signal(SIGTERM, exit);
 
     // If there are no devices capable of offloading TIDL on the SoC, exit
-    uint32_t num_dla =
-                Executor::GetNumDevices(DeviceType::EVE);
-    uint32_t num_dsp =
-                Executor::GetNumDevices(DeviceType::DSP);
-    if (num_dla == 0 && num_dsp == 0)
+    uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
+    int num_layers_groups = 1;
+
+    if (num_eves == 0 && num_dsps == 0)
     {
         std::cout << "TI DL not supported on this SoC." << std::endl;
         return EXIT_SUCCESS;
@@ -157,17 +165,12 @@ int main(int argc, char *argv[])
 
     // Process arguments
     std::string config_file;
-    int         num_devices = 1;
-    DeviceType  device_type = DeviceType::EVE;
-    ProcessArgs(argc, argv, config_file, num_devices, device_type);
+    ProcessArgs(argc, argv, config_file, num_dsps, num_eves, num_layers_groups);
 
-    bool status = true;
+    bool status = false;
     if (!config_file.empty()) {
         std::cout << "Run single configuration: " << config_file << std::endl;
-        status = RunConfiguration(config_file, num_devices, device_type);
-    } else
-    {
-        status = false;
+        status = RunConfiguration(config_file, num_layers_groups, num_dsps, num_eves);
     }
 
     if (!status)
@@ -180,13 +183,8 @@ int main(int argc, char *argv[])
     return EXIT_SUCCESS;
 }
 
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type)
+bool RunConfiguration(const std::string& config_file, int num_layers_groups, uint32_t num_dsps, uint32_t num_eves)
 {
-    DeviceIds ids;
-    char imagenet_win[160];
-    for (int i = 0; i < num_devices; i++)
-        ids.insert(static_cast<DeviceId>(i));
 
     // Read the TI DL configuration file
     Configuration configuration;
@@ -203,39 +201,165 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
     assert (input_data_file.good());
     assert (output_data_file.good());
 
-    sprintf(imagenet_win, "Imagenet_%sx%d", (device_type == DeviceType::EVE) ? "EVE" : "DSP", num_devices);
-
-    // Determine input frame size from configuration
-    size_t frame_sz_in = configuration.inWidth * configuration.inHeight *
-                         configuration.inNumChannels * (configuration.inNumChannels == 1 ? 1 : 1);
-    size_t frame_sz_out = configuration.inWidth * configuration.inHeight * 3;
 
     try
     {
-        // Create a executor with the approriate core type, number of cores
-        // and configuration specified
-        Executor executor(device_type, ids, configuration);
+        // Create ExecutionObjectPipelines
+        Executor *e_eve = NULL;
+        Executor *e_dsp = NULL;
+        std::vector<ExecutionObjectPipeline *> eops;
+        if (! CreateExecutionObjectPipelines(num_eves, num_dsps, configuration,
+                                        num_layers_groups, e_eve, e_dsp, eops))
+            return false;
+        uint32_t num_eops = eops.size();
+
+        // Allocate input/output memory for each EOP
+        AllocateMemory(eops);
+
+        // Setup Live Display
+        SetupLiveDisplay(num_eves, num_dsps);
+
+        // Setup Input
+        VideoCapture cap;
+        VideoWriter writer;  // gstreamer
+        if (! SetupInput(cap, writer))  return false;
+
+
+        // More initialization
+        for (int k = 0; k < NUM_ROI; k++)
+            for(int i = 0; i < 3; i ++)
+                selclass_history[k][i] = -1;
+        avg_fps = 0.0;
+        int num_frames = configuration.numFrames;
+        std::cout << "About to start ProcessFrame loop!!" << std::endl;
+        // Process frames with available EOPs in a pipelined manner
+        // additional num_eops iterations to flush the pipeline (epilogue)
+        for (uint32_t frame_idx = 0;
+             frame_idx < configuration.numFrames + num_eops; frame_idx++)
+        {
+            ExecutionObjectPipeline* eop = eops[frame_idx % num_eops];
 
+            // Wait for previous frame on the same eo to finish processing
+            if (eop->ProcessFrameWait())
+            {
+                 #ifdef PERF_VERBOSE
+                 ReportTime(eop);
+                 #endif
+                 DisplayFrame(eop, writer, frame_idx, num_eops,
+                              num_eves, num_dsps);
+            }
 
-        // Query Executor for set of ExecutionObjects created
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
+            if (ReadFrame(eop, frame_idx, num_frames, cap, writer))
+                eop->ProcessFrameStartAsync();
+        }
 
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
+        // Cleanup
+        for (auto eop : eops)
         {
-            ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz_in),  frame_sz_in)};
-            ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz_out), frame_sz_out)};
-            eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
+            free(eop->GetInputBufferPtr());
+            free(eop->GetOutputBufferPtr());
+            delete eop;
         }
+        if(num_dsps) delete e_dsp;
+        if(num_eves) delete e_eve;
+    }
+    catch (tidl::Exception &e)
+    {
+        std::cerr << e.what() << std::endl;
+        status = false;
+    }
 
+
+    input_data_file.close();
+    output_data_file.close();
+
+    return status;
+}
+
+
+bool CreateExecutionObjectPipelines(uint32_t num_eves, uint32_t num_dsps,
+                                    Configuration& configuration, 
+                                    uint32_t num_layers_groups,
+                                    Executor*& e_eve, Executor*& e_dsp,
+                                    std::vector<ExecutionObjectPipeline*>& eops)
+{
+    DeviceIds ids_eve, ids_dsp;
+    for (uint32_t i = 0; i < num_eves; i++)
+        ids_eve.insert(static_cast<DeviceId>(i));
+    for (uint32_t i = 0; i < num_dsps; i++)
+        ids_dsp.insert(static_cast<DeviceId>(i));
+
+    switch(num_layers_groups)
+    {
+    case 1: // Single layers group
+        e_eve = num_eves == 0 ? nullptr :
+                new Executor(DeviceType::EVE, ids_eve, configuration);
+        e_dsp = num_dsps == 0 ? nullptr :
+                new Executor(DeviceType::DSP, ids_dsp, configuration);
+
+        // Construct ExecutionObjectPipeline with single Execution Object to
+        // process each frame. This is parallel processing of frames with
+        // as many DSP and EVE cores that we have on hand.
+        for (uint32_t i = 0; i < num_eves; i++)
+            eops.push_back(new ExecutionObjectPipeline({(*e_eve)[i]}));
+        for (uint32_t i = 0; i < num_dsps; i++)
+            eops.push_back(new ExecutionObjectPipeline({(*e_dsp)[i]}));
+        break;
+
+    case 2: // Two layers group
+        // JacintoNet11 specific : specify only layers that will be in
+        // layers group 2 ... by default all other layers are in group 1.
+        configuration.layerIndex2LayerGroupId = { {12, 2}, {13, 2}, {14, 2} };
+
+        // Create Executors with the approriate core type, number of cores
+        // and configuration specified
+        // EVE will run layersGroupId 1 in the network, while
+        // DSP will run layersGroupId 2 in the network
+        e_eve = num_eves == 0 ? nullptr :
+                new Executor(DeviceType::EVE, ids_eve, configuration, 1);
+        e_dsp = num_dsps == 0 ? nullptr :
+                new Executor(DeviceType::DSP, ids_dsp, configuration, 2);
+
+        // Construct ExecutionObjectPipeline that utilizes multiple
+        // ExecutionObjects to process a single frame, each ExecutionObject
+        // processes one layerGroup of the network
+        for (uint32_t i = 0; i < std::max(num_eves, num_dsps); i++)
+            eops.push_back(new ExecutionObjectPipeline({(*e_eve)[i%num_eves],
+                                                        (*e_dsp)[i%num_dsps]}));
+        break;
+
+    default:
+        std::cout << "Layers groups can be either 1 or 2!" << std::endl;
+        return false;
+        break;
+    }
+
+    return true;
+}
+
+void AllocateMemory(const std::vector<ExecutionObjectPipeline*>& eops)
+{
+    for (auto eop : eops)
+    {
+       size_t in_size  = eop->GetInputBufferSizeInBytes();
+       size_t out_size = eop->GetOutputBufferSizeInBytes();
+       void*  in_ptr   = malloc(in_size);
+       void*  out_ptr  = malloc(out_size);
+       assert(in_ptr != nullptr && out_ptr != nullptr);
+
+       ArgInfo in(in_ptr,   in_size);
+       ArgInfo out(out_ptr, out_size);
+       eop->SetInputOutputBuffer(in, out);
+    }
+}
+
+void SetupLiveDisplay(uint32_t num_eves, uint32_t num_dsps)
+{
 #ifdef LIVE_DISPLAY
-    if(NUM_ROI > 1) 
+    sprintf(imagenet_win, "Imagenet_EVEx%d_DSPx%d", num_eves, num_dsps);
+
+    if(NUM_ROI > 1)
     {
       for(int i = 0; i < NUM_ROI; i ++) {
         char tmp_string[80];
@@ -243,13 +367,18 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
         namedWindow(tmp_string, WINDOW_AUTOSIZE | CV_GUI_NORMAL);
       }
     }
-    Mat sw_stack_image = imread("/usr/share/ti/tidl/examples/classification/tidl-sw-stack-small.png", IMREAD_COLOR); // Read the file
+    Mat sw_stack_image = imread(
+          "/usr/share/ti/tidl/examples/classification/tidl-sw-stack-small.png",
+          IMREAD_COLOR); // Read the file
     if( sw_stack_image.empty() )                      // Check for invalid input
     {
-      std::cout <<  "Could not open or find the tidl-sw-stack-small image" << std::endl ;
+      std::cout <<  "Could not open or find the tidl-sw-stack-small image"
+                << std::endl ;
     } else {
-      namedWindow( "TIDL SW Stack", WINDOW_AUTOSIZE | CV_GUI_NORMAL ); // Create a window for display.
-      cv::imshow( "TIDL SW Stack", sw_stack_image );                // Show our image inside it.
+      // Create a window for display.
+      namedWindow( "TIDL SW Stack", WINDOW_AUTOSIZE | CV_GUI_NORMAL );
+      // Show our image inside it.
+      cv::imshow( "TIDL SW Stack", sw_stack_image );
     }
 
     namedWindow("ClassList", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
@@ -257,14 +386,15 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
     //set the callback function for any mouse event
     setMouseCallback(imagenet_win, imagenetCallBackFunc, NULL);
 
-    Mat classlist_image = cv::Mat::zeros(40 + selected_items_size * 20, 220, CV_8UC3);
-    char tmp_classwindow_string[160];
+    classlist_image = cv::Mat::zeros(40 + selected_items_size * 20, 220,
+                                     CV_8UC3);
     //Erase window
     classlist_image.setTo(Scalar::all(0));
 
     for (int i = 0; i < selected_items_size; i ++)
     {
-      sprintf(tmp_classwindow_string, "%2d) %12s", 1+i, labels_classes[selected_items[i]].c_str());
+      sprintf(tmp_classwindow_string, "%2d) %12s", 1+i,
+              labels_classes[selected_items[i]].c_str());
       cv::putText(classlist_image, tmp_classwindow_string,
                   cv::Point(5, 40 + i * 20),
                   cv::FONT_HERSHEY_COMPLEX_SMALL,
@@ -272,31 +402,31 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
                   cv::Scalar(255,255,255), 1, 8);
     }
     cv::imshow("ClassList", classlist_image);
-
 #endif
-    Mat r_frame, r_mframe, r_blend;
-    Mat to_stream;
-    VideoCapture cap;
+}
 
+bool SetupInput(VideoCapture& cap, VideoWriter& writer)
+{
    if(live_input >= 0)
    {
       cap.open(live_input);
-      VideoWriter writer;  // gstreamer
 
       const double fps = cap.get(CAP_PROP_FPS);
       const int width  = cap.get(CAP_PROP_FRAME_WIDTH);
       const int height = cap.get(CAP_PROP_FRAME_HEIGHT);
-      std::cout << "Capture camera with " << fps << " fps, " << width << "x" << height << " px" << std::endl;
+      std::cout << "Capture camera with " << fps << " fps, " << width << "x"
+                << height << " px" << std::endl;
 
 #ifdef RMT_GST_STREAMER
       writer.open(" appsrc ! videoconvert ! video/x-raw, format=(string)NV12, width=(int)640, height=(int)480, framerate=(fraction)30/1 ! \
                 ducatih264enc bitrate=2000 ! queue ! h264parse config-interval=1 ! \
-                mpegtsmux ! udpsink host=158.218.102.235 sync=false port=5000",
+                mpegtsmux ! udpsink host=192.168.1.2 sync=false port=5000",
                 0,fps,Size(640,480),true);
 
       if (!writer.isOpened()) {
         cap.release();
-        std::cerr << "Can't create gstreamer writer. Do you have the correct version installed?" << std::endl;
+        std::cerr << "Can't create gstreamer writer. "
+                  << "Do you have the correct version installed?" << std::endl;
         std::cerr << "Print out OpenCV build information" << std::endl;
         std::cout << getBuildInformation() << std::endl;
         return false;
@@ -308,241 +438,199 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
       const double fps = cap.get(CAP_PROP_FPS);
       const int width  = cap.get(CAP_PROP_FRAME_WIDTH);
       const int height = cap.get(CAP_PROP_FRAME_HEIGHT);
-      std::cout << "Clip with " << fps << " fps, " << width << "x" << height << " px" << std::endl;
-
+      std::cout << "Clip with " << fps << " fps, " << width << "x"
+                << height << " px" << std::endl;
    }
-   std::cout << "About to start ProcessFrame loop!!" << std::endl;
 
+   if (!cap.isOpened()) {
+      std::cout << "Video input not opened!" << std::endl;
+      return false;
+   }
 
-    Rect rectCrop[NUM_ROI];
-    for (int y = 0; y < NUM_ROI_Y; y ++) {
+   for (int y = 0; y < NUM_ROI_Y; y ++) {
       for (int x = 0; x < NUM_ROI_X; x ++) {
-         rectCrop[y * NUM_ROI_X + x] = Rect(X_OFFSET + x * X_STEP, Y_OFFSET + y * Y_STEP, 224, 224);
-         std::cout << "Rect[" << X_OFFSET + x * X_STEP << ", " << Y_OFFSET + y * Y_STEP << "]" << std::endl;
+         rectCrop[y * NUM_ROI_X + x] = Rect(X_OFFSET + x * X_STEP,
+                                            Y_OFFSET + y * Y_STEP, X_STEP, Y_STEP);
+         std::cout << "Rect[" << X_OFFSET + x * X_STEP << ", "
+                   << Y_OFFSET + y * Y_STEP << "]" << std::endl;
       }
-    }
-    int num_frames = 99999;
-
-    if (!cap.isOpened()) {
-      std::cout << "Video input not opened!" << std::endl;
-      return false;
-    }
-    Mat in_image, image, r_image, show_image, bgr_frames[3];
-    int is_object;
-    for(int k = 0; k < NUM_ROI; k++) {
-      for(int i = 0; i < 3; i ++) selclass_history[k][i] = -1;
-    }
+   }
 
-        #define MAX_NUM_EOS  4
-        struct timespec t0[MAX_NUM_EOS], t1;
+   return true;
+}
 
-        // Process frames with available execution objects in a pipelined manner
-        // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < configuration.numFrames + num_eos; frame_idx++)
+bool ReadFrame(ExecutionObjectPipeline* eop,
+               uint32_t frame_idx, uint32_t num_frames,
+               VideoCapture &cap, VideoWriter& writer)
+{
+    if (cap.grab() && frame_idx < num_frames)
+    {
+        if (cap.retrieve(in_image))
         {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
-
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
-            {
-                clock_gettime(CLOCK_MONOTONIC, &t1);
-                double elapsed_host =
-                                ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
-                double elapsed_device = eo->GetProcessTimeInMilliSeconds();
-                double overhead = 100 - (elapsed_device/elapsed_host*100);
-#ifdef PERF_VERBOSE
-                std::cout << "frame[" << eo->GetFrameIndex() << "]: "
-                          << "Time on device: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_device << "ms, "
-                          << "host: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_host << "ms ";
-                std::cout << "API overhead: "
-                          << std::setw(6) << std::setprecision(3)
-                          << overhead << " %" << std::endl;
-#endif
-
-             int f_id = eo->GetFrameIndex();
-             int curr_roi = f_id % NUM_ROI;
-             is_object = tf_postprocess((uchar*) eo->GetOutputBufferPtr(), IMAGE_CLASSES_NUM, curr_roi, frame_idx, f_id);
-             selclass_history[curr_roi][2] = selclass_history[curr_roi][1];
-             selclass_history[curr_roi][1] = selclass_history[curr_roi][0];
-             selclass_history[curr_roi][0] = is_object;
-
-             if(is_object >= 0) {
-                  std::cout << "frame[" << eo->GetFrameIndex() << "]: "
-                          << "Time on device: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_device << "ms, "
-                          << "host: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_host << "ms ";
-             }
-
-             for (int r = 0; r < NUM_ROI; r ++) 
-             {
-               int rpt_id =  ShowRegion(selclass_history[r]);
-                if(rpt_id >= 0)
-                {
-                  // overlay the display window, if ball seen during last two times
-                  cv::putText(show_image, labels_classes[rpt_id].c_str(),
-                    cv::Point(rectCrop[r].x + 5,rectCrop[r].y + 20), // Coordinates
-                    cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
-                    1.0, // Scale. 2.0 = 2x bigger
-                    cv::Scalar(0,0,255), // Color
-                    1, // Thickness
-                    8); // Line type
-                  cv::rectangle(show_image, rectCrop[r], Scalar(255,0,0), 3);
-                  std::cout << "ROI(" << r << ")(" << rpt_id << ")=" << labels_classes[rpt_id].c_str() << std::endl;
-
-                  classlist_image.setTo(Scalar::all(0));
-                  for (int k = 0; k < selected_items_size; k ++)
-                  {
-                     sprintf(tmp_classwindow_string, "%2d) %12s", 1+k, labels_classes[selected_items[k]].c_str());
-                     cv::putText(classlist_image, tmp_classwindow_string,
-                                 cv::Point(5, 40 + k * 20),
-                                 cv::FONT_HERSHEY_COMPLEX_SMALL,
-                                 0.75,
-                                 selected_items[k] == rpt_id ? cv::Scalar(0,0,255) : cv::Scalar(255,255,255), 1, 8);
-                  }
-                  sprintf(tmp_classwindow_string, "FPS:%5.2lf", (double)num_devices * 1000.0 / elapsed_host );
-                  cv::putText(classlist_image, tmp_classwindow_string,
-                              cv::Point(5, 20),
-                              cv::FONT_HERSHEY_COMPLEX_SMALL,
-                              0.75,
-                              cv::Scalar(0,255,0), 1, 8);
-                  cv::imshow("ClassList", classlist_image);
-               }
-             }
-#ifdef LIVE_DISPLAY
-             cv::imshow(imagenet_win, show_image);
-#endif
-
-#ifdef RMT_GST_STREAMER
-             cv::resize(show_image, to_stream, cv::Size(640,480));
-             writer << to_stream;
-#endif
-
-#ifdef LIVE_DISPLAY
-             waitKey(2);
-#endif
-
+            if(live_input >= 0)
+            { //Crop central square portion
+              int loc_xmin = (in_image.size().width - in_image.size().height) / 2; //Central position
+              int loc_ymin = 0;
+              int loc_w = in_image.size().height;
+              int loc_h = in_image.size().height;
+
+              cv::resize(in_image(Rect(loc_xmin, loc_ymin, loc_w, loc_h)), image, Size(RES_X, RES_Y));
+            } else {
+              if((in_image.size().width != RES_X) || (in_image.size().height != RES_Y)) 
+              {  
+                cv::resize(in_image, image, Size(RES_X,RES_Y));
+              }
             }
 
-
-        if (cap.grab() && frame_idx < num_frames)
-        {
-            if (cap.retrieve(in_image))
-            {
-                cv::resize(in_image, image, Size(RES_X,RES_Y));
-                r_image = Mat(image, rectCrop[frame_idx % NUM_ROI]);
+            r_image = Mat(image, rectCrop[frame_idx % NUM_ROI]);
 
 #ifdef LIVE_DISPLAY
-                if(NUM_ROI > 1)
-                {
-                   char tmp_string[80];
-                   sprintf(tmp_string, "ROI[%02d]", frame_idx % NUM_ROI);
-                   cv::imshow(tmp_string, r_image);
-                }
+            if(NUM_ROI > 1)
+            {
+               char tmp_string[80];
+               sprintf(tmp_string, "ROI[%02d]", frame_idx % NUM_ROI);
+               cv::imshow(tmp_string, r_image);
+            }
 #endif
                 //Convert from BGR pixel interleaved to BGR plane interleaved!
-                cv::split(r_image, bgr_frames);
-                tf_preprocess((uchar*) eo->GetInputBufferPtr(), bgr_frames[0].ptr(), 224*224);
-                tf_preprocess((uchar*) eo->GetInputBufferPtr()+224*224, bgr_frames[1].ptr(), 224*224);
-                tf_preprocess((uchar*) eo->GetInputBufferPtr()+2*224*224, bgr_frames[2].ptr(), 224*224);
-                eo->SetFrameIndex(frame_idx);
-                clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
-                eo->ProcessFrameStartAsync();
+            cv::resize(r_image, cnn_image, Size(224,224));
+            cv::split(cnn_image, bgr_frames);
+            tf_preprocess((uchar*) eop->GetInputBufferPtr(),
+                          bgr_frames[0].ptr(), 224*224);
+            tf_preprocess((uchar*) eop->GetInputBufferPtr()+224*224,
+                          bgr_frames[1].ptr(), 224*224);
+            tf_preprocess((uchar*) eop->GetInputBufferPtr()+2*224*224,
+                          bgr_frames[2].ptr(), 224*224);
+            eop->SetFrameIndex(frame_idx);
 
 #ifdef RMT_GST_STREAMER
-                cv::resize(Mat(image, Rect(0,32,640,448)), to_stream, Size(640,480));
-                writer << to_stream;
+            cv::resize(Mat(image, Rect(0,32,640,448)), to_stream,
+                       Size(640,480));
+            writer << to_stream;
 #endif
 
 #ifdef LIVE_DISPLAY
                 //waitKey(2);
-                image.copyTo(show_image);
+            image.copyTo(show_image);
 #endif
-            }
-        } else {
-          if(live_input == -1) {
+            return true;
+        }
+    } else {
+        if(live_input == -1) {
             //Rewind!
             cap.release();
-            cap.open(std::string(video_clip)); 
-          }
+            cap.open(std::string(video_clip));
         }
-        }
-
-        for (auto b : buffers)
-            __free_ddr(b);
-
-    }
-    catch (tidl::Exception &e)
-    {
-        std::cerr << e.what() << std::endl;
-        status = false;
     }
 
-
-    input_data_file.close();
-    output_data_file.close();
-
-    return status;
+    return false;
 }
 
-bool ReadFrame(ExecutionObject &eo, int frame_idx,
-               const Configuration& configuration,
-               std::istream& input_file)
+void ReportTime(const ExecutionObjectPipeline* eop)
 {
-    if (frame_idx >= configuration.numFrames)
-        return false;
-
-    char*  frame_buffer = eo.GetInputBufferPtr();
-    assert (frame_buffer != nullptr);
-
-    memset (frame_buffer, 0,  eo.GetInputBufferSizeInBytes());
-    input_file.read(frame_buffer, eo.GetInputBufferSizeInBytes() / (configuration.inNumChannels == 1 ? 2 : 1));
-
-    if (input_file.eof())
-        return false;
-
-    assert (input_file.good());
-
-    // Set the frame index  being processed by the EO. This is used to
-    // sort the frames before they are output
-    eo.SetFrameIndex(frame_idx);
-
-    if (input_file.good())
-        return true;
-
-    return false;
+    uint32_t frame_index    = eop->GetFrameIndex();
+    std::string device_name = eop->GetDeviceName();
+    float elapsed_host      = eop->GetHostProcessTimeInMilliSeconds();
+    float elapsed_device    = eop->GetProcessTimeInMilliSeconds();
+    double overhead         = 100 - (elapsed_device/elapsed_host*100);
+    std::cout << "frame[" << frame_index << "]: "
+              << "Time on " << device_name << ": "
+              << std::setw(6) << std::setprecision(4)
+              << elapsed_device << "ms, "
+              << "host: "
+              << std::setw(6) << std::setprecision(4)
+              << elapsed_host << "ms ";
+    std::cout << "API overhead: "
+              << std::setw(6) << std::setprecision(3)
+              << overhead << " %" << std::endl;
 }
 
-bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
+void DisplayFrame(const ExecutionObjectPipeline* eop, VideoWriter& writer,
+                  uint32_t frame_idx, uint32_t num_eops,
+                  uint32_t num_eves, uint32_t num_dsps)
 {
-    output_file.write(
-            eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
-    assert(output_file.good() == true);
+    int f_id = eop->GetFrameIndex();
+    int curr_roi = f_id % NUM_ROI;
+    int is_object = tf_postprocess((uchar*) eop->GetOutputBufferPtr(),
+                                 IMAGE_CLASSES_NUM, curr_roi, frame_idx, f_id);
+    selclass_history[curr_roi][2] = selclass_history[curr_roi][1];
+    selclass_history[curr_roi][1] = selclass_history[curr_roi][0];
+    selclass_history[curr_roi][0] = is_object;
+    for (int r = 0; r < NUM_ROI; r ++)
+    {
+        int rpt_id =  ShowRegion(selclass_history[r]);
+        if(rpt_id >= 0)
+        {
+            // overlay the display window, if ball seen during last two times
+            cv::putText(show_image, labels_classes[rpt_id].c_str(),
+                cv::Point(rectCrop[r].x + 5,rectCrop[r].y + 20), // Coordinates
+                cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
+                1.0, // Scale. 2.0 = 2x bigger
+                cv::Scalar(0,0,255), // Color
+                1, // Thickness
+                8); // Line type
+            cv::rectangle(show_image, rectCrop[r], Scalar(255,0,0), 3);
+            std::cout << "ROI(" << r << ")(" << rpt_id << ")="
+                      << labels_classes[rpt_id].c_str() << std::endl;
+
+            classlist_image.setTo(Scalar::all(0));
+            for (int k = 0; k < selected_items_size; k ++)
+            {
+               sprintf(tmp_classwindow_string, "%2d) %12s", 1+k,
+                       labels_classes[selected_items[k]].c_str());
+               cv::putText(classlist_image, tmp_classwindow_string,
+                           cv::Point(5, 40 + k * 20),
+                           cv::FONT_HERSHEY_COMPLEX_SMALL,
+                           0.75,
+                           selected_items[k] == rpt_id ? cv::Scalar(0,0,255) :
+                                                cv::Scalar(255,255,255), 1, 8);
+            }
+            double elapsed_host = eop->GetHostProcessTimeInMilliSeconds();
+            /* Exponential averaging */
+            avg_fps = 0.1 * ((double)num_eops * 1000.0 /
+                             ((double)NUM_ROI * elapsed_host)) + 0.9 * avg_fps;
+            sprintf(tmp_classwindow_string, "FPS:%5.2lf", avg_fps );
 
-    if (output_file.good())
-        return true;
+#ifdef PERF_VERBOSE
+            std::cout << "Device:" << eop->GetDeviceName() << " eops("
+                      << num_eops << "), EVES(" << num_eves << ") DSPS("
+                      << num_dsps << ") FPS:" << avg_fps << std::endl;
+#endif
+            cv::putText(classlist_image, tmp_classwindow_string,
+                       cv::Point(5, 20),
+                       cv::FONT_HERSHEY_COMPLEX_SMALL,
+                       0.75,
+                       cv::Scalar(0,255,0), 1, 8);
+            cv::imshow("ClassList", classlist_image);
+        }
+    }
 
-    return false;
+#ifdef LIVE_DISPLAY
+    cv::imshow(imagenet_win, show_image);
+#endif
+
+#ifdef RMT_GST_STREAMER
+    cv::resize(show_image, to_stream, cv::Size(640,480));
+    writer << to_stream;
+#endif
+
+#ifdef LIVE_DISPLAY
+    waitKey(2);
+#endif
 }
 
+// Function to process all command line arguments
 void ProcessArgs(int argc, char *argv[], std::string& config_file,
-                 int& num_devices, DeviceType& device_type)
+                 uint32_t & num_dsps, uint32_t & num_eves, int & num_layers_groups )
 {
     const struct option long_options[] =
     {
         {"labels_classes_file", required_argument, 0, 'l'},
         {"selected_classes_file", required_argument, 0, 's'},
         {"config_file", required_argument, 0, 'c'},
-        {"num_devices", required_argument, 0, 'n'},
-        {"device_type", required_argument, 0, 't'},
+        {"num_dsps", required_argument, 0, 'd'},
+        {"num_eves", required_argument, 0, 'e'},
+        {"num_layers_groups", required_argument, 0, 'g'},
         {"help",        no_argument,       0, 'h'},
         {"verbose",     no_argument,       0, 'v'},
         {0, 0, 0, 0}
@@ -552,7 +640,7 @@ void ProcessArgs(int argc, char *argv[], std::string& config_file,
 
     while (true)
     {
-        int c = getopt_long(argc, argv, "l:c:s:i:n:t:hv", long_options, &option_index);
+        int c = getopt_long(argc, argv, "l:c:s:i:d:e:g:hv", long_options, &option_index);
 
         if (c == -1)
             break;
@@ -577,20 +665,16 @@ void ProcessArgs(int argc, char *argv[], std::string& config_file,
             case 'c': config_file = optarg;
                       break;
 
-            case 'n': num_devices = atoi(optarg);
-                      assert (num_devices > 0 && num_devices <= 4);
+            case 'g': num_layers_groups = atoi(optarg);
+                      assert(num_layers_groups >= 1 && num_layers_groups <= 2);
                       break;
 
-            case 't': if (*optarg == 'e')
-                          device_type = DeviceType::EVE;
-                      else if (*optarg == 'd')
-                          device_type = DeviceType::DSP;
-                      else
-                      {
-                          std::cerr << "Invalid argument to -t, only e or d"
-                                       " allowed" << std::endl;
-                          exit(EXIT_FAILURE);
-                      }
+            case 'd': num_dsps = atoi(optarg);
+                      assert (num_dsps >= 0 && num_dsps <= 2);
+                      break;
+
+            case 'e': num_eves = atoi(optarg);
+                      assert (num_eves >= 0 && num_eves <= 2);
                       break;
 
             case 'v': __TI_show_debug_ = true;
@@ -609,26 +693,29 @@ void ProcessArgs(int argc, char *argv[], std::string& config_file,
                       break;
         }
     }
+
+    // if no eves available, we can only run full net as one layer group
+    if (num_eves == 0)  num_layers_groups = 1;
 }
 
 void DisplayHelp()
 {
-    std::cout << "Usage: tidl\n"
+    std::cout << "Usage: tidl_classification\n"
                  "  Will run all available networks if tidl is invoked without"
                  " any arguments.\n  Use -c to run a single network.\n"
                  "Optional arguments:\n"
                  " -c                   Path to the configuration file\n"
-                 " -n <number of cores> Number of cores to use (1 - 4)\n"
-                 " -t <d|e>             Type of core. d -> DSP, e -> EVE\n"
+                 " -d <number of DSP cores> Number of DSP cores to use (0 - 2)\n"
+                 " -e <number of EVE cores> Number of EVE cores to use (0 - 2)\n"
+                 " -g <1|2>             Number of layer groups\n"
                  " -l                   List of label strings (of all classes in model)\n"
                  " -s                   List of strings with selected classes\n"
                  " -i                   Video input (for camera:0,1 or video clip)\n"
                  " -v                   Verbose output during execution\n"
                  " -h                   Help\n";
-
 }
 
-
+// Function to filter all the reported decisions
 bool tf_expected_id(int id)
 {
    // Filter out unexpected IDs
@@ -641,9 +728,9 @@ bool tf_expected_id(int id)
 
 int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id)
 {
+  //prob_i = exp(TIDL_Lib_output_i) / sum(exp(TIDL_Lib_output))
   // sort and get k largest values and corresponding indices
   const int k = TOP_CANDIDATES;
-  int accum_in = 0;
   int rpt_id = -1;
 
   typedef std::pair<uchar, int> val_index;
@@ -652,7 +739,6 @@ int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id)
   // initialize priority queue with smallest value on top
   for (int i = 0; i < k; i++) {
     queue.push(val_index(in[i], i));
-    accum_in += (int)in[i];
   }
   // for rest input, if larger than current minimum, pop mininum, push new val
   for (int i = k; i < size; i++)
@@ -662,7 +748,6 @@ int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id)
       queue.pop();
       queue.push(val_index(in[i], i));
     }
-    accum_in += (int)in[i];
   }
 
   // output top k values in reverse order: largest val first
@@ -676,16 +761,13 @@ int tf_postprocess(uchar *in, int size, int roi_idx, int frame_idx, int f_id)
   for (int i = k-1; i >= 0; i--)
   {
       int id = sorted[i].second;
-      char res2show[320];
-      bool found = false;
 
       if (tf_expected_id(id))
       {
         std::cout << "Frame:" << frame_idx << "," << f_id << " ROI[" << roi_idx << "]: rank="
-                  << k-i << ", prob=" << (float) sorted[i].first / 255 << ", "
-                  << labels_classes[sorted[i].second] << " accum_in=" << accum_in << std::endl;
+                  << k-i << ", outval=" << (float)sorted[i].first / 255 << ", "
+                  << labels_classes[sorted[i].second] << std::endl;
         rpt_id = id;
-        found  = true;
       }
   }
   return rpt_id;
@@ -701,9 +783,9 @@ void tf_preprocess(uchar *out, uchar *in, int size)
 
 int ShowRegion(int roi_history[])
 {
-  if((roi_history[0] >= 0) && (roi_history[0] == roi_history[1])) return roi_history[0];    
-  if((roi_history[0] >= 0) && (roi_history[0] == roi_history[2])) return roi_history[0];    
-  if((roi_history[1] >= 0) && (roi_history[1] == roi_history[2])) return roi_history[1];    
+  if((roi_history[0] >= 0) && (roi_history[0] == roi_history[1])) return roi_history[0];
+  if((roi_history[0] >= 0) && (roi_history[0] == roi_history[2])) return roi_history[0];
+  if((roi_history[1] >= 0) && (roi_history[1] == roi_history[2])) return roi_history[1];
   return -1;
 }
 
diff --git a/examples/classification/multiple_executors.cpp b/examples/classification/multiple_executors.cpp
deleted file mode 100644 (file)
index 78a1789..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions are met:
- *       * Redistributions of source code must retain the above copyright
- *         notice, this list of conditions and the following disclaimer.
- *       * Redistributions in binary form must reproduce the above copyright
- *         notice, this list of conditions and the following disclaimer in the
- *         documentation and/or other materials provided with the distribution.
- *       * Neither the name of Texas Instruments Incorporated nor the
- *         names of its contributors may be used to endorse or promote products
- *         derived from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- *   THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
-
-//! @file multiple_executors.cpp
-//! Illustrates how to setup multiple Executor instances using
-//! non-overlapping sets of device ids and running the Executor instances
-//! in parallel - each in its own thread
-
-#include <signal.h>
-#include <getopt.h>
-#include <iostream>
-#include <fstream>
-#include <cassert>
-#include <string>
-#include <functional>
-#include <algorithm>
-#include <pthread.h>
-
-#include "executor.h"
-#include "execution_object.h"
-#include "configuration.h"
-
-using namespace tidl;
-
-extern bool ReadFrame(ExecutionObject&     eo,
-               int                  frame_idx,
-               const Configuration& configuration,
-               std::istream&        input_file);
-
-extern bool WriteFrame(const ExecutionObject &eo,
-                std::ostream& output_file);
-
-void* run_network(void *data);
-
-struct ThreadArg
-{
-    std::string config_file;
-    DeviceIds ids;
-    ThreadArg(const DeviceIds& ids, const std::string& s):
-        ids(ids), config_file(s) {}
-
-};
-
-bool thread_status[2];
-
-bool RunMultipleExecutors(const std::string& config_file_1,
-                          const std::string& config_file_2,
-                          uint32_t num_devices_available)
-{
-    // If there is only 1 device available, skip
-    if (num_devices_available == 1)
-        return true;
-
-    DeviceIds ids1, ids2;
-
-    if (num_devices_available == 4)
-    {
-        ids1 = {DeviceId::ID2, DeviceId::ID3};
-        ids2 = {DeviceId::ID0, DeviceId::ID1};
-    }
-    else
-    {
-        ids1 = {DeviceId::ID0};
-        ids2 = {DeviceId::ID1};
-    }
-
-    // Set up devices and config files for each thread
-    ThreadArg arg1(ids2, config_file_1);
-    ThreadArg arg2(ids1, config_file_2);
-
-    // Run network 1 in a thread
-    std::cout << std::endl << "Multiple Executor..." << std::endl;
-    std::cout << "Running network "
-              << arg1.config_file.substr(arg1.config_file.find("tidl"))
-              << " on EVEs: ";
-    for (DeviceId id : arg1.ids)
-        std::cout << static_cast<int>(id) << " ";
-    std::cout << " in thread 0" << std::endl;
-
-    pthread_t network_thread_1;
-    pthread_create(&network_thread_1, 0, &run_network, &arg1);
-
-    // Run network 2 in a thread
-    std::cout << "Running network "
-              << arg2.config_file.substr(arg2.config_file.find("tidl"))
-              << " on EVEs: ";
-    for (DeviceId id : arg2.ids)
-        std::cout << static_cast<int>(id) << " ";
-    std::cout << " in thread 1" << std::endl;
-
-    pthread_t network_thread_2;
-    pthread_create(&network_thread_2, 0, &run_network, &arg2);
-
-    // Wait for both networks to complete
-    void *thread_return_val1;
-    void *thread_return_val2;
-    pthread_join(network_thread_1, &thread_return_val1);
-    pthread_join(network_thread_2, &thread_return_val2);
-
-    if (thread_return_val1 == 0 || thread_return_val2 == 0)
-    {
-        std::cout << "Multiple executors: FAILED" << std::endl;
-        return false;
-    }
-
-    std::cout << "Multiple executors: PASSED" << std::endl;
-    return true;
-}
-
-
-void* run_network(void *data)
-{
-    const ThreadArg* arg = static_cast<const ThreadArg *>(data);
-
-    const DeviceIds& ids = arg->ids;
-    const std::string& config_file = arg->config_file;
-
-    // Read the TI DL configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
-    assert (status != false);
-
-    configuration.outData += std::to_string(pthread_self());
-
-    // Open input and output files
-    std::ifstream input_data_file(configuration.inData, std::ios::binary);
-    std::ofstream output_data_file(configuration.outData, std::ios::binary);
-    assert (input_data_file.good());
-    assert (output_data_file.good());
-
-    // Determine input frame size from configuration
-    size_t frame_sz = configuration.inWidth * configuration.inHeight *
-                      configuration.inNumChannels;
-
-    try
-    {
-        // Create a executor with the approriate core type, number of cores
-        // and configuration specified
-        Executor executor(DeviceType::EVE, ids, configuration);
-
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
-
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
-        {
-            ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
-            ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
-            eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-        }
-
-        // Process frames with available execution objects in a pipelined manner
-        // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < configuration.numFrames + num_eos; frame_idx++)
-        {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
-
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
-                WriteFrame(*eo, output_data_file);
-
-            // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
-                eo->ProcessFrameStartAsync();
-        }
-
-
-        for (auto b : buffers)
-            __free_ddr(b);
-    }
-    catch (tidl::Exception &e)
-    {
-        std::cerr << e.what() << std::endl;
-        status = false;
-    }
-
-    input_data_file.close();
-    output_data_file.close();
-
-    // Return 1 for true, 0 for false. void * pattern follows example from:
-    // "Advanced programming in the Unix Environment"
-    if (!status) return ((void *)0);
-
-    return ((void *)1);
-}
index c1207afe0873d777dbb5bb531ce5d765ae4c2a95..565807a0fcf6e7e1dbd7233a75cbadefe0d39e5c 100644 (file)
@@ -1,4 +1,10 @@
-# Live camera input
-./tidl_classification -n 2 -t e -l imagenet.txt -i 1 -s ./classlist.txt -c ./stream_config_j11_v2.txt
-# Use video clip as input stream
-./tidl_classification -n 2 -t e -l imagenet.txt -i ./clips/test1.mp4 -s ./classlist.txt -c ./stream_config_j11_v2.txt
+#Various use cases:
+#
+# 1. Live camera input, using 2xEVE and 2xDSP cores, based on model with single layers group 
+./tidl_classification -g 1 -d 2 -e 2 -l ./imagenet.txt -s ./classlist.txt -i 1 -c ./stream_config_j11_v2.txt
+# 2. Use video clip as input stream, using 2xEVE and 2xDSP cores, based on model with single layers group 
+./tidl_classification -g 1 -d 2 -e 2 -l ./imagenet.txt -s ./classlist.txt -i ./clips/test50.mp4 -c ./stream_config_j11_v2.txt
+# 3. Use video clip as input stream, using 2xEVE and 1xDSP cores, based on model with two layers group (1st layers group running on EVE, 2nd layers group on DSP)
+./tidl_classification -g 2 -d 1 -e 2 -l ./imagenet.txt -s ./classlist.txt -i ./clips/test50.mp4 -c ./stream_config_j11_v2.txt
+# 4. Use video clip as input stream, using no EVEs and 2xDSP cores, based on model with single layers group
+./tidl_classification -g 1 -d 2 -e 0 -l ./imagenet.txt -s ./classlist.txt -i ./clips/test50.mp4 -c ./stream_config_j11_v2.txt
diff --git a/examples/classification/stream_config_dogs.txt b/examples/classification/stream_config_dogs.txt
deleted file mode 100644 (file)
index a11f130..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-numFrames   = 9000
-inData   = /usr/share/ti/tidl/examples/test/testvecs/input/shar_pei.raw
-outData   = "/usr/share/ti/tidl/examples/classification/stats_tool_out.bin"
-netBinFile      = "/usr/share/ti/tidl/examples/test/testvecs/config/tidl_models/dogs_net_j11v2.bin"
-paramsBinFile   = "/usr/share/ti/tidl/examples//test/testvecs/config/tidl_models/dogs_param_j11v2.bin"
-inWidth = 224
-inHeight = 224
-inNumChannels = 3
index 21db2433e9451c942053de2700b204e009ff6d6a..8c8f0d560228d7cde2e7a036047c4e2611a8b23f 100644 (file)
@@ -1,4 +1,4 @@
-numFrames   = 9000
+numFrames   = 999900
 inData   = /usr/share/ti/tidl/examples/test/testvecs/input/preproc_0_224x224.y
 outData   = "/usr/share/ti/tidl/examples/classification/stats_tool_out.bin"
 netBinFile      = "/usr/share/ti/tidl/examples/test/testvecs/config/tidl_models/tidl_net_imagenet_jacintonet11v2.bin"
index e1e430b19d5336086fb4645a5ed0391ae792f72c..d216d35004f1b0d6ed391c871e95bdc93713bc8d 100644 (file)
Binary files a/examples/classification/tidl-sw-stack-small.png and b/examples/classification/tidl-sw-stack-small.png differ
diff --git a/examples/common/utils.cpp b/examples/common/utils.cpp
new file mode 100644 (file)
index 0000000..43810a9
--- /dev/null
@@ -0,0 +1,251 @@
+/******************************************************************************
+ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Texas Instruments Incorporated nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <boost/format.hpp>
+#include <cstring>
+
+#include "utils.h"
+
+using namespace tidl;
+
+using boost::format;
+using std::string;
+using std::istream;
+using std::ostream;
+using std::vector;
+
+
+// Create an Executor with the specified type and number of EOs
+Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c,
+                         int layer_group_id)
+{
+    if (num == 0) return nullptr;
+
+    DeviceIds ids;
+    for (int i = 0; i < num; i++)
+        ids.insert(static_cast<DeviceId>(i));
+
+    return new Executor(dt, ids, c, layer_group_id);
+}
+static bool read_frame_helper(char* ptr, size_t size, istream& input_file);
+
+bool ReadFrame(ExecutionObject*     eo,
+               int                  frame_idx,
+               const Configuration& configuration,
+               std::istream&        input_file)
+{
+    if (frame_idx >= configuration.numFrames)
+        return false;
+
+    // Note: Frame index is used by the EO for debug messages only
+    eo->SetFrameIndex(frame_idx);
+
+    return read_frame_helper(eo->GetInputBufferPtr(),
+                             eo->GetInputBufferSizeInBytes(),
+                             input_file);
+}
+
+bool ReadFrame(ExecutionObjectPipeline* eop,
+               int                      frame_idx,
+               const Configuration&     configuration,
+               std::istream&            input_file)
+{
+    if (frame_idx >= configuration.numFrames)
+        return false;
+
+    // Note: Frame index is used by the EOP for debug messages only
+    eop->SetFrameIndex(frame_idx);
+
+    return read_frame_helper(eop->GetInputBufferPtr(),
+                             eop->GetInputBufferSizeInBytes(),
+                             input_file);
+}
+
+bool read_frame_helper(char* ptr, size_t size, istream& input_file)
+{
+    assert (ptr != nullptr);
+    assert (input_file.good());
+
+    input_file.read(ptr, size);
+    assert (input_file.good());
+
+    if (input_file.eof())
+        return false;
+
+    // Wrap-around : if EOF is reached, start reading from the beginning.
+    if (input_file.peek() == EOF)
+        input_file.seekg(0, input_file.beg);
+
+    if (input_file.good())
+        return true;
+
+    return false;
+}
+
+
+bool WriteFrame(const ExecutionObject* eo, ostream& output_file)
+{
+    output_file.write(eo->GetOutputBufferPtr(),
+                      eo->GetOutputBufferSizeInBytes());
+    assert(output_file.good() == true);
+
+    if (output_file.good())
+        return true;
+
+    return false;
+}
+
+void ReportTime(const ExecutionObject* eo)
+{
+    double elapsed_host   = eo->GetHostProcessTimeInMilliSeconds();
+    double elapsed_device = eo->GetProcessTimeInMilliSeconds();
+    double overhead = 100 - (elapsed_device/elapsed_host*100);
+
+    std::cout << format("frame[%3d]: Time on %s: %4.2f ms, host: %4.2f ms"
+                        " API overhead: %2.2f %%\n")
+                        % eo->GetFrameIndex() % eo->GetDeviceName()
+                        % elapsed_device % elapsed_host % overhead;
+}
+
+void ReportTime(const ExecutionObjectPipeline* eop)
+{
+    double elapsed_host   = eop->GetHostProcessTimeInMilliSeconds();
+    double elapsed_device = eop->GetProcessTimeInMilliSeconds();
+    double overhead = 100 - (elapsed_device/elapsed_host*100);
+
+    std::cout << format("frame[%3d]: Time on %s: %4.2f ms, host: %4.2f ms"
+                        " API overhead: %2.2f %%\n")
+                        % eop->GetFrameIndex() % eop->GetDeviceName()
+                        % elapsed_device % elapsed_host % overhead;
+}
+
+// Compare output against reference output
+bool CheckFrame(const ExecutionObject *eo, const char *ref_output)
+{
+    if (std::memcmp(static_cast<const void*>(ref_output),
+               static_cast<const void*>(eo->GetOutputBufferPtr()),
+               eo->GetOutputBufferSizeInBytes()) == 0)
+        return true;
+
+    return false;
+}
+
+bool CheckFrame(const ExecutionObjectPipeline *eop, const char *ref_output)
+{
+    if (std::memcmp(static_cast<const void*>(ref_output),
+               static_cast<const void*>(eop->GetOutputBufferPtr()),
+               eop->GetOutputBufferSizeInBytes()) == 0)
+        return true;
+
+    return false;
+}
+
+
+namespace tidl {
+std::size_t GetBinaryFileSize (const std::string &F);
+bool        ReadBinary        (const std::string &F, char* buffer, int size);
+}
+
+// Read file into a buffer.
+const char* ReadReferenceOutput(const string& name)
+{
+    size_t size = GetBinaryFileSize(name);
+
+    if (size == 0)
+        return nullptr;
+
+    char* buffer = new char[size];
+
+    if (!buffer)
+        return nullptr;
+
+    if (!ReadBinary(name, buffer, size))
+    {
+        delete [] buffer;
+        return nullptr;
+    }
+
+    return buffer;
+}
+
+// Allocate input and output memory for each EO
+void AllocateMemory(const vector<ExecutionObject *>& eos)
+{
+    // Allocate input and output buffers for each execution object
+    for (auto eo : eos)
+    {
+        size_t in_size  = eo->GetInputBufferSizeInBytes();
+        size_t out_size = eo->GetOutputBufferSizeInBytes();
+        void*  in_ptr   = malloc(in_size);
+        void*  out_ptr  = malloc(out_size);
+        assert(in_ptr != nullptr && out_ptr != nullptr);
+
+        ArgInfo in  = { ArgInfo(in_ptr,  in_size)};
+        ArgInfo out = { ArgInfo(out_ptr, out_size)};
+        eo->SetInputOutputBuffer(in, out);
+    }
+}
+
+// Free the input and output memory associated with each EO
+void FreeMemory(const vector<ExecutionObject *>& eos)
+{
+    for (auto eo : eos)
+    {
+        free(eo->GetInputBufferPtr());
+        free(eo->GetOutputBufferPtr());
+    }
+}
+
+// Allocate input and output memory for each EOP
+void AllocateMemory(const vector<ExecutionObjectPipeline *>& eops)
+{
+    // Allocate input and output buffers for each execution object
+    for (auto eop : eops)
+    {
+        size_t in_size  = eop->GetInputBufferSizeInBytes();
+        size_t out_size = eop->GetOutputBufferSizeInBytes();
+        void*  in_ptr   = malloc(in_size);
+        void*  out_ptr  = malloc(out_size);
+        assert(in_ptr != nullptr && out_ptr != nullptr);
+
+        ArgInfo in  = { ArgInfo(in_ptr,  in_size)};
+        ArgInfo out = { ArgInfo(out_ptr, out_size)};
+        eop->SetInputOutputBuffer(in, out);
+    }
+}
+
+// Free the input and output memory associated with each EOP
+void FreeMemory(const vector<ExecutionObjectPipeline *>& eops)
+{
+    for (auto eop : eops)
+    {
+        free(eop->GetInputBufferPtr());
+        free(eop->GetOutputBufferPtr());
+    }
+}
+
diff --git a/examples/common/utils.h b/examples/common/utils.h
new file mode 100644 (file)
index 0000000..57c570d
--- /dev/null
@@ -0,0 +1,71 @@
+/******************************************************************************
+ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Texas Instruments Incorporated nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#pragma once
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include "executor.h"
+#include "execution_object.h"
+#include "execution_object_pipeline.h"
+#include "configuration.h"
+
+using tidl::Executor;
+using tidl::ExecutionObject;
+using tidl::ExecutionObjectPipeline;
+using tidl::Configuration;
+using tidl::DeviceType;
+
+Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c,
+                         int layer_group_id);
+
+bool ReadFrame(ExecutionObject*     eo,
+               int                  frame_idx,
+               const Configuration& configuration,
+               std::istream&        input_file);
+
+bool ReadFrame(ExecutionObjectPipeline* eop,
+               int                      frame_idx,
+               const Configuration&     configuration,
+               std::istream&            input_file);
+
+bool WriteFrame(const ExecutionObject* eo, std::ostream& output_file);
+
+void ReportTime(const ExecutionObject* eo);
+void ReportTime(const ExecutionObjectPipeline* eop);
+
+bool CheckFrame(const ExecutionObject* eo, const char *ref_output);
+bool CheckFrame(const ExecutionObjectPipeline *eop, const char *ref_output);
+
+const char* ReadReferenceOutput(const std::string& name);
+
+void AllocateMemory(const std::vector<ExecutionObject *>& eos);
+void FreeMemory(const std::vector<ExecutionObject *>& eos);
+void AllocateMemory(const std::vector<ExecutionObjectPipeline *>& eops);
+void FreeMemory(const std::vector<ExecutionObjectPipeline *>& eops);
diff --git a/examples/common/video_utils.cpp b/examples/common/video_utils.cpp
new file mode 100644 (file)
index 0000000..bfb7642
--- /dev/null
@@ -0,0 +1,149 @@
+/******************************************************************************
+ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Texas Instruments Incorporated nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <getopt.h>
+#include <cassert>
+#include "video_utils.h"
+
+using namespace std;
+using namespace tidl;
+
+
+bool ProcessArgs(int argc, char *argv[], cmdline_opts_t& opts)
+{
+    opts.num_frames      = 0;
+    opts.output_width    = 0;
+    opts.verbose         = false;
+    opts.is_camera_input = false;
+    opts.is_video_input  = false;
+
+    const struct option long_options[] =
+    {
+        {"config",       required_argument, 0, 'c'},
+        {"num_dsps",     required_argument, 0, 'd'},
+        {"num_eves",     required_argument, 0, 'e'},
+        {"num_frames",   required_argument, 0, 'f'},
+        {"input_file",   required_argument, 0, 'i'},
+        {"output_width", required_argument, 0, 'w'},
+        {"help",         no_argument,       0, 'h'},
+        {"verbose",      no_argument,       0, 'v'},
+        {0, 0, 0, 0}
+    };
+
+    int option_index = 0;
+
+    while (true)
+    {
+        int c = getopt_long(argc, argv, "c:d:e:f:i:w:hv", long_options,
+                            &option_index);
+
+        if (c == -1)
+            break;
+
+        switch (c)
+        {
+            case 'c': opts.config = optarg;
+                      break;
+
+            case 'd': opts.num_dsps = atoi(optarg);
+                      assert(opts.num_dsps >= 0 && opts.num_dsps <=
+                                     Executor::GetNumDevices(DeviceType::DSP));
+                      break;
+
+            case 'e': opts.num_eves = atoi(optarg);
+                      assert(opts.num_eves >= 0 && opts.num_eves <=
+                                     Executor::GetNumDevices(DeviceType::EVE));
+                      break;
+
+            case 'f': opts.num_frames = atoi(optarg);
+                      assert (opts.num_frames > 0);
+                      break;
+
+            case 'i': opts.input_file = optarg;
+                      break;
+
+            case 'w': opts.output_width = atoi(optarg);
+                      assert (opts.output_width > 0);
+                      break;
+
+            case 'v': opts.verbose = true;
+                      break;
+
+            case 'h': return false;
+                      break;
+
+            case '?': // Error in getopt_long
+                      exit(EXIT_FAILURE);
+                      break;
+
+            default:
+                      cerr << "Unsupported option: " << c << endl;
+                      return false;
+                      break;
+        }
+    }
+
+    opts.is_camera_input = (opts.input_file.size() > 5 &&
+                            opts.input_file.substr(0, 6) == "camera");
+    if (opts.input_file.size() > 4)
+    {
+        string suffix = opts.input_file.substr(opts.input_file.size() - 4, 4);
+        opts.is_video_input = (suffix == ".mp4") || (suffix == ".avi") ||
+                              (suffix == ".mov");
+    }
+
+    return true;
+}
+
+// Set Video Input and Output
+bool SetVideoInputOutput(VideoCapture &cap, const cmdline_opts_t& opts,
+                         const char* window_name)
+{
+    if (opts.is_camera_input || opts.is_video_input)
+    {
+        if (opts.is_camera_input)
+        {
+            int port_num = 1;  // if TMDSCM572X camera module on AM57x EVM
+            if (opts.input_file.size() > 6)  // "camera#"
+                port_num = stoi(opts.input_file.substr(6,
+                                                  opts.input_file.size() - 6));
+            cap = VideoCapture(port_num);
+        }
+        else
+            cap = VideoCapture(opts.input_file);
+        if (! cap.isOpened())
+        {
+            cerr << "Cannot open video input: " << opts.input_file << endl;
+            return false;
+        }
+        namedWindow(window_name, WINDOW_AUTOSIZE | CV_GUI_NORMAL);
+    }
+
+    return true;
+}
+
diff --git a/examples/common/video_utils.h b/examples/common/video_utils.h
new file mode 100644 (file)
index 0000000..124eed6
--- /dev/null
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Texas Instruments Incorporated nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#pragma once
+
+#include "utils.h"
+#include "opencv2/core.hpp"
+#include "opencv2/imgproc.hpp"
+#include "opencv2/highgui.hpp"
+#include "opencv2/videoio.hpp"
+
+using namespace cv;
+
+typedef struct cmdline_opts_t_ {
+  std::string config;
+  uint32_t    num_dsps;
+  uint32_t    num_eves;
+  uint32_t    num_frames;
+  std::string input_file;
+  uint32_t    output_width;
+  bool        verbose;
+  bool        is_camera_input;
+  bool        is_video_input;
+} cmdline_opts_t;
+
+
+bool ProcessArgs(int argc, char *argv[], cmdline_opts_t& opts);
+bool SetVideoInputOutput(VideoCapture &cap, const cmdline_opts_t& opts,
+                         const char* window_name);
index cfb3ba849df205058678d73181f054778c566dea..637cd07e940077a232f6bcfb486753f56834a160 100644 (file)
@@ -31,7 +31,8 @@ include ../make.common
 LIBS     += -lopencv_highgui -lopencv_imgcodecs -lopencv_videoio\
                        -lopencv_imgproc -lopencv_core
 
-SOURCES = main.cpp imagenet_classes.cpp
+SOURCES = main.cpp imagenet_classes.cpp ../common/utils.cpp \
+          ../common/video_utils.cpp
 
 $(EXE): $(TIDL_API_LIB) $(TIDL_API_LIB_IMGUTIL) $(HEADERS) $(SOURCES)
        $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(TIDL_API_LIB_IMGUTIL) \
index 5d54c66c36acbd77d4b6d6cf5ce04090f0632ce8..9039271de28b4a85f5a552cb94b90da9bc0c48ca 100644 (file)
@@ -26,7 +26,6 @@
  *   THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 #include <signal.h>
-#include <getopt.h>
 #include <iostream>
 #include <iomanip>
 #include <fstream>
 
 #include <queue>
 #include <vector>
+#include <chrono>
 
 #include "executor.h"
 #include "execution_object.h"
+#include "execution_object_pipeline.h"
 #include "configuration.h"
 #include "imagenet_classes.h"
 #include "imgutil.h"
+#include "../common/video_utils.h"
 
 #include "opencv2/core.hpp"
 #include "opencv2/imgproc.hpp"
 #include "opencv2/highgui.hpp"
 #include "opencv2/videoio.hpp"
 
-
-bool __TI_show_debug_ = false;
-
+using namespace std;
 using namespace tidl;
 using namespace tidl::imgutil;
 using namespace cv;
 
-#define NUM_VIDEO_FRAMES  100
-
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type, std::string& input_file);
-bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
-
-bool ReadFrame(ExecutionObject& eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap);
-
-bool WriteFrameOutput(const ExecutionObject &eo);
-
-static void ProcessArgs(int argc, char *argv[],
-                        std::string& config,
-                        int& num_devices,
-                        DeviceType& device_type,
-                        std::string& input_file);
-
-static void DisplayHelp();
+#define NUM_VIDEO_FRAMES  300
+#define DEFAULT_CONFIG    "j11_v2"
+#define NUM_DEFAULT_INPUTS  1
+const char *default_inputs[NUM_DEFAULT_INPUTS] =
+{
+    "../test/testvecs/input/objects/cat-pet-animal-domestic-104827.jpeg"
+};
 
-static double ms_diff(struct timespec &t0, struct timespec &t1)
-{ return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c);
+bool RunConfiguration(cmdline_opts_t& opts);
+bool ReadFrame(ExecutionObjectPipeline& eop,
+               uint32_t frame_idx, const Configuration& c,
+               const cmdline_opts_t& opts, VideoCapture &cap);
+bool WriteFrameOutput(const ExecutionObjectPipeline &eop);
+void DisplayHelp();
 
 
 int main(int argc, char *argv[])
@@ -89,173 +82,183 @@ int main(int argc, char *argv[])
     signal(SIGTERM, exit);
 
     // If there are no devices capable of offloading TIDL on the SoC, exit
-    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
-    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
-    if (num_eve == 0 && num_dsp == 0)
+    uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eves == 0 && num_dsps == 0)
     {
-        std::cout << "TI DL not supported on this SoC." << std::endl;
+        cout << "TI DL not supported on this SoC." << endl;
         return EXIT_SUCCESS;
     }
 
     // Process arguments
-    std::string config      = "j11_v2";
-    std::string input_file  = "../test/testvecs/input/objects/cat-pet-animal-domestic-104827.jpeg";
-    int         num_devices = 1;
-    DeviceType  device_type = (num_eve > 0 ? DeviceType::EVE:DeviceType::DSP);
-    ProcessArgs(argc, argv, config, num_devices, device_type, input_file);
-
-    std::cout << "Input: " << input_file << std::endl;
-    std::string config_file = "../test/testvecs/config/infer/tidl_config_"
-                              + config + ".txt";
-    bool status = RunConfiguration(config_file, num_devices, device_type,
-                                   input_file);
+    cmdline_opts_t opts;
+    opts.config = DEFAULT_CONFIG;
+    if (num_eves != 0) { opts.num_eves = 1;  opts.num_dsps = 0; }
+    else               { opts.num_eves = 0;  opts.num_dsps = 1; }
+    if (! ProcessArgs(argc, argv, opts))
+    {
+        DisplayHelp();
+        exit(EXIT_SUCCESS);
+    }
+    assert(opts.num_dsps != 0 || opts.num_eves != 0);
+    if (opts.num_frames == 0)
+        opts.num_frames = (opts.is_camera_input || opts.is_video_input) ?
+                          NUM_VIDEO_FRAMES : 1;
+    if (opts.input_file.empty())
+        cout << "Input: " << default_inputs[0] << endl;
+    else
+        cout << "Input: " << opts.input_file << endl;
 
+    // Run network
+    bool status = RunConfiguration(opts);
     if (!status)
     {
-        std::cout << "imagenet FAILED" << std::endl;
+        cout << "imagenet FAILED" << endl;
         return EXIT_FAILURE;
     }
 
-    std::cout << "imagenet PASSED" << std::endl;
+    cout << "imagenet PASSED" << endl;
     return EXIT_SUCCESS;
 }
 
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type, std::string& input_file)
+bool RunConfiguration(cmdline_opts_t& opts)
 {
-    DeviceIds ids;
-    for (int i = 0; i < num_devices; i++)
-        ids.insert(static_cast<DeviceId>(i));
-
     // Read the TI DL configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
+    Configuration c;
+    string config_file = "../test/testvecs/config/infer/tidl_config_"
+                              + opts.config + ".txt";
+    bool status = c.ReadFromFile(config_file);
     if (!status)
     {
-        std::cerr << "Error in configuration file: " << config_file
-                  << std::endl;
+        cerr << "Error in configuration file: " << config_file << endl;
         return false;
     }
+    c.enableApiTrace = opts.verbose;
 
-    // setup input
-    int num_frames = 1;
+    // setup camera/video input/output
     VideoCapture cap;
-    std::string image_file;
-    if (input_file == "camera")
-    {
-        cap = VideoCapture(1);  // cap = VideoCapture("test.mp4");
-        if (! cap.isOpened())
-        {
-            std::cerr << "Cannot open camera input." << std::endl;
-            return false;
-        }
-        num_frames = NUM_VIDEO_FRAMES;
-        namedWindow("ImageNet", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
-    }
-    else
-    {
-        image_file = input_file;
-    }
+    if (! SetVideoInputOutput(cap, opts, "ImageNet"))  return false;
 
     try
     {
-        // Create a executor with the approriate core type, number of cores
+        // Create Executors with the approriate core type, number of cores
         // and configuration specified
-        Executor executor(device_type, ids, configuration);
-
-        // Query Executor for set of ExecutionObjects created
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
-
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
-        {
-            size_t in_size  = eo->GetInputBufferSizeInBytes();
-            size_t out_size = eo->GetOutputBufferSizeInBytes();
-            ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
-            ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
-            eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-        }
-
-        #define MAX_NUM_EOS  4
-        struct timespec t0[MAX_NUM_EOS], t1;
-
-        // Process frames with available execution objects in a pipelined manner
+        Executor* e_eve = CreateExecutor(DeviceType::EVE, opts.num_eves, c);
+        Executor* e_dsp = CreateExecutor(DeviceType::DSP, opts.num_dsps, c);
+
+        // Get ExecutionObjects from Executors
+        vector<ExecutionObject*> eos;
+        for (uint32_t i = 0; i < opts.num_eves; i++) eos.push_back((*e_eve)[i]);
+        for (uint32_t i = 0; i < opts.num_dsps; i++) eos.push_back((*e_dsp)[i]);
+        uint32_t num_eos = eos.size();
+
+        // Use duplicate EOPs to do double buffering on frame input/output
+        //    because each EOP has its own set of input/output buffers,
+        //    so that host ReadFrame() can be overlapped with device processing
+        // Use one EO as an example, with different buffer_factor,
+        //    we have different execution behavior:
+        // If buffer_factor is set to 1 -> single buffering
+        //    we create one EOP: eop0 (eo0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [RF][eo0.....][WF]
+        //    eop0:                   [RF][eo0.....][WF]
+        //    eop0:                                     [RF][eo0.....][WF]
+        // If buffer_factor is set to 2 -> double buffering
+        //    we create two EOPs: eop0 (eo0), eop1(eo0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [RF][eo0.....][WF]
+        //    eop1:     [RF]      [eo0.....][WF]
+        //    eop0:                   [RF]  [eo0.....][WF]
+        //    eop1:                             [RF]  [eo0.....][WF]
+        vector<ExecutionObjectPipeline *> eops;
+        uint32_t buffer_factor = 2;  // set to 1 for single buffering
+        for (uint32_t j = 0; j < buffer_factor; j++)
+            for (uint32_t i = 0; i < num_eos; i++)
+                eops.push_back(new ExecutionObjectPipeline({eos[i]}));
+        uint32_t num_eops = eops.size();
+
+        // Allocate input and output buffers for each EOP
+        AllocateMemory(eops);
+
+        chrono::time_point<chrono::steady_clock> tloop0, tloop1;
+        tloop0 = chrono::steady_clock::now();
+
+        // Process frames with available eops in a pipelined manner
         // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < num_frames + num_eos; frame_idx++)
+        for (uint32_t frame_idx = 0;
+             frame_idx < opts.num_frames + num_eops; frame_idx++)
         {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
+            ExecutionObjectPipeline* eop = eops[frame_idx % num_eops];
 
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
+            // Wait for previous frame on the same eop to finish processing
+            if (eop->ProcessFrameWait())
             {
-                clock_gettime(CLOCK_MONOTONIC, &t1);
-                double elapsed_host =
-                                ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
-                double elapsed_device = eo->GetProcessTimeInMilliSeconds();
-                double overhead = 100 - (elapsed_device/elapsed_host*100);
-
-                std::cout << "frame[" << eo->GetFrameIndex() << "]: "
-                          << "Time on device: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_device << "ms, "
-                          << "host: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_host << "ms ";
-                std::cout << "API overhead: "
-                          << std::setw(6) << std::setprecision(3)
-                          << overhead << " %" << std::endl;
-
-                WriteFrameOutput(*eo);
+                ReportTime(eop);
+                WriteFrameOutput(*eop);
             }
 
-            // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, num_frames,
-                          image_file, cap))
-            {
-                clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
-                eo->ProcessFrameStartAsync();
-            }
+            // Read a frame and start processing it with current eop
+            if (ReadFrame(*eop, frame_idx, c, opts, cap))
+                eop->ProcessFrameStartAsync();
         }
 
-        for (auto b : buffers)
-            free(b);
+        tloop1 = chrono::steady_clock::now();
+        chrono::duration<float> elapsed = tloop1 - tloop0;
+        cout << "Loop total time (including read/write/opencv/print/etc): "
+                  << setw(6) << setprecision(4)
+                  << (elapsed.count() * 1000) << "ms" << endl;
 
+        FreeMemory(eops);
+        for (auto eop : eops)  delete eop;
+        delete e_eve;
+        delete e_dsp;
     }
     catch (tidl::Exception &e)
     {
-        std::cerr << e.what() << std::endl;
+        cerr << e.what() << endl;
         status = false;
     }
 
     return status;
 }
 
+// Create an Executor with the specified type and number of EOs
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c)
+{
+    if (num == 0) return nullptr;
 
-bool ReadFrame(ExecutionObject &eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap)
+    DeviceIds ids;
+    for (uint32_t i = 0; i < num; i++)
+        ids.insert(static_cast<DeviceId>(i));
+
+    return new Executor(dt, ids, c);
+}
+
+bool ReadFrame(ExecutionObjectPipeline &eop,
+               uint32_t frame_idx, const Configuration& c,
+               const cmdline_opts_t& opts, VideoCapture &cap)
 {
-    if (frame_idx >= num_frames)
+    if (frame_idx >= opts.num_frames)
         return false;
-    eo.SetFrameIndex(frame_idx);
 
-    char*  frame_buffer = eo.GetInputBufferPtr();
+    eop.SetFrameIndex(frame_idx);
+
+    char*  frame_buffer = eop.GetInputBufferPtr();
     assert (frame_buffer != nullptr);
 
     Mat image;
-    if (! image_file.empty())
+    if (! opts.is_camera_input && ! opts.is_video_input)
     {
-        image = cv::imread(image_file, CV_LOAD_IMAGE_COLOR);
+        if (opts.input_file.empty())
+            image = cv::imread(default_inputs[frame_idx % NUM_DEFAULT_INPUTS],
+                               CV_LOAD_IMAGE_COLOR);
+        else
+            image = cv::imread(opts.input_file, CV_LOAD_IMAGE_COLOR);
         if (image.empty())
         {
-            std::cerr << "Unable to read from: " << image_file << std::endl;
+            cerr << "Unable to read input image" << endl;
             return false;
         }
     }
@@ -264,33 +267,37 @@ bool ReadFrame(ExecutionObject &eo, int frame_idx,
         Mat v_image;
         if (! cap.grab())  return false;
         if (! cap.retrieve(v_image)) return false;
-        // Crop 640x480 camera input to center 256x256 input
-        image = Mat(v_image, Rect(192, 112, 256, 256));
+        int orig_width  = v_image.cols;
+        int orig_height = v_image.rows;
+        // Crop camera/video input to center 256x256 input
+        if (orig_width > 256 && orig_height > 256)
+        {
+            image = Mat(v_image, Rect((orig_width-256)/2, (orig_height-256)/2,
+                                       256, 256));
+        }
+        else
+            image = v_image;
         cv::imshow("ImageNet", image);
         waitKey(2);
     }
 
     // TI DL image preprocessing, into frame_buffer
-    return PreProcImage(image, frame_buffer, 1, 3,
-                        configuration.inWidth, configuration.inHeight,
-                        configuration.inWidth,
-                        configuration.inWidth * configuration.inHeight,
-                        1, configuration.preProcType);
+    return PreProcImage(image, frame_buffer, 1, 3, c.inWidth, c.inHeight,
+                        c.inWidth, c.inWidth * c.inHeight, 1, c.preProcType);
 }
 
 // Display top 5 classified imagenet classes with probabilities
-bool WriteFrameOutput(const ExecutionObject &eo)
+bool WriteFrameOutput(const ExecutionObjectPipeline &eop)
 {
     const int k = 5;
-    unsigned char *out = (unsigned char *) eo.GetOutputBufferPtr();
-    int out_size = eo.GetOutputBufferSizeInBytes();
+    unsigned char *out = (unsigned char *) eop.GetOutputBufferPtr();
+    int out_size = eop.GetOutputBufferSizeInBytes();
 
     // sort and get k largest values and corresponding indices
-    typedef std::pair<unsigned char, int> val_index;
+    typedef pair<unsigned char, int> val_index;
     auto constexpr cmp = [](val_index &left, val_index &right)
                          { return left.first > right.first; };
-    std::priority_queue<val_index, std::vector<val_index>, decltype(cmp)>
-            queue(cmp);
+    priority_queue<val_index, vector<val_index>, decltype(cmp)> queue(cmp);
     // initialize priority queue with smallest value on top
     for (int i = 0; i < k; i++)
         queue.push(val_index(out[i], i));
@@ -306,7 +313,7 @@ bool WriteFrameOutput(const ExecutionObject &eo)
     }
 
     // output top k values in reverse order: largest val first
-    std::vector<val_index> sorted;
+    vector<val_index> sorted;
     while (! queue.empty())
     {
       sorted.push_back(queue.top());
@@ -314,94 +321,28 @@ bool WriteFrameOutput(const ExecutionObject &eo)
     }
 
     for (int i = k - 1; i >= 0; i--)
-    {
-        std::cout << k-i << ": " << imagenet_classes[sorted[i].second]
-                  << std::endl;
-    }
+        cout << k-i << ": " << imagenet_classes[sorted[i].second] << endl;
 
     return true;
 }
 
-
-void ProcessArgs(int argc, char *argv[], std::string& config,
-                 int& num_devices, DeviceType& device_type,
-                 std::string& input_file)
-{
-    const struct option long_options[] =
-    {
-        {"config",      required_argument, 0, 'c'},
-        {"num_devices", required_argument, 0, 'n'},
-        {"device_type", required_argument, 0, 't'},
-        {"image_file",  required_argument, 0, 'i'},
-        {"help",        no_argument,       0, 'h'},
-        {"verbose",     no_argument,       0, 'v'},
-        {0, 0, 0, 0}
-    };
-
-    int option_index = 0;
-
-    while (true)
-    {
-        int c = getopt_long(argc, argv, "c:n:t:i:hv", long_options, &option_index);
-
-        if (c == -1)
-            break;
-
-        switch (c)
-        {
-            case 'c': config = optarg;
-                      break;
-
-            case 'n': num_devices = atoi(optarg);
-                      assert (num_devices > 0 && num_devices <= 4);
-                      break;
-
-            case 't': if (*optarg == 'e')
-                          device_type = DeviceType::EVE;
-                      else if (*optarg == 'd')
-                          device_type = DeviceType::DSP;
-                      else
-                      {
-                          std::cerr << "Invalid argument to -t, only e or d"
-                                       " allowed" << std::endl;
-                          exit(EXIT_FAILURE);
-                      }
-                      break;
-
-            case 'i': input_file = optarg;
-                      break;
-
-            case 'v': __TI_show_debug_ = true;
-                      break;
-
-            case 'h': DisplayHelp();
-                      exit(EXIT_SUCCESS);
-                      break;
-
-            case '?': // Error in getopt_long
-                      exit(EXIT_FAILURE);
-                      break;
-
-            default:
-                      std::cerr << "Unsupported option: " << c << std::endl;
-                      break;
-        }
-    }
-}
-
 void DisplayHelp()
 {
-    std::cout << "Usage: imagenet\n"
-                 "  Will run imagenet network to predict top 5 object"
-                 " classes for the input.\n  Use -c to run a"
-                 "  different imagenet network. Default is j11_v2.\n"
-                 "Optional arguments:\n"
-                 " -c <config>          Valid configs: j11_bn, j11_prelu, j11_v2\n"
-                 " -n <number of cores> Number of cores to use (1 - 4)\n"
-                 " -t <d|e>             Type of core. d -> DSP, e -> EVE\n"
-                 " -i <image>           Path to the image file\n"
-                 " -i camera            Use camera as input\n"
-                 " -v                   Verbose output during execution\n"
-                 " -h                   Help\n";
+    cout <<
+    "Usage: imagenet\n"
+    "  Will run imagenet network to predict top 5 object"
+    " classes for the input.\n  Use -c to run a"
+    "  different imagenet network. Default is j11_v2.\n"
+    "Optional arguments:\n"
+    " -c <config>          Valid configs: j11_bn, j11_prelu, j11_v2\n"
+    " -d <number>          Number of dsp cores to use\n"
+    " -e <number>          Number of eve cores to use\n"
+    " -i <image>           Path to the image file as input\n"
+    " -i camera<number>    Use camera as input\n"
+    "                      video input port: /dev/video<number>\n"
+    " -i <name>.{mp4,mov,avi}  Use video file as input\n"
+    " -f <number>          Number of frames to process\n"
+    " -v                   Verbose output during execution\n"
+    " -h                   Help\n";
 }
 
diff --git a/examples/layer_output/Makefile b/examples/layer_output/Makefile
new file mode 100644 (file)
index 0000000..52b3eae
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+EXE = layer_output
+
+include ../make.common
+
+CXXFLAGS += -I../common
+
+SOURCES = main.cpp ../common/utils.cpp
+
+$(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
+       $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
diff --git a/examples/layer_output/j11_v2_trace.txt b/examples/layer_output/j11_v2_trace.txt
new file mode 100644 (file)
index 0000000..79480dd
--- /dev/null
@@ -0,0 +1,12 @@
+numFrames     = 1
+preProcType   = 0
+inData        = ../test/testvecs/input/preproc_0_224x224.y
+outData       = stats_tool_out.bin
+netBinFile    = ../test/testvecs/config/tidl_models/tidl_net_imagenet_jacintonet11v2.bin
+paramsBinFile = ../test/testvecs/config/tidl_models/tidl_param_imagenet_jacintonet11v2.bin
+inWidth       = 224
+inHeight      = 224
+inNumChannels = 3
+
+# Enable tracing of output buffers
+enableTrace = true
diff --git a/examples/layer_output/main.cpp b/examples/layer_output/main.cpp
new file mode 100644 (file)
index 0000000..9b33f23
--- /dev/null
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * Copyright (c) 2017-2018  Texas Instruments Incorporated - http://www.ti.com/
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *       * Neither the name of Texas Instruments Incorporated nor the
+ *         names of its contributors may be used to endorse or promote products
+ *         derived from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *   THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <signal.h>
+#include <getopt.h>
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+#include "executor.h"
+#include "execution_object.h"
+#include "configuration.h"
+#include "utils.h"
+
+using namespace tidl;
+
+bool RunConfiguration(const std::string& config_file, int num_devices,
+                      DeviceType device_type);
+
+static void ProcessTrace(const ExecutionObject* eo, const Configuration& c);
+
+int main(int argc, char *argv[])
+{
+    // Catch ctrl-c to ensure a clean exit
+    signal(SIGABRT, exit);
+    signal(SIGTERM, exit);
+
+    std::cout << "API Version: " << Executor::GetAPIVersion() << std::endl;
+
+    // If there are no devices capable of offloading TIDL on the SoC, exit
+    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eve == 0 && num_dsp == 0)
+    {
+        std::cout << "TI DL not supported on this SoC." << std::endl;
+        return EXIT_SUCCESS;
+    }
+
+    // Configuration file with tracing enabled
+    std::string config_file = "j11_v2_trace.txt";
+    int         num_devices = 1;
+    DeviceType  device_type = num_eve > 0 ? DeviceType::EVE :
+                                            DeviceType::DSP;
+
+    bool status = RunConfiguration(config_file, num_devices, device_type);
+
+    if (!status)
+    {
+        std::cout << "tidl FAILED" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::cout << "tidl PASSED" << std::endl;
+    return EXIT_SUCCESS;
+}
+
+bool RunConfiguration(const std::string& config_file, int num_devices,
+                      DeviceType device_type)
+{
+    DeviceIds ids;
+    for (int i = 0; i < num_devices; i++)
+        ids.insert(static_cast<DeviceId>(i));
+
+    // Read the TI DL configuration file
+    Configuration c;
+    if (!c.ReadFromFile(config_file))
+        return false;
+
+    // Open input files
+    std::ifstream input(c.inData, std::ios::binary);
+    assert (input.good());
+
+    bool status = true;
+
+    try
+    {
+        // Create a executor with the specified core type, number of cores
+        // and configuration
+        Executor E(device_type, ids, c);
+
+        std::vector<ExecutionObject *> EOs;
+        for (unsigned int i = 0; i < E.GetNumExecutionObjects(); i++)
+            EOs.push_back(E[i]);
+
+        int num_eos = EOs.size();
+
+        // Allocate input and output buffers for each execution object
+        for (auto eo : EOs)
+        {
+            size_t in_size  = eo->GetInputBufferSizeInBytes();
+            size_t out_size = eo->GetOutputBufferSizeInBytes();
+            ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
+            ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
+            eo->SetInputOutputBuffer(in, out);
+        }
+
+        // Process frames with available EOs in a pipelined manner
+        // additional num_eos iterations to flush the pipeline (epilogue)
+        for (int frame_idx = 0; frame_idx < c.numFrames + num_eos; frame_idx++)
+        {
+            ExecutionObject* eo = EOs[frame_idx % num_eos];
+
+            // Wait for previous frame on the same eo to finish processing
+            if (eo->ProcessFrameWait())
+                ProcessTrace(eo, c);
+
+            // Read a frame and start processing it with current eo
+            if (ReadFrame(eo, frame_idx, c, input))
+                eo->ProcessFrameStartAsync();
+        }
+
+        for (auto eo : EOs)
+        {
+            free(eo->GetInputBufferPtr());
+            free(eo->GetOutputBufferPtr());
+        }
+    }
+    catch (tidl::Exception &e)
+    {
+        std::cerr << e.what() << std::endl;
+        status = false;
+    }
+
+    input.close();
+
+    return status;
+}
+
+
+// APIs for accessing output buffers from individual layers
+// 1. ExecutionObject::WriteLayerOutputsToFile
+// 2. ExecutionObject::GetOutputsFromAllLayers
+// 3. ExecutionObject::GetOutputFromLayer
+void ProcessTrace(const ExecutionObject* eo, const Configuration& c)
+{
+    if (!c.enableOutputTrace)
+    {
+        std::cout << "Trace is not enabled. Set"
+                     " Configuration::enableOutputTrace to true"
+                  << std::endl;
+        return;
+    }
+
+    // 1. Write the outputs from each layer to files
+    // filename: trace_data_<layer_index>_<channels>_<width>_<height>.bin
+    eo->WriteLayerOutputsToFile();
+
+    // 2. Get all outputs from all layers and iterate through them
+    const LayerOutputs* los = eo->GetOutputsFromAllLayers();
+    if (!los) return;
+
+    for (const std::unique_ptr<const LayerOutput> &lo : *los)
+    {
+        std::cout << "Layer index: " << lo->LayerIndex()
+              << " Shape: " << lo->NumberOfChannels() << " x "
+              << lo->Width() << " x " << lo->Height()
+              << " Data ptr: " << static_cast<const void*>(lo->Data())
+              << " Size in bytes: " << lo->Size()
+              << std::endl;
+    }
+
+    // Call delete to free the memory used to store layer outputs
+    delete los;
+
+    // 3. Get the output from a single layer
+    const LayerOutput* lo = eo->GetOutputFromLayer(1);
+    if (!lo) return;
+
+    std::cout << "Layer index: " << lo->LayerIndex()
+          << " Shape: " << lo->NumberOfChannels() << " x "
+          << lo->Width() << " x " << lo->Height()
+          << " Data ptr: " << static_cast<const void*>(lo->Data())
+          << " Size in bytes: " << lo->Size()
+          << std::endl;
+
+    delete lo;
+}
index e0111173572380e0ddc4c2c2ec0419829204ce25..aab1d584fac5021dd9a209e0ec2b7f5489e18d72 100644 (file)
@@ -39,7 +39,7 @@ include $(TIDL_API_DIR)/make.inc
 ifeq ($(BUILD), debug)
        CXXFLAGS += -Og -g -ggdb
 else
-       CXXFLAGS += -O3
+       CXXFLAGS += -O3 -Wall -Werror -Wno-error=ignored-attributes
 endif
 
 CXXFLAGS += -I. -I$(TIDL_API_DIR)/inc -std=c++11
@@ -54,3 +54,5 @@ all: $(EXE)
 clean::
        $(RM) -f $(EXE) stats_tool_out.* *.out
 
+run:
+       ./$(EXE)
diff --git a/examples/one_eo_per_frame/Makefile b/examples/one_eo_per_frame/Makefile
new file mode 100644 (file)
index 0000000..8420254
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+EXE = one_eo_per_frame
+
+include ../make.common
+
+CXXFLAGS += -I../common
+
+SOURCES = main.cpp ../common/utils.cpp
+
+$(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
+       $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
+
diff --git a/examples/one_eo_per_frame/main.cpp b/examples/one_eo_per_frame/main.cpp
new file mode 100644 (file)
index 0000000..36197c8
--- /dev/null
@@ -0,0 +1,173 @@
+/******************************************************************************
+ * Copyright (c) 2017-2018  Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//
+// This example illustrates using a single EO to process a frame. To increase
+// throughput, multiple EOs are used.
+// For details, refer http://downloads.ti.com/mctools/esd/docs/tidl-api/
+//
+#include <signal.h>
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+#include "executor.h"
+#include "execution_object.h"
+#include "configuration.h"
+#include "utils.h"
+
+using namespace tidl;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+bool Run(const string& config_file, int num_eve,int num_dsp,
+         const char* ref_output);
+
+Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c);
+void      CollectEOs(const Executor *e, vector<ExecutionObject *>& EOs);
+
+
+int main(int argc, char *argv[])
+{
+    // Catch ctrl-c to ensure a clean exit
+    signal(SIGABRT, exit);
+    signal(SIGTERM, exit);
+
+    // If there are no devices capable of offloading TIDL on the SoC, exit
+    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eve == 0 && num_dsp == 0)
+    {
+        std::cout << "TI DL not supported on this SoC." << std::endl;
+        return EXIT_SUCCESS;
+    }
+
+    string config_file ="../test/testvecs/config/infer/tidl_config_j11_v2.txt";
+    string ref_file    ="../test/testvecs/reference/j11_v2_ref.bin";
+
+    unique_ptr<const char> reference_output(ReadReferenceOutput(ref_file));
+
+    bool status = Run(config_file, num_eve, num_dsp, reference_output.get());
+
+    if (!status)
+    {
+        std::cout << "FAILED" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::cout << "PASSED" << std::endl;
+    return EXIT_SUCCESS;
+}
+
+bool Run(const string& config_file, int num_eve, int num_dsp,
+         const char* ref_output)
+{
+    Configuration c;
+    if (!c.ReadFromFile(config_file))
+        return false;
+
+    // Heap sizes for this network determined using Configuration::showHeapStats
+    c.PARAM_HEAP_SIZE   = (3 << 20); // 3MB
+    c.NETWORK_HEAP_SIZE = (20 << 20); // 20MB
+
+    c.numFrames = 16;
+
+    // Open input file for reading
+    std::ifstream input_data_file(c.inData, std::ios::binary);
+
+    bool status = true;
+    try
+    {
+        // Create Executors - use all the DSP and EVE cores available
+        unique_ptr<Executor> e_dsp(CreateExecutor(DeviceType::DSP, num_dsp, c));
+        unique_ptr<Executor> e_eve(CreateExecutor(DeviceType::EVE, num_eve, c));
+
+        // Accumulate all the EOs from across the Executors
+        vector<ExecutionObject *> EOs;
+        CollectEOs(e_eve.get(), EOs);
+        CollectEOs(e_dsp.get(), EOs);
+
+        AllocateMemory(EOs);
+
+        // Process frames with EOs in a pipelined manner
+        // additional num_eos iterations to flush the pipeline (epilogue)
+        int num_eos = EOs.size();
+        for (int frame_idx = 0; frame_idx < c.numFrames + num_eos; frame_idx++)
+        {
+            ExecutionObject* eo = EOs[frame_idx % num_eos];
+
+            // Wait for previous frame on the same eo to finish processing
+            if (eo->ProcessFrameWait())
+            {
+                ReportTime(eo);
+                if (frame_idx < num_eos && !CheckFrame(eo, ref_output))
+                    status = false;
+            }
+
+            // Read a frame and start processing it with current eo
+            if (ReadFrame(eo, frame_idx, c, input_data_file))
+                eo->ProcessFrameStartAsync();
+        }
+
+        FreeMemory(EOs);
+
+    }
+    catch (tidl::Exception &e)
+    {
+        std::cerr << e.what() << std::endl;
+        status = false;
+    }
+
+    input_data_file.close();
+
+    return status;
+}
+
+// Create an Executor with the specified type and number of EOs
+Executor* CreateExecutor(DeviceType dt, int num, const Configuration& c)
+{
+    if (num == 0) return nullptr;
+
+    DeviceIds ids;
+    for (int i = 0; i < num; i++)
+        ids.insert(static_cast<DeviceId>(i));
+
+    return new Executor(dt, ids, c);
+}
+
+// Accumulate EOs from an Executor into a vector of EOs
+void CollectEOs(const Executor *e, vector<ExecutionObject *>& EOs)
+{
+    if (!e) return;
+
+    for (unsigned int i = 0; i < e->GetNumExecutionObjects(); i++)
+        EOs.push_back((*e)[i]);
+}
+
index ac22366af10d0ba402cfe3439791591a6b4a8183..50f3fe38cf00672c24584443bee3ac5232cbe024 100644 (file)
@@ -31,7 +31,8 @@ include ../make.common
 LIBS     += -lopencv_highgui -lopencv_imgcodecs -lopencv_videoio\
                        -lopencv_imgproc -lopencv_core
 
-SOURCES = main.cpp object_classes.cpp
+SOURCES = main.cpp object_classes.cpp ../common/utils.cpp \
+          ../common/video_utils.cpp
 
 $(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
        $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) \
index 86f81e55cd8c21f464ef19320ff86ef83f5bf073..35294c4d444a302dbf203369317d7adde1313a1b 100644 (file)
@@ -26,7 +26,6 @@
  *   THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 #include <signal.h>
-#include <getopt.h>
 #include <iostream>
 #include <iomanip>
 #include <fstream>
 #include <queue>
 #include <vector>
 #include <cstdio>
+#include <chrono>
 
 #include "executor.h"
 #include "execution_object.h"
 #include "configuration.h"
 #include "object_classes.h"
+#include "../common/utils.h"
+#include "../common/video_utils.h"
 
-#include "opencv2/core.hpp"
-#include "opencv2/imgproc.hpp"
-#include "opencv2/highgui.hpp"
-#include "opencv2/videoio.hpp"
-
-#define NUM_VIDEO_FRAMES  100
-#define DEFAULT_CONFIG    "jseg21_tiscapes"
-#define DEFAULT_INPUT     "../test/testvecs/input/000100_1024x512_bgr.y"
-
-bool __TI_show_debug_ = false;
-bool is_default_input = false;
-bool is_preprocessed_input = false;
-bool is_camera_input       = false;
-int  orig_width;
-int  orig_height;
-object_class_table_t *object_class_table;
-
+using namespace std;
 using namespace tidl;
 using namespace cv;
 
 
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type, std::string& input_file);
-bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
-
-bool ReadFrame(ExecutionObject& eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap);
-
-bool WriteFrameOutput(const ExecutionObject &eo,
-                      const Configuration& configuration);
+#define NUM_VIDEO_FRAMES  300
+#define DEFAULT_CONFIG    "jseg21_tiscapes"
+#define DEFAULT_INPUT     "../test/testvecs/input/000100_1024x512_bgr.y"
+#define DEFAULT_INPUT_FRAMES  (9)
 
-static void ProcessArgs(int argc, char *argv[],
-                        std::string& config,
-                        int& num_devices,
-                        DeviceType& device_type,
-                        std::string& input_file);
+object_class_table_t *object_class_table;
+uint32_t orig_width;
+uint32_t orig_height;
 
-static void DisplayHelp();
 
-static double ms_diff(struct timespec &t0, struct timespec &t1)
-{ return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
+bool RunConfiguration(const cmdline_opts_t& opts);
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c);
+bool ReadFrame(ExecutionObjectPipeline& eop,
+               uint32_t frame_idx, const Configuration& c,
+               const cmdline_opts_t& opts, VideoCapture &cap);
+bool WriteFrameOutput(const ExecutionObjectPipeline &eop,
+                      const Configuration& c, const cmdline_opts_t& opts);
+void DisplayHelp();
 
 
 int main(int argc, char *argv[])
@@ -97,198 +80,200 @@ int main(int argc, char *argv[])
     signal(SIGTERM, exit);
 
     // If there are no devices capable of offloading TIDL on the SoC, exit
-    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
-    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
-    if (num_eve == 0 && num_dsp == 0)
+    uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eves == 0 && num_dsps == 0)
     {
-        std::cout << "TI DL not supported on this SoC." << std::endl;
+        cout << "TI DL not supported on this SoC." << endl;
         return EXIT_SUCCESS;
     }
 
     // Process arguments
-    std::string config      = DEFAULT_CONFIG;
-    std::string input_file  = DEFAULT_INPUT;
-    int         num_devices = 1;
-    DeviceType  device_type = (num_eve > 0 ? DeviceType::EVE:DeviceType::DSP);
-    ProcessArgs(argc, argv, config, num_devices, device_type, input_file);
+    cmdline_opts_t opts;
+    opts.config = DEFAULT_CONFIG;
+    if (num_eves != 0) { opts.num_eves = 1;  opts.num_dsps = 0; }
+    else               { opts.num_eves = 0;  opts.num_dsps = 1; }
+    if (! ProcessArgs(argc, argv, opts))
+    {
+        DisplayHelp();
+        exit(EXIT_SUCCESS);
+    }
+    assert(opts.num_dsps != 0 || opts.num_eves != 0);
+    if (opts.num_frames == 0)
+        opts.num_frames = (opts.is_camera_input || opts.is_video_input) ?
+                          NUM_VIDEO_FRAMES :
+                          (opts.input_file.empty() ? DEFAULT_INPUT_FRAMES : 1);
+    if (opts.input_file.empty())
+        cout << "Input: " << DEFAULT_INPUT << endl;
+    else
+        cout << "Input: " << opts.input_file << endl;
 
-    if ((object_class_table = GetObjectClassTable(config)) == nullptr)
+    // Get object class table
+    if ((object_class_table = GetObjectClassTable(opts.config)) == nullptr)
     {
-        std::cout << "No object classes defined for this config." << std::endl;
+        cout << "No object classes defined for this config." << endl;
         return EXIT_FAILURE;
     }
 
-    if (input_file == DEFAULT_INPUT)  is_default_input = true;
-    if (input_file == "camera")       is_camera_input = true;
-    if (input_file.length() > 2 &&
-        input_file.compare(input_file.length() - 2, 2, ".y") == 0)
-        is_preprocessed_input = true;
-    std::cout << "Input: " << input_file << std::endl;
-    std::string config_file = "../test/testvecs/config/infer/tidl_config_"
-                              + config + ".txt";
-    bool status = RunConfiguration(config_file, num_devices, device_type,
-                                   input_file);
-
+    // Run network
+    bool status = RunConfiguration(opts);
     if (!status)
     {
-        std::cout << "segmentation FAILED" << std::endl;
+        cout << "segmentation FAILED" << endl;
         return EXIT_FAILURE;
     }
 
-    std::cout << "segmentation PASSED" << std::endl;
+    cout << "segmentation PASSED" << endl;
     return EXIT_SUCCESS;
 }
 
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type, std::string& input_file)
+bool RunConfiguration(const cmdline_opts_t& opts)
 {
-    DeviceIds ids;
-    for (int i = 0; i < num_devices; i++)
-        ids.insert(static_cast<DeviceId>(i));
-
     // Read the TI DL configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
+    Configuration c;
+    std::string config_file = "../test/testvecs/config/infer/tidl_config_"
+                              + opts.config + ".txt";
+    bool status = c.ReadFromFile(config_file);
     if (!status)
     {
-        std::cerr << "Error in configuration file: " << config_file
-                  << std::endl;
+        cerr << "Error in configuration file: " << config_file << endl;
         return false;
     }
+    c.enableApiTrace = opts.verbose;
 
-    // setup input
-    int num_frames = is_default_input ? 3 : 1;
+    // setup camera/video input/output
     VideoCapture cap;
-    std::string image_file;
-    if (is_camera_input)
-    {
-        cap = VideoCapture(1);  // cap = VideoCapture("test.mp4");
-        if (! cap.isOpened())
-        {
-            std::cerr << "Cannot open camera input." << std::endl;
-            return false;
-        }
-        num_frames = NUM_VIDEO_FRAMES;
-        namedWindow("Segmentation", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
-    }
-    else
-    {
-        image_file = input_file;
-    }
+    if (! SetVideoInputOutput(cap, opts, "Segmentation"))  return false;
 
     try
     {
-        // Create a executor with the approriate core type, number of cores
+        // Create Executors with the approriate core type, number of cores
         // and configuration specified
-        Executor executor(device_type, ids, configuration);
-
-        // Query Executor for set of ExecutionObjects created
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
-
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
-        {
-            size_t in_size  = eo->GetInputBufferSizeInBytes();
-            size_t out_size = eo->GetOutputBufferSizeInBytes();
-            ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
-            ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
-            eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-        }
-
-        #define MAX_NUM_EOS  4
-        struct timespec t0[MAX_NUM_EOS], t1;
-
-        // Process frames with available execution objects in a pipelined manner
+        Executor* e_eve = CreateExecutor(DeviceType::EVE, opts.num_eves, c);
+        Executor* e_dsp = CreateExecutor(DeviceType::DSP, opts.num_dsps, c);
+
+        // Get ExecutionObjects from Executors
+        vector<ExecutionObject*> eos;
+        for (uint32_t i = 0; i < opts.num_eves; i++) eos.push_back((*e_eve)[i]);
+        for (uint32_t i = 0; i < opts.num_dsps; i++) eos.push_back((*e_dsp)[i]);
+        uint32_t num_eos = eos.size();
+
+        // Use duplicate EOPs to do double buffering on frame input/output
+        //    because each EOP has its own set of input/output buffers,
+        //    so that host ReadFrame() can be overlapped with device processing
+        // Use one EO as an example, with different buffer_factor,
+        //    we have different execution behavior:
+        // If buffer_factor is set to 1 -> single buffering
+        //    we create one EOP: eop0 (eo0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [RF][eo0.....][WF]
+        //    eop0:                   [RF][eo0.....][WF]
+        //    eop0:                                     [RF][eo0.....][WF]
+        // If buffer_factor is set to 2 -> double buffering
+        //    we create two EOPs: eop0 (eo0), eop1(eo0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [RF][eo0.....][WF]
+        //    eop1:     [RF]      [eo0.....][WF]
+        //    eop0:                   [RF]  [eo0.....][WF]
+        //    eop1:                             [RF]  [eo0.....][WF]
+        vector<ExecutionObjectPipeline *> eops;
+        uint32_t buffer_factor = 2;  // set to 1 for single buffering
+        for (uint32_t j = 0; j < buffer_factor; j++)
+            for (uint32_t i = 0; i < num_eos; i++)
+                eops.push_back(new ExecutionObjectPipeline({eos[i]}));
+        uint32_t num_eops = eops.size();
+
+        // Allocate input and output buffers for each EOP
+        AllocateMemory(eops);
+
+        chrono::time_point<chrono::steady_clock> tloop0, tloop1;
+        tloop0 = chrono::steady_clock::now();
+
+        // Process frames with available eops in a pipelined manner
         // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < num_frames + num_eos; frame_idx++)
+        for (uint32_t frame_idx = 0;
+             frame_idx < opts.num_frames + num_eops; frame_idx++)
         {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
+            ExecutionObjectPipeline* eop = eops[frame_idx % num_eops];
 
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
+            // Wait for previous frame on the same eop to finish processing
+            if (eop->ProcessFrameWait())
             {
-                clock_gettime(CLOCK_MONOTONIC, &t1);
-                double elapsed_host =
-                                ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
-                double elapsed_device = eo->GetProcessTimeInMilliSeconds();
-                double overhead = 100 - (elapsed_device/elapsed_host*100);
-
-                std::cout << "frame[" << eo->GetFrameIndex() << "]: "
-                          << "Time on device: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_device << "ms, "
-                          << "host: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_host << "ms ";
-                std::cout << "API overhead: "
-                          << std::setw(6) << std::setprecision(3)
-                          << overhead << " %" << std::endl;
-
-                WriteFrameOutput(*eo, configuration);
+                ReportTime(eop);
+                WriteFrameOutput(*eop, c, opts);
             }
 
-            // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, num_frames,
-                          image_file, cap))
-            {
-                clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
-                eo->ProcessFrameStartAsync();
-            }
+            // Read a frame and start processing it with current eop
+            if (ReadFrame(*eop, frame_idx, c, opts, cap))
+                eop->ProcessFrameStartAsync();
         }
 
-        for (auto b : buffers)
-            free(b);
+        tloop1 = chrono::steady_clock::now();
+        chrono::duration<float> elapsed = tloop1 - tloop0;
+        cout << "Loop total time (including read/write/opencv/print/etc): "
+                  << setw(6) << setprecision(4)
+                  << (elapsed.count() * 1000) << "ms" << endl;
 
+        FreeMemory(eops);
+        for (auto eop : eops)  delete eop;
+        delete e_eve;
+        delete e_dsp;
     }
     catch (tidl::Exception &e)
     {
-        std::cerr << e.what() << std::endl;
+        cerr << e.what() << endl;
         status = false;
     }
 
     return status;
 }
 
+// Create an Executor with the specified type and number of EOs
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c)
+{
+    if (num == 0) return nullptr;
+
+    DeviceIds ids;
+    for (uint32_t i = 0; i < num; i++)
+        ids.insert(static_cast<DeviceId>(i));
+
+    return new Executor(dt, ids, c);
+}
 
-bool ReadFrame(ExecutionObject &eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap)
+bool ReadFrame(ExecutionObjectPipeline &eop,
+               uint32_t frame_idx, const Configuration& c,
+               const cmdline_opts_t& opts, VideoCapture &cap)
 {
-    if (frame_idx >= num_frames)
+    if (frame_idx >= opts.num_frames)
         return false;
-    eo.SetFrameIndex(frame_idx);
+    eop.SetFrameIndex(frame_idx);
 
-    char*  frame_buffer = eo.GetInputBufferPtr();
+    char*  frame_buffer = eop.GetInputBufferPtr();
     assert (frame_buffer != nullptr);
-    int channel_size = configuration.inWidth * configuration.inHeight;
+    int channel_size = c.inWidth * c.inHeight;
 
     Mat image;
-    if (! image_file.empty())
+    if (! opts.is_camera_input && ! opts.is_video_input)
     {
-        if (is_preprocessed_input)
+        if (opts.input_file.empty())
         {
-            std::ifstream ifs(image_file, std::ios::binary);
-            ifs.seekg(frame_idx * channel_size * 3);
+            ifstream ifs(DEFAULT_INPUT, ios::binary);
+            ifs.seekg((frame_idx % DEFAULT_INPUT_FRAMES) * channel_size * 3);
             ifs.read(frame_buffer, channel_size * 3);
             bool ifs_status = ifs.good();
             ifs.close();
-            orig_width  = configuration.inWidth;
-            orig_height = configuration.inHeight;
+            orig_width  = c.inWidth;
+            orig_height = c.inHeight;
             return ifs_status;  // already PreProc-ed
         }
         else
         {
-            image = cv::imread(image_file, CV_LOAD_IMAGE_COLOR);
+            image = cv::imread(opts.input_file, CV_LOAD_IMAGE_COLOR);
             if (image.empty())
             {
-                std::cerr << "Unable to read from: " << image_file << std::endl;
+                cerr << "Unable to read from: " << opts.input_file << endl;
                 return false;
             }
         }
@@ -309,8 +294,7 @@ bool ReadFrame(ExecutionObject &eo, int frame_idx,
     Mat s_image, bgr_frames[3];
     orig_width  = image.cols;
     orig_height = image.rows;
-    cv::resize(image, s_image,
-               Size(configuration.inWidth, configuration.inHeight),
+    cv::resize(image, s_image, Size(c.inWidth, c.inHeight),
                0, 0, cv::INTER_AREA);
     cv::split(s_image, bgr_frames);
     memcpy(frame_buffer,                bgr_frames[0].ptr(), channel_size);
@@ -334,13 +318,13 @@ void CreateMask(uchar *classes, uchar *mb, uchar *mg, uchar* mr,
 }
 
 // Create frame overlayed with pixel-level segmentation
-bool WriteFrameOutput(const ExecutionObject &eo,
-                      const Configuration& configuration)
+bool WriteFrameOutput(const ExecutionObjectPipeline &eop,
+                      const Configuration& c,
+                      const cmdline_opts_t& opts)
 {
-    unsigned char *out = (unsigned char *) eo.GetOutputBufferPtr();
-    int out_size       = eo.GetOutputBufferSizeInBytes();
-    int width          = configuration.inWidth;
-    int height         = configuration.inHeight;
+    unsigned char *out = (unsigned char *) eop.GetOutputBufferPtr();
+    int width          = c.inWidth;
+    int height         = c.inHeight;
     int channel_size   = width * height;
 
     Mat mask, frame, blend, r_blend, bgr[3];
@@ -352,7 +336,7 @@ bool WriteFrameOutput(const ExecutionObject &eo,
     cv::merge(bgr, 3, mask);
 
     // Asseembly original frame
-    unsigned char *in = (unsigned char *) eo.GetInputBufferPtr();
+    unsigned char *in = (unsigned char *) eop.GetInputBufferPtr();
     bgr[0] = Mat(height, width, CV_8UC(1), in);
     bgr[1] = Mat(height, width, CV_8UC(1), in + channel_size);
     bgr[2] = Mat(height, width, CV_8UC(1), in + channel_size*2);
@@ -361,17 +345,22 @@ bool WriteFrameOutput(const ExecutionObject &eo,
     // Create overlayed frame
     cv::addWeighted(frame, 0.7, mask, 0.3, 0.0, blend);
 
-    cv::resize(blend, r_blend, Size(orig_width, orig_height));
-    if (is_camera_input)
+    // Resize to output width/height, keep aspect ratio
+    uint32_t output_width = opts.output_width;
+    if (output_width == 0)  output_width = orig_width;
+    uint32_t output_height = (output_width*1.0f) / orig_width * orig_height;
+    cv::resize(blend, r_blend, Size(output_width, output_height));
+
+    if (opts.is_camera_input || opts.is_video_input)
     {
         cv::imshow("Segmentation", r_blend);
         waitKey(1);
     }
     else
     {
-        int frame_index = eo.GetFrameIndex();
+        int frame_index = eop.GetFrameIndex();
         char outfile_name[64];
-        if (is_preprocessed_input)
+        if (opts.input_file.empty())
         {
             snprintf(outfile_name, 64, "frame_%d.png", frame_index);
             cv::imwrite(outfile_name, frame);
@@ -387,87 +376,25 @@ bool WriteFrameOutput(const ExecutionObject &eo,
     return true;
 }
 
-
-void ProcessArgs(int argc, char *argv[], std::string& config,
-                 int& num_devices, DeviceType& device_type,
-                 std::string& input_file)
-{
-    const struct option long_options[] =
-    {
-        {"config",      required_argument, 0, 'c'},
-        {"num_devices", required_argument, 0, 'n'},
-        {"device_type", required_argument, 0, 't'},
-        {"image_file",  required_argument, 0, 'i'},
-        {"help",        no_argument,       0, 'h'},
-        {"verbose",     no_argument,       0, 'v'},
-        {0, 0, 0, 0}
-    };
-
-    int option_index = 0;
-
-    while (true)
-    {
-        int c = getopt_long(argc, argv, "c:n:t:i:hv", long_options, &option_index);
-
-        if (c == -1)
-            break;
-
-        switch (c)
-        {
-            case 'c': config = optarg;
-                      break;
-
-            case 'n': num_devices = atoi(optarg);
-                      assert (num_devices > 0 && num_devices <= 4);
-                      break;
-
-            case 't': if (*optarg == 'e')
-                          device_type = DeviceType::EVE;
-                      else if (*optarg == 'd')
-                          device_type = DeviceType::DSP;
-                      else
-                      {
-                          std::cerr << "Invalid argument to -t, only e or d"
-                                       " allowed" << std::endl;
-                          exit(EXIT_FAILURE);
-                      }
-                      break;
-
-            case 'i': input_file = optarg;
-                      break;
-
-            case 'v': __TI_show_debug_ = true;
-                      break;
-
-            case 'h': DisplayHelp();
-                      exit(EXIT_SUCCESS);
-                      break;
-
-            case '?': // Error in getopt_long
-                      exit(EXIT_FAILURE);
-                      break;
-
-            default:
-                      std::cerr << "Unsupported option: " << c << std::endl;
-                      break;
-        }
-    }
-}
-
 void DisplayHelp()
 {
-    std::cout << "Usage: segmentation\n"
-                 "  Will run segmentation network to perform pixel-level"
-                 " classification.\n  Use -c to run a different"
-                 "  segmentation network. Default is jseg21_tiscapes.\n"
-                 "Optional arguments:\n"
-                 " -c <config>          Valid configs: jseg21_tiscapes, jseg21\n"
-                 " -n <number of cores> Number of cores to use (1 - 4)\n"
-                 " -t <d|e>             Type of core. d -> DSP, e -> EVE\n"
-                 " -i <image>           Path to the image file\n"
-                 "                      Default are 3 frames in testvecs\n"
-                 " -i camera            Use camera as input\n"
-                 " -v                   Verbose output during execution\n"
-                 " -h                   Help\n";
+    std::cout <<
+    "Usage: segmentation\n"
+    "  Will run segmentation network to perform pixel-level"
+    " classification.\n  Use -c to run a different"
+    "  segmentation network. Default is jseg21_tiscapes.\n"
+    "Optional arguments:\n"
+    " -c <config>          Valid configs: jseg21_tiscapes, jseg21\n"
+    " -d <number>          Number of dsp cores to use\n"
+    " -e <number>          Number of eve cores to use\n"
+    " -i <image>           Path to the image file as input\n"
+    "                      Default are 9 frames in testvecs\n"
+    " -i camera<number>    Use camera as input\n"
+    "                      video input port: /dev/video<number>\n"
+    " -i <name>.{mp4,mov,avi}  Use video file as input\n"
+    " -f <number>          Number of frames to process\n"
+    " -w <number>          Output image/video width\n"
+    " -v                   Verbose output during execution\n"
+    " -h                   Help\n";
 }
 
index d930106f0aec6ddac344f04413a4851060bfb702..35aebf744d338cc841813024d96747355e25d692 100644 (file)
@@ -62,7 +62,7 @@ object_class_table_t* GetObjectClassTable(std::string &config)
 
 object_class_t* GetObjectClass(object_class_table_t *table, int index)
 {
-    if (index < 0 || index >= table->num_classes)  index = table->num_classes;
+    if (index < 0 || (unsigned int)index >= table->num_classes)  index = table->num_classes;
     return & (table->classes[index]);
 }
 
index f427f1c948b63d4750ebbedab8cc74aba42be933..3e67ba383c04c317801c1659b4e9b35d748419f0 100644 (file)
@@ -31,7 +31,8 @@ include ../make.common
 LIBS     += -lopencv_highgui -lopencv_imgcodecs -lopencv_videoio\
                        -lopencv_imgproc -lopencv_core
 
-SOURCES = main.cpp ../segmentation/object_classes.cpp
+SOURCES = main.cpp ../segmentation/object_classes.cpp ../common/utils.cpp \
+          ../common/video_utils.cpp
 
 $(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
        $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
index 6d39dda1561658b70a54964fc7efdf8a4505a4cb..4bcc707984bae927bf2c521e45c2138a89061872 100644 (file)
@@ -26,7 +26,6 @@
  *   THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 #include <signal.h>
-#include <getopt.h>
 #include <iostream>
 #include <iomanip>
 #include <fstream>
 #include <queue>
 #include <vector>
 #include <cstdio>
+#include <chrono>
 
 #include "executor.h"
 #include "execution_object.h"
+#include "execution_object_pipeline.h"
 #include "configuration.h"
 #include "../segmentation/object_classes.h"
+#include "../common/utils.h"
+#include "../common/video_utils.h"
+
+using namespace std;
+using namespace tidl;
+using namespace cv;
 
-#include "opencv2/core.hpp"
-#include "opencv2/imgproc.hpp"
-#include "opencv2/highgui.hpp"
-#include "opencv2/videoio.hpp"
 
 #define NUM_VIDEO_FRAMES  100
 #define DEFAULT_CONFIG    "jdetnet"
 #define DEFAULT_INPUT     "../test/testvecs/input/preproc_0_768x320.y"
+#define DEFAULT_INPUT_FRAMES (1)
 
-bool __TI_show_debug_ = false;
-bool is_default_input = false;
-bool is_preprocessed_input = false;
-bool is_camera_input       = false;
-int  orig_width;
-int  orig_height;
 object_class_table_t *object_class_table;
-
-using namespace tidl;
-using namespace cv;
-
-
-bool RunConfiguration(const std::string& config_file, uint32_t num_devices,
-                      DeviceType device_type, std::string& input_file);
-bool ReadFrame(ExecutionObject& eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap);
-bool WriteFrameOutput(const ExecutionObject &eo_in,
-                      const ExecutionObject &eo_out,
-                      const Configuration& configuration);
-
-void ReportTime(int frame_index, std::string device_name, double elapsed_host,
-                double elapsed_device);
-
-static void ProcessArgs(int argc, char *argv[],
-                        std::string& config,
-                        uint32_t& num_devices,
-                        DeviceType& device_type,
-                        std::string& input_file);
-
+uint32_t orig_width;
+uint32_t orig_height;
+
+
+bool RunConfiguration(const cmdline_opts_t& opts);
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c,
+                         int layers_group_id);
+bool ReadFrame(ExecutionObjectPipeline& eop, uint32_t frame_idx,
+               const Configuration& c, const cmdline_opts_t& opts,
+               VideoCapture &cap);
+bool WriteFrameOutput(const ExecutionObjectPipeline& eop,
+                      const Configuration& c, const cmdline_opts_t& opts);
 static void DisplayHelp();
 
-static double ms_diff(struct timespec &t0, struct timespec &t1)
-{ return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
-
 
 int main(int argc, char *argv[])
 {
@@ -98,245 +82,209 @@ int main(int argc, char *argv[])
     signal(SIGTERM, exit);
 
     // If there are no devices capable of offloading TIDL on the SoC, exit
-    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
-    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
-    if (num_eve == 0 || num_dsp == 0)
+    uint32_t num_eves = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsps = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eves == 0 || num_dsps == 0)
     {
-        std::cout << "ssd_multibox requires both EVE and DSP for execution."
-                  << std::endl;
+        cout << "ssd_multibox requires both EVE and DSP for execution." << endl;
         return EXIT_SUCCESS;
     }
 
     // Process arguments
-    std::string config      = DEFAULT_CONFIG;
-    std::string input_file  = DEFAULT_INPUT;
-    uint32_t num_devices    = 1;
-    DeviceType  device_type = DeviceType::EVE;
-    ProcessArgs(argc, argv, config, num_devices, device_type, input_file);
-
-    // Use same number of EVEs and DSPs
-    num_devices = std::min(num_devices, std::min(num_eve, num_dsp));
-    if (num_devices == 0)
+    cmdline_opts_t opts;
+    opts.config = DEFAULT_CONFIG;
+    opts.num_eves = 1;
+    opts.num_dsps = 1;
+    if (! ProcessArgs(argc, argv, opts))
     {
-        std::cout << "Partitioned execution requires at least 1 EVE and 1 DSP."
-                  << std::endl;
-        return EXIT_FAILURE;
+        DisplayHelp();
+        exit(EXIT_SUCCESS);
     }
-    if ((object_class_table = GetObjectClassTable(config)) == nullptr)
+    assert(opts.num_dsps != 0 && opts.num_eves != 0);
+    if (opts.num_frames == 0)
+        opts.num_frames = (opts.is_camera_input || opts.is_video_input) ?
+                          NUM_VIDEO_FRAMES :
+                          (opts.input_file.empty() ? DEFAULT_INPUT_FRAMES : 1);
+    if (opts.input_file.empty())
+        cout << "Input: " << DEFAULT_INPUT << endl;
+    else
+        cout << "Input: " << opts.input_file << endl;
+
+    // Get object class table
+    if ((object_class_table = GetObjectClassTable(opts.config)) == nullptr)
     {
-        std::cout << "No object classes defined for this config." << std::endl;
+        cout << "No object classes defined for this config." << endl;
         return EXIT_FAILURE;
     }
 
-    if (input_file == DEFAULT_INPUT)  is_default_input = true;
-    if (input_file == "camera")       is_camera_input = true;
-    if (input_file.length() > 2 &&
-        input_file.compare(input_file.length() - 2, 2, ".y") == 0)
-        is_preprocessed_input = true;
-    std::cout << "Input: " << input_file << std::endl;
-    std::string config_file = "../test/testvecs/config/infer/tidl_config_"
-                              + config + ".txt";
-    bool status = RunConfiguration(config_file, num_devices, device_type,
-                                   input_file);
-
+    // Run network
+    bool status = RunConfiguration(opts);
     if (!status)
     {
-        std::cout << "ssd_multibox FAILED" << std::endl;
+        cout << "ssd_multibox FAILED" << endl;
         return EXIT_FAILURE;
     }
 
-    std::cout << "ssd_multibox PASSED" << std::endl;
+    cout << "ssd_multibox PASSED" << endl;
     return EXIT_SUCCESS;
 }
 
-bool RunConfiguration(const std::string& config_file, uint32_t num_devices,
-                      DeviceType device_type, std::string& input_file)
+bool RunConfiguration(const cmdline_opts_t& opts)
 {
-    DeviceIds ids;
-    for (int i = 0; i < num_devices; i++)
-        ids.insert(static_cast<DeviceId>(i));
-
     // Read the TI DL configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
+    Configuration c;
+    std::string config_file = "../test/testvecs/config/infer/tidl_config_"
+                              + opts.config + ".txt";
+    bool status = c.ReadFromFile(config_file);
     if (!status)
     {
-        std::cerr << "Error in configuration file: " << config_file
-                  << std::endl;
+        cerr << "Error in configuration file: " << config_file << endl;
         return false;
     }
+    c.enableApiTrace = opts.verbose;
 
-    // setup input
-    int num_frames = is_default_input ? 3 : 1;
+    // setup camera/video input
     VideoCapture cap;
-    std::string image_file;
-    if (is_camera_input)
-    {
-        cap = VideoCapture(1);  // cap = VideoCapture("test.mp4");
-        if (! cap.isOpened())
-        {
-            std::cerr << "Cannot open camera input." << std::endl;
-            return false;
-        }
-        num_frames = NUM_VIDEO_FRAMES;
-        namedWindow("SSD_Multibox", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
-    }
-    else
-    {
-        image_file = input_file;
-    }
+    if (! SetVideoInputOutput(cap, opts, "SSD_Multibox"))  return false;
 
     try
     {
-        // Create a executor with the approriate core type, number of cores
+        // Create Executors with the approriate core type, number of cores
         // and configuration specified
         // EVE will run layersGroupId 1 in the network, while
         // DSP will run layersGroupId 2 in the network
-        Executor executor_eve(DeviceType::EVE, ids, configuration, 1);
-        Executor executor_dsp(DeviceType::DSP, ids, configuration, 2);
-
-        // Query Executor for set of ExecutionObjects created
-        const ExecutionObjects& execution_objects_eve =
-                                            executor_eve.GetExecutionObjects();
-        const ExecutionObjects& execution_objects_dsp =
-                                            executor_dsp.GetExecutionObjects();
-        int num_eos = execution_objects_eve.size();
-
-        // Allocate input and output buffers for each execution object
-        // Note that "out" is both the output of eo_eve and the input of eo_dsp
-        // This is how two layersGroupIds, 1 and 2, are tied together
-        std::vector<void *> buffers;
-        for (int i = 0; i < num_eos; i++)
-        {
-            ExecutionObject *eo_eve = execution_objects_eve[i].get();
-            size_t in_size  = eo_eve->GetInputBufferSizeInBytes();
-            size_t out_size = eo_eve->GetOutputBufferSizeInBytes();
-            ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)  };
-            ArgInfo out = { ArgInfo(malloc(out_size), out_size) };
-            eo_eve->SetInputOutputBuffer(in, out);
-
-            ExecutionObject *eo_dsp = execution_objects_dsp[i].get();
-            size_t out2_size = eo_dsp->GetOutputBufferSizeInBytes();
-            ArgInfo out2 = { ArgInfo(malloc(out2_size), out2_size) };
-            eo_dsp->SetInputOutputBuffer(out, out2);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-            buffers.push_back(out2.ptr());
-        }
-
-        #define MAX_NUM_EOS  4
-        struct timespec t0[MAX_NUM_EOS], t1, tloop0, tloop1;
-        clock_gettime(CLOCK_MONOTONIC, &tloop0);
-
-        // Process frames with available execution objects in a pipelined manner
-        // additional num_eos iterations to flush the pipeline (epilogue)
-        ExecutionObject *eo_eve, *eo_dsp, *eo_input;
-        for (int frame_idx = 0;
-             frame_idx < num_frames + num_eos; frame_idx++)
+        Executor* e_eve = CreateExecutor(DeviceType::EVE, opts.num_eves, c, 1);
+        Executor* e_dsp = CreateExecutor(DeviceType::DSP, opts.num_dsps, c, 2);
+
+        // Construct ExecutionObjectPipeline that utilizes multiple
+        // ExecutionObjects to process a single frame, each ExecutionObject
+        // processes one layerGroup of the network
+        //
+        // Pipeline depth can enable more optimized pipeline execution:
+        // Given one EVE and one DSP as an example, with different
+        //     pipeline_depth, we have different execution behavior:
+        // If pipeline_depth is set to 1,
+        //    we create one EOP: eop0 (eve0, dsp0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [eve0...][dsp0]
+        //    eop0:                [eve0...][dsp0]
+        //    eop0:                               [eve0...][dsp0]
+        //    eop0:                                              [eve0...][dsp0]
+        // If pipeline_depth is set to 2,
+        //    we create two EOPs: eop0 (eve0, dsp0), eop1(eve0, dsp0)
+        //    pipeline execution of multiple frames over time is as follows:
+        //    --------------------- time ------------------->
+        //    eop0: [eve0...][dsp0]
+        //    eop1:          [eve0...][dsp0]
+        //    eop0:                   [eve0...][dsp0]
+        //    eop1:                            [eve0...][dsp0]
+        // Additional benefit of setting pipeline_depth to 2 is that
+        //    it can also overlap host ReadFrame() with device processing:
+        //    --------------------- time ------------------->
+        //    eop0: [RF][eve0...][dsp0]
+        //    eop1:     [RF]     [eve0...][dsp0]
+        //    eop0:                    [RF][eve0...][dsp0]
+        //    eop1:                             [RF][eve0...][dsp0]
+        vector<ExecutionObjectPipeline *> eops;
+        uint32_t pipeline_depth = 2;  // 2 EOs in EOP -> depth 2
+        for (uint32_t j = 0; j < pipeline_depth; j++)
+            for (uint32_t i = 0; i < max(opts.num_eves, opts.num_dsps); i++)
+                eops.push_back(new ExecutionObjectPipeline(
+                      {(*e_eve)[i%opts.num_eves], (*e_dsp)[i%opts.num_dsps]}));
+        uint32_t num_eops = eops.size();
+
+        // Allocate input/output memory for each EOP
+        AllocateMemory(eops);
+
+        chrono::time_point<chrono::steady_clock> tloop0, tloop1;
+        tloop0 = chrono::steady_clock::now();
+
+        // Process frames with available eops in a pipelined manner
+        // additional num_eops iterations to flush pipeline (epilogue)
+        for (uint32_t frame_idx = 0;
+             frame_idx < opts.num_frames + num_eops; frame_idx++)
         {
-            eo_eve = execution_objects_eve[frame_idx % num_eos].get();
-            eo_dsp = execution_objects_dsp[frame_idx % num_eos].get();
+            ExecutionObjectPipeline* eop = eops[frame_idx % num_eops];
 
-            // Wait for previous frame on the same eo to finish processing
-            if (eo_dsp->ProcessFrameWait())
+            // Wait for previous frame on the same eop to finish processing
+            if (eop->ProcessFrameWait())
             {
-                int finished_idx = eo_dsp->GetFrameIndex();
-                clock_gettime(CLOCK_MONOTONIC, &t1);
-                ReportTime(finished_idx, "DSP",
-                           ms_diff(t0[finished_idx % num_eos], t1),
-                           eo_dsp->GetProcessTimeInMilliSeconds());
-
-                eo_input = execution_objects_eve[finished_idx % num_eos].get();
-                WriteFrameOutput(*eo_input, *eo_dsp, configuration);
+                ReportTime(eop);
+                WriteFrameOutput(*eop, c, opts);
             }
 
             // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo_eve, frame_idx, configuration, num_frames,
-                          image_file, cap))
-            {
-                clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
-                eo_eve->ProcessFrameStartAsync();
-
-                if (eo_eve->ProcessFrameWait())
-                {
-                    clock_gettime(CLOCK_MONOTONIC, &t1);
-                    ReportTime(frame_idx, "EVE",
-                               ms_diff(t0[frame_idx % num_eos], t1),
-                               eo_eve->GetProcessTimeInMilliSeconds());
-
-                    clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
-                    eo_dsp->ProcessFrameStartAsync();
-                }
-            }
+            if (ReadFrame(*eop, frame_idx, c, opts, cap))
+                eop->ProcessFrameStartAsync();
         }
 
-        clock_gettime(CLOCK_MONOTONIC, &tloop1);
-        std::cout << "Loop total time (including read/write/print/etc): "
-                  << std::setw(6) << std::setprecision(4)
-                  << ms_diff(tloop0, tloop1) << "ms" << std::endl;
+        tloop1 = chrono::steady_clock::now();
+        chrono::duration<float> elapsed = tloop1 - tloop0;
+        cout << "Loop total time (including read/write/opencv/print/etc): "
+                  << setw(6) << setprecision(4)
+                  << (elapsed.count() * 1000) << "ms" << endl;
 
-        for (auto b : buffers)
-            free(b);
+        FreeMemory(eops);
+        for (auto eop : eops)  delete eop;
+        delete e_eve;
+        delete e_dsp;
     }
     catch (tidl::Exception &e)
     {
-        std::cerr << e.what() << std::endl;
+        cerr << e.what() << endl;
         status = false;
     }
 
     return status;
 }
 
-void ReportTime(int frame_index, std::string device_name, double elapsed_host,
-                double elapsed_device)
+// Create an Executor with the specified type and number of EOs
+Executor* CreateExecutor(DeviceType dt, uint32_t num, const Configuration& c,
+                         int layers_group_id)
 {
-    double overhead = 100 - (elapsed_device/elapsed_host*100);
-    std::cout << "frame[" << frame_index << "]: "
-              << "Time on " << device_name << ": "
-              << std::setw(6) << std::setprecision(4)
-              << elapsed_device << "ms, "
-              << "host: "
-              << std::setw(6) << std::setprecision(4)
-              << elapsed_host << "ms ";
-    std::cout << "API overhead: "
-              << std::setw(6) << std::setprecision(3)
-              << overhead << " %" << std::endl;
-}
+    if (num == 0) return nullptr;
 
+    DeviceIds ids;
+    for (uint32_t i = 0; i < num; i++)
+        ids.insert(static_cast<DeviceId>(i));
 
-bool ReadFrame(ExecutionObject &eo, int frame_idx,
-               const Configuration& configuration, int num_frames,
-               std::string& image_file, VideoCapture &cap)
+    return new Executor(dt, ids, c, layers_group_id);
+}
+
+bool ReadFrame(ExecutionObjectPipeline& eop, uint32_t frame_idx,
+               const Configuration& c, const cmdline_opts_t& opts,
+               VideoCapture &cap)
 {
-    if (frame_idx >= num_frames)
+    if ((uint32_t)frame_idx >= opts.num_frames)
         return false;
-    eo.SetFrameIndex(frame_idx);
 
-    char*  frame_buffer = eo.GetInputBufferPtr();
+    eop.SetFrameIndex(frame_idx);
+
+    char*  frame_buffer = eop.GetInputBufferPtr();
     assert (frame_buffer != nullptr);
-    int channel_size = configuration.inWidth * configuration.inHeight;
+    int channel_size = c.inWidth * c.inHeight;
 
     Mat image;
-    if (! image_file.empty())
+    if (!opts.is_camera_input && !opts.is_video_input)
     {
-        if (is_preprocessed_input)
+        if (opts.input_file.empty())
         {
-            std::ifstream ifs(image_file, std::ios::binary);
-            ifs.seekg(frame_idx * channel_size * 3);
+            ifstream ifs(DEFAULT_INPUT, ios::binary);
+            ifs.seekg((frame_idx % DEFAULT_INPUT_FRAMES) * channel_size * 3);
             ifs.read(frame_buffer, channel_size * 3);
             bool ifs_status = ifs.good();
             ifs.close();
-            orig_width  = configuration.inWidth;
-            orig_height = configuration.inHeight;
+            orig_width  = c.inWidth;
+            orig_height = c.inHeight;
             return ifs_status;  // already PreProc-ed
         }
         else
         {
-            image = cv::imread(image_file, CV_LOAD_IMAGE_COLOR);
+            image = cv::imread(opts.input_file, CV_LOAD_IMAGE_COLOR);
             if (image.empty())
             {
-                std::cerr << "Unable to read from: " << image_file << std::endl;
+                cerr << "Unable to read from: " << opts.input_file << endl;
                 return false;
             }
         }
@@ -357,8 +305,7 @@ bool ReadFrame(ExecutionObject &eo, int frame_idx,
     Mat s_image, bgr_frames[3];
     orig_width  = image.cols;
     orig_height = image.rows;
-    cv::resize(image, s_image,
-               Size(configuration.inWidth, configuration.inHeight),
+    cv::resize(image, s_image, Size(c.inWidth, c.inHeight),
                0, 0, cv::INTER_AREA);
     cv::split(s_image, bgr_frames);
     memcpy(frame_buffer,                bgr_frames[0].ptr(), channel_size);
@@ -368,25 +315,24 @@ bool ReadFrame(ExecutionObject &eo, int frame_idx,
 }
 
 // Create frame with boxes drawn around classified objects
-bool WriteFrameOutput(const ExecutionObject &eo_in,
-                      const ExecutionObject &eo_out,
-                      const Configuration& configuration)
+bool WriteFrameOutput(const ExecutionObjectPipeline& eop,
+                      const Configuration& c, const cmdline_opts_t& opts)
 {
     // Asseembly original frame
-    int width  = configuration.inWidth;
-    int height = configuration.inHeight;
+    int width  = c.inWidth;
+    int height = c.inHeight;
     int channel_size = width * height;
     Mat frame, r_frame, bgr[3];
 
-    unsigned char *in = (unsigned char *) eo_in.GetInputBufferPtr();
+    unsigned char *in = (unsigned char *) eop.GetInputBufferPtr();
     bgr[0] = Mat(height, width, CV_8UC(1), in);
     bgr[1] = Mat(height, width, CV_8UC(1), in + channel_size);
     bgr[2] = Mat(height, width, CV_8UC(1), in + channel_size*2);
     cv::merge(bgr, 3, frame);
 
-    int frame_index = eo_in.GetFrameIndex();
+    int frame_index = eop.GetFrameIndex();
     char outfile_name[64];
-    if (! is_camera_input && is_preprocessed_input)
+    if (opts.input_file.empty())
     {
         snprintf(outfile_name, 64, "frame_%d.png", frame_index);
         cv::imwrite(outfile_name, frame);
@@ -394,15 +340,14 @@ bool WriteFrameOutput(const ExecutionObject &eo_in,
     }
 
     // Draw boxes around classified objects
-    float *out = (float *) eo_out.GetOutputBufferPtr();
-    int num_floats = eo_out.GetOutputBufferSizeInBytes() / sizeof(float);
+    float *out = (float *) eop.GetOutputBufferPtr();
+    int num_floats = eop.GetOutputBufferSizeInBytes() / sizeof(float);
     for (int i = 0; i < num_floats / 7; i++)
     {
         int index = (int)    out[i * 7 + 0];
         if (index < 0)  break;
 
         int   label = (int)  out[i * 7 + 1];
-        float score =        out[i * 7 + 2];
         int   xmin  = (int) (out[i * 7 + 3] * width);
         int   ymin  = (int) (out[i * 7 + 4] * height);
         int   xmax  = (int) (out[i * 7 + 5] * width);
@@ -423,9 +368,13 @@ bool WriteFrameOutput(const ExecutionObject &eo_in,
                              object_class->color.red), 2);
     }
 
-    // output
-    cv::resize(frame, r_frame, Size(orig_width, orig_height));
-    if (is_camera_input)
+    // Resize to output width/height, keep aspect ratio
+    uint32_t output_width = opts.output_width;
+    if (output_width == 0)  output_width = orig_width;
+    uint32_t output_height = (output_width*1.0f) / orig_width * orig_height;
+    cv::resize(frame, r_frame, Size(output_width, output_height));
+
+    if (opts.is_camera_input || opts.is_video_input)
     {
         cv::imshow("SSD_Multibox", r_frame);
         waitKey(1);
@@ -441,77 +390,28 @@ bool WriteFrameOutput(const ExecutionObject &eo_in,
     return true;
 }
 
-
-void ProcessArgs(int argc, char *argv[], std::string& config,
-                 uint32_t& num_devices, DeviceType& device_type,
-                 std::string& input_file)
-{
-    const struct option long_options[] =
-    {
-        {"config",      required_argument, 0, 'c'},
-        {"num_devices", required_argument, 0, 'n'},
-        {"image_file",  required_argument, 0, 'i'},
-        {"help",        no_argument,       0, 'h'},
-        {"verbose",     no_argument,       0, 'v'},
-        {0, 0, 0, 0}
-    };
-
-    int option_index = 0;
-
-    while (true)
-    {
-        int c = getopt_long(argc, argv, "c:n:i:hv", long_options, &option_index);
-
-        if (c == -1)
-            break;
-
-        switch (c)
-        {
-            case 'c': config = optarg;
-                      break;
-
-            case 'n': num_devices = atoi(optarg);
-                      assert (num_devices > 0 && num_devices <= 4);
-                      break;
-
-            case 'i': input_file = optarg;
-                      break;
-
-            case 'v': __TI_show_debug_ = true;
-                      break;
-
-            case 'h': DisplayHelp();
-                      exit(EXIT_SUCCESS);
-                      break;
-
-            case '?': // Error in getopt_long
-                      exit(EXIT_FAILURE);
-                      break;
-
-            default:
-                      std::cerr << "Unsupported option: " << c << std::endl;
-                      break;
-        }
-    }
-}
-
 void DisplayHelp()
 {
-    std::cout << "Usage: ssd_multibox\n"
-                 "  Will run partitioned ssd_multibox network to perform "
-                 "multi-objects detection\n"
-                 "  and classification.  First part of network "
-                 "(layersGroupId 1) runs on EVE,\n"
-                 "  second part (layersGroupId 2) runs on DSP.\n"
-                 "  Use -c to run a different segmentation network. "
-                 "Default is jdetnet.\n"
-                 "Optional arguments:\n"
-                 " -c <config>          Valid configs: jdetnet \n"
-                 " -n <number of cores> Number of cores to use (1 - 4)\n"
-                 " -i <image>           Path to the image file\n"
-                 "                      Default is 1 frame in testvecs\n"
-                 " -i camera            Use camera as input\n"
-                 " -v                   Verbose output during execution\n"
-                 " -h                   Help\n";
+    std::cout <<
+    "Usage: ssd_multibox\n"
+    "  Will run partitioned ssd_multibox network to perform "
+    "multi-objects detection\n"
+    "  and classification.  First part of network "
+    "(layersGroupId 1) runs on EVE,\n"
+    "  second part (layersGroupId 2) runs on DSP.\n"
+    "  Use -c to run a different segmentation network.  Default is jdetnet.\n"
+    "Optional arguments:\n"
+    " -c <config>          Valid configs: jdetnet \n"
+    " -d <number>          Number of dsp cores to use\n"
+    " -e <number>          Number of eve cores to use\n"
+    " -i <image>           Path to the image file as input\n"
+    "                      Default are 9 frames in testvecs\n"
+    " -i camera<number>    Use camera as input\n"
+    "                      video input port: /dev/video<number>\n"
+    " -i <name>.{mp4,mov,avi}  Use video file as input\n"
+    " -f <number>          Number of frames to process\n"
+    " -w <number>          Output image/video width\n"
+    " -v                   Verbose output during execution\n"
+    " -h                   Help\n";
 }
 
index 646fced80a602beb39e4750f23abfe98089fb846..df609aa35d23cb3cb1d94854fadf6a600a85f120 100644 (file)
@@ -28,7 +28,9 @@ EXE = test_tidl
 
 include ../make.common
 
-$(EXE): SOURCES = main.cpp multiple_executors.cpp
+CXXFLAGS += -I../common
+
+SOURCES = main.cpp multiple_executors.cpp ../common/utils.cpp
 
 $(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
        $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
index bc87855ab6fbcb381103bf2d439cd88fb74e440e..3fdb9065438f8bd3ebe47e70af0c1183b3357985 100644 (file)
 #include <signal.h>
 #include <getopt.h>
 #include <iostream>
-#include <iomanip>
 #include <fstream>
 #include <cassert>
 #include <string>
-#include <functional>
-#include <algorithm>
-#include <time.h>
+#include <vector>
 
 #include "executor.h"
 #include "execution_object.h"
 #include "configuration.h"
-
-bool __TI_show_debug_ = false;
+#include "utils.h"
 
 using namespace tidl;
+using std::string;
 
-bool RunMultipleExecutors(const std::string& config_file_1,
-                          const std::string& config_file_2,
-                          uint32_t num_devices_available);
+bool RunMultipleExecutors(const string& config_file_1,
+                          const string& config_file_2,
+                          uint32_t      num_devices_available);
 
-bool RunConfiguration(const std::string& config_file, int num_devices,
-                      DeviceType device_type);
-bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
+bool RunConfiguration(const string& config_file,
+                      int           num_devices,
+                      DeviceType    device_type);
 
-bool ReadFrame(ExecutionObject&     eo,
-               int                  frame_idx,
-               const Configuration& configuration,
-               std::istream&        input_file);
+bool RunAllConfigurations(int32_t    num_devices,
+                          DeviceType device_type);
 
-bool WriteFrame(const ExecutionObject &eo,
-                std::ostream& output_file);
+bool RunNetwork(DeviceType           device_type,
+                const DeviceIds&     ids,
+                const Configuration& c,
+                std::istream&        input,
+                std::ostream&        output);
 
 static void ProcessArgs(int argc, char *argv[],
-                        std::string& config_file,
-                        int& num_devices,
+                        string&     config_file,
+                        int&        num_devices,
                         DeviceType& device_type);
 
 static void DisplayHelp();
 
-static double ms_diff(struct timespec &t0, struct timespec &t1)
-{ return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
-
+bool verbose = false;
 
 int main(int argc, char *argv[])
 {
@@ -82,13 +78,12 @@ int main(int argc, char *argv[])
     uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
     if (num_eve == 0 && num_dsp == 0)
     {
-        std::cout << "TI DL not supported on this SoC." << std::endl;
+        std::cout << "TI DL not supported on this processor." << std::endl;
         return EXIT_SUCCESS;
     }
-    std::cout << "API Version: " << Executor::GetAPIVersion() << std::endl;
 
     // Process arguments
-    std::string config_file;
+    string      config_file;
     int         num_devices = 1;
     DeviceType  device_type = DeviceType::EVE;
     ProcessArgs(argc, argv, config_file, num_devices, device_type);
@@ -102,7 +97,7 @@ int main(int argc, char *argv[])
         {
             // Run on 2 devices because there is not enough CMEM available by
             // default
-            if (num_eve = 4)
+            if (num_eve == 4)
             {
                 std::cout
                  << "Running on 2 EVE devices instead of the available 4 "
@@ -144,90 +139,78 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
         ids.insert(static_cast<DeviceId>(i));
 
     // Read the TI DL configuration file
-    Configuration configuration;
-    bool status = configuration.ReadFromFile(config_file);
-    if (!status)
-    {
-        std::cerr << "Error in configuration file: " << config_file
-                  << std::endl;
-        return false;
-    }
+    Configuration c;
+    if (!c.ReadFromFile(config_file)) return false;
+    if (verbose)                      c.enableApiTrace = true;
 
     // Open input and output files
-    std::ifstream input_data_file(configuration.inData, std::ios::binary);
-    std::ofstream output_data_file(configuration.outData, std::ios::binary);
+    std::ifstream input_data_file(c.inData, std::ios::binary);
+    std::ofstream output_data_file(c.outData, std::ios::binary);
     assert (input_data_file.good());
     assert (output_data_file.good());
 
+    bool status = RunNetwork(device_type, ids, c,
+                             input_data_file, output_data_file);
+
+    input_data_file.close();
+    output_data_file.close();
+
+    return status;
+}
+
+bool RunNetwork(DeviceType           device_type,
+                const DeviceIds&     ids,
+                const Configuration& c,
+                std::istream&        input,
+                std::ostream&        output)
+{
+    bool status = true;
+
     try
     {
-        // Create a executor with the approriate core type, number of cores
-        // and configuration specified
-        Executor executor(device_type, ids, configuration);
+        // Create a executor with the specified core type, number of cores
+        // and configuration
+        Executor E(device_type, ids, c);
+
+        std::vector<ExecutionObject *> EOs;
+        for (unsigned int i = 0; i < E.GetNumExecutionObjects(); i++)
+            EOs.push_back(E[i]);
 
-        // Query Executor for set of ExecutionObjects created
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
+        int num_eos = EOs.size();
 
         // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
+        for (auto eo : EOs)
         {
             size_t in_size  = eo->GetInputBufferSizeInBytes();
             size_t out_size = eo->GetOutputBufferSizeInBytes();
             ArgInfo in  = { ArgInfo(malloc(in_size),  in_size)};
             ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
             eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
         }
 
-        #define MAX_NUM_EOS  4
-        struct timespec t0[MAX_NUM_EOS], t1;
-
         // Process frames with available execution objects in a pipelined manner
         // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < configuration.numFrames + num_eos; frame_idx++)
+        for (int frame_idx = 0; frame_idx < c.numFrames + num_eos; frame_idx++)
         {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
+            ExecutionObject* eo = EOs[frame_idx % num_eos];
 
             // Wait for previous frame on the same eo to finish processing
             if (eo->ProcessFrameWait())
             {
-                clock_gettime(CLOCK_MONOTONIC, &t1);
-                double elapsed_host =
-                                ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
-                double elapsed_device = eo->GetProcessTimeInMilliSeconds();
-                double overhead = 100 - (elapsed_device/elapsed_host*100);
-
-                std::cout << "frame[" << eo->GetFrameIndex() << "]: "
-                          << "Time on device: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_device << "ms, "
-                          << "host: "
-                          << std::setw(6) << std::setprecision(4)
-                          << elapsed_host << "ms ";
-                std::cout << "API overhead: "
-                          << std::setw(6) << std::setprecision(3)
-                          << overhead << " %" << std::endl;
-
-                WriteFrame(*eo, output_data_file);
+                ReportTime(eo);
+                WriteFrame(eo, output);
             }
 
             // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
-            {
-                clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
+            if (ReadFrame(eo, frame_idx, c, input))
                 eo->ProcessFrameStartAsync();
-            }
         }
 
-        for (auto b : buffers)
-            free(b);
-
+        for (auto eo : EOs)
+        {
+            free(eo->GetInputBufferPtr());
+            free(eo->GetOutputBufferPtr());
+        }
     }
     catch (tidl::Exception &e)
     {
@@ -235,13 +218,10 @@ bool RunConfiguration(const std::string& config_file, int num_devices,
         status = false;
     }
 
-
-    input_data_file.close();
-    output_data_file.close();
-
     return status;
 }
 
+
 namespace tidl {
 extern bool CompareFiles (const std::string &F1, const std::string &F2);
 extern bool CompareFrames(const std::string &F1, const std::string &F2,
@@ -301,49 +281,6 @@ bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
     return true;
 }
 
-
-
-bool ReadFrame(ExecutionObject &eo, int frame_idx,
-               const Configuration& configuration,
-               std::istream& input_file)
-{
-    if (frame_idx >= configuration.numFrames)
-        return false;
-
-    char*  frame_buffer = eo.GetInputBufferPtr();
-    assert (frame_buffer != nullptr);
-
-    input_file.read(eo.GetInputBufferPtr(),
-                    eo.GetInputBufferSizeInBytes());
-
-    if (input_file.eof())
-        return false;
-
-    assert (input_file.good());
-
-    // Set the frame index  being processed by the EO. This is used to
-    // sort the frames before they are output
-    eo.SetFrameIndex(frame_idx);
-
-    if (input_file.good())
-        return true;
-
-    return false;
-}
-
-bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
-{
-    output_file.write(
-            eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
-    assert(output_file.good() == true);
-
-    if (output_file.good())
-        return true;
-
-    return false;
-}
-
-
 void ProcessArgs(int argc, char *argv[], std::string& config_file,
                  int& num_devices, DeviceType& device_type)
 {
@@ -387,7 +324,7 @@ void ProcessArgs(int argc, char *argv[], std::string& config_file,
                       }
                       break;
 
-            case 'v': __TI_show_debug_ = true;
+            case 'v': verbose = true;
                       break;
 
             case 'h': DisplayHelp();
index 78a1789c74332e4c3e4f7eaad8a652f1482d5ca5..8bf839f7fc79a4df54a709efe69bd9e9d9fd647b 100644 (file)
@@ -1,29 +1,29 @@
 /******************************************************************************
  * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
- *   All rights reserved.
+ * All rights reserved.
  *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions are met:
- *       * Redistributions of source code must retain the above copyright
- *         notice, this list of conditions and the following disclaimer.
- *       * Redistributions in binary form must reproduce the above copyright
- *         notice, this list of conditions and the following disclaimer in the
- *         documentation and/or other materials provided with the distribution.
- *       * Neither the name of Texas Instruments Incorporated nor the
- *         names of its contributors may be used to endorse or promote products
- *         derived from this software without specific prior written permission.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Texas Instruments Incorporated nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
  *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- *   THE POSSIBILITY OF SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
 //! @file multiple_executors.cpp
 #include "executor.h"
 #include "execution_object.h"
 #include "configuration.h"
+#include "utils.h"
 
 using namespace tidl;
 
-extern bool ReadFrame(ExecutionObject&     eo,
-               int                  frame_idx,
-               const Configuration& configuration,
-               std::istream&        input_file);
-
-extern bool WriteFrame(const ExecutionObject &eo,
-                std::ostream& output_file);
+extern
+bool RunNetwork(DeviceType           device_type,
+                const DeviceIds&     ids,
+                const Configuration& c,
+                std::istream&        input,
+                std::ostream&        output);
 
 void* run_network(void *data);
 
 struct ThreadArg
 {
-    std::string config_file;
-    DeviceIds ids;
-    ThreadArg(const DeviceIds& ids, const std::string& s):
-        ids(ids), config_file(s) {}
+    ThreadArg(const DeviceIds& device_ids, const std::string& s):
+        ids(device_ids), config_file(s) {}
 
+    DeviceIds   ids;
+    std::string config_file;
 };
 
 bool thread_status[2];
@@ -153,57 +153,8 @@ void* run_network(void *data)
     assert (input_data_file.good());
     assert (output_data_file.good());
 
-    // Determine input frame size from configuration
-    size_t frame_sz = configuration.inWidth * configuration.inHeight *
-                      configuration.inNumChannels;
-
-    try
-    {
-        // Create a executor with the approriate core type, number of cores
-        // and configuration specified
-        Executor executor(DeviceType::EVE, ids, configuration);
-
-        const ExecutionObjects& execution_objects =
-                                                executor.GetExecutionObjects();
-        int num_eos = execution_objects.size();
-
-        // Allocate input and output buffers for each execution object
-        std::vector<void *> buffers;
-        for (auto &eo : execution_objects)
-        {
-            ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
-            ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
-            eo->SetInputOutputBuffer(in, out);
-
-            buffers.push_back(in.ptr());
-            buffers.push_back(out.ptr());
-        }
-
-        // Process frames with available execution objects in a pipelined manner
-        // additional num_eos iterations to flush the pipeline (epilogue)
-        for (int frame_idx = 0;
-             frame_idx < configuration.numFrames + num_eos; frame_idx++)
-        {
-            ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
-
-            // Wait for previous frame on the same eo to finish processing
-            if (eo->ProcessFrameWait())
-                WriteFrame(*eo, output_data_file);
-
-            // Read a frame and start processing it with current eo
-            if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
-                eo->ProcessFrameStartAsync();
-        }
-
-
-        for (auto b : buffers)
-            __free_ddr(b);
-    }
-    catch (tidl::Exception &e)
-    {
-        std::cerr << e.what() << std::endl;
-        status = false;
-    }
+    RunNetwork(DeviceType::EVE, ids, configuration,
+               input_data_file, output_data_file);
 
     input_data_file.close();
     output_data_file.close();
diff --git a/examples/two_eo_per_frame/Makefile b/examples/two_eo_per_frame/Makefile
new file mode 100644 (file)
index 0000000..32da437
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+EXE = two_eo_per_frame
+
+include ../make.common
+
+CXXFLAGS += -I../common
+
+SOURCES = main.cpp ../common/utils.cpp
+
+$(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
+       $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
+
diff --git a/examples/two_eo_per_frame/main.cpp b/examples/two_eo_per_frame/main.cpp
new file mode 100644 (file)
index 0000000..5cc618f
--- /dev/null
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * Copyright (c) 2017-2018  Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//
+// This example illustrates using multiple EOs to process a single frame
+// For details, refer http://downloads.ti.com/mctools/esd/docs/tidl-api/
+//
+#include <signal.h>
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+#include "executor.h"
+#include "execution_object.h"
+#include "execution_object_pipeline.h"
+#include "configuration.h"
+#include "utils.h"
+
+using namespace tidl;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+using EOP = tidl::ExecutionObjectPipeline;
+
+bool Run(int num_eve,int num_dsp, const char* ref_output);
+
+int main(int argc, char *argv[])
+{
+    // Catch ctrl-c to ensure a clean exit
+    signal(SIGABRT, exit);
+    signal(SIGTERM, exit);
+
+    // This example requires both EVE and C66x
+    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eve == 0 || num_dsp == 0)
+    {
+        std::cout << "TI DL not supported on this SoC." << std::endl;
+        return EXIT_SUCCESS;
+    }
+
+    string ref_file ="../test/testvecs/reference/j11_v2_ref.bin";
+    unique_ptr<const char> reference_output(ReadReferenceOutput(ref_file));
+
+    bool status = Run(num_eve, num_dsp, reference_output.get());
+
+    if (!status)
+    {
+        std::cout << "FAILED" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::cout << "PASSED" << std::endl;
+    return EXIT_SUCCESS;
+}
+
+bool Run(int num_eve, int num_dsp, const char* ref_output)
+{
+    string config_file ="../test/testvecs/config/infer/tidl_config_j11_v2.txt";
+
+    Configuration c;
+    if (!c.ReadFromFile(config_file))
+        return false;
+
+    // Heap sizes for this network determined using Configuration::showHeapStats
+    c.PARAM_HEAP_SIZE   = (3 << 20); // 3MB
+    c.NETWORK_HEAP_SIZE = (20 << 20); // 20MB
+
+    // Run this example for 16 input frames
+    c.numFrames = 16;
+
+    // Assign layers 12, 13 and 14 to the DSP layer group
+    const int EVE_LG = 1;
+    const int DSP_LG = 2;
+    c.layerIndex2LayerGroupId = { {12, DSP_LG}, {13, DSP_LG}, {14, DSP_LG} };
+
+    // Open input file for reading
+    std::ifstream input(c.inData, std::ios::binary);
+
+    bool status = true;
+    try
+    {
+        // Create Executors - use all the DSP and EVE cores available
+        // Specify layer group id for each Executor
+        unique_ptr<Executor> eve(CreateExecutor(DeviceType::EVE,
+                                                num_eve, c, EVE_LG));
+        unique_ptr<Executor> dsp(CreateExecutor(DeviceType::DSP,
+                                                num_dsp, c, DSP_LG));
+
+        // Create pipelines. Each pipeline has 1 EVE and 1 DSP. If there are
+        // more EVEs than DSPs, the DSPs are shared across multiple
+        // pipelines. E.g.
+        // 2 EVE, 2 DSP: EVE1 -> DSP1, EVE2 -> DSP2
+        // 4 EVE, 2 DSP: EVE1 -> DSP1, EVE2 -> DSP2, EVE3 -> DSP1, EVE4 ->DSP2
+        std::vector<EOP *> EOPs;
+        uint32_t num_pipe = std::max(num_eve, num_dsp);
+        for (uint32_t i = 0; i < num_pipe; i++)
+              EOPs.push_back(new EOP( { (*eve)[i % num_eve],
+                                        (*dsp)[i % num_dsp] } ));
+
+        AllocateMemory(EOPs);
+
+        // Process frames with EOs in a pipelined manner
+        // additional num_eos iterations to flush the pipeline (epilogue)
+        int num_eops = EOPs.size();
+        for (int frame_idx = 0; frame_idx < c.numFrames + num_eops; frame_idx++)
+        {
+            EOP* eop = EOPs[frame_idx % num_eops];
+
+            // Wait for previous frame on the same EOP to finish processing
+            if (eop->ProcessFrameWait())
+            {
+                ReportTime(eop);
+
+                // The reference output is valid only for the first frame
+                // processed on each EOP
+                if (frame_idx < num_eops && !CheckFrame(eop, ref_output))
+                    status = false;
+            }
+
+            // Read a frame and start processing it with current eo
+            if (ReadFrame(eop, frame_idx, c, input))
+                eop->ProcessFrameStartAsync();
+        }
+
+        FreeMemory(EOPs);
+
+    }
+    catch (tidl::Exception &e)
+    {
+        std::cerr << e.what() << std::endl;
+        status = false;
+    }
+
+    input.close();
+
+    return status;
+}
+
+
diff --git a/examples/two_eo_per_frame_opt/Makefile b/examples/two_eo_per_frame_opt/Makefile
new file mode 100644 (file)
index 0000000..8882191
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+EXE = two_eo_per_frame_opt
+
+include ../make.common
+
+CXXFLAGS += -I../common
+
+SOURCES = main.cpp ../common/utils.cpp
+
+$(EXE): $(TIDL_API_LIB) $(HEADERS) $(SOURCES)
+       $(CXX) $(CXXFLAGS) $(SOURCES) $(TIDL_API_LIB) $(LDFLAGS) $(LIBS) -o $@
+
diff --git a/examples/two_eo_per_frame_opt/main.cpp b/examples/two_eo_per_frame_opt/main.cpp
new file mode 100644 (file)
index 0000000..8c67e55
--- /dev/null
@@ -0,0 +1,170 @@
+/******************************************************************************
+ * Copyright (c) 2017-2018  Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//
+// This example illustrates using multiple EOs to process a single frame
+// For details, refer http://downloads.ti.com/mctools/esd/docs/tidl-api/
+//
+#include <signal.h>
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <string>
+
+#include "executor.h"
+#include "execution_object.h"
+#include "execution_object_pipeline.h"
+#include "configuration.h"
+#include "utils.h"
+
+using namespace tidl;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+using EOP = tidl::ExecutionObjectPipeline;
+
+bool Run(int num_eve,int num_dsp, const char* ref_output);
+
+int main(int argc, char *argv[])
+{
+    // Catch ctrl-c to ensure a clean exit
+    signal(SIGABRT, exit);
+    signal(SIGTERM, exit);
+
+    // This example requires both EVE and C66x
+    uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
+    uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
+    if (num_eve == 0 || num_dsp == 0)
+    {
+        std::cout << "TI DL not supported on this SoC." << std::endl;
+        return EXIT_SUCCESS;
+    }
+
+    string ref_file ="../test/testvecs/reference/j11_v2_ref.bin";
+    unique_ptr<const char> reference_output(ReadReferenceOutput(ref_file));
+
+    bool status = Run(num_eve, num_dsp, reference_output.get());
+
+    if (!status)
+    {
+        std::cout << "FAILED" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::cout << "PASSED" << std::endl;
+    return EXIT_SUCCESS;
+}
+
+bool Run(int num_eve, int num_dsp, const char* ref_output)
+{
+    string config_file ="../test/testvecs/config/infer/tidl_config_j11_v2.txt";
+
+    Configuration c;
+    if (!c.ReadFromFile(config_file))
+        return false;
+
+    // Heap sizes for this network determined using Configuration::showHeapStats
+    c.PARAM_HEAP_SIZE   = (3 << 20); // 3MB
+    c.NETWORK_HEAP_SIZE = (20 << 20); // 20MB
+
+    // Run this example for 16 input frames
+    c.numFrames = 16;
+
+    // Assign layers 12, 13 and 14 to the DSP layer group
+    const int EVE_LG = 1;
+    const int DSP_LG = 2;
+    c.layerIndex2LayerGroupId = { {12, DSP_LG}, {13, DSP_LG}, {14, DSP_LG} };
+
+    // Open input file for reading
+    std::ifstream input(c.inData, std::ios::binary);
+
+    bool status = true;
+    try
+    {
+        // Create Executors - use all the DSP and EVE cores available
+        // Specify layer group id for each Executor
+        unique_ptr<Executor> eve(CreateExecutor(DeviceType::EVE,
+                                                num_eve, c, EVE_LG));
+        unique_ptr<Executor> dsp(CreateExecutor(DeviceType::DSP,
+                                                num_dsp, c, DSP_LG));
+
+        // On AM5749, create a total of 4 pipelines (EOPs):
+        // EOPs[0] : { EVE1, DSP1 }
+        // EOPs[1] : { EVE1, DSP1 } for double buffering
+        // EOPs[2] : { EVE2, DSP2 }
+        // EOPs[3] : { EVE2, DSP2 } for double buffering
+
+        const uint32_t pipeline_depth = 2;  // 2 EOs in EOP => depth 2
+        std::vector<EOP *> EOPs;
+        uint32_t num_pipe = std::max(num_eve, num_dsp);
+        for (uint32_t i = 0; i < num_pipe; i++)
+            for (uint32_t j = 0; j < pipeline_depth; j++)
+                EOPs.push_back(new EOP( { (*eve)[i % num_eve],
+                                          (*dsp)[i % num_dsp] } ));
+
+        AllocateMemory(EOPs);
+
+        // Process frames with EOs in a pipelined manner
+        // additional num_eos iterations to flush the pipeline (epilogue)
+        int num_eops = EOPs.size();
+        for (int frame_idx = 0; frame_idx < c.numFrames + num_eops; frame_idx++)
+        {
+            EOP* eop = EOPs[frame_idx % num_eops];
+
+            // Wait for previous frame on the same EOP to finish processing
+            if (eop->ProcessFrameWait())
+            {
+                ReportTime(eop);
+
+                // The reference output is valid only for the first frame
+                // processed on each EOP
+                if (frame_idx < num_eops && !CheckFrame(eop, ref_output))
+                    status = false;
+            }
+
+            // Read a frame and start processing it with current eo
+            if (ReadFrame(eop, frame_idx, c, input))
+                eop->ProcessFrameStartAsync();
+        }
+
+        FreeMemory(EOPs);
+
+    }
+    catch (tidl::Exception &e)
+    {
+        std::cerr << e.what() << std::endl;
+        status = false;
+    }
+
+    input.close();
+
+    return status;
+}
+
+
index 092f09b7e4670107c000555617f96af32999a0e4..f6bde0322b6bcbe00d97b5a71e24068ee785c2c2 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
 TI Deep Learning (TIDL) API
 ---------------------------
 
-TIDL API brings Deep Learning to the edge and enables Linux applications to leverage TI’s proprietary CNN/DNN implementation on EVEs and C66x DSPs in AM57x SoCs.  It requires OpenCL v1.1.15.1 or newer. Refer the User's Guide for details: http://software-dl.ti.com/mctools/esd/docs/tidl-api/index.html
+TIDL API brings Deep Learning to the edge and enables Linux applications to leverage TI’s proprietary CNN/DNN implementation on EVEs and C66x DSPs in AM57x SoCs. Refer to the TIDL API User's Guide for details: http://software-dl.ti.com/mctools/esd/docs/tidl-api/index.html
index bc0a416c398f5dc6543ac314ed5eff75bee127eb..3fc6a2c1922f86dc09aa5e64d83cf7e0c5873f21 100644 (file)
@@ -39,7 +39,8 @@ AR = ar
 
 
 SRCS = ocl_device.cpp configuration_parser.cpp configuration.cpp\
-          executor.cpp execution_object.cpp trace.cpp util.cpp
+          executor.cpp execution_object.cpp trace.cpp util.cpp \
+           execution_object_pipeline.cpp
 SRCS_IMGUTIL = imgutil.cpp
 
 OBJS = $(SRCS:.cpp=.o)
@@ -53,8 +54,7 @@ HOST_OBJ_IMGUTIL_FILES = $(addprefix obj/,$(OBJS_IMGUTIL))
 HEADERS  = src/common_defines.h src/executor_impl.h src/ocl_device.h
 HEADERS += src/parameters.h src/tidl_create_params.h src/trace.h src/util.h
 HEADERS += inc/configuration.h inc/execution_object.h inc/executor.h
-HEADERS += inc/imgutil.h
-
+HEADERS += inc/imgutil.h src/device_arginfo.h inc/execution_object_pipeline.h
 
 ifeq ($(BUILD), debug)
        CXXFLAGS += -Og -g -ggdb
index f94c004d97a71253b0388a87847c5cf3428771ce..698d074c14ecbd482f3af8974223561e28bfd612 100644 (file)
@@ -40,7 +40,7 @@ void ocl_tidl_setup(global unsigned char*        createParams,
 kernel 
 void ocl_tidl_initialize(global unsigned char*            createParams,
                          global unsigned char*            netParamsBuffer,
-                        global unsigned char*            externalMemoryHeapBase,
+                         global unsigned char*            externalMemoryHeapBase,
                          global OCL_TIDL_InitializeParams* initializeParams,
                          local  unsigned char*            l2HeapBase)
 {
@@ -52,12 +52,13 @@ void ocl_tidl_initialize(global unsigned char*            createParams,
                             l2HeapBase);
 }
 
-kernel void ocl_tidl_process(global OCL_TIDL_ProcessParams* processParams,
-                             global unsigned char*          inputFrame,
-                             global unsigned char*          outputData,
-                             global unsigned char*          externalMemory)
+kernel
+void ocl_tidl_process(global OCL_TIDL_ProcessParams* processParams,
+                      global unsigned char*          externalMemoryHeapBase,
+                      global unsigned char*          traceBufferParams)
 {
-    ocl_dsp_tidl_process(processParams, inputFrame, outputData);
+    ocl_dsp_tidl_process(processParams, externalMemoryHeapBase,
+                         traceBufferParams);
 }
 
 
index d530749adcd94128ca2f64d6a3342b4d71a71731..531bb6dc2bb9798156a4717d7305f338aeb72e68 100644 (file)
 //! @file configuration.h
 
 #include <string>
+#include <map>
 #include <iostream>
 
 namespace tidl {
 
 /*! @class Configuration
     @brief Specifies the configuration required for a network
+
+    The Configuration object is used to specify various parameters required
+    for network execution. Applications can directly initialize fields in an
+    instance of Configuration or use the ReadFromFile method to read the
+    configuration from a file.
 */
+
 class Configuration
 {
   public:
@@ -45,14 +52,17 @@ class Configuration
     //! Number of frames of input data (can be 0 if data is not read from file)
     int     numFrames;
 
-    //! Height of the input frame
+    //! Height of the input image. Used by the API, must be specified.
     int     inHeight;
 
-    //! Width of the input frame
+    //! Width of the input image. Used by the API, must be specified.
     int     inWidth;
 
     //! Number of channels in the input frame (e.g. 3 for BGR)
+    //! Used by the API, must be specified.
     int     inNumChannels;
+
+    //! @private
     int     noZeroCoeffsPercentage;
 
     //! Pre-processing type applied to the input frame
@@ -60,43 +70,73 @@ class Configuration
     int     preProcType;
 
     //! Force to run all layers, regardless of layersGroupId partitioning
-    int     runFullNet;
+    bool    runFullNet;
 
     //! When set, inputs are taken from TIDL internal buffers that contain
     //! outputs of previous layersGroupId, instead of from user application
-    int     enableInternalInput;
+    bool     enableInternalInput;
 
-    //! Size of the TI DL per Execution Object heap
-    size_t EXTMEM_HEAP_SIZE;
+    //! @brief Size of the device side network heap
+    //! This heap is used for allocating memory required to
+    //! run the network on the device. One per Execution Object.
+    size_t NETWORK_HEAP_SIZE;
 
-    //! Size of the heap used for paramter data
+    //! @brief Size of the device side heap used for parameter data.
+    //! The size depends on the size of the parameter binary file. The
+    //! constructor for ``Configuration`` sets PARAM_HEAP_SIZE to 9MB.
+    //! There is one parameter heap for each instance of ``Executor`` .
     size_t PARAM_HEAP_SIZE;
 
-    //! @brief Location of the input file
-    //! Can be empty if input data is provided by frameworks such as OpenCV.
+    //! @brief Path to the input image file.
+    //! This field is not used by the TIDL API itself. It can be used by
+    //! applications to load an input image into a buffer. Can be empty if
+    //! the application uses frameworks such as OpenCV to read images. Refer
+    //! examples/test/main.cpp for usage.
     std::string inData;
 
-    //! Location of the output file
-    //! Can be empty if output data is consumed by frameworks such as OpenCV.
+    //! @brief Path to the output image file.
+    //! This field is not used by the TIDL API itself. It can be used by
+    //! applications to specify a name for the output file. Can be empty
+    //! if the application uses frameworks such as OpenCV to read images.
+    //! Refer examples/test/main.cpp for usage.
     std::string outData;
 
-    //! Path to the TIDL network binary file
+    //! Path to the TIDL network binary file.
+    //! Used by the API, must be specified.
     std::string netBinFile;
 
     //! Path to the TIDL parameter binary file
+    //! Used by the API, must be specified.
     std::string paramsBinFile;
 
+    //! Map of layer index to layer group id. Used to override layer group
+    //! assigment for layers. Any layer not specified in this map will
+    //! retain its existing mapping.
+    std::map<int, int> layerIndex2LayerGroupId;
+
+    //! Enable tracing of output buffers associated with each layer
+    bool enableOutputTrace;
+
+    //! Debug - Generates a trace of host and device function calls
+    bool enableApiTrace;
+
+    //! Debug - Shows total size of PARAM and NETWORK heaps. Also shows bytes
+    //! available after all allocations. Can be used to adjust the heap
+    //! size.
+    bool showHeapStats;
+
     //! Default constructor.
     Configuration();
 
     //! Validate the fields in the configuration object
     bool Validate() const;
 
-    //! Print the configuration
+    //! Debug - Print the configuration.
     void Print(std::ostream& os = std::cout) const;
 
     //! Read a configuration from the specified file and validate
     bool ReadFromFile(const std::string& file_name);
+
 };
 
 }
index 3b07c86c90663be5c4473a4d718037c021e1132f..dad586678cac660dc4a682abd68355d10792e5d1 100644 (file)
 #pragma once
 
 #include <memory>
+#include "configuration.h"
+#include "execution_object_internal.h"
 
 namespace tidl {
 
 class Kernel;
 class Device;
+class LayerOutput;
+class IODeviceArgInfo;
+
 
 /*! @class ExecutionObject
     @brief Runs the TIDL network on an OpenCL device
 */
 
-class ExecutionObject
+class ExecutionObject : public ExecutionObjectInternalInterface
 {
     public:
 
@@ -50,55 +55,79 @@ class ExecutionObject
         ExecutionObject(Device* d, uint8_t device_index,
                         const  ArgInfo& create_arg,
                         const  ArgInfo& param_heap_arg,
-                        size_t extmem_heap_size,
-                        bool   internal_input);
+                        const  Configuration& configuration,
+                        int    layersGroupId);
         //! @private
         ~ExecutionObject();
 
         //! Specify the input and output buffers used by the EO
         //! @param in buffer used for input.
         //! @param out buffer used for output.
-        void SetInputOutputBuffer (const ArgInfo& in, const ArgInfo& out);
+        void SetInputOutputBuffer(const ArgInfo& in,
+                                  const ArgInfo& out) override;
 
         //! Returns a pointer to the input buffer set via SetInputOutputBuffer
-        char* GetInputBufferPtr() const;
+        char* GetInputBufferPtr() const override;
 
         //! Returns size of the input buffer
-        size_t GetInputBufferSizeInBytes() const;
+        size_t GetInputBufferSizeInBytes() const override;
+
+        //! Returns a pointer to the output buffer
+        char* GetOutputBufferPtr() const override;
+
+        //! Returns size of the output buffer
+        size_t GetOutputBufferSizeInBytes() const override;
 
         //! @brief Set the frame index of the frame currently processed by the
         //! ExecutionObject. Used for trace/debug messages
         //! @param idx index of the frame
-        void  SetFrameIndex(int idx);
+        void  SetFrameIndex(int idx) override;
 
         //! Returns the index of a frame being processed (set by SetFrameIndex)
-        int   GetFrameIndex() const;
-
-        //! Returns a pointer to the output buffer
-        char* GetOutputBufferPtr() const;
-
-        //! Returns the number of bytes written to the output buffer
-        size_t GetOutputBufferSizeInBytes() const;
+        int   GetFrameIndex() const override;
 
-        //! @brief Start processing a frame. The call is asynchronous and returns
-        //! immediately. Use ExecutionObject::ProcessFrameWait to wait
-        bool ProcessFrameStartAsync();
+        //! @brief Start processing a frame. The call is asynchronous and
+        //! returns immediately. Use ExecutionObject::ProcessFrameWait to wait
+        bool ProcessFrameStartAsync() override;
 
         //! Wait for the execution object to complete processing a frame
         //! @return false if ExecutionObject::ProcessFrameWait was called
         //! without a corresponding call to
         //! ExecutionObject::ProcessFrameStartAsync.
-        bool ProcessFrameWait();
-
-        //! @brief return the number of cycles taken *on the device* to
-        //! execute the process call
-        //! @return Number of cycles to process a frame on the device.
-        uint64_t GetProcessCycles() const;
+        bool ProcessFrameWait() override;
 
         //! @brief return the number of milliseconds taken *on the device* to
         //! execute the process call
         //! @return Number of milliseconds to process a frame on the device.
-        float    GetProcessTimeInMilliSeconds() const;
+        float GetProcessTimeInMilliSeconds() const override;
+
+        //! @brief return the number of milliseconds taken *on the host* to
+        //! execute the process call
+        //! @return Number of milliseconds to process a frame on the host.
+        float GetHostProcessTimeInMilliSeconds() const override;
+
+        //! Returns the device name that the ExecutionObject runs on
+        const std::string& GetDeviceName() const override;
+
+        //! Write the output buffer for each layer to a file
+        //! \<filename_prefix>_<ID>_HxW.bin
+        void WriteLayerOutputsToFile(const std::string& filename_prefix=
+                                     "trace_dump_") const override;
+
+        //! Returns a LayerOutput object corresponding to a layer.
+        //! Caller is responsible for deleting the LayerOutput object.
+        //! @see LayerOutput
+        //! @param layer_index The layer index of the layer
+        //! @param output_index The output index of the buffer for a given
+        //!                     layer. Defaults to 0.
+        const LayerOutput* GetOutputFromLayer(uint32_t layer_index,
+                                       uint32_t output_index=0) const override;
+
+        //! Get output buffers from all layers
+        const LayerOutputs* GetOutputsFromAllLayers() const override;
+
+        //! Returns the layersGrupId that the ExecutionObject is processing
+        int   GetLayersGroupId() const;
 
         //! @private
         // Used by the Executor
@@ -106,13 +135,76 @@ class ExecutionObject
         bool RunAsync(CallType ct);
         bool Wait    (CallType ct);
 
+        //! @private
+        // Used by the ExecutionObjectPipeline
+        bool AddCallback(CallType ct, void *user_data);
+        void AcquireLock();
+        void ReleaseLock();
+
         ExecutionObject()                                  = delete;
         ExecutionObject(const ExecutionObject&)            = delete;
         ExecutionObject& operator=(const ExecutionObject&) = delete;
 
+        //! @private
+        void SetInputOutputBuffer(const IODeviceArgInfo* in,
+                                  const IODeviceArgInfo* out);
+
     private:
         class Impl;
         std::unique_ptr<Impl> pimpl_m;
 };
 
+
+/*! @class LayerOutput
+    @brief Describes the output of a layer in terms of its shape. Also
+    includes a pointer to the data.
+*/
+class LayerOutput
+{
+    public:
+        //! @private
+        //! Constructor called within API, not by the user
+        LayerOutput(int layer_index, int output_index, int buffer_id,
+                    int num_roi_m, int num_channels, size_t height,
+                    size_t width, const char* data);
+
+        //! Must be called to delete the data pointer.
+        ~LayerOutput();
+
+        //! @return The index of a layer
+        int    LayerIndex()       const { return layer_index_m; }
+
+        //! @return The number of channels associated with an output
+        int    NumberOfChannels() const { return num_channels_m; }
+
+        //! @return The height of the output. Can be 1 for 1D outputs
+        size_t Height()           const { return height_m; }
+
+        //! @return The width of the output
+        size_t Width()            const { return width_m; }
+
+        //! @return Size of the output in bytes
+        size_t Size()             const { return height_m * width_m *
+                                                 num_channels_m; }
+        //! @return Pointer to output. Must call destructor to free the
+        //! memory used to hold the output.
+        const char* Data()        const { return data_m; }
+
+        //! @private Disable copy construction and assignment since
+        //! class holds a pointer to allocated data
+        LayerOutput(const LayerOutput&)             = delete;
+        LayerOutput& operator= (const LayerOutput&) = delete;
+
+    private:
+        int layer_index_m;
+        int output_index_m;
+        int buffer_id_m;
+        int num_roi_m;
+        int num_channels_m;
+        size_t height_m;
+        size_t width_m;
+        const char* data_m;
+};
+
+
 } // namespace tidl
diff --git a/tidl_api/inc/execution_object_internal.h b/tidl_api/inc/execution_object_internal.h
new file mode 100644 (file)
index 0000000..816da94
--- /dev/null
@@ -0,0 +1,119 @@
+/******************************************************************************
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+/*! @file execution_object_internal.h */
+
+#pragma once
+
+namespace tidl {
+
+class LayerOutput;
+
+typedef std::vector<std::unique_ptr<const LayerOutput>> LayerOutputs;
+
+/*! @cond HIDDEN_SYMBOLS
+    @class ExecutionObjectInternalInterface
+    @brief Internal interface for running the TIDL network on OpenCL devices
+           Do not use this internal class directly.
+           Please use ExecutionObject or ExecutionObejctPipeline instead.
+*/
+class ExecutionObjectInternalInterface
+{
+    public:
+        virtual ~ExecutionObjectInternalInterface() {};
+
+        //! Specify the input and output buffers used by the EO
+        //! @param in buffer used for input.
+        //! @param out buffer used for output.
+        virtual void SetInputOutputBuffer(const ArgInfo& in,
+                                          const ArgInfo& out) =0;
+
+        //! Returns a pointer to the input buffer set via SetInputOutputBuffer
+        virtual char* GetInputBufferPtr() const =0;
+
+        //! Returns size of the input buffer
+        virtual size_t GetInputBufferSizeInBytes() const =0;
+
+        //! Returns a pointer to the output buffer
+        virtual char* GetOutputBufferPtr() const =0;
+
+        //! Returns size of the output buffer
+        virtual size_t GetOutputBufferSizeInBytes() const =0;
+
+        //! @brief Set the frame index of the frame currently processed by the
+        //! ExecutionObject. Used for trace/debug messages
+        //! @param idx index of the frame
+        virtual void  SetFrameIndex(int idx) =0;
+
+        //! Returns the index of a frame being processed (set by SetFrameIndex)
+        virtual int   GetFrameIndex() const =0;
+
+        //! @brief Start processing a frame. The call is asynchronous and returns
+        //! immediately. Use ExecutionObject::ProcessFrameWait to wait
+        virtual bool ProcessFrameStartAsync() =0;
+
+        //! Wait for the execution object to complete processing a frame
+        //! @return false if ExecutionObject::ProcessFrameWait was called
+        //! without a corresponding call to
+        //! ExecutionObject::ProcessFrameStartAsync.
+        virtual bool ProcessFrameWait() =0;
+
+        //! @brief return the number of milliseconds taken *on the device* to
+        //! execute the process call
+        //! @return Number of milliseconds to process a frame on the device.
+        virtual float GetProcessTimeInMilliSeconds() const =0;
+
+        //! @brief return the number of milliseconds taken *on the host* to
+        //! execute the process call
+        //! @return Number of milliseconds to process a frame on the host.
+        virtual float GetHostProcessTimeInMilliSeconds() const =0;
+
+        //! Returns the device name that the ExecutionObject runs on
+        virtual const std::string& GetDeviceName() const =0;
+
+        //! Write the output buffer for each layer to a file
+        //! \<filename_prefix>_<ID>_HxW.bin
+        virtual void WriteLayerOutputsToFile(const std::string& filename_prefix=
+                                             "trace_dump_") const =0;
+
+        //! Returns a LayerOutput object corresponding to a layer.
+        //! Caller is responsible for deleting the LayerOutput object.
+        //! @see LayerOutput
+        //! @param layer_index The layer index of the layer
+        //! @param output_index The output index of the buffer for a given
+        //!                     layer. Defaults to 0.
+        virtual const LayerOutput* GetOutputFromLayer(uint32_t layer_index,
+                                             uint32_t output_index=0) const =0;
+
+        //! Get output buffers from all layers
+        virtual const LayerOutputs* GetOutputsFromAllLayers() const =0;
+};
+/*!  @endcond
+*/
+
+} // namespace tidl
diff --git a/tidl_api/inc/execution_object_pipeline.h b/tidl_api/inc/execution_object_pipeline.h
new file mode 100644 (file)
index 0000000..b31fec6
--- /dev/null
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//! @file execution_object_pipeline.h
+
+#pragma once
+#include <string>
+#include <vector>
+#include <cstdint>
+#include <cassert>
+
+#include "executor.h"
+#include "execution_object_internal.h"
+#include "execution_object.h"
+
+namespace tidl {
+
+/*! @class ExecutionObjectPipeline
+    @brief Manages the pipelined execution using multiple ExecutionObjects.
+    Each executor runs one layersGroup of the network.  ExecutionObjects
+    must run consecutive layersGroups to form a pipelined execution.
+*/
+class ExecutionObjectPipeline : public ExecutionObjectInternalInterface
+{
+    public:
+        //! @brief Create an ExecutionObjectPipeline object.
+        //!
+        //! The ExecutionObjectPipeline will take the provided ExecutionObjects
+        //! to create an execution pipeline.  E.g.
+        //! @code
+        //!   Configuration config("path to configuration file");
+        //!   DeviceIds ids = {DeviceId::ID0, DeviceId::ID1};
+        //!   Executor exe_eve(DeviceType::EVE, ids, config, 1);
+        //!   Executor exe_dsp(DeviceType::DSP, ids, config, 2);
+        //!   ExecutionObjectPipeline ep0({exe_eve[0], exe_dsp[0]});
+        //!   ExecutionObjectPipeline ep1({exe_eve[1], exe_dsp[1]});
+        //! @endcode
+        //!
+        //! @param eos DSP or EVE ExecutionObjects forming a pipeline
+        ExecutionObjectPipeline(std::vector<ExecutionObject*> eos);
+
+        //! @brief Tear down an ExecutionObjectPipeline and free used resources
+        ~ExecutionObjectPipeline();
+
+        //! Returns the number of ExecutionObjects associated with the
+        //! ExecutionObjectPipeline
+        uint32_t GetNumExecutionObjects() const;
+
+        //! Specify the input and output buffers used by the EOP
+        //! @param in buffer used for input.
+        //! @param out buffer used for output.
+        void SetInputOutputBuffer (const ArgInfo& in,
+                                   const ArgInfo& out) override;
+
+        //! Returns a pointer to the input buffer
+        char* GetInputBufferPtr() const override;
+
+        //! Returns size of the input buffer
+        size_t GetInputBufferSizeInBytes() const override;
+
+        //! Returns a pointer to the output buffer
+        char* GetOutputBufferPtr() const override;
+
+        //! Returns the number of bytes written to the output buffer
+        size_t GetOutputBufferSizeInBytes() const override;
+
+        //! @brief Set the frame index of the frame currently processed by the
+        //! ExecutionObjectPipeline. Used for trace/debug messages
+        //! @param idx index of the frame
+        void SetFrameIndex(int idx) override;
+
+        //! Returns the index of a frame being processed (set by SetFrameIndex)
+        int  GetFrameIndex() const override;
+
+        //! @brief Start processing a frame. The call is asynchronous and
+        //! returns immediately. Use ProcessFrameWait() to wait
+        bool ProcessFrameStartAsync() override;
+
+        //! Wait for the executor pipeline to complete processing a frame
+        //! @return false if ProcessFrameWait() was called
+        //! without a corresponding call to
+        //! ExecutionObjectPipeline::ProcessFrameStartAsync().
+        bool ProcessFrameWait() override;
+
+        //! @brief return the number of milliseconds taken *on the device* to
+        //! execute the process call
+        //! @return Number of milliseconds to process a frame on the device.
+        float GetProcessTimeInMilliSeconds() const override;
+
+        //! @brief return the number of milliseconds taken *on the device*
+        //! to process a layersGroup by a componenet ExecutionObject
+        //! @return Number of milliseconds to process a layersGroup on the
+        //! device by a component ExecutionObject.
+        float GetProcessTimeInMilliSeconds(uint32_t eo_index) const;
+
+        //! @brief return the number of milliseconds taken *on the host* to
+        //! execute the process call
+        //! @return Number of milliseconds to process a frame on the host.
+        float GetHostProcessTimeInMilliSeconds() const override;
+
+        //! @brief return the number of milliseconds taken *on the host*
+        //! to process a layersGroup by a componenet ExecutionObject
+        //! @return Number of milliseconds to process a layersGroup on the
+        //! host by a component ExecutionObject.
+        float GetHostProcessTimeInMilliSeconds(uint32_t eo_index) const;
+
+        //! Return the combined device names that this pipeline runs on
+        const std::string& GetDeviceName() const override;
+
+        //! Write the output buffer for each layer to a file
+        //! \<filename_prefix>_<ID>_HxW.bin
+        void WriteLayerOutputsToFile(const std::string& filename_prefix=
+                                     "trace_dump_") const override;
+
+        //! Returns a LayerOutput object corresponding to a layer.
+        //! Caller is responsible for deleting the LayerOutput object.
+        //! @see LayerOutput
+        //! @param layer_index The layer index of the layer
+        //! @param output_index The output index of the buffer for a given
+        //!                     layer. Defaults to 0.
+        const LayerOutput* GetOutputFromLayer(uint32_t layer_index,
+                                       uint32_t output_index=0) const override;
+
+        //! Get output buffers from all layers
+        const LayerOutputs* GetOutputsFromAllLayers() const override;
+
+        //! @private Used by runtime
+        //! @brief callback function at the completion of each ExecutionObject,
+        //! to chain the next ExectionObject for execution
+        void RunAsyncNext();
+
+        ExecutionObjectPipeline()                                     = delete;
+        ExecutionObjectPipeline(const ExecutionObjectPipeline&)       = delete;
+        ExecutionObjectPipeline& operator=(const ExecutionObjectPipeline&)
+                                                                      = delete;
+
+    private:
+        class Impl;
+        std::unique_ptr<Impl> pimpl_m;
+};
+
+} // namespace tidl
index 2b20eaf9b3c5a5acd0238f76e21c776c1b193556..fb730729f8bd94d4a7c45bd6768933816efef48b 100644 (file)
@@ -64,7 +64,7 @@ class ExecutionObject;
 typedef std::vector<std::unique_ptr<ExecutionObject>> ExecutionObjects;
 
 /*! @class Executor
-    @brief Manages the overall execution of a network using the
+    @brief Manages the overall execution of a layersGroup in a network using the
     specified configuration and the set of devices available to the
     executor.
 */
@@ -78,7 +78,7 @@ class Executor
         //! @code
         //!   Configuration configuration;
         //!   configuration.ReadFromFile("path to configuration file");
-        //!   DeviceIds ids1 = {DeviceId::ID2, DeviceId::ID3};
+        //!   DeviceIds ids = {DeviceId::ID2, DeviceId::ID3};
         //!   Executor executor(DeviceType::EVE, ids, configuration);
         //! @endcode
         //!
@@ -98,6 +98,13 @@ class Executor
         //! available on this instance of the Executor
         const ExecutionObjects& GetExecutionObjects() const;
 
+        //! Returns a single execution object at index
+        ExecutionObject* operator[](uint32_t index) const;
+
+        //! Returns the number of ExecutionObjects associated with the
+        //! Executor
+        uint32_t GetNumExecutionObjects() const;
+
         //! @brief Returns the number of devices of the specified type
         //! available for TI DL.
         //! @param  device_type DSP or EVE/EVE device
@@ -106,7 +113,7 @@ class Executor
 
         //! @brief Returns a string corresponding to the API version
         //!
-        //! @return <major_ver>.<minor_ver>.<patch_ver>.<git_sha>
+        //! @return \<major_ver>.\<minor_ver>.\<patch_ver>.\<git_sha>
         static std::string GetAPIVersion();
 
         Executor(const Executor&) = delete;
@@ -117,17 +124,6 @@ class Executor
         std::unique_ptr<ExecutorImpl> pimpl_m;
 };
 
-/*! @class PipeInfo
- *  @brief Describe input and output required by piping output and input
- *         between Execution Objects
- */
-class PipeInfo
-{
-    public:
-        uint32_t dataQ_m[OCL_TIDL_MAX_IN_BUFS];
-        uint32_t bufAddr_m[OCL_TIDL_MAX_IN_BUFS];
-};
-
 /*! @class ArgInfo
  *  @brief Describe input and output buffers required by ExecutionObjects
  */
@@ -136,20 +132,13 @@ class ArgInfo
     public:
         enum class DeviceAccess { R_ONLY=0, W_ONLY, RW };
 
-        //! Enumerates the types of arguments represented by ArgInfo
-        enum class Kind { BUFFER=0, SCALAR };
-
         //! Construct an ArgInfo object from a pointer to a chunk of memory
         //! and its size.
         ArgInfo(void *p, size_t size) :
-            ptr_m(p), size_m(size),
-            access_m(DeviceAccess::RW), kind_m(Kind::BUFFER)
-        { pipe_m = std::make_shared<PipeInfo>(); }
+            ptr_m(p), size_m(size), access_m(DeviceAccess::RW) {}
 
-        //! Construct an ArgInfo object from a pointer to a chunk of memory
-        //! its size and kind
-        ArgInfo(void *p, size_t size, Kind kind) :
-            ptr_m(p), size_m(size), access_m(DeviceAccess::RW), kind_m(kind) {}
+        ArgInfo(const ArgInfo& arg) = default;
+        ArgInfo& operator=(const ArgInfo& arg) = default;
 
         //! @return Pointer to the buffer or scalar represented by ArgInfo
         void  *ptr()  const { return ptr_m; }
@@ -157,19 +146,10 @@ class ArgInfo
         //! @return The size of the buffer or scalar represented by ArgInfo
         size_t size() const { return size_m; }
 
-        // Only used by tidl::Device
-        Kind   kind() const { return kind_m; }
-        bool   isLocal() const { return (ptr_m == nullptr) && (size_m > 0); }
-
-        // Only used by tidl::ExecutionObject::Impl
-        PipeInfo *GetPipe() const { return pipe_m.get(); }
-
-    private:
+    protected:
         void*        ptr_m;
         size_t       size_m;
         DeviceAccess access_m;
-        Kind         kind_m;
-        std::shared_ptr<PipeInfo> pipe_m;
 };
 
 
index f49b0ad066847b823a15fe8d56687ecb991743af..d8677de52f43ca49018d923b4f75bfaaf8c9dfc5 100644 (file)
@@ -25,9 +25,9 @@
 # THE POSSIBILITY OF SUCH DAMAGE.
 
 MAJOR_VER=1
-MINOR_VER=0
+MINOR_VER=1
 PATCH_VER=0
-BUILD_VER=3
+BUILD_VER=0
 
 ifeq ($(shell git rev-parse --short HEAD 2>&1 1>/dev/null; echo $$?),0)
 BUILD_SHA?=$(shell git rev-parse --short HEAD)
index eca20b5a7c32d100d90a977501f3d1cab819991b..daa15f41dcd5003dc7a559645706b0ae4d2c893a 100644 (file)
@@ -38,10 +38,13 @@ Configuration::Configuration(): numFrames(0), inHeight(0), inWidth(0),
                      inNumChannels(0),
                      noZeroCoeffsPercentage(100),
                      preProcType(0),
-                     runFullNet(0),
-                     enableInternalInput(0),
-                     EXTMEM_HEAP_SIZE(64 << 20),  // 64MB for inceptionNetv1
-                     PARAM_HEAP_SIZE(9 << 20)     // 9MB for mobileNet1
+                     runFullNet(false),
+                     enableInternalInput(false),
+                     NETWORK_HEAP_SIZE(64 << 20),  // 64MB for inceptionNetv1
+                     PARAM_HEAP_SIZE(9 << 20),    // 9MB for mobileNet1
+                     enableOutputTrace(false),
+                     enableApiTrace(false),
+                     showHeapStats(false)
 {
 }
 
@@ -57,7 +60,7 @@ void Configuration::Print(std::ostream &os) const
        << "\nOutputFile             &