1.1. Solar component interfaces are virtual base classes. They shall inherit from class org::bcom::xpcf::IComponentIntrospect
1.2. SolAR Interfaces are defined in a dedicated header file (.h) whose name shall begin with a capital I followed with the name of the abstract class it refers to, e.g. ICamera.h.
1.3. SolAR Interfaces shall not contain member variables, it is an abstract class without committing to a particular implementation of the class. If you need member variables, declare them in the implementation of the component
1.4. A component interface must be a abstract class, meaning that all its methods must be virtual.
1.5. Solar Framework is organized hierarchically via dedicated directories and namespaces. Currently, concerning the interfaces, this organization is as follows :
Directory |
namespace |
SolARFramework/interfaces/api/display |
SolAR::api::display |
SolARFramework/interfaces/api/features |
SolAR::api::features |
SolARFramework/interfaces/api/geom |
SolAR::api::geom |
SolARFramework/interfaces/api/image |
SolAR::api::image |
SolARFramework/interfaces/api/input/devices |
SolAR::api::input::devices |
SolARFramework/interfaces/api/input/files |
SolAR::api::input::files |
SolARFramework/interfaces/api/sink |
SolAR::api::sink |
SolARFramework/interfaces/api/solver/map |
SolAR::api::solver::map |
SolARFramework/interfaces/api/solver/pose |
SolAR::api::solver::pose |
1.6. Namespaces should use lower case.
1.7. Any new interface must fall into one of these categories. Yet, if needed, one may ask the SolAR Team to add a new one to fulfill a particular need not covered by the current organization.
1.8. If possible, an abstract interface of a component must define only one processing method. Exception may be allowed if your processing method need take as an input or output only one or a collection of several objects, as for instance:
virtual void drawCircle(SRef<Point2Df> point, unsigned int radius, int thickness, std::vector<unsigned int> & bgrValues, SRef<Image> displayImage) = 0;
virtual void drawCircles(std::vector<SRef<Point2Df>>& points, unsigned int radius, int thickness, SRef<Image> displayImage) = 0;
or if the processing method can take as input our output parameter an object or an inherited object, as for instance:
virtual void drawCircles(std::vector<SRef<Point2Df>>& points, unsigned int radius, int thickness, SRef<Image> displayImage) = 0;
virtual void drawCircles(std::vector<SRef<Keypoint>>& keypoints, unsigned int radius, int thickness, SRef<Image> displayImage) = 0;
1.9. A 128-bit UUID (Universal Unique IDentifier) shall be associated to any virtual interface and explicitly quoted in the interface header file, preferably after the class definition.
The syntax is the following :
XPCF_DEFINE_INTERFACE_TRAITS(SolARnamespaces::IInterfaceClassName,
"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"Interface description");
XPCF_DEFINE_INTERFACE_TRAITS(SolAR::api::display::I2DOverlay,
"62b8b0b5-9344-40e6-a288-e609eb3ff0f1",
"SolAR::I2DOverlay interface");
1.10. The header file shall contain Doxygen documentation code in order to automatically generate the interface description web page during the continuous integration process used by the Solar Integration Team.
In particular the purpose of the interface shall be documented, as well as each virtual method with its input/ouput parameters.
For instance for class documentation:
and for method documentation:
virtual void drawSBPattern (SRef<SquaredBinaryPattern> pattern, SRef<Image> displayImage) = 0;
Modules are the placeholders for the components. Basically, they are defined to reflect a particular type of implementation, based e.g. on a particular technology or a particular provider, etc …
A component cannot exist by itself. It must be included in a module but a module may contain only one component if needed.
They are are few rules that modules and components must conform to in order to be usable by SolAR. This is explained in the following.
-
Modules are delivered as shared libraries (windows or linux)
-
The recommended naming convention is ModuleName, where name should reflect a characteristic of the module, e.g. ModuleOpencvFree.
-
Components are declared inside a namespace according to the following naming convention : SolAR::MODULES::NAMEOFMODULE
-
A 128-bit UUID (Universal Unique IDentifier) shall be associated to every module
-
A 128-bit UUID (Universal Unique IDentifier) shall be associated to every component included in a module
-
A Module exports its components via a dedicated Export API defined in a header file named NameOfModuleAPI.h, such :
/**
* Copyright and license notice ......
*/
#ifndef NAME_API_H
#define NAMEOFMODULE_API_H
#if _WIN32
#ifdef NameOfModule_API_DLLEXPORT
#define NAMEOFMODULE_EXPORT_API __declspec(dllexport)
#else //NameOfModule_API_DLLEXPORT
#define NAMEOFMODULE_EXPORT_API __declspec(dllimport)
#endif //NameOfModule_API_DLLEXPORT
#else //_WIN32
#define NAMEOFMODULE_EXPORT_API
#endif //_WIN32
#include "NameOfModule_traits.h"
#endif //NAMEOFMODULE_API_H
and where 'NameOfModule_traits.h' exposes the list of components contained in the module. Follows a generic example of this file.
#define NAMEOFMODULE_TRAITS_H
#include "xpcf/component/ComponentTraits.h"
namespace SolAR {
namespace MODULES {
namespace NAME {
class Component1;
class Component2;
}
}
}
XPCF_DEFINE_COMPONENT_TRAITS(SolAR::MODULES::NAME::Component1,
"aaaaaaaa-bbbb-cccc-dddd-eeeeeeee",
"SolAR::MODULES::NAME::Component1 definition")
XPCF_DEFINE_COMPONENT_TRAITS(SolAR::MODULES::NAME::Component1,
"ffffffff-gggg-hhhh-iiii-jjjjjjjj",
"SolAR::MODULES::NAME::Component2 definition")
#endif
#include "xpcf/module/ModuleFactory.h"
#include "component1.h"
#include "component2.h"
namespace xpcf=org::bcom::xpcf;
XPCF_DECLARE_MODULE("kkkkkkkk-llll-mmmm-nnnn-oooooooo", "ModuleName")
extern "C" XPCF_MODULEHOOKS_API xpcf::XPCFErrorCode XPCF_getComponent(const boost::uuids::uuid& componentUUID,SRef<xpcf::IComponentIntrospect>& interfaceRef)
{
xpcf::XPCFErrorCode errCode = xpcf::XPCFErrorCode::_FAIL;
errCode = xpcf::tryCreateComponent<SolAR::MODULES::NAME::component1>(componentUUID,interfaceRef);
if (errCode != xpcf::XPCFErrorCode::_SUCCESS)
{
errCode = xpcf::tryCreateComponent<SolAR::MODULES::NAME::component2>(componentUUID,interfaceRef);
}
return errCode;
}
XPCF_BEGIN_COMPONENTS_DECLARATION
XPCF_ADD_COMPONENT(SolAR::MODULES::Name::component1)
XPCF_ADD_COMPONENT(SolAR::MODULES::Name::component2)
XPCF_END_COMPONENTS_DECLARATION
In the above mentioned code, a two-component module is considered.
Each component is implemented via a class (.cpp/.h).
Components should be implemented for processing.
It should take in parameters and deliver out components and data.
2.1. Data structures should be implemented for the data flow, meaning the data that will be exchanged between components at runtime thanks to the processing methods of the components. To optimize vision pipelines developped thanks to the SolAR Framework, it is of real need to take care to data structure optimization (reduce the memory copy, favour a quick access to data, etc.)
2.2. A data structure must be defined in a namespace SolAR::datastructure
2.3. The header and cpp files must be put under the datastructure directory.
2.4. The header file shall contains Doxygen documentation code in order to automatically generate the data structure description web page during the continuous integration process used by the Solar Integration Team.
For instance for class documentation:
and for method documentation:
void setSize(uint32_t width, uint32_t height);
|
Please refer to existing SolAR component interfaces and take them as examples.
|
A component implements a low-level vision processing in order to offer much more flexibility. They can be be interconnected together to create high-level and real-time vision pipeline (localization, 3D reconstruction, etc.).
That is why a component should take in parameters and deliver out data structures to ease their connection (data flow).
When you intent to create a component, first verify that :
-
a SolAR interface already exists for your kind of components. If so, please use the specified interface.
if no, or if you think the existing interfaces do not totally fit your need, please contact the SolAR team.
Please look at the organization of existing modules and components.
3.1. Any component must be embedded in a module. Modules are used to easily publish one or a group of components to the solAR community. If you want to create a new module, please copy the existing structure of SolAR modules.
3.2. Your components should have explicit names (that means, that ideally, we do not need to read documentation to understands what it is for and on which third party it is based). Please refer to existing components for naming examples.
3.3. Your components must inherit from component interfaces defined by the SolARFramework. If no component interface fit your need or if you think an existing interface should be extended, please contact the SolAR team.
3.4. Your components can define their own attributes which should be defined as private. No attributes can be shared by other components. If you want to shared data between components, pass them as attribute of the processing method.
3.5. Your components can define their own methods which should be defined as private. The only public methods should be the ones defined by the abstract interfaces.
3.6. Processing methods should not pass control or configuration parameters, but only the parameters representing the input and output dataflow of the component. If a parameter does not have vocation to change at each pass of the pipeline, this parameter must be moved as a private attribute of the component.
3.7. All methods defined by the abstract interface you are inheriting must be implemented in your component.
3.8. Your component should inherit from ConfigurableBase if you want to configure it thanks to an external file (please refer to other components, for instance ImageFilter). So you won’t have to recompile your pipeline if you want to change the configuration of its components.
SolARImageFilterBinaryOpencv::SolARImageFilterBinaryOpencv():ConfigurableBase(xpcf::toUUID<SolARImageFilterBinaryOpencv>())
{
addInterface<api::image::IImageFilter>(this);
}
3.9. If you want to initialize the value of component attributes at runtime thanks to an external file, you need to wrap it to a naming string. Thus, you will be able to configure your pipeline by editing an external file defining this attribute by its given name.
example :
params->wrapInteger("min", min);
3.10. There is no need to implement a setParameter and a getParameter method in a SolAR component. To configure a parameter at runtime in your code, get the wanted property of your component through the IConfigurable interface and set it to the desired value.
auto rIConfigurable_imageFilterBinary = imageFilterBinary->bindTo<xpcf::IConfigurable>();
auto imageFilterBinary_property=rIConfigurable_imageFilterBinary->getProperty("min");
imageFilterBinary_property->setIntegerValue(-1);
3.11. All Dataflow parameters of a processing method that are not modified by the function must be const.
3.12. All Dataflow parameters of a processing method that are SRef and that can be instantiated by the method must be a reference on a SRef, otherwise do not use a reference on a SRef. In the following example, the ouptut image of a ImageConvertor can be auotmatically instantiated according to the size of the input image:
virtual FrameworkReturnCode convert(SRef<Image> imgSrc, SRef<Image>& imgDst) = 0;
3.13. The namespace of your component must respect the following form: modules::module_name, where module_name refers to the name of your module.
3.14. The namespaces of your component should use lower case.
|
Please refer to existing SolAR component implementations and take them as examples.
|
|
Please refer to existing SolAR components and take them as examples.
|
3.15. Each parameter of each method of your component should be tested in your code, to detect bad parameter values. When an incorrect value is detected, the method should return an errorcode. This will help developers for implementing and debugging their pose estimation solution.
3.16. Each component should have a corresponding "simple" unit test . The unit test should test component creation, configuration, and simple use of this component, with normal cases. The unit test should be commented, to help other contributors to understand your component.
3.17. Each component should have unit tests with limit cases, as bad instanciation (bad values of attributes), bad use of components (for example: try to load an image that does not exist), and test every error code of each method.