這裡介紹如何使用 Python 與 OpenCV 擷取網路攝影機影像,處理與顯示即時的畫面影像,並將連續的畫面影像寫入影片檔案中儲存起來。
若要使用 Python 取的網路攝影機的串流影像,可以透過 OpenCV 模組的 VideoCapture
影片擷取功能來達成,至於寫入影片檔則可使用 VideoWriter
,操作方式非常簡單,以下是使用教學與簡單的入門範例。
在準備擷取攝影機的影像之前,要先呼叫 cv2.VideoCapture
建立一個 VideoCapture
物件,這個 VideoCapture
物件會連接到一隻網路攝影機,我們可以靠著它的參數來指定要使用那一隻攝影機(0
代表第一隻、1
代表第二隻)。
建立好 VideoCapture
物件之後,就可以使用它的 read
函數來擷取一張張連續的畫面影像了,以下是一個即時擷取與顯示畫面的範例:
import cv2 # 選擇第二隻攝影機 cap = cv2.VideoCapture(1) while(True): # 從攝影機擷取一張影像 ret, frame = cap.read() # 顯示圖片 cv2.imshow('frame', frame) # 若按下 q 鍵則離開迴圈 if cv2.waitKey(1) & 0xFF == ord('q'): break # 釋放攝影機 cap.release() # 關閉所有 OpenCV 視窗 cv2.destroyAllWindows()
在這個無窮迴圈中,每次呼叫 cap.read()
就會讀取一張畫面,其第一個傳回值 ret
代表成功與否(True
代表成功,False
代表失敗),而第二個傳回值 frame
就是攝影機的單張畫面。
在某些情況下網路攝影機可能不會自動打開,這樣的程式執行後就會出錯,若遇到這種狀況的話可以用 cap.isOpened()
檢查攝影機是否有啟動,若沒有啟動則呼叫 cap.open()
啟動它。
將畫面擷取下來之後,馬上使用 cv2.imshow
來顯示,接著再擷取下一張,這樣執行起來就會是串流影像的效果。
如果要即時處理從網路攝影機擷取的串流影像,就把處理圖片的程式碼放在這個迴圈中即可,例如將彩色的 RGB 圖片轉為灰階:
import cv2 cap = cv2.VideoCapture(1) while(True): ret, frame = cap.read() # 將圖片轉為灰階 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) cv2.imshow('frame', gray) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
執行後的畫面會像這樣:
除了簡單的灰階處理,我們也可以加入各式各樣的影像處理演算法(例如物件偵測等),實作出各種應用。
我們可以透過 cap.get(propId)
來取得影片的一些資訊,其中 propId
是屬性的 ID,可用的值是從 0
到 18
,詳細的說明可參考 OpenCV 的文件。
以下是查詢畫面大小以及編碼方式的範例:
import cv2 cap = cv2.VideoCapture(1) # 解析 Fourcc 格式資料的函數 def decode_fourcc(v): v = int(v) return "".join([chr((v >> 8 * i) & 0xFF) for i in range(4)]) # 取得影像的尺寸大小 width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) print("Image Size: %d x %d" % (width, height)) # 取得 Codec 名稱 fourcc = cap.get(cv2.CAP_PROP_FOURCC) codec = decode_fourcc(fourcc) print("Codec: " + codec) cap.release()
執行後的輸出會類似這樣:
Image Size: 640 x 480 Codec: YUYV
若要更改這些影片的設定值,可以使用 cap.set(propId, value)
這個函數,以下是更改攝影機畫面解析度的範例:
import cv2 cap = cv2.VideoCapture(1) # 設定影像的尺寸大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) while(True): ret, frame = cap.read() cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
執行後,畫面就會變成 1280×960 的解析度:
OpenCV 除了從網路攝影機擷取即時的影像之外,也可以從儲存於硬碟中的影片檔案來讀取影像畫面,進行各種處理,而使用方式跟上面網路攝影機的例子都差不多,只是在建立 VideoCapture
物件時,要指定影片檔案的位置。
import cv2 # 開啟影片檔案 cap = cv2.VideoCapture('my_video.avi') # 以迴圈從影片檔案讀取影格,並顯示出來 while(cap.isOpened()): ret, frame = cap.read() cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
這裡我拿一個從 YouTube 上面下載的卡通影片作為示範:
如果想要將網路攝影機所擷取到的串流影像儲存下來,最簡單的方就是呼叫 cv2.imwrite
將每一張畫面都儲存成個別的圖片檔,這種方式就跟一般圖片的處理方式相同。
另一種儲存串流影像的方式就是使用 VideoWriter
輸出成影片檔案(例如 MP4、AVI 等格式的影片),使用時先建立一個 VideoWriter
物件,並指定影片的輸出檔案名稱、解析度、FPS 值以及編碼格式,再將擷取到的每張畫面依序寫入即可,以下是一個簡單的範例:
import cv2 cap = cv2.VideoCapture(1) # 設定擷取影像的尺寸大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 360) # 使用 XVID 編碼 fourcc = cv2.VideoWriter_fourcc(*'XVID') # 建立 VideoWriter 物件,輸出影片至 output.avi # FPS 值為 20.0,解析度為 640x360 out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 360)) while(cap.isOpened()): ret, frame = cap.read() if ret == True: # 寫入影格 out.write(frame) cv2.imshow('frame',frame) if cv2.waitKey(1) & 0xFF == ord('q'): break else: break # 釋放所有資源 cap.release() out.release() cv2.destroyAllWindows()
這個程式執行之後,就會將擷取到的串流影像以 XVID 編碼寫入 output.avi
中。
不同的平台會支援不同的編碼格式,影片常見的編碼格式有:DIVX
、XVID
、MJPG
、X264
、WMV1
、WMV2
等,使用時可以自己嘗試看看。
參考資料:OpenCV