本文主要內容來自於 OpenCV-Python 教程 的 OpenCV 中的 GUI 功能 部分,這個部分的主要內容如下:
- 圖像操作入門 學習加載一幅圖像,顯示它,並保存它
- 視頻入門 學習播放視頻,從攝像頭捕捉視頻,以及寫入視頻
- OpenCV 中的繪製功能 學習通過 OpenCV 繪製線、矩形、橢圓形和圓形等等
- 鼠標作爲畫筆 用鼠標畫東西
- 軌跡欄作爲調色板 創建軌跡欄以控制某些參數
目標
- 學習如何在 OpenCV 中處理鼠標事件
- 我們將學習這些函數:cv.setMouseCallback()
簡單的演示程序
這裏,我們將創建一個簡單的應用程序,當雙擊鼠標左鍵時,它在一幅圖像上繪製一個圓。
首先我們創建一個鼠標事件回調函數,當鼠標事件發生時它將被執行。鼠標事件可以是任何與鼠標相關的事件,比如左鍵按下,左鍵擡起,左鍵雙擊等等。它給我們每個鼠標事件的座標 (x,y)。通過這個事件和位置,我們可以做任何我們想做的。要列出所有可用的事件,可以在 Python 終端中執行如下的代碼:
events = [i for i in dir(cv) if 'EVENT' in i]
print('\n'.join(events))
執行這段代碼將得到類似下面這樣的輸出:
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
創建鼠標事件回調函數具有特定的格式,在任何地方都是相同的。它們僅在函數做什麼方面不同。即鼠標事件回調函數可以是參數列表滿足條件的任何函數。我們的鼠標事件回調函數只做一件事,它在雙擊發生的位置繪製一個圓。參見下面的代碼。代碼是自解釋的:
def draw_circle_follow_mouse():
img = np.zeros((512, 512, 3), np.uint8)
# mouse callback function
def draw_circle(event, x, y, flags, param):
if event == cv.EVENT_LBUTTONDBLCLK:
# Create a black image, a window and bind the function to window
img.fill(0)
cv.circle(img, (x, y), 100, (255, 0, 0), -1)
cv.imshow('image', img)
cv.namedWindow('image')
cv.imshow('image', img)
cv.setMouseCallback('image', draw_circle)
while (1):
if cv.waitKey(20) & 0xFF == 27:
break
cv.destroyAllWindows()
這裏的鼠標事件回調函數其實是個閉包,它綁定了局部上下文。爲了防止兩次鼠標雙擊事件中的繪製相互干擾,每次在鼠標事件回調函數中繪製之前都會先清空圖像。ASCII 碼 27 表示 ESC 鍵,即按下 ESC 鍵是應用程序退出。
更高級的演示程序
現在我們繼續開發一個更好的應用程序。這次,我們像在 Paint 應用程序中一樣,通過拖動鼠標繪製矩形或者圓(依賴我們選擇的模式)。因此我們的鼠標事件回調函數有兩部分,一部分用於繪製矩形,另一部分用於繪製圓形。這個具體的例子將非常有助於創建和理解一些交互式應用程序,如對象跟蹤、圖像分割等。
def draw_shape_follow_mouse():
drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle. Press 'm' to toggle to curve
ix, iy = -1, -1
img = np.zeros((512, 512, 3), np.uint8)
# mouse callback function
def draw_shape(event, x, y, flags, param):
nonlocal ix, iy, drawing, mode
if event == cv.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
img.fill(0)
elif event == cv.EVENT_MOUSEMOVE:
if drawing == True:
if mode == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
else:
cv.circle(img, (x, y), 5, (0, 0, 255), -1)
elif event == cv.EVENT_LBUTTONUP:
drawing = False
if mode == True:
cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
else:
cv.circle(img, (x, y), 5, (0, 0, 255), -1)
cv.imshow('image', img)
cv.namedWindow('image')
cv.imshow('image', img)
cv.setMouseCallback('image', draw_shape)
while (1):
key = cv.waitKey(20) & 0xFF
if key == ord('m'):
mode = not mode
elif key == 27:
break
cv.setMouseCallback(None)
cv.destroyAllWindows()
這裏的鼠標事件回調函數同樣是閉包,通過 cv.setMouseCallback()
將回調函數綁定到 OpenCV 窗口。在主循環中,我們應該爲鍵 'm' 設置一個鍵盤綁定,以在矩形和圓形之間切換。
參考文檔
Done.