Es++ Sample: FlannBasedMatcher |
/****************************************************************************** * * Copyright (c) 2017 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * FeatureMatcher.cpp * *****************************************************************************/ /* See: https://docs.opencv.org/3.1.0/dc/de2/classcv_1_1FlannBasedMatcher.html cv::Algorithm - cv::Descriptor - cv::FlannBasedMatcher FlannBasedMatcher ( const Ptr< flann::IndexParams > &indexParams=makePtr< flann::KDTreeIndexParams >(), const Ptr< flann::SearchParams > &searchParams=makePtr< flann::SearchParams >()) */ #include <es++/Pair.h> #include <es++/gtkmm-3.0/Application.h> #include <es++/gtkmm-3.0/FileOpenDialog.h> #include <es++/gtkmm-3.0/Label.h> #include <es++/gtkmm-3.0/PushButton.h> #include <es++/opencv-4.0/OpenCVMainView.h> #include <es++/opencv-4.0/OpenCVScaleComboBox.h> #include <es++/opencv-4.0/OpenCVScrolledImageView.h> using namespace Gtk; namespace Es { class MainView :public Es::OpenCVMainView { private: ///////////////////////////////////////////////////// //Inner classes start. class SourceImageView :public Es::OpenCVScrolledImageView { private: public: SourceImageView() { } void rescale(int scaling_ratio) { OpenCVScrolledImageView::scaleImage(scaling_ratio); } }; class MatchedImageView :public Es::OpenCVScrolledImageView { private: cv::Mat matched_image; public: MatchedImageView() { } void rescale(int scaling_ratio) { OpenCVScrolledImageView::scaleImage(matched_image, scaling_ratio); } void setMatched(cv::Mat& mat, int scaling_ratio) { matched_image = mat.clone(); scaleImage(matched_image, scaling_ratio); } }; //Inner classes end. ///////////////////////////////////////////////////// // private: typedef enum { /* AKAZEFeatureDetector */ DETECTOR_AKAZE=0, /* BRISKFeatureDetector */ DETECTOR_BRISK, /* ORBFeatureDetector */ DETECTOR_ORB, } DETECTOR; static const int IMAGES_COUNT = 2; static const int FIRST = 0; static const int SECOND = 1; std::string filenames[IMAGES_COUNT]; Es::Label filepath; Es::HorizontalLayout horiz_layout; Es::VerticalLayout view_layout; Es::HorizontalLayout source_view_layout; SourceImageView source_images[IMAGES_COUNT]; MatchedImageView matched_image; Es::VerticalLayout control_pane; Es::OpenCVScaleComboBox combobox; Es::LabeledComboBox detector_combobox; Es::LabeledComboBox besttop_number_combobox; Es::PushButton match_button; Es::Label matched_number; int detector_index; int besttop_number; static const int FILEPATH_HEIGHT = 30; static const int CONTROLPANE_WIDTH = 200; int scaling_ratio; static const int loading_flag = IMREAD_COLOR; Es::FileOpenDialog file_dialog; public: ////////////////////////////////////////////// //Constructor // MainView(Es::Application& applet, std::string& title, Es::Args& args) :OpenCVMainView(applet, title, //We don't use the defaultFilePulldown menu. args.set(XmNuseDefaultFilePulldown, false) ) ,scaling_ratio(100) ,source_view_layout(0) //SPACING ,file_dialog(*this, Es::FileOpenDialog::IMAGE_FILES) { //Define File pulldown menu with multiple [Open File] pulldown menu items. Es::SigcMenuCallback file_menu_callbacks[] = { {"New", sigc::mem_fun(*this, &MainView::file_new)}, {"Open File1", sigc::mem_fun(*this, &MainView::file_open1)}, {"Open File2", sigc::mem_fun(*this, &MainView::file_open2)}, {"Save", sigc::mem_fun(*this, &MainView::file_save)}, {"Save_As", sigc::mem_fun(*this, &MainView::file_save_as)}, {"Quit", sigc::mem_fun(*this, &MainView::file_quit)}, }; file_pulldown_append( file_menu_callbacks, CountOf(file_menu_callbacks)); int w = (int)args.get(XmNwidth); int h = (int)args.get(XmNheight); int ww = (w - CONTROLPANE_WIDTH)/2; int hh = h/2; int ratio = (int)args.get(XmNscalingRatio); scaling_ratio = OpenCVImageView::validateScale(ratio); Es::MainLayout& main_layout = get_main_layout(); filepath.set_size_request(w, FILEPATH_HEIGHT); filepath.set_alignment(Gtk::ALIGN_START); main_layout.pack_start(filepath, FALSE,FALSE, 0); main_layout.pack_start(horiz_layout, FALSE, FALSE, 0); source_images[FIRST].set_size_request(ww, h/2); source_images[SECOND].set_size_request(ww, h/2); matched_image.set_size_request(w, h/2); control_pane.set_spacing(10); control_pane.set_size_request(CONTROLPANE_WIDTH, h); main_layout.pack_start(filepath, FALSE, FALSE, 0); main_layout.pack_start(horiz_layout); horiz_layout.pack_start(view_layout); view_layout.pack_start(source_view_layout); source_view_layout.pack_start(source_images[FIRST]); source_view_layout.pack_start(source_images[SECOND]); view_layout.pack_start(matched_image); horiz_layout.pack_start(control_pane); control_pane.pack_start(combobox, Gtk::PACK_SHRINK ); match_button.set_label("Match"); control_pane.pack_start(match_button, Gtk::PACK_SHRINK); control_pane.pack_start(detector_combobox, Gtk::PACK_SHRINK); control_pane.pack_start(besttop_number_combobox, Gtk::PACK_SHRINK); control_pane.pack_start(matched_number, Gtk::PACK_SHRINK); combobox.set_selection(scaling_ratio); combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::scale_changed) ); match_button.set_label("Match"); match_button.set_clicked_callback(sigc::mem_fun(*this, &MainView::match_button_clicked) ); const char* detectors[] = { "AKAZEFeatureDetector", "BRISKFeatureDetector", "ORBFeatureDetector" }; detector_combobox.set_label("Detector"); detector_combobox.append_items(detectors, CountOf(detectors)); detector_combobox.set_active_text(detectors[DETECTOR_ORB]); //ORB detector_combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::detector_changed) ); detector_index = DETECTOR_ORB; besttop_number_combobox.set_label("BestTopNumber"); for (int i = 1; i<10; i++) { char number[20]; sprintf(number, "%d", i*10); besttop_number_combobox.append_item(number); if (i == 1) { besttop_number_combobox.set_active_text(number); } } besttop_number = 10; besttop_number_combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::besttop_number_changed) ); filenames[FIRST] = "../../images/Tower1.png"; filenames[SECOND] = "../../images/Tower2.png"; source_images[FIRST].loadImage(filenames[FIRST], loading_flag, scaling_ratio); source_images[SECOND].loadImage(filenames[SECOND], loading_flag, scaling_ratio); update_filepath(); show_all(); } void scale_changed() { scaling_ratio = combobox.get_selection(); printf("scale_changed %d\n", scaling_ratio); source_images[FIRST].rescale(scaling_ratio); source_images[SECOND].rescale(scaling_ratio); matched_image.rescale(scaling_ratio); } void update_filepath() { std::string space = " "; filepath.set_label(filenames[FIRST] + space + filenames[SECOND]); } void detector_changed() { std::string text = detector_combobox.get_active_text(); Es::Pair<int, std::string> pairs[] = { {DETECTOR_AKAZE, std::string("AKAZEFeatureDetector")}, {DETECTOR_BRISK, std::string("BRISKFeatureDetector")}, {DETECTOR_ORB, std::string("ORBFeatureDetector")}, }; int prev_detector = detector_index; for (size_t i = 0; i<CountOf(pairs); i++) { if (pairs[i].second == text) { detector_index = pairs[i].first; break; } } //printf("detetorIndex %d %s\n", detector_index, text.c_str()); if (detector_index != prev_detector) { printf("Detector updated call match\n"); match_button_clicked(); } } void besttop_number_changed() { int prev_besttop =besttop_number; besttop_number = besttop_number_combobox.get_active_number(); //printf("besttop_number %d\n", besttop_number); if (besttop_number != prev_besttop) { printf("besttop_number updated call match\n"); match_button_clicked(); } } void set_matched_number(int bestMatched, int totalMatchedNumber) { char number[128]; sprintf(number, //CountOf(number), "Matched Number: %d / %d", bestMatched, totalMatchedNumber); matched_number.set_label(number); } //Matching operation. void match_button_clicked() { try { cv::Ptr<cv::Feature2D> feature; switch (detector_index) { case DETECTOR_AKAZE: feature = cv::AKAZE::create(); break; case DETECTOR_BRISK: feature = cv::BRISK::create(); break; case DETECTOR_ORB: feature = cv::ORB::create(); //use default argments break; default: break; } std::vector<cv::KeyPoint> keypoints[IMAGES_COUNT]; cv::Mat descriptors[IMAGES_COUNT]; cv::Mat mats[IMAGES_COUNT]; //Detect features and compute descriptors. for (int i = 0; i<IMAGES_COUNT; i++) { mats[i] = source_images[i].getOriginalImage(); feature -> detect(mats[i], keypoints[i]); feature -> compute(mats[i], keypoints[i], descriptors[i]); } //FlannBasedMatcher cv::FlannBasedMatcher matcher(new flann::LshIndexParams(20, 10, 2)); std::vector<cv::DMatch> matches; matcher.match(descriptors[FIRST], descriptors[SECOND], matches); int totalMatchedNumber = matches.size(); if (besttop_number <= totalMatchedNumber) { std::nth_element(matches.begin(), matches.begin() + besttop_number - 1, matches.end()); matches.erase(matches.begin() + besttop_number, matches.end()); } int bestMatchedNumber = matches.size(); Mat matchedImg; drawMatches(mats[FIRST], keypoints[FIRST], mats[SECOND], keypoints[SECOND], matches, matchedImg); matched_image.setMatched(matchedImg, scaling_ratio); set_matched_number(bestMatchedNumber, totalMatchedNumber); } catch (cv::Exception& ex) { //MessageBox(NULL, "Caught an excption", "Exception", MB_OK); } } void file_open1() { int rc = file_dialog.popup(); if (rc == Gtk::RESPONSE_OK) { filenames[FIRST] = file_dialog.get_filename(); update_filepath(); source_images[FIRST].loadImage(filenames[FIRST], loading_flag, scaling_ratio); } } void file_open2() { int rc = file_dialog.popup(); if (rc == Gtk::RESPONSE_OK) { filenames[SECOND] = file_dialog.get_filename(); update_filepath(); source_images[SECOND].loadImage(filenames[SECOND], loading_flag, scaling_ratio); } } }; } int main(int argc, char** argv) { Es::Environment env; try { Es::Application applet(argc, argv); std::string title(argv[0]); Es::Args args; args.set(XmNx, 20); args.set(XmNy, 20); args.set(XmNwidth, 900); args.set(XmNheight,460); args.set(XmNscalingRatio,60); Es::MainView mainv(applet, title, args); mainv.show(); applet.run(mainv); } catch (Es::Exception& ex) { caught(ex); } catch (...) { printf("Get exception \n"); } return 0; }