[ OpenCV ] 利用 OpenCV進行臉部辨識批量訓練

透過上一章 "利用 OpenCV抓取影片中的臉部數據",有了大量的圖片數據後,可能沒有辦法像之前一樣,全部一起訓練模型,必須採用批量訓練。
但模型更新受到一定的限制,官方說法是只有 Local Binary Pattern Histogram ( LBPH )可以對模型進行更新。所以本章只會使用 LBPH練習。
開始訓練前的資料夾結構如下
  1. .
  2. ├── Chaeyoung
  3.    └── ...
  4. ├── Dahyun
  5.    └── ...
  6. ├── Jeongyeon
  7.    └── ...
  8. ├── Jihyo
  9.    └── ...
  10. ├── Mina
  11.    └── ...
  12. ├── Momo
  13.    └── ...
  14. ├── Nayeon
  15.    └── ...
  16. ├── Sana
  17.    └── ...
  18. ├── Tzuyu
  19.    └── ...
  20. └── model_train.py
model_train.py代碼如下
  1. import os
  2. import cv2
  3. import numpy as np
  4. batch = 50
  5. names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
  6.  
  7. model_LBPH = cv2.face.createLBPHFaceRecognizer()
  8. if os.path.isfile("./twice_model_LBPH.xml"):
  9. model_LBPH.load("twice_model_LBPH.xml")
  10.  
  11. trained_data = 0
  12. def model_training(x,y):
  13. if not os.path.isfile("./twice_model_LBPH.xml"):
  14. model_LBPH.train(np.asarray(x), np.asarray(y))
  15. else:
  16. model_LBPH.update(np.asarray(x), np.asarray(y))
  17. model_LBPH.save("twice_model_LBPH.xml")
  18.  
  19. global trained_data
  20. trained_data = trained_data + len(y)
  21. print("Model are trained {0} images".format(trained_data))
  22.  
  23. def move_trained_image(image_paths, trained_folders):
  24. for image_path, trained_folder in zip(image_paths, trained_folders):
  25. if not os.path.isdir(trained_folder):
  26. os.system("mkdir {0}".format(trained_folder))
  27. print("Create {0}".format(trained_folder))
  28. os.system("mv {0} {1}".format(image_path, trained_folder))
  29. print("Moved {0} images".format(len(image_paths)))
  30.  
  31. x,y = [],[]
  32. image_paths, trained_folders = [],[]
  33. for label,member in enumerate(names):
  34. for fileNames in os.walk("./{0}".format(member)):
  35. if ("./{0}/trained".format(member)) == fileNames[0]: # 避免 os.walk爬到第二層導致錯誤。
  36. continue
  37. for fileName in fileNames[-1]:
  38. if fileName.endswith('.png'):
  39. image_paths.append("./{0}/{1}".format(member, fileName))
  40. trained_folders.append("./{0}/trained".format(member))
  41. img = cv2.imread("./{0}/{1}".format(member, fileName), cv2.IMREAD_GRAYSCALE)
  42. x.append(img)
  43. y.append(label)
  44. if len(y)!=0 and len(y)%batch == 0:
  45. model_training(x,y)
  46. move_trained_image(image_paths,trained_folders)
  47. x,y = [],[]
  48. image_paths, trained_folders = [],[]
  49. if len(y)!=0:
  50. model_training(x,y)
  51. move_trained_image(image_paths,trained_folders)
  52. x,y = [],[]
  53. image_paths, trained_folders = [],[]
  54.  
  55. print("Done!!")
訓練數據的部分一樣透過 os.walk抓取,訓練後會將訓練過的照片移動到底下的 trained資料夾中。
os.walk抓取也會抓取資料夾中的資料夾內的資料,所以可能要避免讀取移動過的資料(代碼中有提到)。
每當 list中有 50筆資料時就訓練,訓練後移動,移動後就把所有的 list清空。
最後把未滿 50筆的資料也重複一樣的動作。

模型的部分,代碼開頭先建立 LBPH模型,檢查有沒有之前訓練儲存的 xml檔,有就讀取。
model_training function中,一樣先檢查 xml檔,沒有就訓練新的,有就更新。完成後存檔。

