[ OpenCV ] 利用 OpenCV進行臉部辨識

之前寫了捕捉臉部的 Code後,就變得有點貪心了,想說試著看看能不能分類 Twice的成員。
先利用上一篇 "利用 OpenCV抓取臉部數據",捕捉要訓練用的臉部圖片。
很殘酷的是,用同樣的參數抓取大量格式不一的網路圖片時,應該會有很多圖片的臉部捕捉錯誤,可能需要手工移除錯誤的圖片,目前我也沒有有效的方法去解決這個問題。有招的大大拜託不要留招。

開始之前,我的資料夾結構如下
  1. .
  2. ├── cascades
  3.    └── haarcascade_frontalface_default.xml
  4. ├── Chaeyoung
  5.    └── ...
  6. ├── Dahyun
  7.    └── ...
  8. ├── Jeongyeon
  9.    └── ...
  10. ├── Jihyo
  11.    └── ...
  12. ├── Mina
  13.    └── ...
  14. ├── Momo
  15.    └── ...
  16. ├── Nayeon
  17.    └── ...
  18. ├── Sana
  19.    └── ...
  20. ├── Tzuyu
  21.    └── ...
  22. └── facial_recognition.py
裡面放著類似這樣的圖,大約每個成員都有40張左右的數據。
 
 
一個都不認識?沒關係,讓 OpenCV介紹給你認識。
  1. import os
  2. import cv2
  3. import numpy as np
  4. import random
  5.  
  6. names = ['Nayeon', 'Jeongyeon', 'Momo', 'Sana', 'Jihyo', 'Mina', 'Dahyun', 'Chaeyoung', 'Tzuyu']
  7.  
  8. x,y = [],[]
  9. for idx,member in enumerate(names):
  10. for fileNames in os.walk("./%s" % member):
  11. for fileName in fileNames[-1]:
  12. if fileName == '.DS_Store':
  13. pass
  14. else:
  15. img = cv2.imread(str("./"+member+"/"+fileName), cv2.IMREAD_GRAYSCALE)
  16. x.append(img)
  17. y.append(idx)
  18.  
  19. data = list(zip(x, y))
  20. random.shuffle(data)
  21. data_np = np.array(data)
  22. x = data_np[:,0].tolist()
  23. y = data_np[:,1].tolist()
先把成員的名字建立成 list,再將每個成員的名字和檔案名稱加工成路徑,再利用 OpenCV讀取檔案加入 Training Data的 list,並同時建立 y作為 Label。
雖然不知道對 OpenCV的學習系統有沒有影響,但還是把資料的順序打亂一下好了。
(用 os.walk來找資料夾中的文件並不是很有效率,比較好的做法可以使用 glob。文中雖然不是寫得很聰明,但這是小弟學習的過程,所以就沒有更新代碼了,有需要的朋友試著自己動手改吧。)

OpenCV提供了三種辨識用的演算法,分別是
  • Eigenfaces
  • Fisherfaces
  • Local Binary Pattern Histogram ( LBPH )
有興趣的話可以查查這些算法的論文。

為了比較一下三種算法的差異,所以把每個模型都拿來訓練一下。
  1. model_EigenFaces = cv2.face.createEigenFaceRecognizer()
  2. model_EigenFaces.train(np.asarray(x), np.asarray(y))
  3.  
  4. model_FisherFaces = cv2.face.createFisherFaceRecognizer()
  5. model_FisherFaces.train(np.asarray(x), np.asarray(y))
  6.  
  7. model_LBPH = cv2.face.createLBPHFaceRecognizer()
  8. model_LBPH.train(np.asarray(x), np.asarray(y))
