本文主要內容來自於 OpenCV-Python 教程 的 OpenCV 中的 GUI 功能 部分,這個部分的主要內容如下:
- 圖像操作入門 學習加載一幅圖像,顯示它,並保存它
- 視頻入門 學習播放視頻,從攝像頭捕捉視頻,以及寫入視頻
- OpenCV 中的繪製功能 學習通過 OpenCV 繪製線、矩形、橢圓形和圓形等等
- 鼠標作爲畫筆 用鼠標畫東西
- 軌跡欄作爲調色板 創建軌跡欄以控制某些參數
目標
- 學習讀取視頻,顯示視頻,和保存視頻
- 學習從攝像頭採集視頻並顯示它
- 我們將學習這些函數:cv.VideoCapture(),cv.VideoWriter()
從攝像頭採集視頻
通常,我們必須用攝像頭捕捉實時流。OpenCV 提供了一個非常簡單的接口來做這些。讓我們從攝像頭採集一段視頻(我使用我筆記本電腦上內置的 webcam),把它轉換爲灰度視頻並顯示。只是一個入門的簡單任務。
要捕捉視頻,我們需要創建一個 VideoCapture 對象。它的參數可以是設備索引或視頻文件的文件名。設備索引只是一個用於指定使用那個攝像頭的數字。通常連接了一個攝像頭(就我而言)。因而,我簡單地傳入 0 (或 -1)。你可以通過傳入 1 選擇第二個攝像頭等等。隨後你可以一幀一幀地捕捉圖像。最後,不要忘記釋放 capture。
#!/use/bin/env python
import sys
import numpy as np
import cv2 as cv
def main():
cap = cv.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
sys.exit()
while True:
# Capture frame-by-frame
ret, frame = cap.read()
# if frame is read correctly ret is True
if not ret:
print("Can't receive frame() (stream end?). Exiting ...")
break
# Our operations on the frame come here
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# Display the resulting frame
cv.imshow('frame', gray)
if cv.waitKey(1) == ord('q'):
break
cap.release()
cv.destroyAllWindows()
if __name__ == "__main__":
main()
cap.read()
返回一個布爾值 (True
/False
)。如果正確地讀取了幀,它爲 True
。因此可以通過檢查這個返回值來確認視頻是否結束。
有時,cap 可能還沒有初始化捕捉。在那種情況下,這段代碼將顯示錯誤。我們可以通過 cap.isOpened() 方法檢查它是否初始化。如果它是 True
,則 OK。否則使用 cap.open()
打開它。
我們還可以使用 cap.get(propId) 方法訪問這個視頻的一些功能,其中 propId 是一個從 0 到 68 的數字。每個數字表示視頻的一個屬性(如果它適用於該視頻的話)。完整的描述可以在這裏找到:cv::VideoCapture::get()。這些值中的一些可以使用 cap.set(propId, value) 來修改。value 是想要的新值。
比如,我們可以通過 cap.get(
cv.CAP_PROP_FRAME_WIDTH
)
和 cap.get(
cv.CAP_PROP_FRAME_HEIGHT
)
檢查幀的寬度和高度。它默認給出了 640x480。但我們想要把它修改爲 320x240。則使用 ret = cap.set(
cv.CAP_PROP_FRAME_WIDTH
,320)
和 ret = cap.set(
cv.CAP_PROP_FRAME_HEIGHT
,240)
。如:
width = cap.get(cv.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv.CAP_PROP_FRAME_WIDTH)
fps = cap.get(cv.CAP_PROP_FPS)
print("FPS:{}, width x height: {} x {}".format(fps, width, height))
可以得到如下輸出:
FPS:30.0, width x height: 640.0 x 640.0
注意:如果遇到了錯誤,則可以使用任何其它的攝像頭應用程序(比如 Linux 下的 Cheese)來確認攝像頭是否工作良好。
播放視頻文件
播放視頻文件與從攝像頭採集類似,只是把攝像頭索引改爲視頻文件的文件名。同樣在顯示視頻幀的時候,傳入適當的時間調用 cv.waitKey()
。如果時間值太低,則視頻會非常快,如果它太高,則視頻會非常慢(好吧,這就是我們可以慢動作顯示視頻的方式)。一般情況下 25 毫秒就不錯。
def play_video_file():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
cap = cv.VideoCapture(cv.samples.findFile("vtest.avi"))
while cap.isOpened():
ret, frame = cap.read()
# if frame is read correctly ret is True
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
cv.imshow('frame', gray)
if cv.waitKey(25) == ord('q'):
break
cap.release()
cv.destroyAllWindows()
cap.get(propId) 可用的 propId
由一些枚舉值定義,這其中部分適用於攝像頭的 VideoCapture
,部分則適用於視頻文件的 VideoCapture
。對於視頻文件,我們可以通過給 cap.get(propId) 傳入 cv.CAP_PROP_FRAME_COUNT
獲得視頻文件的視頻幀總數。
注意,請確保安裝了適當版本的 ffmpeg 或 gstreamer。有時使用 video capture 比較頭疼,這大多數是由於 ffmpeg/gstreamer 的錯誤安裝。
保存視頻
我們捕捉了視頻,並能一幀一幀地處理它,但我們還想保存視頻。對於圖像,它很簡單:使用 cv.imwrite()
就好。在這裏,需要做更多的工作。
這次我們創建一個 VideoWriter 對象。我們應該指定輸出文件名(比如:output.avi)。然後我們應該指定 FourCC 碼(下一段會有詳細說明)。應該傳入每秒多少幀 (fps) 以及幀大小。最後一個是 isColor 標記。如果它是 True
,則編碼器期望是彩色幀,否則,它使用灰度幀。
FourCC 是一個用於指定視頻編解碼器的 4 字節碼。在其官網 fourcc.org 可以找到可用的編解碼器的列表。它是平臺獨立的。以下編解碼器對我來說很好用。
- 在 Fedora 中:DIVX,XVID,MJPG,X264,WMV1,WMV2。(XVID 是更優選的。 MJPG 會產生大尺寸的視頻。 X264 提供非常小尺寸的視頻)
- 在 Windows 上:DIVX (更多有待測試和添加)
- 在 OSX 上:MJPG (.mp4),DIVX (.avi),X264 (.mkv)。
以 cv.VideoWriter_fourcc('M','J','P','G')
或 cv.VideoWriter_fourcc(*'MJPG')
的形式爲 MJPG 傳 FourCC
碼。
如下的代碼從攝像頭捕捉圖形,在垂直方向翻轉每一幀,並保存視頻。
def save_video_file():
cap = cv.VideoCapture(0)
# Define the codec and create VideoWriter object
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
frame = cv.flip(frame, 0)
# write the flipped frame
out.write(frame)
cv.imshow('frame', frame)
if cv.waitKey(1) == ord('q'):
break
# Release everything if job is finished
cap.release()
out.release()
cv.destroyAllWindows()
Done.