As this is currently still in a proof-of-concept phase, only a subset of Vital data structures and algorithms are available via the Python interface, and yet also limited in functionality within Python (e.g. only simple data accessors and manipuators are available.
Before interacting with the Vital python bindings, the Vital common setup script should be sourced. This is located in either the build or installation directory, which ever is relevant.
When working with the bindings while in the source tree, a setup script may be sourced in
<SOURCE>/vital/bindings/python/setup_vital_python.sh
This simply adds that containing directory to the PYTHON_PATH.
A Windows equivalent batch script will be provided in the future.
In [ ]:
# Importing Vital Python module
import vital
print vital
NOTE: Currently, the only data structure that is 100% implemented (compared to the source data structure in C++) is the config_block structue (the VitalConfigBlock class in Python). Other data structures in the Python interface are only partially implemented in support of the currently implemented algorithms.
Currently implemented data structures (in whole or part):
In [ ]:
# The config block structure
from vital import ConfigBlock
# Creating an empyty config block:
cb = ConfigBlock("SomeNameNotRequired")
print "empty ConfigBlock keys", cb.available_keys() # an empty list
cb.set_value('foobar', 'true')
print "updated ConfigBlock keys", cb.available_keys() # Now has one element, 'foobar'
print "value of 'foobar':", cb.get_value('foobar') # Get string value
# This happens to be a valid boolean string, so we can get it as a boolean value, too
if cb.get_value_bool('foobar'):
print "foobar is on (%s)" % cb.get_value_bool('foobar')
else:
print "foobar is off (%s)" % cb.get_value_bool('foobar')
The C interface implements an error handle structure, that many functions take in and set, in case an exception is thrown in the C++ code. When an exception is detected, a non-zero error code is set. The Python interface uses these handles to propagate any errors that occur in the C/C++ code (aside from unavoidable things like segfaults) as raised exceptions in Python.
While there is a catch-all return code and Python exception class for generic errors, specific Python exception classes may be associated to specific return codes on a per-function basis for more fine-grained exception handling.
Config blocks may be read from file. If constructed from a file that doesn't exist, the C++ interface would throw an exception. This is also the case in the Python interface due to automatic error propagation, which happens to be a specific exception class due to the Python implementation knowing that the C interface will return different error codes for specific errors.
In [ ]:
from vital import ConfigBlock
from vital.exceptions.config_block_io import VitalConfigBlockIoFileNotFoundException
try:
cb = ConfigBlock.from_file("/This/is/probably/not/a/file/on/your/disk.lalalalala")
except VitalConfigBlockIoFileNotFoundException as err:
print "Exception caught:", err
Other functions may only throw the generic base VITAL Python exception due to a current lack of implementation on the Python side, or the C interface does not yet return fine-grained error codes.
In [ ]:
from vital.types import TrackSet
from vital.exceptions.base import VitalBaseException
# An empty track set
ts = TrackSet()
try:
ts.write_tracks_file("not_enough_tracks.txt")
except VitalBaseException as err:
print "Exception caught:", err
Just as in C++, we need to load the dynamic plugins before we can instantiate abstract algorithms with concrete instances. In Python this is done via the vital.apm
module. In order for plugins to be picked up, the environment variable KWIVER_PLUGIN_PATH
should be set to a colon separated sequence of directories to look in.
In the below example, we set the path to point to a build of MAP-Tk's built plugin libraries.
In [ ]:
import os
from vital import apm
# os.environ['KWIVER_PLUGIN_PATH'] = '/home/purg/dev/maptk/build-dev_ocv_3.x/lib/maptk'
# OR
apm.add_search_path( '/home/purg/dev/maptk/build-dev_ocv_3.x/lib/maptk' )
# Nothing registered initially:
print "Initially registered modlues:", apm.registered_module_names()
# Register an invalid specific module:
apm.register_plugins("vital_core")
print "Single module registration:", apm.registered_module_names()
# Register a valid specific module:
apm.register_plugins("maptk_core_plugin")
print "Single module registration:", apm.registered_module_names()
# Register all available modules (recommended, thread-safe):
print "Reg all once:", apm.register_plugins_once()
print "All available modules:", apm.registered_module_names()
NOTE: It is possible to compile the VITAL system statically, but the C interface libraray dynamically. In this case, dynamic plugins are not supported. It is still required to call VitalAlgorithmPluginManager.register_plugins
to register available algorithm implementations, however the system will only register those implementations that have been baked into the libraries at compile time. Be aware that in this case no modules will be reported as registered via the VitalAlgorithmPluginManager.registered_module_names()
method even when algorithm implementations are actually registered.
In the C++ interface, abstract algorithms are defined, but need to be instantiated with concrete derived algorithms provided by the plugins. Static member functions on abstract base class for each algorithm can list the loaded algorithm implementations by name and create an instance of any implementaiton by string name.
In the Python interface, each algorithm class represents one of the C++ declared algorithm definition types. They act like a shared pointer would in the C++ interface.
All algorithm instances must be named (a configuration requirement) and can be initially created with an undefined implementation type, or with a specific implementation. Valid implementation names (types) are determined by what plugins are loaded at the time of instance construction.
When undefined, a call to the impl_name()
instance method returns None, and calls to implementation methods raise an exception stating that we cannot operate on a null pointer.
In [ ]:
from vital.algo import ImageIo
from vital.exceptions.base import VitalBaseException
iio = ImageIo('algo_name')
print "iio implementation name:", iio.impl_name()
try:
iio.load('foo.jpg')
except VitalBaseException as err:
print err
In [ ]:
ImageIo.registered_names()
If a specific implementation is known, it may be initialized via the create(...)
class method, or by VitalConfigBlock configuration.
In [ ]:
# Directly creating a new algorithm via implementation name
iio_ocv = ImageIo.create("iio_ocv", "ocv")
print "Created Implementation type:", iio_ocv.impl_name()
In [ ]:
iio = ImageIo('iio') # and unconfigured image_io algorithm
cb = iio.get_config() # get the configuration
# iio.impl_name() == None
print cb.as_string() # To see the current configuration
In [ ]:
cb.set_value('iio:type', 'ocv')
iio.set_config(cb)
print "Using Image IO implementation:", iio.impl_name()
print iio.get_config().as_string()
In [ ]:
from vital.algo import TrackFeatures
tracker = TrackFeatures.create("tracker", "core")
print tracker.get_config().as_string()
In [ ]:
cb = tracker.get_config()
cb.set_value("tracker:core:descriptor_extractor:type", "ocv_SURF")
tracker.set_config(cb)
print tracker.get_config().as_string()
In [ ]:
cb = tracker.get_config()
surf_cb = cb.subblock_view("tracker:core:descriptor_extractor:ocv_SURF")
print "Before:"
print surf_cb.as_string()
print "----------------"
surf_cb.set_value("upright", True)
surf_cb.set_value("hessian_threshold", 750)
print "After:"
print surf_cb.as_string()
print "----------------"
tracker.set_config(cb)
print tracker.get_config().as_string()
Going forward, the following should be achieved: