PyArmNN Updates

* Updated setup.py to raise error on mandatory ext
* Updated examples section of main readme
* Added readme to img class
* Moved img class to new subdir

Change-Id: Iea5f6d87c97e571b8ca5636268231506538840c7
Signed-off-by: Éanna Ó Catháin <eanna.ocathain@arm.com>
Signed-off-by: Jakub Sujak <jakub.sujak@arm.com>
diff --git a/python/pyarmnn/README.md b/python/pyarmnn/README.md
index 9ccf34d..3091fb8 100644
--- a/python/pyarmnn/README.md
+++ b/python/pyarmnn/README.md
@@ -75,7 +75,7 @@
 $ export SWIG_EXECUTABLE=<path_to_swig>
 $ export ARMNN_INCLUDE=<path_to_armnn_include>
 $ export ARMNN_LIB=<path_to_armnn_libraries>
-``` 
+```
 
 ##### 2. Clean and build SWIG wrappers:
 
@@ -212,15 +212,20 @@
 
 #### Examples
 
-To further explore PyArmNN API there are several examples provided in the examples folder running classification on an image. To run them first install the dependencies:
- ```bash
-$ pip install -r examples/requirements.txt
-```
-Afterwards simply execute the example scripts, e.g.:
- ```bash
-$ python tflite_mobilenetv1_quantized.py
-```
-All resources are downloaded during execution, so if you do not have access to the internet, you may need to download these manually. `example_utils.py` contains code shared between the examples.
+To further explore PyArmNN API there are several examples provided in the `/examples` folder for you to explore.
+
+##### Image Classification
+
+This sample application performs image classification on an image and outputs the <i>Top N</i> results, listing the classes and probabilities associated with the classified image. All resources are downloaded during execution, so if you do not have access to the internet, you may need to download these manually.
+
+Sample scripts are provided for performing image classification with TFLite and ONNX models with `tflite_mobilenetv1_quantized.py` and `onnx_mobilenetv2.py`.
+
+##### Object Detection
+
+This sample application guides the user and shows how to perform object detection using PyArmNN API. By taking a model and video file or camera feed as input, and running inference on each frame, we are able to interpret the output to draw bounding boxes around detected objects and overlay the corresponding labels and confidence scores.
+
+Sample scripts are provided for performing object detection from video file and video stream with `run_video_file.py` and `run_video_stream.py`.
+
 
 ## Tox for automation
 
diff --git a/python/pyarmnn/examples/image_classification/README.md b/python/pyarmnn/examples/image_classification/README.md
new file mode 100644
index 0000000..61efbc4
--- /dev/null
+++ b/python/pyarmnn/examples/image_classification/README.md
@@ -0,0 +1,46 @@
+# PyArmNN Image Classification Sample Application

+

+## Overview

+

+To further explore PyArmNN API, we provide an example for running image classification on an image.

+

+All resources are downloaded during execution, so if you do not have access to the internet, you may need to download these manually. The file `example_utils.py` contains code shared between the examples.

+

+## Prerequisites

+

+##### PyArmNN

+

+Before proceeding to the next steps, make sure that you have successfully installed the newest version of PyArmNN on your system by following the instructions in the README of the PyArmNN root directory.

+

+You can verify that PyArmNN library is installed and check PyArmNN version using:

+```bash

+$ pip show pyarmnn

+```

+

+You can also verify it by running the following and getting output similar to below:

+```bash

+$ python -c "import pyarmnn as ann;print(ann.GetVersion())"

+'22.0.0'

+```

+

+##### Dependencies

+

+Install the dependencies:

+

+```bash

+$ pip install -r requirements.txt

+```

+

+## Perform Image Classification

+

+Perform inference with TFLite model by running the sample script:

+```bash

+$ python tflite_mobilenetv1_quantized.py

+```

+

+Perform inference with ONNX model by running the sample script:

+```bash

+$ python onnx_mobilenetv2.py

+```

+

+The output from inference will be printed as <i>Top N</i> results, listing the classes and probabilities associated with the classified image.

