| OZ++ Sample: TemplateMatcher |
/****************************************************************************** * * Copyright (c) 2019 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. * * * TemplateMatcher.cpp * *****************************************************************************/ // 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 // //2017/05/30 //2019/01/03 #include <opencv2/stitching.hpp> #include <oz++/Pair.h> #include <oz++/motif/Label.h> #include <oz++/motif/RowColumn.h> #include <oz++/motif/LabeledTrackBar.h> #include <oz++/opencv/OpenCVScaleComboBox.h> #include <oz++/opencv/OpenCVMainView.h> #include <oz++/opencv/OpenCVImageView.h> #include <oz++/motif/FileOpenDialog.h> #include <opencv2/features2d/features2d.hpp> #include "PrivateFileMenu.h" namespace OZ { class MainView :public OpenCVMainView { private: /////////////////////////////////////////////// //Inner classes start. class SourceImageView: public OpenCVImageView { private: cv::Mat originalImage; cv::Mat matchedImage; cv::Mat scaledImage; virtual void display() { show(scaledImage); } public: SourceImageView(View* parent, const char* name, Args& args) :OpenCVImageView(parent, name, args) { try { const char* filename = (const char*)args.get(XmNimageFileName); int imageLoadingFlag = args.get(XmNimageLoadingFlag); int scalingRatio = (int)args.get(XmNimageScalingRatio); loadImage(filename, imageLoadingFlag, scalingRatio); } catch (OZ::Exception ex) { caught(ex); } } ~SourceImageView() { } cv::Mat& getMat() { return originalImage; } void setMatchedImage(cv::Mat& image, int scalingRatio) { matchedImage = image; scaleImage(matchedImage, scaledImage, scalingRatio); } void loadImage(const char* filename, int imageLoadingFlag= CV_LOAD_IMAGE_COLOR, int scalingRatio=100) { originalImage = readImage(filename, imageLoadingFlag); matchedImage = originalImage.clone(); scaleImage(matchedImage, scaledImage, scalingRatio); invalidate(); refresh(); } void clear(int scalingRatio) { matchedImage = originalImage.clone(); scaleImage(matchedImage, scaledImage, scalingRatio); } void rescale(int scalingRatio) { scaleImage(matchedImage, scaledImage, scalingRatio); } }; //Template image class starts. class TemplateImageView: public OpenCVImageView { private: cv::Mat originalImage; cv::Mat scaledImage; virtual void display() { show(scaledImage); } public: TemplateImageView(View* parent, const char* name, Args& args) :OpenCVImageView(parent, name, args) { try { const char* filename = (const char*)args.get(XmNimageFileName); int imageLoadingFlag = args.get(XmNimageLoadingFlag); int scalingRatio = (int)args.get(XmNimageScalingRatio); loadImage(filename, imageLoadingFlag, scalingRatio); } catch (OZ::Exception ex) { caught(ex); } } ~TemplateImageView() { } cv::Mat& getMat() { return originalImage; } void loadImage(const char* filename, int imageLoadingFlag= CV_LOAD_IMAGE_COLOR, int scalingRatio=100) { originalImage = readImage(filename, imageLoadingFlag); scaleImage(originalImage, scaledImage, scalingRatio); invalidate(); refresh(); } void rescale(int scalingRatio) { scaleImage(originalImage, scaledImage, scalingRatio); } }; //Inner classes end. private: typedef enum { SQDIFF = 0, SQDIFF_NORMED, CCORR, CCORR_NORMED, CCOEFF, CCOEFF_NORMED, } MATCHING_METHOD; StringT<char> sourceImageFile; StringT<char> templateImageFile; static const int FIRST = 0; static const int SECOND = 1; int fileOpenIndex; SmartPtr<SourceImageView> sourceImage; SmartPtr<TemplateImageView> templateImage; int imageLoadingFlag; int imageScalingRatio; //percentage SmartPtr<Label> label; SmartPtr<RowColumn> controlPane; SmartPtr<OpenCVScaleComboBox> scaleComboBox; int matchingMethod; SmartPtr<LabeledComboBox> matchingMethodComboBox; SmartPtr<PushButton> clearButton; SmartPtr<PushButton> matchButton; SmartPtr<FileOpenDialog> fileDialog; //Callback for the clearButton void clear(Action& action) { sourceImage->clear(imageScalingRatio); } //Callback for the matchButton void match(Action& action) { templateMatch(); } //Template matching operation. void templateMatch() { 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; } } cv::Mat& simage = sourceImage->getMat(); cv::Mat& timage = templateImage->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 ->setMatchedImage(matchedImage, imageScalingRatio); } catch (cv::Exception& ex) { //MessageBox(NULL, "Caught an excption", "Exception", MB_OK); } } //Callback for the matchingMethodComboBox void matchingMethodChanged(Action& action) { matchingMethod = (MATCHING_METHOD)matchingMethodComboBox->getSelectedPosition(); printf("matchingMethodChanged: %d\n", matchingMethod); templateMatch(); } public: void scaleChanged(Action& action) { int val = scaleComboBox->getScale(); if (val > 0 && imageScalingRatio != val) { imageScalingRatio = val; sourceImage -> rescale(imageScalingRatio); templateImage -> rescale(imageScalingRatio); } } void fileOpen(int index, Action& action) { fileOpenIndex = index; fileDialog->popup(); } void updateLabel() { char path[PATH_MAX]; sprintf(path, "%s, %s", (const char*)sourceImageFile, (const char*)templateImageFile); label -> setText(path); } //OK button callback for FileOpenDialog void ok(Action& action) { try { fileDialog->popdown(); if (fileOpenIndex == 0) { sourceImageFile = fileDialog->getFileName(); sourceImage->invalidate(); sourceImage->loadImage((const char*)sourceImageFile, imageLoadingFlag, imageScalingRatio); } if (fileOpenIndex == 1) { templateImageFile = fileDialog->getFileName(); templateImage->invalidate(); templateImage->loadImage((const char*)templateImageFile, imageLoadingFlag, imageScalingRatio); } updateLabel(); resize(width(), height()); flush(); } catch (OZ::Exception& ex) { caught(ex); } } void resize(Dimension w, Dimension h) { int CP_WIDTH = 220; int LB_HEIGHT = 30; int ww = w-CP_WIDTH; int hh = h - LB_HEIGHT; if (label && sourceImage && templateImage && controlPane ) { label -> reshape(0, 0, w, LB_HEIGHT); sourceImage -> reshape(0, LB_HEIGHT, ww/2, hh ); templateImage-> reshape(ww/2, LB_HEIGHT, ww/2-1, hh); controlPane -> reshape(ww-1, LB_HEIGHT, CP_WIDTH+1, hh); controlPane -> unmap(); controlPane -> map(); } flush(); } //Callback to handle file pulldown events. virtual void privateFileMenu(Action& action) { menuId = action.getClientIntData(); switch(menuId) { case NEW: fileNew(action); break; case OPEN_FILE1: fileOpen(FIRST, action); break; case OPEN_FILE2: fileOpen(SECOND, action); break; case SAVE: fileSave(action); break; case SAVE_AS: fileSaveAs(action); break; case EXIT: confirm(action); break; } } virtual void addFilePulldownMenu() { //1 Recreate pulldown menu for File. MenuBar* menubar = getMenuBar(); getFileMenuButton() -> recreatePulldownMenu(menubar, "file"); //2 Add PrivateFileItems to the FilePulldown. getFileMenuButton() -> addItems(PrivateFileItems, XtNumber(PrivateFileItems), this, (Callback)&MainView::privateFileMenu); } public: MainView(OpenCVApplication& applet, const char* name, Args& args) :OpenCVMainView(applet, name, args) { addFilePulldownMenu(); BulletinBoard* bboard = getBulletinBoard(); imageLoadingFlag = CV_LOAD_IMAGE_COLOR; imageScalingRatio = 60; //% try { //sourceImageFile = "../images/SlantFigure.png"; //templateImageFile = "../images/FigureFace.png"; //sourceImageFile = "../images/Tokyo2020.png"; //templateImageFile = "../images/Tokyo.png"; sourceImageFile = "../images/MaterializedToruses.png"; templateImageFile = "../images/GreenMaterializedTorus.png"; //1 Create a label to display image file names. Args ar; ar.set(XmNalignment, XmALIGNMENT_BEGINNING); label = new Label(bboard, "", ar); updateLabel(); int imageLoadingFlag = CV_LOAD_IMAGE_COLOR; //2 Create a sourceImageView. ar.reset(); ar.set(XmNimageFileName, sourceImageFile); ar.set(XmNimageLoadingFlag, imageLoadingFlag); sourceImage = new SourceImageView(bboard, "", ar); sourceImage->rescale(imageScalingRatio); //3 Create a templateImageView. ar.reset(); ar.set(XmNimageFileName, templateImageFile); ar.set(XmNimageLoadingFlag, imageLoadingFlag); templateImage = new TemplateImageView(bboard, "", ar); templateImage->rescale(imageScalingRatio); //4 Create a rowColumn controlPane to manage controls. ar.reset(); controlPane = new RowColumn(bboard, "", ar); //5 Create a scaleComboBox in the controlPane const char* defaultScale = "60%"; ar.reset(); CompoundString scaler("Scale"); ar.set(XmNlabelString, scaler); ar.set(XmNdefaultScale, defaultScale); scaleComboBox = new OpenCVScaleComboBox(controlPane, "", ar); scaleComboBox->addCallback(XmNselectionCallback, this, (Callback)&MainView::scaleChanged, NULL); //6 Create a matchingMethodComboBox. ar.reset(); const char* items[] = { "SQDIFF", "SQDIFF_NORMED", "CCORR", "CCORR_NORMED", "CCOEFF", "CCOEFF_NORMED" }; matchingMethod = SQDIFF; CompoundStringList csl(items, CountOf(items)); ar.set(XmNitems, csl); ar.set(XmNitemCount, CountOf(items)); ar.set(XmNselectedPosition, matchingMethod); matchingMethodComboBox = new LabeledComboBox(controlPane, "MatchingMethod", ar); matchingMethodComboBox -> addCallback(XmNselectionCallback, this, (Callback)&MainView::matchingMethodChanged, NULL); //7 Create a clear push button. ar.reset(); clearButton = new PushButton(controlPane, "Clear", ar); clearButton -> addCallback(XmNactivateCallback, this, (Callback)&MainView::clear, NULL); //8 Create a match push button. ar.reset(); matchButton = new PushButton(controlPane, "Match", ar); matchButton -> addCallback(XmNactivateCallback, this, (Callback)&MainView::match, NULL); //9 Call match method to match template image in sourceimages templateMatch(); ar.reset(); fileDialog = new FileOpenDialog(this, "FileOpenDialog", ar); fileDialog -> getOkButton() -> addCallback(XmNactivateCallback, this, (Callback)&MainView::ok, NULL); sendConfigureEvent(); } catch(OZ::Exception& ex) { caught(ex); } } ~MainView() { } }; } // int main(int argc, char** argv) { try { const char* appclass = argv[0]; OpenCVApplication applet(appclass, argc, argv); Args args; args.set(XmNwidth, 900); args.set(XmNheight, 380); MainView view(applet, argv[0], args); view.realize(); applet.run(); } catch (OZ::Exception& ex) { caught(ex); } return 0; }