本系列專欄寫作方式
本系列專欄寫作將采用首創的問答式寫作形式,快速讓你學習到 OpenCV 的初級、中級、高級知識,
4. Python OpenCV 中滑鼠事件相關處理與常見問題解決方案
本篇博客主要分析 cv2.setMouseCallback 函式,以及該函式在日常編碼中出現問題是如何進行解決,
本函式主要是 OpenCV 中用來處理滑鼠相關事件的函式,通過它可以捕獲到資料觸發的事件,并對其進行處理,
使用該函式前,可以先通過 help 函式查閱基本用法,
該函式原型如下:
setMouseCallback(windowName, onm ouse [, param]) -> None
可以看到該函式有兩個引數,其一是視窗的名稱,其二是回呼函式,視窗名稱與cv2.imshow 中的名稱保持一致即可,
通過函式原型,可以看出 cv2.setMouseCallback 函式是在給視窗設定一個回呼函式,
OpenCV 中滑鼠都有哪些事件?
查看事件的代碼如下,通過內置函式 dir 可以進行查閱,
import cv2
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
if __name__ == "__main__":
show_event()
運行結果如下,所有與 event(事件相關的函式,都羅列了出來)
['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN',
'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP']
以上事件中,最常用的為 EVENT_LBUTTONDOWN,EVENT_LBUTTONUP,我們接下來就重點掌握,
EVENT_LBUTTONDOWN 滑鼠左鍵按下事件,
先通過以下代碼呈現一個表單,測驗一下滑鼠左鍵按下,
import cv2
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
def mouse_handler(event, x, y, flags, userdata):
if event == 1: # cv2.EVENT_LBUTTONDOWN
print("滑鼠左鍵按下")
if __name__ == "__main__":
image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
cv2.imshow("mouse_event", image)
cv2.setMouseCallback("mouse_event", mouse_handler)
cv2.waitKey()
代碼運行效果如下,在圖片上點擊滑鼠左鍵,會在控制臺進行資料的輸入,輸出內容如截圖紅框位置所示,