diff --git a/python/pyarmnn/examples/example_utils.py b/python/pyarmnn/examples/image_classification/example_utils.py
similarity index 99%
rename from python/pyarmnn/examples/example_utils.py
rename to python/pyarmnn/examples/image_classification/example_utils.py
index e5425dd..090ce2f 100644
--- a/python/pyarmnn/examples/example_utils.py
+++ b/python/pyarmnn/examples/image_classification/example_utils.py
@@ -1,4 +1,4 @@
-# Copyright 2020 NXP
+# Copyright © 2020 NXP and Contributors. All rights reserved.
 # SPDX-License-Identifier: MIT
 
 from urllib.parse import urlparse
diff --git a/python/pyarmnn/examples/onnx_mobilenetv2.py b/python/pyarmnn/examples/image_classification/onnx_mobilenetv2.py
old mode 100755
new mode 100644
similarity index 97%
rename from python/pyarmnn/examples/onnx_mobilenetv2.py
rename to python/pyarmnn/examples/image_classification/onnx_mobilenetv2.py
index 05bfd7b..9e95f76
--- a/python/pyarmnn/examples/onnx_mobilenetv2.py
+++ b/python/pyarmnn/examples/image_classification/onnx_mobilenetv2.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# Copyright 2020 NXP
+# Copyright © 2020 NXP and Contributors. All rights reserved.
 # SPDX-License-Identifier: MIT
 
 import pyarmnn as ann
diff --git a/python/pyarmnn/examples/requirements.txt b/python/pyarmnn/examples/image_classification/requirements.txt
similarity index 79%
rename from python/pyarmnn/examples/requirements.txt
rename to python/pyarmnn/examples/image_classification/requirements.txt
index 9af2b27..f97e856 100644
--- a/python/pyarmnn/examples/requirements.txt
+++ b/python/pyarmnn/examples/image_classification/requirements.txt
@@ -2,4 +2,3 @@
 urllib3>=1.25.8
 Pillow>=6.1.0
 numpy>=1.18.1
-pyarmnn>=19.8.0
diff --git a/python/pyarmnn/examples/tflite_mobilenetv1_quantized.py b/python/pyarmnn/examples/image_classification/tflite_mobilenetv1_quantized.py
old mode 100755
new mode 100644
similarity index 96%
rename from python/pyarmnn/examples/tflite_mobilenetv1_quantized.py
rename to python/pyarmnn/examples/image_classification/tflite_mobilenetv1_quantized.py
index cb2c91c..229a9b6
--- a/python/pyarmnn/examples/tflite_mobilenetv1_quantized.py
+++ b/python/pyarmnn/examples/image_classification/tflite_mobilenetv1_quantized.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# Copyright 2020 NXP
+# Copyright © 2020 NXP and Contributors. All rights reserved.
 # SPDX-License-Identifier: MIT
 
 import numpy as np
diff --git a/python/pyarmnn/setup.py b/python/pyarmnn/setup.py
index ac6ee24..e1a4ea4 100755
--- a/python/pyarmnn/setup.py
+++ b/python/pyarmnn/setup.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 # Copyright © 2020 Arm Ltd. All rights reserved.
-# Copyright 2020 NXP
+# Copyright © 2020 NXP and Contributors. All rights reserved.
 # SPDX-License-Identifier: MIT
 """Python bindings for Arm NN
 
@@ -55,16 +55,18 @@
         self.failed_ext = []
 
     def build_extension(self, ext):
-        try:
+        if ext.optional:
+            try:
+                super().build_extension(ext)
+            except Exception as err:
+                self.failed_ext.append(ext)
+                logger.warning('Failed to build extension %s. \n %s', ext.name, str(err))
+        else:
             super().build_extension(ext)
-        except Exception as err:
-            self.failed_ext.append(ext)
-            logger.warning('Failed to build extension %s. \n %s', ext.name, str(err))
-
-        if ext.name == 'pyarmnn._generated._pyarmnn_version':
-            sys.path.append(os.path.abspath(os.path.join(self.build_lib, str(Path(ext._file_name).parent))))
-            from _pyarmnn_version import GetVersion
-            check_armnn_version(GetVersion(), __arm_ml_version__)
+            if ext.name == 'pyarmnn._generated._pyarmnn_version':
+                sys.path.append(os.path.abspath(os.path.join(self.build_lib, str(Path(ext._file_name).parent))))
+                from _pyarmnn_version import GetVersion
+                check_armnn_version(GetVersion(), __arm_ml_version__)
 
     def copy_extensions_to_source(self):
 
@@ -76,7 +78,7 @@
 def linux_gcc_name():
     """Returns the name of the `gcc` compiler. Might happen that we are cross-compiling and the
     compiler has a longer name.
