SOL9 Sample: TemplateMatcher
|
1 Screenshot
2 Source code
/*
* TemplateMatcher.cpp
* Copyright (c) 2015 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
*/
//2017/09/12
// This is an elementary template matching sample program,
// based on OpenCV3.3
// See http://docs.opencv.org/3.2.0/de/da9/tutorial_template_matching.html
//
#define _CONSOLE_
#include <sol/ModuleFileName.h>
#include <sol/Static.h>
#include <sol/LabeledTrackBar.h>
#include <sol/LabeledComboBox.h>
#include <sol/Pair.h>
#include <sol/DropFiles.h>
#include <sol/FileDialog.h>
#include <sol/opencv/OpenCVApplicationView.h>
#include <sol/opencv/OpenCVScrolledImageView.h>
#include <sol/PushButton.h>
#include "resource.h"
namespace SOL {
static const int WM_TEMPLATE_MATCHING_REQUEST = (WM_USER + 2011);
class MainView :public OpenCVApplicationView {
private:
////////////////////////////////////////////////////////////////////////////////////////
//Inner class starts.
class SourceImageView :public OpenCVScrolledImageView {
private:
cv::Mat originalImage;
cv::Mat matchedImage;
cv::Mat scaledImage;
public:
//This is a mandatory method, because in parent class it's declared
//as a pure virtual function.
cv::Mat& getMat()
{
return originalImage;
}
cv::Mat getMatClone()
{
return originalImage.clone();
}
public:
SourceImageView(View* parent, const char* name, Args& args)
:OpenCVScrolledImageView(parent, name, args)
{
try {
const char* filename = (const char*)args.get(XmNimageFileName);
int imageLoadingFlag = args.get(XmNimageLoadingFlag);
loadImage(filename, imageLoadingFlag);
} catch (SOL::Exception ex) {
caught(ex);
}
}
~SourceImageView()
{
}
void rescaleImage(cv::Mat& mat, cv::Mat& scaled, int scale)
{
double ratio = (double)scale/100.0f;
cv::resize(mat, scaled, cv::Size(), ratio, ratio);
}
void loadImage(const char* filename, int imageLoadingFlag= CV_LOAD_IMAGE_COLOR, int scalingRatio=100)
{
try {
originalImage = readImage(filename, imageLoadingFlag);
matchedImage = originalImage.clone();
rescaleImage(matchedImage, scaledImage, scalingRatio);
refresh();
} catch (Exception& ex) {
caught(ex);
}
}
void setMatchedImage(cv::Mat& image, int scalingRatio)
{
matchedImage = image;
setScaledImage(matchedImage, scalingRatio);
}
};
//Inner class ends.
////////////////////////////////////////////////////////////////////////////////////////
private:
typedef enum {
SQDIFF = 0,
SQDIFF_NORMED,
CCORR,
CCORR_NORMED,
CCOEFF,
CCOEFF_NORMED,
} MATCHING_METHOD;
static const int IMAGES_COUNT = 2;
static const int FIRST = 0;
static const int SECOND = 1;
StringT<char> imageFiles[IMAGES_COUNT];
SmartPtr<SourceImageView> sourceImage[IMAGES_COUNT];
int matchingMethod;
SmartPtr<LabeledComboBox> matchingMethodComboBox;
int imageScalingRatio;
FileDialog filedlg;
SmartPtr<PushButton> clearButton;
SmartPtr<PushButton> matchButton;
void updateCaption()
{
char caption[MAX_PATH];
sprintf_s(caption, CountOf(caption), "%s %s - %s",
(const char*)imageFiles[FIRST],
(const char*)imageFiles[SECOND],
getAppName() );
setText(caption);
}
void resize(int w, int h)
{
if (sourceImage[FIRST] && sourceImage[SECOND] &&
matchingMethodComboBox &&
clearButton && matchButton) {
sourceImage[FIRST] -> reshape(2, 2, (w-160)/2-1, h-4);
sourceImage[SECOND] -> reshape((w-160)/2+1, 2, (w-160)/2-1, h-4);
matchingMethodComboBox -> reshape(w-160+4, 10, 140, 60);
clearButton -> reshape(w-160+4, 100, 140, 30);
matchButton -> reshape(w-160+4, 150, 140, 30);
}
}
void openFile(int index, const char* filename)
{
try {
if (index >= FIRST && index <= SECOND) {
sourceImage[index] -> loadImage(filename);
const char* fname = strrchr(filename, '\\');
if (fname) {
fname++;
}
imageFiles[index] = fname;
updateCaption();
}
} catch (Exception& ex) {
caught(ex);
}
}
//Event handler for WM_DROPFILES
long dropFiles(Event& event)
{
View* view = (View*)event.getData();
char fileName[MAX_PATH] = { 0 };
DropFiles drop((HDROP)event.getWParam());
//fileName[0] = ZERO;
int num = drop.queryFile(0, fileName, CountOf(fileName));
if(num > 0) {
if (filedlg.isImageFileName(fileName)) {
if (view == sourceImage[FIRST]) {
openFile(FIRST, fileName);
} else if (view == sourceImage[SECOND]) {
openFile(SECOND, fileName);
}
bringUp();
} else {
bringUp();
showErrorDialog("Invalid image filename", fileName, MB_OK);
}
}
return 0;
}
void confirm(Action& action)
{
int rc = MessageBox(NULL, "Are you sure to close this window?", "Confirmation",
MB_OKCANCEL|MB_ICONEXCLAMATION);
if (rc == IDOK) {
exit(action);
}
}
//Template matching operation.
long templateMatching(Event& event)
{
try {
static const Pair<MATCHING_METHOD, int> methods[] = {
{SQDIFF, CV_TM_SQDIFF},
{SQDIFF_NORMED, CV_TM_SQDIFF_NORMED},
{CCORR, CV_TM_CCORR},
{CCORR_NORMED, CV_TM_CCORR_NORMED},
{CCOEFF, CV_TM_CCOEFF},
{CCOEFF_NORMED, CV_TM_CCOEFF_NORMED},
};
int match_method = CV_TM_SQDIFF;
for (int i = 0; i<CountOf(methods); i++) {
if (matchingMethod == methods[i].first) {
match_method = methods[i].second;
break;
}
}
cv::Mat& simage = sourceImage[FIRST]->getMat();
cv::Mat& timage = sourceImage[SECOND]->getMat();
int result_cols = simage.cols - timage.cols + 1;
int result_rows = simage.rows - timage.rows + 1;
cv::Mat result;
result.create( result_rows, result_cols, CV_32FC1 );
cv::matchTemplate(simage, timage, result, match_method);
cv::normalize( result, result, 0, 1, NORM_MINMAX, -1, cv::Mat() );
double minVal, maxVal;
cv::Point minLoc, maxLoc;
cv::Point matchLoc;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat() );
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED ) {
matchLoc = minLoc;
} else {
matchLoc = maxLoc;
}
cv::Mat matchedImage = simage.clone();
cv::rectangle(matchedImage, matchLoc,
Point( matchLoc.x + timage.cols , matchLoc.y + timage.rows ),
CV_RGB(255, 0, 0), 3 );
sourceImage[FIRST] ->setMatchedImage(matchedImage, imageScalingRatio);
} catch (cv::Exception& ex) {
//MessageBox(NULL, "Caught an excption", "Exception", MB_OK);
}
return 0;
}
void clear(Action& action)
{
cv::Mat& simage = sourceImage[FIRST]->getMat();
cv::Mat matchedImage = simage.clone();
sourceImage[FIRST] ->setMatchedImage(matchedImage, imageScalingRatio);
}
void match(Action& action)
{
post(WM_TEMPLATE_MATCHING_REQUEST);
}
//Callback for the matchingMethodComboBox
void matchingMethodChanged(Action& action)
{
matchingMethod = (MATCHING_METHOD)matchingMethodComboBox->getCurSel();
post(WM_TEMPLATE_MATCHING_REQUEST);
}
//Callback for the event WM_DROPFILES
void dropCallback(Action& action)
{
View* view = (View*)action.getData();
char fileName[MAX_PATH] = { 0 };
DropFiles drop((HDROP)action.getWParam());
int num = drop.queryFile(0, fileName, CountOf(fileName));
if(num > 0) {
if (filedlg.isImageFileName(fileName)) {
if (view == sourceImage[FIRST]) {
openFile(FIRST, fileName);
} else if (view == sourceImage[SECOND]) {
openFile(SECOND, fileName);
}
bringUp();
} else {
bringUp(); //Activate and raise this view
showErrorDialog("Invalid image filename", fileName, MB_OK);
}
}
}
public:
// Constructor
MainView(OpenCVApplication& applet, const char* name, Args& args)
:OpenCVApplicationView(applet, name, args)
{
imageScalingRatio = 100;
try {
imageFiles[FIRST] = "..\\images\\MaterializedToruses.png";
imageFiles[SECOND] = "..\\images\\GreenMaterializedTorus.png";
int imageLoadingFlag = CV_LOAD_IMAGE_COLOR;
//1 Create two sourceImageViews.
Args ar;
for (int i = 0; i< IMAGES_COUNT; i++) {
ar.reset();
ar.set(XmNimageFileName, imageFiles[i]);
ar.set(XmNimageLoadingFlag, imageLoadingFlag);
sourceImage[i] = new SourceImageView(this, "", ar);
//Add XmNdropCallback to each sourceImage view.
sourceImage[i] -> addCallback(XmNdropCallback, this,
(Callback)&MainView::dropCallback, sourceImage[i]);
}
//2 Create a matchingMethodComboBox
ar.reset();
ar.set(XmNstyle, CBS_DROPDOWNLIST);
matchingMethodComboBox = new LabeledComboBox(this, "MatchingMethod", ar);
const char* items[] = {
"SQDIFF",
"SQDIFF_NORMED",
"CCORR",
"CCORR_NORMED",
"CCOEFF",
"CCOEFF_NORMED" };
matchingMethod = SQDIFF;
for (int i =0; i<CountOf(items); i++) {
matchingMethodComboBox->addString(items[i]);
}
matchingMethodComboBox -> setCurSel((int)matchingMethod);
matchingMethodComboBox -> addCallback(XmNselChangeCallback, this,
(Callback)&MainView::matchingMethodChanged, NULL);
//3 Create a clearButton
ar.reset();
clearButton = new PushButton(this, "Clear", ar);
clearButton -> addCallback(XmNactivateCallback, this,
(Callback)&MainView::clear, NULL);
//4 Create a matchButton
ar.reset();
matchButton = new PushButton(this, "Match", ar);
matchButton -> addCallback(XmNactivateCallback, this,
(Callback)&MainView::match, NULL);
//5 Add menu callbacks
addCallback(XmNmenuCallback, IDM_OPEN_FIRST_FILE, this,
(Callback)&MainView::openFile, (void*)FIRST);
addCallback(XmNmenuCallback, IDM_OPEN_SECOND_FILE, this,
(Callback)&MainView::openFile, (void*)SECOND);
addCallback(XmNmenuCallback, IDM_EXIT, this,
(Callback)&MainView::confirm, NULL);
updateCaption();
ar.reset();
ar.set(XmNfilter, FileDialog::getImageFilesFilter());
filedlg.create(this, "OpenFile", ar);
//Add event handler for WM_TEMPLATE_MATCHING_REQUEST event,
addEventHandler(WM_TEMPLATE_MATCHING_REQUEST, this,
(Handler)&MainView::templateMatching, NULL);
//Post a feature matching request event.
post(WM_TEMPLATE_MATCHING_REQUEST);
postResizeRequest();
} catch (Exception& ex) {
caught(ex);
}
}
~MainView()
{
}
void openFile(Action& action)
{
Args ar;
int index = (int)action.getData();
char dir[MAX_PATH] = { 0 };
if (restoreFileFolder(dir, CountOf(dir))) {
ar.set(XmNdirectory, dir);
filedlg.setValues(ar);
}
try {
if(filedlg.open()) {
const char* filename = filedlg.getFileName();
saveFileFolder(filename);
openFile(index, filename);
}
} catch (Exception& ex) {
caught(ex);
}
}
};
}
//
int main(int argc, char** argv)
{
try {
ModuleFileName module(argv[0]);
const char* name = module.getAppName();
OpenCVApplication applet(name, argc, argv);
Args args;
args.set(XmNwidth, 1000);
args.set(XmNheight, 400);
MainView view(applet, name, args);
view.realize();
applet.run();
} catch (SOL::Exception& ex) {
caught(ex);
}
return 0;
}
Last modified: 2 Dec. 2017
Copyright (c) 2017 Antillia.com ALL RIGHTS RESERVED.