【從零學習OpenCV 4】滑鼠響應

  • 2019 年 12 月 13 日
  • 筆記

經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。

有時我們需要在影像中標記出重要的區域,這時通過滑鼠可以很好的完成這項任務,因此OpenCV 4中也提供了滑鼠響應相關函數setMouseCallback(),該函數的函數原型在程式碼清單3-56中給出。

程式碼清單3-56 setMouseCallback()函數原型  1.  void cv::setMouseCallback(const String & winname,  2.                                 MouseCallback onMouse,  3.                                 void * userdata = 0  4.                                 )
  • winname:添加滑鼠響應的窗口的名字
  • onMouse:滑鼠響應的回調函數。
  • userdata:傳遞給回調函數的可選參數。

該函數能夠為指定的影像窗口創建滑鼠響應。函數第一個參數是需要創建滑鼠響應的影像窗口的名字。第二個參數為滑鼠響應的回調函數,該函數在滑鼠狀態發生改變時被調用,是一個MouseCallback類型的函數。最後一個參數是傳遞給回調函數的可選參數,一般情況下使用默認值0即可。

接下來將介紹MouseCallback類型的回調函數,該類型函數的原型在程式碼清單3-57中給出。

程式碼清單3-57 MouseCallback類型原型  1.  typedef void(* cv::MouseCallback)(int  event,  2.                                           int  x,  3.                                           int  y,  4.                                           int  flags,  5.                                           void  *userdata  6.                                           )
  • event:滑鼠響應事件標誌,參數為EVENT_*形式,具體可選參數及含義在表3-9給出。
  • x:滑鼠指針在影像坐標系中的x坐標
  • y:滑鼠指針在影像坐標系中的y坐標
  • flags:滑鼠響應標誌,參數為EVENT_FLAG_*形式,具體可選參數及含義在表3-10給出。
  • userdata:傳遞給回調函數的可選參數

MouseCallback類型的回調函數是一個無返回值的函數,函數名可以任意設置,有五個參數,在滑鼠狀態發生改變的時候被調用。函數第一個參數是滑鼠響應事件標誌,參數為EVENT_*形式,具體可選參數及含義在表3-9給出。第二個和第三個參數分別是滑鼠當前位置在影像坐標系中的x坐標和y坐標。第四個參數是滑鼠響應標誌,參數為EVENT_FLAG_*形式,具體可選參數及含義在表3-10給出。最後一個參數是傳遞給回調函數的可選參數,一般情況下用void*預設即可。

表3-9 MouseCallback類型回調函數滑鼠響應事件標誌可選參數及含義

標誌參數

簡記

含義

EVENT_MOUSEMOVE

0

表示滑鼠指針在窗口上移動

EVENT_LBUTTONDOWN

1

表示按下滑鼠左鍵

EVENT_RBUTTONDOWN

2

表示按下滑鼠右鍵

EVENT_MBUTTONDOWN

3

表示按下滑鼠中鍵

EVENT_LBUTTONUP

4

表示釋放滑鼠左鍵

EVENT_RBUTTONUP

5

表示釋放滑鼠右鍵

EVENT_MBUTTONUP

6

表示釋放滑鼠中鍵

EVENT_LBUTTONDBLCLK

7

表示雙擊滑鼠左鍵

EVENT_RBUTTONDBLCLK

8

表示雙擊滑鼠右鍵

EVENT_MBUTTONDBLCLK

9

表示雙擊滑鼠中間

EVENT_MOUSEWHEEL

10

正值表示向前滾動,負值表示向後滾動

EVENT_MOUSEHWHEEL

11

正值表示向左滾動,負值表示向右滾動

表3-10 MouseCallback類型回調函數滑鼠響應標誌及含義

標誌參數

簡記

含義

EVENT_FLAG_LBUTTON

1

按住左鍵拖拽

EVENT_FLAG_RBUTTON

2

按住右鍵拖拽

EVENT_FLAG_MBUTTON

4

按住中鍵拖拽