-    
+
     Args:
         None
 
@@ -92,7 +94,7 @@
 
 def linux_gcc_lib_search(gcc_compiler_name: str = linux_gcc_name()):
     """Calls the `gcc` to get linker default system paths.
-    
+
     Args:
         gcc_compiler_name(str): Name of the GCC compiler
 
@@ -158,6 +160,9 @@
         raise RuntimeError("""ArmNN library {} was not found in {}. Please install ArmNN to one of the standard
                            locations or set correct ARMNN_INCLUDE and ARMNN_LIB env variables.""".format(lib_name,
                                                                                                          lib_search))
+    if optional and len(armnn_libs) == 0:
+        logger.warning("""Optional parser library %s was not found in %s and will not be installed.""", lib_name,
+                                                                                                        lib_search)
 
     # gives back tuple of names of the libs, set of unique libs locations and includes.
     return list(armnn_libs.keys()), list(set(
@@ -181,6 +186,7 @@
         self._library_dirs = None
         self._runtime_library_dirs = None
         self._armnn_libs = armnn_libs
+        self._optional = optional[0]
         # self.__swig_opts = None
         super().__init__(name, sources, include_dirs, define_macros, undef_macros, library_dirs, libraries,
                          runtime_library_dirs, extra_objects, extra_compile_args, extra_link_args, export_symbols,
@@ -198,7 +204,7 @@
     def library_dirs(self):
         library_dirs = self._library_dirs
         for lib in self._armnn_libs:
-            _, lib_path = find_armnn(lib)
+            _, lib_path = find_armnn(lib, self._optional)
             library_dirs = library_dirs + lib_path
 
         return library_dirs
@@ -211,7 +217,7 @@
     def runtime_library_dirs(self):
         library_dirs = self._runtime_library_dirs
         for lib in self._armnn_libs:
-            _, lib_path = find_armnn(lib)
+            _, lib_path = find_armnn(lib, self._optional)
             library_dirs = library_dirs + lib_path
 
         return library_dirs
@@ -224,7 +230,7 @@
     def libraries(self):
         libraries = self._libraries
         for lib in self._armnn_libs:
-            lib_names, _ = find_armnn(lib)
+            lib_names, _ = find_armnn(lib, self._optional)
             libraries = libraries + lib_names
 
         return libraries
@@ -249,13 +255,15 @@
                                               sources=['src/pyarmnn/_generated/armnn_wrap.cpp'],
                                               extra_compile_args=['-std=c++14'],
                                               language='c++',
-                                              armnn_libs=['libarmnn.so']
+                                              armnn_libs=['libarmnn.so'],
+                                              optional=[False]
                                               )
     pyarmnn_v_module = LazyArmnnFinderExtension('pyarmnn._generated._pyarmnn_version',
                                                 sources=['src/pyarmnn/_generated/armnn_version_wrap.cpp'],
                                                 extra_compile_args=['-std=c++14'],
                                                 language='c++',
-                                                armnn_libs=['libarmnn.so']
+                                                armnn_libs=['libarmnn.so'],
+                                                optional=[False]
                                                 )
     extensions_to_build = [pyarmnn_v_module, pyarmnn_module]
 
@@ -267,7 +275,8 @@
                                                                name.lower())],
                                                            extra_compile_args=['-std=c++14'],
                                                            language='c++',
-                                                           armnn_libs=['libarmnn.so', 'libarmnn{}.so'.format(name)]
+                                                           armnn_libs=['libarmnn.so', 'libarmnn{}.so'.format(name)],
+                                                           optional=[True]
                                                            )
         ext_list.append(pyarmnn_optional_module)
 
@@ -316,7 +325,7 @@
         python_requires='>=3.5',
         install_requires=['numpy'],
         cmdclass={
-            'build_py': ExtensionPriorityBuilder, 
+            'build_py': ExtensionPriorityBuilder,
             'build_ext': ArmnnVersionCheckerExtBuilder
         },
         ext_modules=extensions_to_build