SOL9 Sample: TemplateMatcher

SOL9 2.0 Samples

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.