訓練數據共使用了九位成員的 TT前導宣傳短片做數據捕捉,大約抓了 5000多張相片來訓練這個模型。
對這幾部影片有興趣的話可以點擊這裡,可以在右邊的 list看到許多由 JYP娛樂提供的影片。
接著就拿它來進行辨識吧。
  1. import cv2
  2. import numpy as np
  3. # import matplotlib.pyplot as plt
  4.  
  5. names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
  6.  
  7. model_LBPH = cv2.face.createLBPHFaceRecognizer()
  8. model_LBPH.load("twice_model_LBPH.xml")
  9.  
  10. face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
  11. eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')
  12.  
  13. def detect(img, idx):
  14. font_size = 1.2
  15. font_thickness = 3
  16. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  17. faces = face_cascade.detectMultiScale(gray,
  18. scaleFactor=1.1,
  19. minNeighbors=3,
  20. minSize=(130,130),)
  21. for (x,y,w,h) in faces:
  22. roi = gray[y:y+h, x:x+w]
  23. eyes = eye_cascade.detectMultiScale(roi,
  24. scaleFactor=1.06,
  25. minNeighbors=5,
  26. minSize=(30,30),)
  27. if len(eyes)>=1:
  28. try:
  29. roi = cv2.resize(roi, (200,200), interpolation=cv2.INTER_LINEAR)
  30. params = model_LBPH.predict(roi)
  31. print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
  32. if params[1] < 80:
  33. img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),3)
  34. cv2.putText(img, names[params[0]], (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
  35. else:
  36. pass
  37. # img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
  38. # cv2.putText(img, 'Happy partner', (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
  39. except:
  40. pass
  41. # img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
  42. # cv2.putText(img, 'Happy partner', (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
  43. print('Working with %s frame' % idx)
  44. return img
  45.  
  46.  
  47. input_file = 'twice_song.mp4'
  48. output_file = 'face_twice_song.avi'
  49. videoCapture = cv2.VideoCapture(input_file)
  50. fps = videoCapture.get(cv2.CAP_PROP_FPS)
  51. size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
  52. int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
  53. videoWriter = cv2.VideoWriter(output_file, cv2.VideoWriter_fourcc('I','4','2','0'), fps, size)
  54.  
  55. success, frame = videoCapture.read()
  56. frame_counter = 1
  57. # plt.ion()
  58. while success:
  59. frame = detect(frame, frame_counter)
  60. videoWriter.write(frame)
  61. # plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
  62. # plt.pause(.01)
  63. # plt.cla() # Clear axis
  64. # plt.clf() # Clear figure
  65. success, frame = videoCapture.read()
  66. frame_counter += 1
  67.  
  68. # plt.ioff()
  69. print("Done!!")
內容與 "利用 OpenCV進行臉部辨識" 這張相當類似,參數的部分可以參考該篇。
比較值得注意的是第 33行的 "if params[1] < 80:",這裡可以對精度設定條件,再決定要不要加上標籤。
Eigenfaces、Fisherfaces和 LBPH顯示出的數字對精度的定義不太一樣,這裡也需要注意一下。
本篇是針對 LBPH設定,至於要設定多少,我也不好拿捏,不過數字越低,則表示預測的可靠度越高。
matplotlib.pyplot的部分被著解掉了,因為實在太消耗資源了,處理速度慢太多了。
接著我把臉部偵測的條件降低,眼睛至少偵測到一隻,就進行辨識,若辨識不出來或不精準精度高過 80就不顯示。
利用這個辨識模型製作了下面兩部影片。
(實際捕捉時的參數不一定和上面的代碼一樣,辨識不同的素材時,多少做了些修改,所以用上面的代碼可能是某次調適時所設定的,不一定會得到一樣的結果。)
第一部是使用了相同訓練素材製作的 MV,影片中的精度還算高,當然也因為有對精度進行篩選。

第二部也是使用了相同的模型對沒有訓練過的影片進行辨識,但可能因為造型變化大,且鏡頭移動快速,結果慘不人睹。


其實嘗試辨識了五六部影片的,但有些因為臉部捕捉率太低,或是版權問題無法分享,
由於對 OpenCV的辨識系統沒有很了解,但下意識覺得有可能是 Overfitting之類的問題,或是訓練數據仍不夠或品質不佳等原因。
有機會再來用 Face Alignment的方式提高數據品質,還有機會的話也來做個用深度學習的辨識系統。

留言

  1. 錯誤/修改:model_train.py代碼如下

    os.system("mv {0} {1}".format(image_path, trained_folder))
    中在在命令台中顯示
    >> 'mv' 不是內部或外部命令、可執行的程式或批次檔。
    這裡的'mv'須改為 'move'
    os.system("move {0} {1}".format(image_path, trained_folder))

    回覆刪除

張貼留言