Es++ Sample: ImageStitcher |
/****************************************************************************** * * 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. * * * ImageStitchr.cpp * *****************************************************************************/ // #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> #include <opencv2/stitching.hpp> 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 StitchedImageView :public Es::OpenCVScrolledImageView { private: cv::Mat stitched_image; public: StitchedImageView() { } void rescale(int scaling_ratio) { OpenCVScrolledImageView::scaleImage(stitched_image, scaling_ratio); } void set_stitched_image(cv::Mat& mat, int scaling_ratio) { stitched_image = mat.clone(); scaleImage(stitched_image, scaling_ratio); } }; //Inner classes end. ///////////////////////////////////////////////////// // private: 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]; StitchedImageView stitched_image; Es::VerticalLayout control_pane; Es::OpenCVScaleComboBox combobox; Es::LabeledComboBox stitcher_mode_combobox; Es::PushButton stitch_button; int stitcher_mode_index; cv::Ptr<cv::Stitcher> stitcher; cv::Stitcher::Mode mode; bool tryToUseGPU; 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) { tryToUseGPU = false; //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); stitched_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(stitched_image); horiz_layout.pack_start(control_pane); control_pane.pack_start(combobox, Gtk::PACK_SHRINK ); control_pane.pack_start(stitcher_mode_combobox, Gtk::PACK_SHRINK); control_pane.pack_start(stitch_button, Gtk::PACK_SHRINK); stitch_button.set_label("Stitch"); combobox.set_selection(scaling_ratio); combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::scale_changed) ); stitch_button.set_clicked_callback(sigc::mem_fun(*this, &MainView::stitch_button_clicked) ); const char* types[] = { "Panorama", "Scans", }; stitcher_mode_combobox.set_label("Detector"); stitcher_mode_combobox.append_items(types, CountOf(types)); stitcher_mode_combobox.set_active_text(types[0]); //Panorama stitcher_mode_combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::stitcher_mode_changed) ); stitcher_mode_index = 0; //7 Create an instance of cv::Stitcher. mode = cv::Stitcher::PANORAMA; stitcher = cv::Stitcher::create(mode, tryToUseGPU); filenames[FIRST] = "../../images/Lake2.png"; filenames[SECOND] = "../../images/Lake1.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); stitched_image.rescale(scaling_ratio); } void update_filepath() { std::string space = " "; filepath.set_label(filenames[FIRST] + space + filenames[SECOND]); } cv::Stitcher::Mode get_stitcher_mode(int index) { int n = 0; Pair<int, cv::Stitcher::Mode> types[] = { {n++, cv::Stitcher::PANORAMA }, {n++, cv::Stitcher::SCANS }, }; mode = cv::Stitcher::PANORAMA; if (index >= 0 && index <n) { mode = types[index].second; } return mode; } void stitcher_mode_changed() { int prev_index = stitcher_mode_index; stitcher_mode_index = stitcher_mode_combobox.get_active_row_number(); printf("stitcher_mode_index %d \n", stitcher_mode_index); if (stitcher_mode_index != prev_index) { mode = get_stitcher_mode(stitcher_mode_index); //Rebuild stitcher. stitcher = cv::Stitcher::create(mode, tryToUseGPU); stitch_images(); } } //When the stitch button is clicked. void stitch_button_clicked() { //call stitch_images method stitch_images(); } /* enum Status { OK = 0, ERR_NEED_MORE_IMGS = 1, ERR_HOMOGRAPHY_EST_FAIL = 2, ERR_CAMERA_PARAMS_ADJUST_FAIL = 3 */ const char* getErrorMessage(cv::Stitcher::Status status) { static const Pair<const char*, cv::Stitcher::Status> errors[] = { {"Need more images", cv::Stitcher::Status::ERR_NEED_MORE_IMGS}, {"Homography est fail", cv::Stitcher::Status::ERR_HOMOGRAPHY_EST_FAIL}, {"Camera params adjust fail", cv::Stitcher::Status::ERR_CAMERA_PARAMS_ADJUST_FAIL}, }; const char* err = ""; for (int i = 0; i <CountOf(errors); i++) { if (status == errors[i].second) { err = errors[i].first; break; } } return err; } void stitch_images() { try { std::vector<cv::Mat> images; images.push_back( source_images[FIRST ].getOriginalImage() ); images.push_back( source_images[SECOND ].getOriginalImage() ); cv::Mat panorama; cv::Stitcher::Status status = stitcher->stitch(images, panorama); if (status == cv::Stitcher::Status::OK) { stitched_image.set_stitched_image(panorama, scaling_ratio); } else { const char* err = getErrorMessage(status); throw IException("Failed to Stitcher::stitch method error=%s", err); } } catch (Es::Exception& ex) { caught(ex); } } 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; }