diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4a52dc875b48e6905515f0635c58858ff4fe791..48f474bcf30b38f20b9bb5142f3544252fdaee03 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,7 +52,9 @@ SET(sources descriptors/daisy/descriptor_daisy_load_yaml.cpp descriptors/lucid/descriptor_lucid.cpp descriptors/lucid/descriptor_lucid_load_yaml.cpp - matchers/matcher_base.cpp ) + matchers/matcher_base.cpp + matchers/flannbased/matcher_flannbased.cpp + matchers/flannbased/matcher_flannbased_load_yaml.cpp) # application header files SET(headers @@ -89,7 +91,8 @@ SET(headers descriptors/daisy/descriptor_daisy.h descriptors/lucid/descriptor_lucid.h matchers/matcher_factory.h - matchers/matcher_base.h) + matchers/matcher_base.h + matchers/flannbased/matcher_flannbased.h) # locate the necessary dependencies FIND_PACKAGE(Eigen3 REQUIRED) diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index f31331a075ebe1b0cd41cba494398abe6985638a..37b432a910cecc09df53226f093e8cbc39fdf5da 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -3,9 +3,11 @@ ADD_EXECUTABLE(test_factories test_factories.cpp ) ADD_EXECUTABLE(test_sensor test_sensor.cpp) ADD_EXECUTABLE(test_detector test_detector.cpp) ADD_EXECUTABLE(test_descriptor test_descriptor.cpp) +ADD_EXECUTABLE(test_matcher test_matcher.cpp) # link necessary libraries TARGET_LINK_LIBRARIES(test_factories ${PROJECT_NAME}) TARGET_LINK_LIBRARIES(test_sensor vision_utils) TARGET_LINK_LIBRARIES(test_detector vision_utils) -TARGET_LINK_LIBRARIES(test_descriptor vision_utils) \ No newline at end of file +TARGET_LINK_LIBRARIES(test_descriptor vision_utils) +TARGET_LINK_LIBRARIES(test_matcher vision_utils) \ No newline at end of file diff --git a/src/examples/test_descriptor.cpp b/src/examples/test_descriptor.cpp index fcc03e81de6c4b9e31b70bf433a60670307bc5c0..6ea20092eabb2037d9edee4eec43b0e5a9e8aece 100644 --- a/src/examples/test_descriptor.cpp +++ b/src/examples/test_descriptor.cpp @@ -121,6 +121,8 @@ int main(void) cv::startWindowThread(); cv::namedWindow(det_ptr->getName(), cv::WINDOW_NORMAL); + // The following line is used to remove the OpenCV "init done" from the terminal + std::cout << "\e[A" << " " << std::endl; for (int nframe = 0; nframe < 1000; ++nframe) { diff --git a/src/examples/test_detector.cpp b/src/examples/test_detector.cpp index 517d0ca4e2762bfd918bf7aa419f7bc054756176..8eef026bc64b66644fd9309fba5723902fa91cf5 100644 --- a/src/examples/test_detector.cpp +++ b/src/examples/test_detector.cpp @@ -78,6 +78,8 @@ int main(void) cv::startWindowThread(); cv::namedWindow(det_ptr->getName(), cv::WINDOW_NORMAL); + // The following line is used to remove the OpenCV "init done" from the terminal + std::cout << "\e[A" << " " << std::endl; for (int nframe = 0; nframe < 1000; ++nframe) { diff --git a/src/examples/test_matcher.cpp b/src/examples/test_matcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba8f60ddc39065e5199a55eefed590594b114c65 --- /dev/null +++ b/src/examples/test_matcher.cpp @@ -0,0 +1,211 @@ +#include <iostream> +#include <iomanip> +#include <cstdlib> + +#include "../vision_utils.h" + +// Sensors +#include "../sensors/usb_cam/usb_cam.h" + +// Detectors +#include "../detectors/orb/detector_orb.h" +#include "../detectors/fast/detector_fast.h" +#include "../detectors/sift/detector_sift.h" +#include "../detectors/surf/detector_surf.h" +#include "../detectors/brisk/detector_brisk.h" +#include "../detectors/mser/detector_mser.h" +#include "../detectors/gftt/detector_gftt.h" +#include "../detectors/harris/detector_harris.h" +#include "../detectors/sbd/detector_sbd.h" +#include "../detectors/kaze/detector_kaze.h" +#include "../detectors/akaze/detector_akaze.h" +#include "../detectors/agast/detector_agast.h" + +// Descriptors +#include "../descriptors/orb/descriptor_orb.h" +#include "../descriptors/sift/descriptor_sift.h" +#include "../descriptors/surf/descriptor_surf.h" +#include "../descriptors/brisk/descriptor_brisk.h" +#include "../descriptors/kaze/descriptor_kaze.h" +#include "../descriptors/akaze/descriptor_akaze.h" +#include "../descriptors/latch/descriptor_latch.h" +#include "../descriptors/freak/descriptor_freak.h" +#include "../descriptors/brief/descriptor_brief.h" +#include "../descriptors/daisy/descriptor_daisy.h" +#include "../descriptors/lucid/descriptor_lucid.h" + +// Matchers +#include "../matchers/flannbased/matcher_flannbased.h" + +int main(void) +{ + using namespace vision_utils; + using std::shared_ptr; + using std::make_shared; + using std::static_pointer_cast; + + // YAML file with parameters + std::string path_yaml_file = "/src/examples/yaml"; + + // Root dir path + std::string vu_root = _VU_ROOT_DIR; + + // Setup camera sensor by default + SensorBasePtr sen_b_ptr = setupSensor("USB_CAM", "CAMERA_test", vu_root + path_yaml_file + "/FAST.yaml"); // Any YAML with sensor type setup correctly + SensorCameraPtr sen_ptr = std::static_pointer_cast<SensorCamera>(sen_b_ptr); + + std::string def_detector = "ORB"; + std::cout << std::endl << "Which DETECTOR do you want to test? Type one of the registered names [default: " << def_detector << "]: "; + std::string det_name = readFromUser(def_detector); + + DetectorBasePtr det_ptr = setupDetector(det_name, det_name + " detector", vu_root + path_yaml_file + "/" + det_name + ".yaml"); + + if (det_name.compare("ORB") == 0) + det_ptr = std::static_pointer_cast<DetectorORB>(det_ptr); + else if (det_name.compare("FAST") == 0) + det_ptr = std::static_pointer_cast<DetectorFAST>(det_ptr); + else if (det_name.compare("SIFT") == 0) + det_ptr = std::static_pointer_cast<DetectorSIFT>(det_ptr); + else if (det_name.compare("SURF") == 0) + det_ptr = std::static_pointer_cast<DetectorSURF>(det_ptr); + else if (det_name.compare("BRISK") == 0) + det_ptr = std::static_pointer_cast<DetectorBRISK>(det_ptr); + else if (det_name.compare("MSER") == 0) + det_ptr = std::static_pointer_cast<DetectorMSER>(det_ptr); + else if (det_name.compare("GFTT") == 0) + det_ptr = std::static_pointer_cast<DetectorGFTT>(det_ptr); + else if (det_name.compare("HARRIS") == 0) + det_ptr = std::static_pointer_cast<DetectorHARRIS>(det_ptr); + else if (det_name.compare("SBD") == 0) + det_ptr = std::static_pointer_cast<DetectorSBD>(det_ptr); + else if (det_name.compare("KAZE") == 0) + det_ptr = std::static_pointer_cast<DetectorKAZE>(det_ptr); + else if (det_name.compare("AKAZE") == 0) + det_ptr = std::static_pointer_cast<DetectorAKAZE>(det_ptr); + else if (det_name.compare("AGAST") == 0) + det_ptr = std::static_pointer_cast<DetectorAGAST>(det_ptr); + + std::string def_descriptor = "ORB"; + std::cout << std::endl << "Which DESCRIPTOR do you want to test? Type one of the registered names [default: " << def_descriptor << "]: "; + std::string des_name = readFromUser(def_descriptor); + + DescriptorBasePtr des_ptr = setupDescriptor(des_name, des_name + " descriptor", vu_root + path_yaml_file + "/" + des_name + ".yaml"); + + if (des_name.compare("ORB") == 0) + des_ptr = std::static_pointer_cast<DescriptorORB>(des_ptr); + else if (des_name.compare("SIFT") == 0) + des_ptr = std::static_pointer_cast<DescriptorSIFT>(des_ptr); + else if (des_name.compare("SURF") == 0) + des_ptr = std::static_pointer_cast<DescriptorSURF>(des_ptr); + else if (des_name.compare("BRISK") == 0) + des_ptr = std::static_pointer_cast<DescriptorBRISK>(des_ptr); + else if (des_name.compare("KAZE") == 0) + des_ptr = std::static_pointer_cast<DescriptorKAZE>(des_ptr); + else if (des_name.compare("AKAZE") == 0) + des_ptr = std::static_pointer_cast<DescriptorAKAZE>(des_ptr); + else if (des_name.compare("LATCH") == 0) + des_ptr = std::static_pointer_cast<DescriptorLATCH>(des_ptr); + else if (des_name.compare("FREAK") == 0) + des_ptr = std::static_pointer_cast<DescriptorFREAK>(des_ptr); + else if (des_name.compare("BRIEF") == 0) + des_ptr = std::static_pointer_cast<DescriptorBRIEF>(des_ptr); + else if (des_name.compare("DAISY") == 0) + des_ptr = std::static_pointer_cast<DescriptorDAISY>(des_ptr); + else if (des_name.compare("LUCID") == 0) + des_ptr = std::static_pointer_cast<DescriptorLUCID>(des_ptr); + + std::cout << "\n================ MATCHER TEST =================" << std::endl; + + std::string def_matcher = "FLANNBASED"; + std::cout << std::endl << "Which MATCHER do you want to test? Type one of the registered names [default: " << def_matcher << "]: "; + std::string mat_name = readFromUser(def_matcher); + + MatcherBasePtr mat_ptr = setupMatcher(mat_name, mat_name + " matcher", vu_root + path_yaml_file + "/" + mat_name + ".yaml"); + + if (mat_name.compare("FLANNBASED") == 0) + mat_ptr = std::static_pointer_cast<MatcherFLANNBASED>(mat_ptr); + + std::cout << std::endl << "... Testing " << det_ptr->getName() << " with " << des_ptr->getName() << " and " << mat_ptr->getName() << " ..." << std::endl; + + // Open camera + sen_ptr->open(0); + + cv::startWindowThread(); + cv::namedWindow(det_ptr->getName(), cv::WINDOW_NORMAL); + cv::namedWindow(mat_ptr->getName(), cv::WINDOW_NORMAL); + + // The following line is used to remove the OpenCV "init done" from the terminal + std::cout << "\e[A" << " " << std::endl; + + cv::Mat frame_old; + KeyPointVector keypoints_old; + KeyPointVector good_keypoints; + cv::Mat descriptors_old; + std::vector<cv::DMatch> good_matches; + + for (int nframe = 0; nframe < 1000; ++nframe) + { + // Get frame + cv::Mat frame; + sen_ptr->getFrame(frame); + cv::Mat frame_matches = frame.clone(); + + // Detector + KeyPointVector keypoints = det_ptr->detect(frame); + + // Descriptor + cv::Mat descriptors = des_ptr->getDescriptor(frame,keypoints); + + // Matcher + if (nframe > 1) + { + // get params + MatcherParamsBasePtr mat_params_ptr = mat_ptr->getParams(); + + if (mat_params_ptr->match_type == MATCH) + { + std::vector<cv::DMatch> matches; + mat_ptr->match(descriptors,descriptors_old,matches); + } + else + { + std::vector< std::vector<cv::DMatch> > matches; + mat_ptr->match(descriptors,descriptors_old,matches); + + // TODO: Filter matches +// good_matches.clear(); +// good_keypoints.clear(); +// mat_ptr->filterMatches(keypoints_old, keypoints, matches, frame.rows, frame.cols, good_matches, good_keypoints); + } + + // Draw matches + if (!keypoints_old.empty() && !keypoints.empty()) + { + // draw detections + drawKeyPoints(frame, keypoints); + + // Draw matches + drawKeyPoints(frame_matches, good_keypoints); + for (unsigned int ii = 0; ii < good_keypoints.size(); ++ii) + cv::line(frame_matches,good_keypoints[ii].pt,good_keypoints[ii].pt,cv::Scalar(0,255,0),3); + + std::cout << std::fixed << std::setprecision(4) << "\e[A" << "Detection time: " << det_ptr->getTime() << " Description time: " << des_ptr->getTime() << " Matching time: " << mat_ptr->getTime() << " TOTAL time: " << det_ptr->getTime() + des_ptr->getTime() + mat_ptr->getTime() << std::endl; + + // Show frames + cv::imshow(det_ptr->getName(), frame); + cv::imshow(mat_ptr->getName(), frame_matches); + cv::waitKey(1); + } + } + // Update objects + keypoints_old.clear(); + keypoints_old.resize(keypoints.size()); + for (unsigned int ii = 0; ii < keypoints.size(); ++ii) + keypoints_old.push_back(keypoints[ii]); + descriptors_old = cv::Mat(descriptors.size(),descriptors.type()); + descriptors_old = descriptors.clone(); + frame_old = frame; + } + + cv::destroyAllWindows(); +} diff --git a/src/examples/yaml/FLANNBASED.yaml b/src/examples/yaml/FLANNBASED.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f68781e0282ac10cdf54fd06d72219eabbd55a54 --- /dev/null +++ b/src/examples/yaml/FLANNBASED.yaml @@ -0,0 +1,5 @@ +sensor: + type: "USB_CAM" +matcher: + type: "FLANNBASED" + match type: 1 # Match type. MATCH = 1, KNNMATCH = 2, RADIUSMATCH = 3 \ No newline at end of file diff --git a/src/matchers/flannbased/matcher_flannbased.cpp b/src/matchers/flannbased/matcher_flannbased.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2286433e8039bfc8d2a6ea718759712aca78292b --- /dev/null +++ b/src/matchers/flannbased/matcher_flannbased.cpp @@ -0,0 +1,17 @@ +#include "matcher_flannbased.h" + +namespace vision_utils { + +MatcherFLANNBASED::MatcherFLANNBASED(void) +{} + +MatcherFLANNBASED::~MatcherFLANNBASED(void) +{} + +} /* namespace vision_utils */ + +// Register in the MatchersFactory +namespace vision_utils +{ +VU_REGISTER_MATCHER("FLANNBASED", MatcherFLANNBASED); +} /* namespace vision_utils */ diff --git a/src/matchers/flannbased/matcher_flannbased.h b/src/matchers/flannbased/matcher_flannbased.h new file mode 100644 index 0000000000000000000000000000000000000000..fbfbc4e4655fcb536449cd3e2160fbc2881a93ac --- /dev/null +++ b/src/matchers/flannbased/matcher_flannbased.h @@ -0,0 +1,66 @@ +#ifndef _MATCHER_FLANNBASED_H_ +#define _MATCHER_FLANNBASED_H_ + +#include "../matcher_base.h" +#include "../matcher_factory.h" + +// yaml-cpp library +#ifdef USING_YAML + #include <yaml-cpp/yaml.h> +#endif + +namespace vision_utils { + +// Create all pointers +VU_PTR_TYPEDEFS(MatcherFLANNBASED); +VU_PTR_TYPEDEFS(MatcherParamsFLANNBASED); + +/** \brief Class parameters + * + */ +struct MatcherParamsFLANNBASED: public MatcherParamsBase +{ + // TODO: Add possible parameters +}; + +/** \brief DETECTOR class + * + */ +class MatcherFLANNBASED : public MatcherBase { + + public: + MatcherFLANNBASED(); + virtual ~MatcherFLANNBASED(void); + + // Factory method + static MatcherBasePtr create(const std::string& _unique_name, const ParamsBasePtr _params); + + private: + + void defineMatcher(const ParamsBasePtr _params); +}; + +/* + * brief Define detector + */ +inline void MatcherFLANNBASED::defineMatcher(const ParamsBasePtr _params) +{ + params_base_ptr_ = std::static_pointer_cast<MatcherParamsBase>(_params); + MatcherParamsFLANNBASEDPtr params_ptr = std::static_pointer_cast<MatcherParamsFLANNBASED>(_params); + matcher_ = cv::FlannBasedMatcher::create("FlannBased"); +} + +/* + * brief Create object in factory + */ +inline MatcherBasePtr MatcherFLANNBASED::create(const std::string& _unique_name, const ParamsBasePtr _params) +{ + MatcherFLANNBASEDPtr mat_ptr = std::make_shared<MatcherFLANNBASED>(); + mat_ptr->setName(_unique_name); + mat_ptr->defineMatcher(_params); + return mat_ptr; +} + +} /* namespace vision_utils */ + +#endif /* _MATCHER_FLANNBASED_H_ */ diff --git a/src/matchers/flannbased/matcher_flannbased_load_yaml.cpp b/src/matchers/flannbased/matcher_flannbased_load_yaml.cpp new file mode 100644 index 0000000000000000000000000000000000000000..353962e5f98ed48b5a91fb1feb7989afea86be94 --- /dev/null +++ b/src/matchers/flannbased/matcher_flannbased_load_yaml.cpp @@ -0,0 +1,46 @@ +#include "matcher_flannbased.h" + +#ifdef USING_YAML + +// yaml-cpp library +#include <yaml-cpp/yaml.h> + +namespace vision_utils +{ + +namespace +{ + +static ParamsBasePtr createParamsFLANNBASEDMatcher(const std::string & _filename_dot_yaml) +{ + MatcherParamsFLANNBASEDPtr params_ptr = std::make_shared<MatcherParamsFLANNBASED>(); + + using std::string; + using YAML::Node; + Node yaml_params = YAML::LoadFile(_filename_dot_yaml); + if (!yaml_params.IsNull()) + { + Node d_yaml = yaml_params["matcher"]; + if(d_yaml["type"].as<string>() == "FLANNBASED") + { + params_ptr->match_type = d_yaml["match type"].as<int>(); + // TODO: Add possible parameters + }else + { + std::cerr << "Bad configuration file. Wrong type " << d_yaml["type"].as<string>() << std::endl; + return nullptr; + } + } + + return params_ptr; +} + +// Register in the SensorFactory +const bool registered_matFLANNBASED_params = ParamsFactory::get().registerCreator("FLANNBASED MAT", createParamsFLANNBASEDMatcher); + +} /* namespace [unnamed] */ + +} /* namespace vision_utils */ + +#endif /* IF USING_YAML */ + diff --git a/src/matchers/matcher_base.cpp b/src/matchers/matcher_base.cpp index b8fe2f5dc09523f13ae2a36b3a3d9099f8add6ad..03e374b09a8df04941b14f10ffa4de11528fb409 100644 --- a/src/matchers/matcher_base.cpp +++ b/src/matchers/matcher_base.cpp @@ -10,20 +10,38 @@ MatcherBase::~MatcherBase(void) { } -//KeyPointVector MatcherBase::detect(const cv::Mat& _image) -//{ -// cv::Mat mask = cv::Mat::ones(_image.size(), CV_8U); -// return detect(_image, mask); -//} -//KeyPointVector MatcherBase::detect(const cv::Mat& _image, const cv::Mat& _mask) -//{ -// KeyPointVector kpts; -// clock_t tStart = clock(); -// Matcher_->detect(_image, kpts, _mask); -// comp_time_ = (double)(clock() - tStart) / CLOCKS_PER_SEC; -// -// return kpts; -//} +void MatcherBase::match(const cv::Mat& _desc1, const cv::Mat& _desc2, std::vector<cv::DMatch>& matches) +{ + if (params_base_ptr_->match_type==MATCH) + { + clock_t tStart = clock(); + if (!_desc1.empty() && !_desc2.empty()) + matcher_->match( _desc1, _desc2, matches); + comp_time_ = (double)(clock() - tStart) / CLOCKS_PER_SEC; + } + else + std::cerr << "[FeatureMatcher::match]: The selected matcher output is different than your object." << std::endl; +} + +void MatcherBase::match(const cv::Mat& _desc1, const cv::Mat& _desc2, std::vector< std::vector<cv::DMatch> >& matches) +{ + if (params_base_ptr_->match_type == KNNMATCH)// knn match + { + clock_t tStart = clock(); + if (!_desc1.empty() && !_desc2.empty()) + matcher_->knnMatch(_desc1, _desc2, matches, 2); + comp_time_ = (double)(clock() - tStart) / CLOCKS_PER_SEC; + } + else if (params_base_ptr_->match_type == RADIUSMATCH) // radius match + { + clock_t tStart = clock(); + if (!_desc1.empty() && !_desc2.empty()) + matcher_->radiusMatch(_desc1, _desc2, matches, 2); + comp_time_ = (double)(clock() - tStart) / CLOCKS_PER_SEC; + } + else + std::cerr << "[FeatureMatcher::match]: The selected matcher output is different than your object." << std::endl; +} } /* namespace vision_utils */ @@ -34,14 +52,14 @@ namespace vision_utils MatcherBasePtr setupMatcher(const std::string& _type, const std::string& _unique_name, const ParamsBasePtr& _params) { - MatcherBasePtr des_ptr = MatcherFactory::get().create(_type, _unique_name, _params); - return des_ptr; + MatcherBasePtr mat_ptr = MatcherFactory::get().create(_type, _unique_name, _params); + return mat_ptr; }; #ifdef USING_YAML MatcherBasePtr setupMatcher(const std::string& _type, const std::string& _unique_name, const std::string& _filename_dot_yaml) { - ParamsBasePtr params_ptr = ParamsFactory::get().create(_type, _filename_dot_yaml); + ParamsBasePtr params_ptr = ParamsFactory::get().create(_type+" MAT", _filename_dot_yaml); return setupMatcher(_type, _unique_name, params_ptr); } #endif diff --git a/src/matchers/matcher_base.h b/src/matchers/matcher_base.h index deecac63a8ab9569e15674efc3e2eaa560899518..3fe9021bf6453e55452a7ceb1a8d8f2e44685a61 100644 --- a/src/matchers/matcher_base.h +++ b/src/matchers/matcher_base.h @@ -15,6 +15,21 @@ namespace vision_utils // Create all pointers VU_PTR_TYPEDEFS(MatcherBase); +VU_PTR_TYPEDEFS(MatcherParamsBase); + +enum MATCH_TYPE{ + MATCH = 1, + KNNMATCH = 2, + RADIUSMATCH = 3 +}; + +/** \brief Class parameters + * + */ +struct MatcherParamsBase: public ParamsBase +{ + int match_type = MATCH; // Type of Match. MATCH = 1, KNNMATCH = 2, RADIUSMATCH = 3 +}; /** \brief base class for Matcher base * @@ -34,11 +49,13 @@ class MatcherBase : public VUBase, public std::enable_shared_from_this<MatcherBa */ virtual ~MatcherBase(void); -// KeyPointVector detect(const cv::Mat& _image); -// KeyPointVector detect(const cv::Mat& _image, const cv::Mat& _mask); + void match(const cv::Mat& _desc1, const cv::Mat& _desc2, std::vector<cv::DMatch>& matches); + void match(const cv::Mat& _desc1, const cv::Mat& _desc2, std::vector< std::vector<cv::DMatch> >& matches); std::string getName(void); + MatcherParamsBasePtr getParams(void); + // Factory method static MatcherBasePtr create(const std::string& _unique_name, const ParamsBasePtr _params); @@ -46,7 +63,9 @@ class MatcherBase : public VUBase, public std::enable_shared_from_this<MatcherBa std::string name_; - FeatureMatcherPtr Matcher_; + FeatureMatcherPtr matcher_; + + MatcherParamsBasePtr params_base_ptr_; void setName(const std::string& _name); @@ -63,6 +82,11 @@ inline std::string MatcherBase::getName(void) { return name_; } */ inline void MatcherBase::setName(const std::string& _name){ name_ = _name; } +/* + * brief Retrieve object parameters + */ +inline MatcherParamsBasePtr MatcherBase::getParams(void) { return params_base_ptr_; } + /* * brief Setup and get the corresponding pointer */