番外4. Python OpenCV 中鼠標事件相關處理與常見問題解決方案
本系列專欄寫作方式
本系列專欄寫作將采用首創的問答式寫作形式,快速讓你學習到 OpenCV 的初級、中級、高級知識。
4. Python OpenCV 中鼠標事件相關處理與常見問題解決方案
本篇博客主要分析 cv2.setMouseCallback 函數,以及該函數在日常編碼中出現問題是如何進行解決。
本函數主要是 OpenCV 中用來處理鼠標相關事件的函數,通過它可以捕獲到數據觸發的事件,并對其進行處理。
使用該函數前,可以先通過 help 函數查閱基本用法。
該函數原型如下:
setMouseCallback(windowName, onMouse [, 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 函數,加上繪制直線函數,實現一個多邊形手動繪制工具。
OpenCV Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。