/******************************************************************************
*
* 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
*
*****************************************************************************/
//2017/10/29
/*
*/
#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/superres/optical_flow.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 TrackedImageView :public Es::OpenCVScrolledImageView {
private:
cv::Mat tracked_image;
public:
TrackedImageView()
{
}
void rescale(int scaling_ratio)
{
OpenCVScrolledImageView::scaleImage(tracked_image, scaling_ratio);
}
void set_tracked(cv::Mat& mat, int scaling_ratio)
{
tracked_image = mat.clone();
scaleImage(tracked_image, scaling_ratio);
}
};
//Inner classes end.
/////////////////////////////////////////////////////
//
private:
typedef enum {
// SimpleFlow=0,
TV_L1 = 0,
Farneback,
} ALGORITHM;
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];
TrackedImageView tracked_image;
Es::VerticalLayout control_pane;
Es::OpenCVScaleComboBox combobox;
Es::LabeledComboBox algorithm_combobox;
Es::LabeledComboBox besttop_number_combobox;
Es::PushButton track_button;
Es::Label matched_number;
int algorithm_index;
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);
tracked_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(tracked_image);
horiz_layout.pack_start(control_pane);
control_pane.pack_start(combobox, Gtk::PACK_SHRINK );
track_button.set_label("Match");
control_pane.pack_start(track_button, Gtk::PACK_SHRINK);
control_pane.pack_start(algorithm_combobox, Gtk::PACK_SHRINK);
combobox.set_selection(scaling_ratio);
combobox.set_changed_callback(sigc::mem_fun(*this, &MainView::scale_changed) );
track_button.set_label("Track");
track_button.set_clicked_callback(sigc::mem_fun(*this,
&MainView::track_button_clicked) );
const char* algorithms[] = {
"TV-L1",
"Farneback",
};
algorithm_combobox.set_label("Algorithm");
algorithm_combobox.append_items(algorithms, CountOf(algorithms));
algorithm_combobox.set_active_text(algorithms[Farneback]);
algorithm_combobox.set_changed_callback(sigc::mem_fun(*this,
&MainView::algorithm_changed) );
algorithm_index = Farneback;
filenames[FIRST] = "../../images/Dog0.jpg";
filenames[SECOND] = "../../images/Dog2.jpg";
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);
tracked_image.rescale(scaling_ratio);
}
void update_filepath()
{
std::string space = " ";
filepath.set_label(filenames[FIRST] + space + filenames[SECOND]);
}
void algorithm_changed()
{
int prev_algorithm = algorithm_index;
algorithm_index = algorithm_combobox.get_active_row_number();
//printf("detetorIndex %d %s\n", algorithm_index, text.c_str());
if (algorithm_index != prev_algorithm) {
printf("Detector updated call match\n");
calculateFlow();
}
}
//Matching operation.
void track_button_clicked()
{
calculateFlow();
}
void calculateFlow()
{
try {
cv::Ptr<cv::superres::DenseOpticalFlowExt> opticalFlow;
switch (algorithm_index) {
// case Simple:
// opticalFlow = cv::superres::createOptFlow_Simple();
// break;
case TV_L1:
opticalFlow = cv::superres::createOptFlow_DualTVL1();
break;
case Farneback:
opticalFlow = cv::superres::createOptFlow_Farneback();
break;
default:
throw IException("Invalid algorithm.");
}
cv::Mat& source1 = source_images[FIRST] .getOriginalImage();
cv::Mat& source2 = source_images[SECOND].getOriginalImage();
cv::Mat gray1, gray2;
cvtColor(source1, gray1, COLOR_BGR2GRAY);
cvtColor(source2, gray2, COLOR_BGR2GRAY);
cv::Mat flow;
opticalFlow->calc(gray1, gray2, flow);
cv::Mat tracked = source_images[SECOND].getClone();
for (int y = 0; y < source2.rows; y += 10) {
for (int x = 0; x < source2.cols; x += 10) {
const Point2f point = flow.at<Point2f>(y, x) * 1;
line(tracked, Point(x, y),
Point(cvRound(x + point.x), cvRound(y + point.y)), Scalar(0, 0, 255)); //red
circle(tracked, Point(x, y), 1, Scalar(0, 0, 0), -1);
}
}
tracked_image.set_tracked(tracked, scaling_ratio);
} catch (cv::Exception& 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;
}