EVENT_FLAG_CTRLKEY

8

按下CTRL鍵

EVENT_FLAG_SHIFTKEY

16

按下SHIFT鍵

EVENT_FLAG_ALTKEY

32

按下ALT鍵

滑鼠響應簡單來說就是當滑鼠位於對應的影像窗口內時,時刻檢測滑鼠狀態,當滑鼠狀態發生改變時調用回調函數,根據回調函數中的判斷邏輯選擇執行相應的操作。例如回調函數中只處理滑鼠左鍵按下的事件,即判斷event標誌是否為EVENT_LBUTTONDOWN,只有當event==EVENT_LBUTTONDOWN時才有相應的邏輯操作,否則將不會執行任何操作。

為了了解滑鼠響應的使用方法,在程式碼清單3-58中給出了繪製滑鼠移動軌跡的示常式序。程式中如果滑鼠右鍵被按下,則會提示「點擊滑鼠左鍵才可以繪製軌跡」,點擊左鍵會輸出當前滑鼠的坐標,並將該點坐標定義為某段軌跡的起始位置。之後按住左鍵移動滑鼠,會進入到第三個邏輯判斷,繪製滑鼠的移動軌跡。示常式序中提供了兩種繪製軌跡的方法,第一種是每次調用回調函數獲得滑鼠位置時更改周圍的影像像素值,這種方式比較直觀,但是由於回調函數有一定的執行時間,因此當滑鼠移動較快時繪製的影像軌跡會出現斷點。第二種繪製軌跡的方式是在前一時刻和當前時刻滑鼠位置間繪製直線,這種方式可以避免因滑鼠移動過快而帶來的軌跡出現斷點的問題。程式運行結果在圖3-35給出。

程式碼清單3-58 myMouse.cpp繪製滑鼠移動軌跡  1.  #include <opencv2/opencv.hpp>  2.  #include <iostream>  3.  4.  using namespace std;  5.  using namespace cv;  6.  7.  Mat img,imgPoint; //全局的影像  8.  Point prePoint; //前一時刻滑鼠的坐標,用於繪製直線  9.  void mouse(int event, int x, int y, int flags, void*);  10.  11.  int main()  12. {  13.    img = imread("lena.png");  14.    if (!img.data)  15.    {  16.      cout << "請確認輸入影像名稱是否正確!" << endl;  17.      return -1;  18.    }  19.    img.copyTo(imgPoint);  20.    imshow("影像窗口1", img);  21.    imshow("影像窗口2", imgPoint);  22.    setMouseCallback("影像窗口1", mouse,0 ); //滑鼠影響  23.    waitKey(0);  24.    return 0;  25.  }  26.  27.  void mouse(int event, int x, int y, int flags, void*)  28. {  29.    if (event == EVENT_RBUTTONDOWN) //單擊右鍵  30.    {  31.      cout << "點擊滑鼠左鍵才可以繪製軌跡" << endl;  32.    }  33.    if (event == EVENT_LBUTTONDOWN) //單擊左鍵,輸出坐標  34.    {  35.      prePoint = Point(x, y);  36.      cout << "軌跡起使坐標" << prePoint << endl;  37.  38.    }  39.    if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //滑鼠按住左鍵移動  40.    {  41.      //通過改變影像像素顯示滑鼠移動軌跡  42.      imgPoint.at<Vec3b>(y, x) = Vec3b(0, 0, 255);  43.      imgPoint.at<Vec3b>(y, x-1) = Vec3b(0, 0, 255);  44.      imgPoint.at<Vec3b>(y, x+1) = Vec3b(0, 0, 255);  45.      imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255);  46.      imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255);  47.      imshow("影像窗口2", imgPoint);  48.  49.      //通過繪製直線顯示滑鼠移動軌跡  50.      Point pt(x, y);  51.      line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0);  52.      prePoint = pt;  53.      imshow("影像窗口1", img);  54.    }  55.  }

圖3-35 myMouse.cpp程式中繪製的滑鼠移動軌