diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f610a8a5bb23c5df57f99e562841a193cb9f33a..7eef9dc97b86120262fd4986ba28410776c926f4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ SET(sources vision_utils.cpp cam_utils/cam_utils.cpp feature_detector/feature_detector.cpp feature_descriptor/feature_descriptor.cpp feature_matcher/feature_matcher.cpp) # application header files -SET(headers vision_utils.h vu_base/vu_base.h cam_utils/cam_utils.h feature_detector/feature_detector.h feature_descriptor/feature_descriptor.h feature_matcher/feature_matcher.h) +SET(headers vision_utils.h common/vu_base.h cam_utils/cam_utils.h feature_detector/feature_detector.h feature_descriptor/feature_descriptor.h feature_matcher/feature_matcher.h) # locate the necessary dependencies FIND_PACKAGE(Eigen3 REQUIRED) diff --git a/src/common/factory.h b/src/common/factory.h new file mode 100644 index 0000000000000000000000000000000000000000..fe451fdbcb272a04c899a52b7bfd342e3224f463 --- /dev/null +++ b/src/common/factory.h @@ -0,0 +1,330 @@ +#ifndef FACTORY_H_ +#define FACTORY_H_ + +// wolf +//#include "wolf.h" + +// std +#include <string> +#include <map> +#include <iostream> +#include <iomanip> + +namespace cv_utils +{ + +/** \brief Singleton template factory + * + * This class implements a generic factory as a singleton. + * + * > IMPORTANT: This template factory can be used to construct many different objects except: + * > - Objects deriving from DetectorBase --> see DetectorFactory + * > - Objects deriving from DescriptorBase --> see DescriptorFactory + * > - Objects deriving from MatcherBase --> see MatcherFactory + * > + * > The reason for this is that the two cases above need a more elaborated API than the one in this template class. + * + * \param TypeBase base type of all the objects created by the factory + * \param TypeInput type of the input argument. Typical cases are std::string for file names, and YAML::Node for YAML nodes. + * + * - The class is templatized on the class of the produced objects, __TypeBase__. + * The produced objects are always of a class deriving from TypeBase. + * The returned data is always a pointer to TypeBase. + * + * For example, you may use as __TypeBase__ the following types: + * - DetectorBase: the Factory creates detectors deriving from DetectorBase and returns base pointers ````DetectorBasePtr```` to them + * - XxxBase: the Factory creates objects deriving from XxxBase and returns pointers ````XxxBasePtr```` to them. + * + * - The class in also templatized on the type of the input parameter of the creator, __TypeInput__: + * - ````std::string```` is used when the input parameter is a file name from which to read data (typically a YAML file). + * - ````YAML::Node```` is used when the input parameter is a YAML node with structured data. + * + * ### Operation of the factory + * + * #### Rationale + * + * This factory can create objects of classes deriving from TypeBase. + * + * > For example, if you want to make a Detector factory, set TypeBase = DetectorBase.\n + * > Then, the factory will create specific detectors deriving from DetectorBase.\n + * > The specific type of detector (e.g. FAST, SIFT, ORB, etc) is indicated by a string that we call TYPE in this documentation. + * + * Specific object creation is invoked by the method ````create(TYPE, params ... )````, where + * - the TYPE of object to create is identified with a string + * - the params may be provided in different forms -- see TypeInput. + * + * The methods to create specific objects are called __creators__. + * Creators must be registered to the factory before they can be invoked for object creation. + * + * This documentation shows you how to: + * - Define correct TYPE names + * - Access the factory + * - Write object creators + * - Register and unregister object creators to the factory + * - Create objects using the factory + * - Examples: Write and register a detector creator for FAST. + * + * #### Define correct TYPE names + * The rule to make new TYPE strings unique is that you skip the generic 'Type' prefix from your class name, + * and you build a string in CAPITALS with space separators, e.g.: + * - ParamsDetectorORB -> ````"ORB PARAMS"```` + * - ParamsMatcherKnn -> ````"KNN PARAMS"```` + * - etc. + * + * #### Access the factory + * The first thing to know is that we have defined typedefs for the templates that we are using. For example: + * + * \code + * typedef Factory<ParamsBase, std::string> ParamsFactory; + * typedef Factory<DetectorBase, std::string> DetectorFactory; + * typedef Factory<DescriptorBase, YAML::Node> DescriptorFactory; + * \endcode + * + * + * Second to know, the Factory class is a <a href="http://stackoverflow.com/questions/1008019/c-singleton-design-pattern#1008289">singleton</a>: it can only exist once in your application. + * To access it, use the static method get(), + * + * \code + * Factory<MyTypeBase, MyTypeInput>::get() + * \endcode + * + * where, of course, you better make use of the appropriate typedef in place of ````Factory<MyTypeBase, MyTypeInput>````. + * + * You can then call the methods you like, e.g. to create a detector, you use: + * + * \code + * DetectorFactory::get().create(...); // see below for creating objects ... + * \endcode + * + * #### Write creator methods (in your derived object classes) + * The method DetectorORB::create(...) exists in the DetectorORB class as a static method. + * All these ````XxxXxx::create()```` methods need to have exactly the same API, regardless of the object type. + * The API puts into play the two template parameters: + * + * \code + * static TypeBase* create( const TypeInput& ); + * \endcode + * + * This API includes an element of type TypeInput, which might be either a std::string, or a YAML::node: + * - ````std::string```` is used to indicate the name of a configuration file. These files are usually YAML files containing configuration data to create your object. + * - ````YAML::Node```` is used to access parts of a YAML file already encoded as nodes, such as when loading landmarks from a SLAM map stored as a YAML file. + * + * + * Two examples: + * + * \code + * static ParamsBasePtr create(const std::string& _params_dot_yaml) + * static DetectorBasePtr create(const YAML::Node& _lmk_yaml_node) + * \endcode + * + * See further down for an implementation example. + * + * #### Register object creators + * Prior to invoking the creation of an object of a particular TYPE, + * you must register the creator for this type into the factory. + * + * Registering object creators into the factory is done through registerCreator(). + * You provide an object TYPE string (above), and a pointer to a static method + * that knows how to create your specific object, e.g.: + * + * \code + * DetectorFactory::get().registerCreator("DETECTOR ORB", DetectorORB::create); + * \endcode + * + * #### Automatic registration + * Currently, registering is performed in specific source files, object_xxxx.cpp. + * For example, in detector_orb_yaml.cpp we find the line: + * + * \code + * const bool registered_detector_params = ParamsFactory::get().registerCreator("ORB PARAMS", createORBParams); + * \endcode + * + * which is a static invocation (i.e., it is placed at global scope outside of the ORBParams class). + * + * Therefore, at application level, all objects that have a .cpp file compiled are automatically registered. + * + * #### Unregister object creators + * The method unregisterCreator() unregisters the ObjectXxx::create() method. It only needs to be passed the string of the object type. + * + * \code + * Factory<MyTypeBase, MyTypeInput>::get().unregisterCreator("CAMERA"); + * \endcode + * + * #### Create objects using the factory + * Note: Prior to invoking the creation of a object of a particular type, + * you must register the creator for this type into the factory. + * + * To create e.g. a DetectorORB from a YAML node you type: + * + * \code + * DetectorBasePtr det_orb_ptr = Factory<DetectorBasePtr, YAML::Node>::get().create("DETECTOR ORB", det_orb_yaml_node); + * \endcode + * + * or even better, make use of the convenient typedefs: + * + * \code + * DetectorBasePtr det_orb_ptr = DetectorFactory::get().create("DETECTOR ORB", det_orb_yaml_node); + * \endcode + * + * ### Examples + * TODO: Update this + * #### Example 1: Writing the creator of DetectorORB from a YAML node + * + * You can find this code in the detector_orb.cpp file. + * + * \code + * // Creator (this method is static): + * DetectorBasePtr DetectorORB::create(const YAML::Node& _det_orb_node) + * { + * // Parse YAML node with lmk info and data + * unsigned int id = _det_orb_node["id"].as<unsigned int>(); + * int first_id = _det_orb_node["first_id"].as<int>(); + * bool first_defined = _det_orb_node["first_defined"].as<bool>(); + * bool last_defined = _det_orb_node["last_defined"].as<bool>(); + * unsigned int npoints = _det_orb_node["points"].size(); + * Eigen::MatrixXs points(2,npoints); + * for (unsigned int i = 0; i < npoints; i++) + * { + * points.col(i) = _lmk_node["points"][i].as<Eigen::Vector2s>(); + * } + * + * // Create a new landmark + * LandmarkBasePtr lmk_ptr = new LandmarkPolyline2D(points, first_defined, last_defined, first_id); + * lmk_ptr->setId(id); + * + * return lmk_ptr; + * } + * \endcode + * + * #### Example 2: Registering the creator of LandmarkPolyline2D from a YAML node + * + * You can find this code in the landmark_polyline_2D.cpp file. + * + * \code + * // Register landmark creator (put the register code inside an unnamed namespace): + * namespace + * { + * const bool registered_lmk_polyline_2D = LandmarkFactory::get().registerCreator("POLYLINE 2D", LandmarkPolyline2D::create); + * } + * + * \endcode + * + * ### More information + * - IntrinsicsFactory: typedef of this template to create intrinsic structs deriving from IntrinsicsBase directly from YAML files. + * - ProcessorParamsFactory: typedef of this template to create processor params structs deriving from ProcessorParamsBase directly from YAML files. + * - LandmarkFactory: typedef of this template to create landmarks deriving from LandmarkBase directly from YAML nodes. + * - Problem::loadMap() : to load a maps directly from YAML files. + * - You can also check the code in the example file ````src/examples/test_map_yaml.cpp````. + * + * #### See also + * - SensorFactory: to create sensors + * - ProcessorFactory: to create processors. + * - Problem::installSensor() : to install sensors in WOLF Problem. + * - Problem::installProcessor() : to install processors in WOLF Problem. + * + */ +template<class TypeBase, typename... TypeInput> +class Factory +{ + typedef std::shared_ptr<TypeBase> TypeBasePtr; + public: + // example of creator callback (see typedefs below) + typedef TypeBasePtr (*CreatorCallback)(TypeInput... _input); + + // Main factory API + bool registerCreator(const std::string& _type, CreatorCallback createFn); + bool unregisterCreator(const std::string& _type); + TypeBasePtr create(const std::string& _type, TypeInput... _input); + std::string getClass(); + + private: + + typedef std::map<std::string, CreatorCallback> CallbackMap; + + CallbackMap callbacks_; + + // Singleton --------------------------------------------------- + // This class is a singleton. The code below guarantees this. + // See: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern + public: + static Factory& get(); + Factory(const Factory&) = delete; + void operator=(Factory const&) = delete; + private: + Factory() { } + ~Factory() { } +}; + +template<class TypeBase, typename... TypeInput> +inline bool Factory<TypeBase, TypeInput...>::registerCreator(const std::string& _type, CreatorCallback createFn) +{ + bool reg = callbacks_.insert(typename CallbackMap::value_type(_type, createFn)).second; + if (reg) + std::cout << std::setw(22) << std::left << getClass() << " <-- registered " << _type << std::endl; + else + std::cout << std::setw(22) << std::left << getClass() << " X-- skipping " << _type << ": already registered." << std::endl; + + return reg; +} + +template<class TypeBase, typename... TypeInput> +inline bool Factory<TypeBase, TypeInput...>::unregisterCreator(const std::string& _type) +{ + return callbacks_.erase(_type) == 1; +} + +template<class TypeBase, typename... TypeInput> +inline typename Factory<TypeBase, TypeInput...>::TypeBasePtr Factory<TypeBase, TypeInput...>::create(const std::string& _type, TypeInput... _input) +{ + typename CallbackMap::const_iterator creator_callback_it = callbacks_.find(_type); + + if (creator_callback_it == callbacks_.end()) + // not found + throw std::runtime_error(getClass() + " : Unknown type \"" + _type + "\". Possibly you tried to use an unregistered creator."); + + // Invoke the creation function + return (creator_callback_it->second)(std::forward<TypeInput>(_input)...); +} + +template<class TypeBase, typename... TypeInput> +inline Factory<TypeBase, TypeInput...>& Factory<TypeBase, TypeInput...>::get() +{ + static Factory instance_; + return instance_; +} + +template<class TypeBase, typename... TypeInput> +inline std::string Factory<TypeBase, TypeInput...>::getClass() +{ + return "Factory<class TypeBase>"; +} + +// Some specializations +//====================== + +// Parameters +struct ParamsBase; +typedef Factory<ParamsBase, + const std::string&> ParamsFactory; +template<> +inline std::string ParamsFactory::getClass() +{ + return "ParamsFactory"; +} + +// Frames +class FrameBase; +class TimeStamp; +typedef Factory<FrameBase, const FrameType&, const TimeStamp&, const Eigen::VectorXs&> FrameFactory; +template<> +inline std::string FrameFactory::getClass() +{ + return "FrameFactory"; +} +#define WOLF_REGISTER_FRAME(FrameType, FrameName) \ + namespace{ const bool FrameName##Registered = \ + FrameFactory::get().registerCreator(FrameType, FrameName::create); }\ + +} /* namespace cv_utils */ + +#endif /* FACTORY_H_ */ diff --git a/src/vu_base/vu_base.h b/src/common/vu_base.h similarity index 100% rename from src/vu_base/vu_base.h rename to src/common/vu_base.h diff --git a/src/feature_descriptor/feature_descriptor.h b/src/feature_descriptor/feature_descriptor.h index 9020f6bd2c8f68eecf0acd6dce378cc5e9ac60d3..27cd86676dd7230dbf96a3cffefd70e33b2926e6 100644 --- a/src/feature_descriptor/feature_descriptor.h +++ b/src/feature_descriptor/feature_descriptor.h @@ -1,7 +1,7 @@ #ifndef _FEATURE_DESCRIPTOR_H #define _FEATURE_DESCRIPTOR_H -#include <vu_base/vu_base.h> +#include "../common/vu_base.h" typedef cv::Ptr<cv::DescriptorExtractor> FeatureDescriptorPtr; diff --git a/src/feature_detector/feature_detector.h b/src/feature_detector/feature_detector.h index 4d1bf2055752ba78a204fb47adf9cb828237bbed..fc0d945f90064f91d695244db4ebe7e68f3eb4db 100644 --- a/src/feature_detector/feature_detector.h +++ b/src/feature_detector/feature_detector.h @@ -1,7 +1,7 @@ #ifndef _FEATURE_DETECTOR_H #define _FEATURE_DETECTOR_H -#include <vu_base/vu_base.h> +#include "../common/vu_base.h" #define SCALE_FACTOR_LINE_DETECTOR 2 #define NUM_OCTAVE_LINE_DETECTOR 1 diff --git a/src/feature_matcher/feature_matcher.h b/src/feature_matcher/feature_matcher.h index 3e3e484ddc5b7712cf79e6b3f0f4fa4530859967..6c5eda9a36f625f89a08bb430099ea8653f7d16b 100644 --- a/src/feature_matcher/feature_matcher.h +++ b/src/feature_matcher/feature_matcher.h @@ -1,7 +1,7 @@ #ifndef _FEATURE_MATCHER_H #define _FEATURE_MATCHER_H -#include "vu_base/vu_base.h" +#include "../common/vu_base.h" typedef cv::Ptr<cv::DescriptorMatcher> FeatureMatcherPtr;