JeVois
1.22
JeVois Smart Embedded Machine Vision Toolkit
|
|
#include <jevois/Component/Component.H>
A component of a model hierarchy.
Model Components form the heart of every JeVois application, as their main purpose is to allow 1) building complex vision processing pipelines from shared, re-usable processing elements; 2) a uniform interface for tunable parameters that these processing elements may have. In fact, every class which needs to expose parameters to the user should inherit directly or indirectly from Component. Parameters are simple wrappers around any valid C++ type, along with some description, default values, possibly specification of valid values, etc. Parameters are used to adjust the behavior of processing elements by allowing end-users to change the parameter values (think, e.g., of a threshold in some image processing algorithm).
Components typically form a component hierarchy with a Manager (including Engine) at the root, and a tree of sub-Components below. To form this tree, just use the Manager::addComponent() and Component::addSubComponent() methods to add some children, and then add some children to those children, etc...
One intent behind Component is to enable substantial code re-use when implementing complex vision processing frameworks, and to alleviate the burden of passing many parameters to function calls when invoking the processing elements. This is a strong departure from many other frameworks such as OpenCV, which by and large rely on functions with many parameters (though later versions do use classes as well). For example, the way one invokes a Canny edge detector in OpenCV is to call the function:
Beyond possible confusion about which value goes to which argument in the long list (which languages such as Python solve by allowing access to arguments by name), one major issue with this approach is that either every function using Canny must provide a mechanism for the user to set the parameters (threshold1, threshold2, etc), or, in most cases, those will just end up being hardwired, limiting the applicability of the end application to different image sizes, environment types, etc.
In contrast, in the JeVois framework, one would create a Canny Component, with Parameter settings for the thresholds, etc and a member function
One would typically first set the parameter values, then call the function. Setting the parameters can be done by the code that will use the function, but, more often, it is left to the user. In a particular vision pipeline, resonable default values may be provided for the parameters at the beginning, then leaving those parameters accessible to end users who may want to modify them. Modification of parameters in JeVois is handled either at the start of the application by parsing command-line arguments, when a new processing Module is instantiated, or while it is running, by interacting with the Engine that manages the system via its Serial ports.
Any complex vision processing pipeline that includes the Canny component in its hierarchy will expose its parameters so that they can be set, either by users (via command-line arguments, commands issued over serial ports, etc), or by other components in the hierarchy. If multiple instances of a Component are present in a hierarchy, their parameters can either all be set at once to a single (shared) value, or they can be accessed and set individually to different values (each Component has a unique instance name, and a so-called descriptor is created by concatenating instance names of a component and all its parents; parameters of a specific Component instance can be accessed by prefixing that instance's descriptor to the parameter name). Parameters provide a uniform interface for setting/getting the parameter values, so that programmers do not have to program accessor functions in each of their components for each of their parameters(e.g., adding a Parameter<int> for threshold1 to your component will automatically add set(), get(), etc functions for it into your component).
In addition, Component provides some level of introspection or reflection: one can access a component instance name, class name, descriptor that includes names of all its parents in the hierarchy, and iterate over its parameters, over its sub-components, etc.
Component is inherently thread-safe and the component interface is written so as to encourage thread-safe use; for example, no function is provided that returns the parent of a component, as another thread could change that between the time that parent is returned and the time it is used. Instead, functions are provided that can traverse the hierarchy up or down in a thread-safe manner and execute some action on the components that are encountered during that traversal.
Components are brought into action in several phases:
initialization: When init() is called on the manager (including Engine), it propagates down the entire Component hierarchy, as follows:
The initialization flow is fixed. Classes that derive from Component can override preInit() for intializations that do not rely on Parameter values and postInit() for those that do. Once init() is complete on the Manager, all components in the hierarchy are considered ready for operation.
If a component is added to an already-initialized hierarchy, it will be brought up to the same init state as its parent at the time of addition. This is typically the case when one selects a new video format from a USB host on a JeVois system that is already running and streaming video. The Module handling the old format is furst un-initialized by Engine, then it is destroyed, the new Module is instantiated for the new format, and it is initialized before the stream of video frames is directed to it.
Note how both init() and uninit() are private in Component, and thus cannot be called directly. Manager (and hence Engine which derives from Manager) makes them public so that you can call them on the manager. That is, your entire hierarchy is either initialized or not.
Because powering up the JeVois hardware platform automatically constructs and initializes the default processing pipeline, and because of Engine instantiates and then initializes processing modules when users change video format, most components just assume that their vision processing functions will never be called when the component is not initialized, and thus they just skip testing for initialized() altogether (to save CPU cycles).
Definition at line 181 of file Component.H.
Public Member Functions | |
Component (std::string const &instance) | |
Constructor. | |
virtual | ~Component () |
Virtual destructor for safe inheritance. | |
Component hierarchies | |
template<class Comp , typename... Args> | |
std::shared_ptr< Comp > | addSubComponent (std::string const &instance, Args &&...args) |
Pseudo-constructor: construct and add another component as a subcomponent of this one. | |
template<class Comp > | |
void | removeSubComponent (std::shared_ptr< Comp > &component) |
Remove a sub-Component from this Component, by shared_ptr. | |
void | removeSubComponent (std::string const &instance, bool warnIfNotFound=true) |
Remove a sub-Component from this Component, by instance name. | |
template<class Comp = jevois::Component> | |
std::shared_ptr< Comp > | getSubComponent (std::string const &instance) const |
Get a sub-component by instance name. | |
bool | isTopLevel () const |
Returns true if this component is top-level, i.e., its parent is jevois::Manager. | |
Component runtime | |
bool | initialized () const |
Has this component been initialized yet? | |
Component metainfo-related functions | |
std::string const & | className () const |
The class name of this component. | |
std::string const & | instanceName () const |
The instance name of this component. | |
Component Parameter-related functions | |
Each Component can hold Parameter objects (through inheritance) that can be set externally by users to modify the operation of the Component, and that can be accessed or set by the Component itself. Note how the JeVois framework uses inheritance for parameters as opposed to making them class data members of the owning Component. See Parameter-related classes and functions for detailed explanations. | |
template<typename T > | |
std::vector< std::string > | setParamVal (std::string const ¶mdescriptor, T const &val) |
Set a parameter value. | |
template<typename T > | |
void | setParamValUnique (std::string const ¶mdescriptor, T const &val) |
Set a parameter value, simple version assuming only one parameter match. | |
template<typename T > | |
std::vector< std::pair< std::string, T > > | getParamVal (std::string const ¶mdescriptor) const |
Get parameter(s) value(s) by descriptor. | |
template<typename T > | |
T | getParamValUnique (std::string const ¶mdescriptor) const |
Get a parameter value, simple version assuming only one parameter match. | |
std::vector< std::string > | setParamString (std::string const ¶mdescriptor, std::string const &val) |
Set a parameter value, by string. | |
void | setParamStringUnique (std::string const ¶mdescriptor, std::string const &val) |
Set a parameter value by string, simple version assuming only one parameter match. | |
std::vector< std::pair< std::string, std::string > > | getParamString (std::string const ¶mdescriptor) const |
Get a parameter value, by string. | |
std::string | getParamStringUnique (std::string const ¶mdescriptor) const |
Get a parameter value by string, simple version assuming only one parameter match. | |
void | freezeParam (std::string const ¶mdescriptor, bool doit) |
Freeze/unfreeze a parameter, by name, see ParameterBase::freeze() | |
void | freezeAllParams (bool doit) |
Freeze all parameters. | |
std::string | descriptor () const |
Get our full descriptor (including all parents) as [Instancename]:[...]:[...]. | |
void | setParamsFromFile (std::string const &filename) |
Set some parameters from a file. | |
std::istream & | setParamsFromStream (std::istream &is, std::string const &absfile) |
Set some parameters from an open stream. | |
virtual void | paramInfo (std::shared_ptr< UserInterface > s, std::map< std::string, std::string > &categs, bool skipFrozen, std::string const &cname="", std::string const &pfx="") |
Get machine-oriented descriptions of all parameters. | |
void | foreachParam (std::function< void(std::string const &compname, ParameterBase *p)> func, std::string const &cname="") |
Run a function on every param we hold. | |
template<typename T > | |
std::shared_ptr< DynamicParameter< T > > | addDynamicParameter (std::string const &name, std::string const &description, T const &defaultValue, ParameterCategory const &category) |
Add a new parameter after the Component has already been constructed. | |
template<typename T , template< typename > class ValidValuesSpec> | |
std::shared_ptr< DynamicParameter< T > > | addDynamicParameter (std::string const &name, std::string const &description, T const &defaultValue, ValidValuesSpec< T > const &validValuesSpec, ParameterCategory const &category) |
Add a new parameter after the Component has already been constructed. | |
template<typename T > | |
void | setDynamicParameterCallback (std::string const &name, std::function< void(T const &)> cb, bool callnow=true) |
Register a callback with a previously created dynamic parameter. | |
void | removeDynamicParameter (std::string const &name, bool throw_if_not_found=true) |
Remove a previously added dynamic parameter. | |
Component path-related functions | |
Each Component can keep track of a preferred filesystem path. Typically, users should not tamper with this, but Module objects (which derive from Component) that are dynamically loaded will have that path set to the path where the Module's shared object file (.so) was found. This allows those modules to access some of their local configuration data, which may, for example, be stored in an | |
void | setPath (std::string const &path) |
Assign a filesystem path to this component. | |
std::filesystem::path | absolutePath (std::filesystem::path const &path="") |
If given path is relative (not starting with /), prepend the Component path to it. | |
Public Member Functions inherited from jevois::ParameterRegistry | |
virtual | ~ParameterRegistry () |
Virtual destructor for safe inheritance. | |
Protected Member Functions | |
Component setup functions overloadable by derived classes | |
virtual void | preInit () |
Called before all sub-Components are init()ed. | |
virtual void | postInit () |
Called after all sub-Components are init()ed. | |
virtual void | preUninit () |
Called before all sub-Components are uninit()ed. | |
virtual void | postUninit () |
Called after all sub-Components are uninit()ed. | |
Protected Member Functions inherited from jevois::ParameterRegistry | |
void | addParameter (ParameterBase *const param) |
The Parameter class uses this method to register itself on construction with its owning Component. | |
void | removeParameter (ParameterBase *const param) |
The Parameter class uses this method to un-register itself on destruction with its owning Component. | |
void | callbackInitCall () |
For all parameters that have a callback which has never been called, call it with the default param value. | |
Friends | |
template<typename T > | |
class | ParameterCore |
class | Manager |
class | Engine |
class | Module |
jevois::Component::Component | ( | std::string const & | instance | ) |
Constructor.
The standard way to create a component is via Component::addSubComponent() or Manager::addComponent(), rather than constructing them by hand. Components constructed via the constructor (e.g., calling operator new) will not be attached to a Component hierarchy. It is recommended that derived components also have an instance constructor argument and pass it down to the base Component class, to allow complex systems with several instances of a same Component. In fact, for most components, the inherited constructor is appropriate:
Definition at line 32 of file Component.C.
References JEVOIS_TRACE.
|
virtual |
Virtual destructor for safe inheritance.
Calls uninit() if component is initialized.
Definition at line 54 of file Component.C.
References JEVOIS_TRACE, and LDEBUG.
std::filesystem::path jevois::Component::absolutePath | ( | std::filesystem::path const & | path = "" | ) |
If given path is relative (not starting with /), prepend the Component path to it.
If path is absolute, no-op. If path is empty, return the Component's path as set with setPath().
Definition at line 514 of file Component.C.
References jevois::absolutePath(), and JEVOIS_TRACE.
std::shared_ptr< DynamicParameter< T > > jevois::Component::addDynamicParameter | ( | std::string const & | name, |
std::string const & | description, | ||
T const & | defaultValue, | ||
ParameterCategory const & | category | ||
) |
Add a new parameter after the Component has already been constructed.
Dynamic parameters can only be accessed by descriptor using Component::setParamVal(), Component::getParamVal(), etc., since the owning component does not inherit from them like standard parameters. Callbacks can be added manually after creation using Component::setDynamicParameterCallback(). This version creates a Parameter with no given valid values, valid values are whatever T can take.
std::shared_ptr< DynamicParameter< T > > jevois::Component::addDynamicParameter | ( | std::string const & | name, |
std::string const & | description, | ||
T const & | defaultValue, | ||
ValidValuesSpec< T > const & | validValuesSpec, | ||
ParameterCategory const & | category | ||
) |
Add a new parameter after the Component has already been constructed.
Dynamic parameters can only be accessed by descriptor, since the owning component does not inherit from them like standard parameters. Callbacks can be added manually after creation using Component::setDynamicParameterCallback(). This version creates a Parameter with specified valid values from a ValidValueSpec
std::shared_ptr< Comp > jevois::Component::addSubComponent | ( | std::string const & | instance, |
Args &&... | args | ||
) |
Pseudo-constructor: construct and add another component as a subcomponent of this one.
A child component of type Comp (which must derive from jevois::Component) will be created and added as a sub-component of this. The child logically "belongs" to this component, and will automatically be initialized, un-initialized, and deleted when the parent component is. In addition to construction, adding a subcomponent will bring it up to the same init() state as the owner component.
When setting parameters, the sub-Component can be referenced as a child of this component. For instance, if we have a ComponentParent which has ComponentChild as a sub-Component, and ComponentChild has parameter named coolChildParam, then that parameter could be specified on the command line by
or over a Serial port by
instance | A string identifying a particular instance of a Component, no two sub-components of a component may have the same instance name. The instance name should be passed in through your derived Component's constructor, allowing users to disambiguate between multiple instances of your Component. If the instance name is empty, the actual instance will be named ComponentClassname# with # replaced (if necessary) by a unique number. If the instance name is not empty but contains a #, only that # is replaced (if necessary) by a number that makes the instance name unique. The final name is accessible via instanceName() once your component is constructed. There is no default value for instance in the base class to catch derived classes that forgot to pass it down to the base, but it may be a good idea to set an empty string default to instance in derived classes. |
args | any constructor parameters used to instantiate the sub-component |
std::string const & jevois::Component::className | ( | ) | const |
The class name of this component.
Definition at line 39 of file Component.C.
References jevois::demangle().
std::string jevois::Component::descriptor | ( | ) | const |
Get our full descriptor (including all parents) as [Instancename]:[...]:[...].
Definition at line 276 of file Component.C.
References JEVOIS_TRACE.
Referenced by jevois::PythonParameter::descriptor(), and jevois::dnn::PostProcessorPose::process().
void jevois::Component::foreachParam | ( | std::function< void(std::string const &compname, ParameterBase *p)> | func, |
std::string const & | cname = "" |
||
) |
Run a function on every param we hold.
Definition at line 568 of file Component.C.
References JEVOIS_TRACE.
Referenced by jevois::GUIhelper::drawParameters().
void jevois::Component::freezeAllParams | ( | bool | doit | ) |
Freeze all parameters.
Definition at line 429 of file Component.C.
void jevois::Component::freezeParam | ( | std::string const & | paramdescriptor, |
bool | doit | ||
) |
Freeze/unfreeze a parameter, by name, see ParameterBase::freeze()
Definition at line 417 of file Component.C.
References jevois::ParameterBase::freeze().
std::vector< std::pair< std::string, std::string > > jevois::Component::getParamString | ( | std::string const & | paramdescriptor | ) | const |
Get a parameter value, by string.
jevois::exception::ParameterException | if no Parameter matches the given descriptor. |
Definition at line 389 of file Component.C.
References JEVOIS_TRACE, and jevois::ParameterBase::strget().
std::string jevois::Component::getParamStringUnique | ( | std::string const & | paramdescriptor | ) | const |
Get a parameter value by string, simple version assuming only one parameter match.
This calls getParamVal(), checks that exactly one match was found, and returns its value as a string.
jevois::exception::ParameterException | if not exactly one Parameter matches the given descriptor. |
Definition at line 405 of file Component.C.
References JEVOIS_TRACE.
Referenced by jevois::GUIhelper::resetstate().
std::vector< std::pair< std::string, T > > jevois::Component::getParamVal | ( | std::string const & | paramdescriptor | ) | const |
Get parameter(s) value(s) by descriptor.
Use this method to get the current value of a Component's parameter from the string descriptor. Values for all parameters that match the descriptor are returned.
For example, if the class MyComponent has an integer parameter named "myparam" you could get the value like so:
jevois::exception::ParameterException | if no Parameter matches the given descriptor. |
T jevois::Component::getParamValUnique | ( | std::string const & | paramdescriptor | ) | const |
Get a parameter value, simple version assuming only one parameter match.
This calls getParamVal(), checks that exactly one match was found, and returns its value. For example, if the class MyComponent has an integer parameter named "myparam" you could get the value like so:
std::range_error | if not exactly one Parameter matches the given descriptor. |
Referenced by jevois::GUIhelper::drawConsole().
std::shared_ptr< Comp > jevois::Component::getSubComponent | ( | std::string const & | instance | ) | const |
Get a sub-component by instance name.
This method does a dynamic_pointer_cast to Comp if it is not the default (jevois::Component). Throws if component is not found by instance name, or it is found but not of type Comp (if Comp is specified). Note that once you hold a shared_ptr to a Component, it is guaranteed that the component will not be destroyed until that shared_ptr is released. If the JeVois system tries to destroy the component (e.g., someone calls removeSubComponent()), the component will be un-initialized and its parent will be unset, so it will not be fully operational and will be actually deleted when the last shared_ptr to it runs out of scope.
bool jevois::Component::initialized | ( | ) | const |
Has this component been initialized yet?
Definition at line 206 of file Component.C.
References JEVOIS_TRACE.
std::string const & jevois::Component::instanceName | ( | ) | const |
The instance name of this component.
Definition at line 50 of file Component.C.
Referenced by jevois::Engine::registerPythonComponent(), and jevois::Engine::unRegisterPythonComponent().
bool jevois::Component::isTopLevel | ( | ) | const |
Returns true if this component is top-level, i.e., its parent is jevois::Manager.
The Module of Engine is top-level.
Definition at line 119 of file Component.C.
References JEVOIS_TRACE.
|
virtual |
Get machine-oriented descriptions of all parameters.
Definition at line 521 of file Component.C.
References jevois::ParameterSummary::category, jevois::ParameterSummary::categorydescription, jevois::ParameterSummary::defaultvalue, jevois::ParameterSummary::description, jevois::ParameterSummary::frozen, JEVOIS_TRACE, jevois::ParameterSummary::name, jevois::ParameterSummary::validvalues, jevois::ParameterSummary::value, and jevois::ParameterSummary::valuetype.
|
inlineprotectedvirtual |
Called after all sub-Components are init()ed.
Reimplemented in jevois::Manager, jevois::Engine, jevois::ICM20948, jevois::Serial, jevois::dnn::Pipeline, and jevois::dnn::PostProcessorSegment.
Definition at line 501 of file Component.H.
|
inlineprotectedvirtual |
Called after all sub-Components are uninit()ed.
Reimplemented in jevois::PythonModule, and jevois::Serial.
Definition at line 507 of file Component.H.
|
inlineprotectedvirtual |
Called before all sub-Components are init()ed.
Reimplemented in jevois::Manager, jevois::Engine, jevois::ICM20948, and jevois::PythonModule.
Definition at line 498 of file Component.H.
|
inlineprotectedvirtual |
Called before all sub-Components are uninit()ed.
Reimplemented in jevois::ICM20948, and jevois::dnn::Pipeline.
Definition at line 504 of file Component.H.
void jevois::Component::removeDynamicParameter | ( | std::string const & | name, |
bool | throw_if_not_found = true |
||
) |
Remove a previously added dynamic parameter.
Definition at line 498 of file Component.C.
References LFATAL.
void jevois::Component::removeSubComponent | ( | std::shared_ptr< Comp > & | component | ) |
Remove a sub-Component from this Component, by shared_ptr.
void jevois::Component::removeSubComponent | ( | std::string const & | instance, |
bool | warnIfNotFound = true |
||
) |
Remove a sub-Component from this Component, by instance name.
Definition at line 75 of file Component.C.
References JEVOIS_TRACE, and LERROR.
void jevois::Component::setDynamicParameterCallback | ( | std::string const & | name, |
std::function< void(T const &)> | cb, | ||
bool | callnow = true |
||
) |
Register a callback with a previously created dynamic parameter.
If callnow is true, the callback will be called right after it is registered, using the parameter's current value. This is to mimic standard parameters where the callback is called at least once during init.
void jevois::Component::setParamsFromFile | ( | std::string const & | filename | ) |
Set some parameters from a file.
The file should have entries "descriptor=value", one per line, where the parameter descriptors should be relative to this component. If the file name is relative, our component path will be prefixed to it using absolutePath().
Definition at line 437 of file Component.C.
References jevois::absolutePath(), and LFATAL.
std::istream & jevois::Component::setParamsFromStream | ( | std::istream & | is, |
std::string const & | absfile | ||
) |
Set some parameters from an open stream.
The stream should have entries "descriptor=value", one per line, where the parameter descriptors should be relative to this component. absfile is only used for error messages and should be the absolute path of the file opened in 'is', or some other text for error messages.
Definition at line 446 of file Component.C.
References LFATAL.
std::vector< std::string > jevois::Component::setParamString | ( | std::string const & | paramdescriptor, |
std::string const & | val | ||
) |
Set a parameter value, by string.
jevois::exception::ParameterException | if no Parameter matches the given descriptor or if the given string cannot be converted to the Parameter's native type. |
Definition at line 358 of file Component.C.
References JEVOIS_TRACE, and jevois::ParameterBase::strset().
void jevois::Component::setParamStringUnique | ( | std::string const & | paramdescriptor, |
std::string const & | val | ||
) |
Set a parameter value by string, simple version assuming only one parameter match.
This calls setParamVal(), and checks that exactly one match was found.
std::range_error | if not exactly one Parameter matches the given descriptor. |
Definition at line 374 of file Component.C.
References JEVOIS_TRACE.
Referenced by jevois::GUIhelper::setparstr().
std::vector< std::string > jevois::Component::setParamVal | ( | std::string const & | paramdescriptor, |
T const & | val | ||
) |
Set a parameter value.
Throws if we don't have a parameter by that name or the value does not work out. Here is how the descriptor works:
The basic format is
Note that any [ComponentInstanceName]
token can also be replaced by a *, which is equivalent to any number of any ComponentInstanceName specifications. So use the * to reach parameters when you don't know how deep they are. If there is some other ComponentInstanceName between a * and the final paramname, then recursion is turned off and anything between the * and paramname must match intervening components/instances.
For example,
would match parameters where MyInst matches the top-level component (the one on which you call setParamVal()), then would recurse through any number of subcomponents, until one of them matches CoolComp, and then we would look for MyParam parameter in that subcomponent, and we would not look any deeper.
Finally note that there is an implicit first *: that is automatically prepended to your description, so if you just specify a paramname and nothing else before it, we will set all params by that name in all subcomponents no matter how deep they are (as if you had specified *:paramname).
std::exception | if no Parameter matches the given descriptor. |
void jevois::Component::setParamValUnique | ( | std::string const & | paramdescriptor, |
T const & | val | ||
) |
Set a parameter value, simple version assuming only one parameter match.
This calls setParamVal(), and checks that exactly one match was found.
std::exception | if not exactly one Parameter matches the given descriptor. |
Referenced by jevois::GUIhelper::drawConsole().
void jevois::Component::setPath | ( | std::string const & | path | ) |
Assign a filesystem path to this component.
Definition at line 484 of file Component.C.
References JEVOIS_TRACE.
|
friend |
Definition at line 514 of file Component.H.
|
friend |
Definition at line 513 of file Component.H.
|
friend |
Definition at line 515 of file Component.H.
|
friend |
Definition at line 512 of file Component.H.