此時需要注意的問題是,即使你沒有加載任何圖片,只是使用 nameWindow 命名了一下表單,對應的 setMouseCallback 函式也會系結成功,具體測驗代碼如下,
# image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
# cv2.imshow("mouse_event", image)
cv2.setMouseCallback("mouse_event", mouse_handler)
由上面的案例,我們還能得到下述推論,cv2.setMouseCallback 函式中第二個引數回呼函式onMouse ,具備某種格式,因為在上述代碼中出現了這樣一段內容:
def mouse_handler(event, x, y, flags, userdata):
這里其實對于所有滑鼠事件,回呼函式格式都是統一的,只是函式內部的具體實作不同,
引數說明如下:
- event:滑鼠事件名稱,通過該值可以獲取滑鼠進行的何種事件操作;
- x, y:滑鼠進行事件操作一瞬間,所在的坐標位置;
- flags:指的是與 event 相關的實踐中包含 FLAG 的事件;
- userdata:滑鼠回呼函式觸發時傳遞進來的引數,
以上引數都非常容易理解,但是目前網路上很多內容對 flags 引數都一帶而過,沒有進行說明,
該引數其實就是我們在上文獲取到的所有事件的一個子集,在看一下之前獲取到的所有事件,
['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN',
'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP']
在其中你重點尋找帶 flag 的值,檢索如下:
'EVENT_FLAG_ALTKEY',
'EVENT_FLAG_CTRLKEY',
'EVENT_FLAG_LBUTTON',
'EVENT_FLAG_MBUTTON',
'EVENT_FLAG_RBUTTON',
'EVENT_FLAG_SHIFTKEY'
稍微對英文進行一下翻譯,就能了解 flags 引數,例如我們想要實作按住滑鼠左鍵的同時進行拖動,那核心代碼為:
event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON
按住鍵盤 CTRL 的同時,按下滑鼠左鍵,代碼如下
event == cv2.EVENT_LBUTTONUP and flags == cv2.EVENT_FLAG_CTRLKEY
接下來我們就實作一下如何按住滑鼠左鍵并進行拖動,繪制一個矩形,
import cv2
image = cv2.imread("./tt.jpg")
cv2.namedWindow("mouse_event")
x1, y1 = 0, 0
def show_event():
events = [i for i in dir(cv2) if 'EVENT' in i]
print(events)
def mouse_handler(event, x, y, flags, userdata):
global x1, y1
if event == cv2.EVENT_LBUTTONDOWN:
print("左鍵點擊")
x1, y1 = x, y
if event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON:
# print("滑鼠左鍵按下拖動")
cv2.rectangle(image, (x1, y1), (x, y), (0, 255, 0), -1)
if __name__ == "__main__":
cv2.setMouseCallback("mouse_event", mouse_handler)
while True:
cv2.imshow("mouse_event", image)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
上述代碼,并未用到最后一個引數 userdata,接下來我們通過傳參的方式應用一下該引數,
核心修改一個地方即可,在 cv2.setMouseCallback 函式部分將讀取的影像 image 傳遞到回呼的函式中去,具體如下
cv2.setMouseCallback("mouse_event", mouse_handler, image)
OpenCV 在視頻中捕獲滑鼠事件的解決方案
上文已經實作了在圖片中捕獲滑鼠事件,接下來我們看一下如何去在視頻中進行相同的操作,
由以前的知識已經知道,視頻處理就是對視頻的每一幀進行相應的操作,那可以按照下述代碼進行,
import cv2
def mouse_handler(event, x, y, flags, frame):
if frame is not None:
# 獲取坐標,測驗用
# print(x, y)
if event == cv2.EVENT_MOUSEMOVE:
cv2.putText(frame, "Hello OpenCV", (x, y),
cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0))
cv2.imshow("video", frame)
if __name__ == "__main__":
cap = cv2.VideoCapture("./test.mp4")
while cap.isOpened():
ret, frame = cap.read()
if ret:
cv2.imshow("video", frame)
cv2.setMouseCallback("video", mouse_handler, frame)
if cv2.waitKey(25) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
以上代碼因為系結在 EVENT_MOUSEMOVE 事件上,所以當滑鼠移動的時候,會出現一個 Hello OpenCV 的字樣,但是該方式會導致視頻不斷重復渲染,效率不好,頻繁重繪幾次之后,頁面就會崩潰掉,
如果你單純為了測驗,可以將 cv2.waitKey(25) 中的數字設定到 1000,這樣視頻播放速度就會變慢,即可抓取到最終效果,

對于滑鼠回呼函式的學習,重點要掌握的依舊是各種事件,還有一個需要注意的是組合按鍵與滑鼠位置的計算,你可以基于此實作一個簡單的 OpenCV 畫板,當然前提是你對之前學的圖形繪制函式已經十分熟悉,
補充知識,OpenCV 繪制多邊形
在 上一篇博客 中,我們缺少了一個函式,繪制多邊形,這里進行一下補充,該函式為 cv2.polylines,函式原型如下:
polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]]) -> img
其中最重要的引數 pts,該引數表示待繪制多邊形的折線陣列,也可以理解為多邊形的頂點順序坐標,
例如下述代碼:
image = np.zeros((400, 400, 3), np.uint8)
points = np.array(
[[50, 50], [170, 100], [200, 150], [300, 320]], np.int32)
cv2.polylines(image, [points], True, (255, 0, 0))
cv2.imshow('image',image)
cv2.waitKey()
繪制的多邊形如下:

最后,你可以結合本文學到的 cv2.setMouseCallback 函式,加上繪制直線函式,實作一個多邊形手動繪制工具,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292626.html
標籤:AI
上一篇:TensorFlow2簡單入門-model.train_on_batch介紹
下一篇:人工智能學習筆記----06
