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

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

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

  1. import cv2
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. import skvideo.io
  5.  
  6. face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
  7. eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')
  8.  
  9. def detect(img, idx):
  10. gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
  11. faces = face_cascade.detectMultiScale(gray,
  12. scaleFactor=1.3,
  13. minNeighbors=5,
  14. minSize=(220,220),)
  15. for (x,y,w,h) in faces:
  16. roi_gray = gray[y:y+h, x:x+w]
  17. eyes = eye_cascade.detectMultiScale(roi_gray,
  18. scaleFactor=1.05,
  19. minNeighbors=5,
  20. minSize=(50,50),)
  21. img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),5)
  22. for (ex,ey,ew,eh) in eyes:
  23. img = cv2.rectangle(img,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(0,255,0),5)
  24. print('Working with %s frame' % idx)
  25. return img
  26.  
  27. videogen = skvideo.io.vreader('tzuyu.mp4')
  28. plt.ion()
  29. for idx,frame in enumerate(videogen):
  30. frame = detect(frame, idx)
  31. plt.imshow(frame)
  32. plt.pause(.01)
  33. plt.cla() # Clear axis
  34. plt.clf() # Clear figure
  35.  
  36. plt.ioff()
先利用 scikit-video讀取影片,在建立一個 for迴圈處理每個幀。
可以利用 matplotlib.pyplot來觀察捕捉的情況,但會消耗較多的資源。不懂這裡怎麼回事的可以參考上一篇
大部分都與 "利用 OpenCV抓取相片中的臉部數據" 這篇差不多。
稍微需要注意的是 scikit-video所讀取的圖片格式是 RGB,所以在第 10行灰化時,是使用 RGB2GRAY。
雖然有時候會把眼睛誤認為嘴巴,但看起來沒有太多問題,先用這樣的參數來儲存臉部數據吧。
  1. import cv2
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. import skvideo.io
  5.  
  6. face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
  7. eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')
  8.  
  9. face_filename = 1
  10. def detect(img, idx):
  11. gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
  12. faces = face_cascade.detectMultiScale(gray,
  13. scaleFactor=1.3,
  14. minNeighbors=5,
  15. minSize=(220,220),)
  16. for (x,y,w,h) in faces:
  17. roi_gray = gray[y:y+h, x:x+w]
  18. eyes = eye_cascade.detectMultiScale(roi_gray,
  19. scaleFactor=1.05,
  20. minNeighbors=5,
  21. minSize=(50,50),)
  22. img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),5)
  23. for (ex,ey,ew,eh) in eyes:
  24. img = cv2.rectangle(img,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(0,255,0),5)
  25.  
  26. if len(eyes)>=2:
  27. f = cv2.resize(gray[y:y+h, x:x+w], (200, 200))
  28. global face_filename
  29. name = './face/%d.png' % face_filename
  30. cv2.imwrite(name, f)
  31. face_filename += 1
  32. else:
  33. pass
  34.  
  35. print('Working with %s frame' % idx)
  36. return img
  37.  
  38. videogen = skvideo.io.vreader('tzuyu.mp4')
  39. plt.ion()
  40. for idx,frame in enumerate(videogen):
  41. frame = detect(frame, idx)
  42. plt.imshow(frame)
  43. plt.pause(.01)
  44. plt.cla() # Clear axis
  45. plt.clf() # Clear figure
  46.  
  47. plt.ioff()
在第 27行開始加入了儲存的條件等。
執行 matplotlib.pyplot其實會花費滿多計算資源,可以註解掉 matplotlib.pyplot的部分,速度會快上許多。
短短 59秒的影片,在這樣的條件下儲存了 475張圖像。先來看看幾張。
 
除了一些捕捉到不好的圖像外,該影片在剪接的過程中使用了一些淡化轉場的效果,所以會有一些畫面有重疊的圖像。
以下是一些本人覺得應該要刪除的圖像。

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

留言