1. 程式人生 > >opencv(5)GUI

opencv(5)GUI

系統 show sample 信息 是我 delet amp 圖形用戶界面 msh

OpenCV的圖形用戶界面(Graphical User Interface, GUI)和繪圖等相關功能也是很有用的功能,無論是可視化,圖像調試還是我們這節要實現的標註任務,都可以有所幫助。

窗口循環

OpenCV顯示一幅圖片的函數是cv2.imshow(),第一個參數是顯示圖片的窗口名稱,第二個參數是圖片的array。不過如果直接執行這個函數的話,什麽都不會發生,因為這個函數得配合cv2.waitKey()一起使用。cv2.waitKey()指定當前的窗口顯示要持續的毫秒數,比如cv2.waitKey(1000)就是顯示一秒,然後窗口就關閉了。比較特殊的是cv2.waitKey(0),並不是顯示0毫秒的意思,而是一直顯示,直到有鍵盤上的按鍵被按下,或者鼠標點擊了窗口的小叉子才關閉。cv2.waitKey()的默認參數就是0,所以對於圖像展示的場景,cv2.waitKey()或者cv2.waitKey(0)是最常用的。

cv2.waitKey()參數不為零的時候則可以和循環結合產生動態畫面。Python的itertools模塊中的cycle函數可以把一個可遍歷結構編程一個無限循環的叠代器。cv2.waitKey()返回的就是鍵盤上出發的按鍵。對於字母就是ascii碼,特殊按鍵比如上下左右等,則對應特殊的值,其實這就是鍵盤事件的最基本用法。

鼠標和鍵盤事件

cv2.waitKey()就是獲取鍵盤消息的最基本方法。下面這段循環代碼就能夠獲取鍵盤上按下的按鍵,並在終端輸出:

while key != 27:
    cv2.imshow(Honeymoon Island, img)
    key = cv2.waitKey()
    
# 如果獲取的鍵值小於256則作為ascii碼輸出對應字符,否則直接輸出值 msg = {} is pressed.format(chr(key) if key < 256 else key) print(msg)

通過這個程序我們能獲取一些常用特殊按鍵的值。

需要註意的是在不同的操作系統裏這些值可能是不一樣的。鼠標事件比起鍵盤事件稍微復雜一點點,需要定義一個回調函數,然後把回調函數和一個指定名稱的窗口綁定,這樣只要鼠標位於畫面區域內的事件就都能捕捉到。把下面這段代碼插入到上段代碼的while之前,就能獲取當前鼠標的位置和動作並輸出:

# 定義鼠標事件回調函數
def
on_mouse(event, x, y, flags, param): # 鼠標左鍵按下,擡起,雙擊 if event == cv2.EVENT_LBUTTONDOWN: print(Left button down at ({}, {}).format(x, y)) elif event == cv2.EVENT_LBUTTONUP: print(Left button up at ({}, {}).format(x, y)) elif event == cv2.EVENT_LBUTTONDBLCLK: print(Left button double clicked at ({}, {}).format(x, y)) # 鼠標右鍵按下,擡起,雙擊 elif event == cv2.EVENT_RBUTTONDOWN: print(Right button down at ({}, {}).format(x, y)) elif event == cv2.EVENT_RBUTTONUP: print(Right button up at ({}, {}).format(x, y)) elif event == cv2.EVENT_RBUTTONDBLCLK: print(Right button double clicked at ({}, {}).format(x, y)) # 鼠標中/滾輪鍵(如果有的話)按下,擡起,雙擊 elif event == cv2.EVENT_MBUTTONDOWN: print(Middle button down at ({}, {}).format(x, y)) elif event == cv2.EVENT_MBUTTONUP: print(Middle button up at ({}, {}).format(x, y)) elif event == cv2.EVENT_MBUTTONDBLCLK: print(Middle button double clicked at ({}, {}).format(x, y)) # 鼠標移動 elif event == cv2.EVENT_MOUSEMOVE: print(Moving at ({}, {}).format(x, y)) # 為指定的窗口綁定自定義的回調函數 cv2.namedWindow(Honeymoon Island) cv2.setMouseCallback(Honeymoon Island, on_mouse)

標註小工具

基本思路是對要標註的圖像建立一個窗口循環,然後每次循環的時候對圖像進行一次拷貝。鼠標在畫面上畫框的操作,以及已經畫好的框的相關信息在全局變量中保存,並且在每個循環中根據這些信息,在拷貝的圖像上再畫一遍,然後顯示這份拷貝的圖像。

基於這種實現思路,使用上我們采用一個盡量簡化的設計:

- 輸入是一個文件夾,下面包含了所有要標註物體框的圖片。如果圖片中標註了物體,則生成一個相同名稱加額外後綴名的文件保存標註信息。

- 標註的方式是按下鼠標左鍵選擇物體框的左上角,松開鼠標左鍵選擇物體框的右下角,鼠標右鍵刪除上一個標註好的物體框。所有待標註物體的類別,和標註框顏色由用戶自定義,如果沒有定義則默認只標註一種物體,定義該物體名稱叫“Object”。

- 方向鍵的←和→用來遍歷圖片,↑和↓用來選擇當前要標註的物體,Delete鍵刪除一張圖片和對應的標註信息。

每張圖片的標註信息,以及自定義標註物體和顏色的信息,用一個元組表示,第一個元素是物體名字,第二個元素是代表BGR顏色的tuple或者是代表標註框坐標的元組。對於這種並不復雜復雜的數據結構,我們直接利用Python的repr()函數,把數據結構保存成機器可讀的字符串放到文件裏,讀取的時候用eval()函數就能直接獲得數據。這樣的方便之處在於不需要單獨寫個格式解析器。如果需要可以在此基礎上再編寫一個轉換工具就能夠轉換成常見的Pascal VOC的標註格式或是其他的自定義格式。

在這些思路和設計下,我們定義標註信息文件的格式的例子如下:

(‘Hill‘, ((221, 163), (741, 291)))
(‘Horse‘, ((465, 430), (613, 570)))

元組中第一項是物體名稱,第二項是標註框左上角和右下角的坐標。這裏之所以不把標註信息的數據直接用pickle保存,是因為數據本身不會很復雜,直接保存還有更好的可讀性。自定義標註物體和對應標註框顏色的格式也類似,不過更簡單些,因為括號可以不寫,具體如下:

‘Horse‘, (255, 255, 0)
‘Hill‘, (0, 255, 255)
‘DiaoSi‘, (0, 0, 255)

第一項是物體名稱,第二項是物體框的顏色。使用的時候把自己定義好的內容放到一個文本裏,然後保存成和待標註文件夾同名,後綴名為labels的文件。比如我們在一個叫samples的文件夾下放上一些草原的照片,然後自定義一個samples.labels的文本文件。把上段代碼的內容放進去,就定義了小山頭的框為黃色,駿馬的框為青色,以及紅色的屌絲。

opencv(5)GUI