建立模型後,用這些模型來辨識,經 OpenCV辨識出的臉吧。
  1. face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
  2.  
  3. def detect(filename):
  4. img = cv2.imread(filename)
  5. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  6. faces = face_cascade.detectMultiScale(gray, 1.3, 5, minSize=(100,100))
  7. for (x,y,w,h) in faces:
  8. img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
  9. roi = gray[y:y+h, x:x+w]
  10. try:
  11. roi = cv2.resize(roi, (200,200), interpolation=cv2.INTER_LINEAR)
  12. params = model_EigenFaces.predict(roi)
  13. print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
  14. cv2.putText(img, names[params[0]], (x,y-35), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,0,0), 2)
  15. except:
  16. cv2.putText(img, 'Happy partner', (x,y-35), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,0,0), 2)
  17. try:
  18. roi = cv2.resize(roi, (200,200), interpolation=cv2.INTER_LINEAR)
  19. params = model_FisherFaces.predict(roi)
  20. print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
  21. cv2.putText(img, names[params[0]], (x,y-20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
  22. except:
  23. cv2.putText(img, 'Happy partner', (x,y-20), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
  24. try:
  25. roi = cv2.resize(roi, (200,200), interpolation=cv2.INTER_LINEAR)
  26. params = model_LBPH.predict(roi)
  27. print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
  28. cv2.putText(img, names[params[0]], (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,255), 2)
  29. except:
  30. cv2.putText(img, 'Happy partner', (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,255), 2)
  31. cv2.imwrite('./face_tzuyu.png', img)
  32. print('Working with %s' % filename)
  33.  
  34. detect('tzuyu.jpg')
接著把辨識出來的臉,先用 "try" 試看看有沒有辦法辨認是哪位成員,如果辨識成功就會在附近標注名字。若辨識失敗,就會標注 "Happy partner"。
利用 cv2.putText在圖片上進行標注,其參數依序如下
  • 要被標注的圖片
  • 標注的文字
  • 位置
  • 字型
  • 字體大小
  • 字體顏色
  • 字體粗細
其中我把 Eigenfaces、Fisherfaces、LBPH的辨識結果分別設為藍色、綠色和紅色,以利分辨。
遺憾的是,OpenCV似乎沒有辦法支援中文字型的輸出,如果各位大大有招,拜託教一下。

完成後,先試試看有沒有辦法辨識子瑜女神

看來 LBPH辨識錯誤了,再來試試 Twice和愉快伙伴們?
 
結果挺令人失望了,幾乎全部辨識錯誤,連愉快伙伴都被辨識為 Twice的成員。
會不會任誰的臉都會被辨識為 Twice的成員呢?
為了測試模型,所以做了以下實驗性的嘗試,Once和其他照片中的粉絲請不要生氣。
看來在這個分類器眼中,似乎沒有愉快伙伴,幾乎什麼人都可以是 Twice。
有可能是資料量太少,或是 Training Data解析度差異太大等原因。說實話,我也不曉得。
所以對不起摟,目前這個分類器沒有辦法介紹 Twice成員。之後有機會再來寫個用其他深度學習模塊的分類器好了。

雖然這三個模型都沒有辦法準確的辨識 Twice,但還是儲存一下好了。
儲存時,副檔名可以選擇 yml或是 xml。
  1. model_EigenFaces.save('twice_model_EigenFaces.yml')
  2. model_FisherFaces.save('twice_model_FisherFaces.yml')
  3. model_LBPH.save('twice_model_LBPH.yml')
要在讀取已訓練的數據時,先建立一個相對應的模型,再讀取 yml檔或 xml檔。
  1. model_EigenFaces_new = cv2.face.createEigenFaceRecognizer()
  2. model_EigenFaces_new.load('twice_model_EigenFaces.yml')
  3.  
  4. model_FisherFaces_new = cv2.face.createFisherFaceRecognizer()
  5. model_FisherFaces_new.load('twice_model_FisherFaces.yml')
  6.  
  7. model_LBPH_new = cv2.face.createLBPHFaceRecognizer()
  8. model_LBPH_new.load('twice_model_LBPH.yml')

留言