Skip to main content

Opencv mouse drawing C++ tutorial, mouse setMouseCallback region selection

This c++ Opencv tutorial shows you how to draw a rectangle by mouse over the object. The purpose of this is to take a Region of interest for your tracker or any other crop region applications. This is a very useful task. I created a youtube video with who explanation, but everything will be described in the following text. 
opencv mouse callback function tutorial

Opencv video example and code explanation

I try to explain the code and functionality as well on this youtube video. The video describes the code and shows mouse in action drawing the rectangle and update based on actual mouse move. 



The opencv mouse call back function code basic description 

The first is a structure called initRoi that represents the global state of the mouse. The members of the structure are initial coordinates of mouse x and y, some indicator init that something happened and actual coordinates of x and y. The structure is global, where members are accessible by SelectedRoi.init, SelectedRoi.initX, SelectedRoi.initY, SelectedRoi.actualX and SelectedRoi.actualY. This member value can be access and updated in man function as well in CallBackF function, which is used to determine the type of the action and updating the structure values. 

In CallBackF the values are updated as follows: The init is set to 1 by mouse left button down. This action as well as updates the initial x and y coordinates. The init set to 1 SelectedRoi.init != 0 is used in the main function to display rectangle based on initial coordinates and actual mouse position updated by EVENT_MOUSEMOVE. The mouse button-up is set the actual values as well. This is just a basic idea behind it. You can use any other logic behind it.

Opencv setMouseCallback

The windows to display video img Mat content with a rectangle is called "Video". The namedWindow and imshow are the standard functions to display the video content in opencv. The setMouseCallback the function to work with mouse over the "Video" window. It is named callBack because the CallBackF is the function used as the parameter of the function. 
namedWindow("Video", WINDOW_AUTOSIZE);
setMouseCallback("Video", CallBackF, 0);
imshow("Video", img);
My CallBackF has parameter event, x, y, flags and img are not used right now.
static void CallBackF(int eventint xint yint flagsvoid* img)

Note:  Instead of holding the state information by the structure the class can be used as well. The classic gets and sets function can update the initial and actual coordinates. The structure is not an ideal solution but simplest.

Opencv mouse region selection tutorial code

#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio/videoio.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <iostream>

using namespace cv;
using namespace std

// global structure to remember some states changed
// by mouse action and move
struct initRoi {
  // on off
  int init;

  //initial coordination based on EVENT_LBUTTONDOWN
  int initX;
  int initY;

  // actual coordination 
  int actualX;
  int actualY;

  //Selected Rect
  Rect roiRect; 

  //Selected Mat roi
  Mat takenRoi;
}SelectedRoi;

//event int is compared to determine action of the mouse EVENT_RBUTTONDOWN
// EVENT_LBUTTONDOWN, EVENT_LBUTTONUP, EVENT_MOUSEMOVE.
// If the event is evauated as happened the global structure SelectedRoi
// is updated.
static void CallBackF(int eventint xint yint flagsvoid* img) {
//Mouse Right button down
  if (event == EVENT_RBUTTONDOWN) {
    cout << "right button " << endl;
    return;
  }
//Mouse Left button down
  if (event == EVENT_LBUTTONDOWN) {
    SelectedRoi.initX = x;
    SelectedRoi.initY = y;
    SelectedRoi.init = 1;
    cout << "left button DOWN" << endl; 
    return;
  }
//Mouse Left button up
  if (event == EVENT_LBUTTONUP) {
    SelectedRoi.actualX = x;
    SelectedRoi.actualX = y;
    cout << "left button UP" << endl;
    return;
  }
//Mouse move coordinates update
  if (event == EVENT_MOUSEMOVE) {
  
     cout << "event mouse move"<< endl; 
      SelectedRoi.actualX = x;
      SelectedRoi.actualY = y;
      SelectedRoi.roiRect = Rect(SelectedRoi.initXSelectedRoi.initY,
   SelectedRoi.actualX,  SelectedRoi.actualY);
    return;
  }
}

void main()
{
  // capture
  VideoCapture cap(0);
  // Set initial state for the SelectedRoi structure
//- The init is set = 1 by left button down action
//- This start to display image if (SelectedRoi.init != 0) in main for loop
  SelectedRoi.init = 0;

    for (;;)
    {
      if (!cap.isOpened()) {
      cout << "Video Capture Fail" << endl;
    break;
    }

    else {
      Mat img;
      cap >> img;

      namedWindow("Video", WINDOW_AUTOSIZE);
// mouse call back function, where CallBackF is function
// with parameters event type, x y coorfinates
      setMouseCallback("Video", CallBackF, 0);

      if (SelectedRoi.init != 0) {
// draw the rectangle updated by mouse move
rectangle(img, Rect(SelectedRoi.initXSelectedRoi.initY
SelectedRoi.actualXSelectedRoi.initXSelectedRoi.actualYSelectedRoi.initY), 
Scalar(255255255), 180);
        
      }
      imshow("Video", img);
      int key2 = waitKey(20);
    }
  }
}

Comments

Post a Comment