[ OpenCV ] 利用 OpenCV抓取影片中的臉部數據

除了網路爬蟲,也可以透過影片搜集數據。但由於畫面太連續,數據差異太小,訓練 ML或 DL可能會有 overfitting的問題,有必要對擷取的畫面進行一些篩選。
篩選的方法可以使用 Hamming distance或是 SSIM,可以參考 "利用 PhotoHash篩選相似度過高的圖像" 或 "利用 SSIM篩選相似度過高的圖像"。

之前不知道手殘打錯了什麼,讓我誤以為 MacOS的 cv2.VideoCapture不能使用,所以找了 scikit-video作為替代的方案,用法也很簡單,歡迎參考 scikit-video官網
先使用以下由 JYP官方提供的子瑜女神特寫影片吧。
可以利用 OnlineVideoConverter這個網站下載 YouTube的影片,頁面上可以透過 "more setting"選擇下載的格式和畫質。
這次實作使用的是 1080p的 mp4檔。

import cv2
import numpy as np
import matplotlib.pyplot as plt
import skvideo.io

face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')

def detect(img, idx):
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    faces = face_cascade.detectMultiScale(gray, 
                                          scaleFactor=1.3, 
                                          minNeighbors=5,
                                          minSize=(220,220),)
    for (x,y,w,h) in faces:
        roi_gray = gray[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(roi_gray,
                                            scaleFactor=1.05,
                                            minNeighbors=5,
                                            minSize=(50,50),)
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),5)
        for (ex,ey,ew,eh) in eyes:
            img = cv2.rectangle(img,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(0,255,0),5)
    print('Working with %s frame' % idx)
    return img

videogen = skvideo.io.vreader('tzuyu.mp4')
plt.ion()
for idx,frame in enumerate(videogen):
    frame = detect(frame, idx)
    plt.imshow(frame)
    plt.pause(.01)
    plt.cla()  # Clear axis
    plt.clf()  # Clear figure

plt.ioff()
先利用 scikit-video讀取影片,在建立一個 for迴圈處理每個幀。
可以利用 matplotlib.pyplot來觀察捕捉的情況,但會消耗較多的資源。不懂這裡怎麼回事的可以參考上一篇
大部分都與 "利用 OpenCV抓取相片中的臉部數據" 這篇差不多。
稍微需要注意的是 scikit-video所讀取的圖片格式是 RGB,所以在第 10行灰化時,是使用 RGB2GRAY。
雖然有時候會把眼睛誤認為嘴巴,但看起來沒有太多問題,先用這樣的參數來儲存臉部數據吧。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import skvideo.io

face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')

face_filename = 1
def detect(img, idx):
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    faces = face_cascade.detectMultiScale(gray, 
                                          scaleFactor=1.3, 
                                          minNeighbors=5,
                                          minSize=(220,220),)
    for (x,y,w,h) in faces:
        roi_gray = gray[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(roi_gray,
                                            scaleFactor=1.05,
                                            minNeighbors=5,
                                            minSize=(50,50),)
        img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),5)
        for (ex,ey,ew,eh) in eyes:
            img = cv2.rectangle(img,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(0,255,0),5)

        if len(eyes)>=2:
            f = cv2.resize(gray[y:y+h, x:x+w], (200, 200))
            global face_filename
            name = './face/%d.png' % face_filename
            cv2.imwrite(name, f)
            face_filename += 1
        else:
            pass

    print('Working with %s frame' % idx)
    return img

videogen = skvideo.io.vreader('tzuyu.mp4')
plt.ion()
for idx,frame in enumerate(videogen):
    frame = detect(frame, idx)
    plt.imshow(frame)
    plt.pause(.01)
    plt.cla()  # Clear axis
    plt.clf()  # Clear figure

plt.ioff()
在第 27行開始加入了儲存的條件等。
執行 matplotlib.pyplot其實會花費滿多計算資源,可以註解掉 matplotlib.pyplot的部分,速度會快上許多。
短短 59秒的影片,在這樣的條件下儲存了 475張圖像。先來看看幾張。
 
除了一些捕捉到不好的圖像外,該影片在剪接的過程中使用了一些淡化轉場的效果,所以會有一些畫面有重疊的圖像。
以下是一些本人覺得應該要刪除的圖像。

由於從影片中可以捕捉到非常大量的數據,但進行篩選後應該還是有一定的量。
另外捕捉錯誤的圖像除了手動刪除外,也可以有技巧地使用 Hamming distance或 SSIM進行篩選,整理起來應該會輕鬆許多。

留言