[ OpenCV ] 利用 OpenCV進行臉部辨識批量訓練
透過上一章 "利用 OpenCV抓取影片中的臉部數據",有了大量的圖片數據後,可能沒有辦法像之前一樣,全部一起訓練模型,必須採用批量訓練。
但模型更新受到一定的限制,官方說法是只有 Local Binary Pattern Histogram ( LBPH )可以對模型進行更新。所以本章只會使用 LBPH練習。
開始訓練前的資料夾結構如下
os.walk抓取也會抓取資料夾中的資料夾內的資料,所以可能要避免讀取移動過的資料(代碼中有提到)。
每當 list中有 50筆資料時就訓練,訓練後移動,移動後就把所有的 list清空。
最後把未滿 50筆的資料也重複一樣的動作。
模型的部分,代碼開頭先建立 LBPH模型,檢查有沒有之前訓練儲存的 xml檔,有就讀取。
model_training function中,一樣先檢查 xml檔,沒有就訓練新的,有就更新。完成後存檔。
訓練數據共使用了九位成員的 TT前導宣傳短片做數據捕捉,大約抓了 5000多張相片來訓練這個模型。
對這幾部影片有興趣的話可以點擊這裡,可以在右邊的 list看到許多由 JYP娛樂提供的影片。
接著就拿它來進行辨識吧。
比較值得注意的是第 33行的 "if params[1] < 80:",這裡可以對精度設定條件,再決定要不要加上標籤。
Eigenfaces、Fisherfaces和 LBPH顯示出的數字對精度的定義不太一樣,這裡也需要注意一下。
本篇是針對 LBPH設定,至於要設定多少,我也不好拿捏,不過數字越低,則表示預測的可靠度越高。
matplotlib.pyplot的部分被著解掉了,因為實在太消耗資源了,處理速度慢太多了。
接著我把臉部偵測的條件降低,眼睛至少偵測到一隻,就進行辨識,若辨識不出來或不精準精度高過 80就不顯示。
利用這個辨識模型製作了下面兩部影片。
(實際捕捉時的參數不一定和上面的代碼一樣,辨識不同的素材時,多少做了些修改,所以用上面的代碼可能是某次調適時所設定的,不一定會得到一樣的結果。)
第一部是使用了相同訓練素材製作的 MV,影片中的精度還算高,當然也因為有對精度進行篩選。
第二部也是使用了相同的模型對沒有訓練過的影片進行辨識,但可能因為造型變化大,且鏡頭移動快速,結果慘不人睹。
其實嘗試辨識了五六部影片的,但有些因為臉部捕捉率太低,或是版權問題無法分享,
由於對 OpenCV的辨識系統沒有很了解,但下意識覺得有可能是 Overfitting之類的問題,或是訓練數據仍不夠或品質不佳等原因。
有機會再來用 Face Alignment的方式提高數據品質,還有機會的話也來做個用深度學習的辨識系統。
但模型更新受到一定的限制,官方說法是只有 Local Binary Pattern Histogram ( LBPH )可以對模型進行更新。所以本章只會使用 LBPH練習。
開始訓練前的資料夾結構如下
model_train.py代碼如下
- .
- ├── Chaeyoung
- │ └── ...
- ├── Dahyun
- │ └── ...
- ├── Jeongyeon
- │ └── ...
- ├── Jihyo
- │ └── ...
- ├── Mina
- │ └── ...
- ├── Momo
- │ └── ...
- ├── Nayeon
- │ └── ...
- ├── Sana
- │ └── ...
- ├── Tzuyu
- │ └── ...
- └── model_train.py
訓練數據的部分一樣透過 os.walk抓取,訓練後會將訓練過的照片移動到底下的 trained資料夾中。
- import os
- import cv2
- import numpy as np
- batch = 50
- names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
- model_LBPH = cv2.face.createLBPHFaceRecognizer()
- if os.path.isfile("./twice_model_LBPH.xml"):
- model_LBPH.load("twice_model_LBPH.xml")
- trained_data = 0
- def model_training(x,y):
- if not os.path.isfile("./twice_model_LBPH.xml"):
- model_LBPH.train(np.asarray(x), np.asarray(y))
- else:
- model_LBPH.update(np.asarray(x), np.asarray(y))
- model_LBPH.save("twice_model_LBPH.xml")
- global trained_data
- trained_data = trained_data + len(y)
- print("Model are trained {0} images".format(trained_data))
- def move_trained_image(image_paths, trained_folders):
- for image_path, trained_folder in zip(image_paths, trained_folders):
- if not os.path.isdir(trained_folder):
- os.system("mkdir {0}".format(trained_folder))
- print("Create {0}".format(trained_folder))
- os.system("mv {0} {1}".format(image_path, trained_folder))
- print("Moved {0} images".format(len(image_paths)))
- x,y = [],[]
- image_paths, trained_folders = [],[]
- for label,member in enumerate(names):
- for fileNames in os.walk("./{0}".format(member)):
- if ("./{0}/trained".format(member)) == fileNames[0]: # 避免 os.walk爬到第二層導致錯誤。
- continue
- for fileName in fileNames[-1]:
- if fileName.endswith('.png'):
- image_paths.append("./{0}/{1}".format(member, fileName))
- trained_folders.append("./{0}/trained".format(member))
- img = cv2.imread("./{0}/{1}".format(member, fileName), cv2.IMREAD_GRAYSCALE)
- x.append(img)
- y.append(label)
- if len(y)!=0 and len(y)%batch == 0:
- model_training(x,y)
- move_trained_image(image_paths,trained_folders)
- x,y = [],[]
- image_paths, trained_folders = [],[]
- if len(y)!=0:
- model_training(x,y)
- move_trained_image(image_paths,trained_folders)
- x,y = [],[]
- image_paths, trained_folders = [],[]
- print("Done!!")
os.walk抓取也會抓取資料夾中的資料夾內的資料,所以可能要避免讀取移動過的資料(代碼中有提到)。
每當 list中有 50筆資料時就訓練,訓練後移動,移動後就把所有的 list清空。
最後把未滿 50筆的資料也重複一樣的動作。
模型的部分,代碼開頭先建立 LBPH模型,檢查有沒有之前訓練儲存的 xml檔,有就讀取。
model_training function中,一樣先檢查 xml檔,沒有就訓練新的,有就更新。完成後存檔。
訓練數據共使用了九位成員的 TT前導宣傳短片做數據捕捉,大約抓了 5000多張相片來訓練這個模型。
對這幾部影片有興趣的話可以點擊這裡,可以在右邊的 list看到許多由 JYP娛樂提供的影片。
接著就拿它來進行辨識吧。
內容與 "利用 OpenCV進行臉部辨識" 這張相當類似,參數的部分可以參考該篇。
- import cv2
- import numpy as np
- # import matplotlib.pyplot as plt
- names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
- model_LBPH = cv2.face.createLBPHFaceRecognizer()
- model_LBPH.load("twice_model_LBPH.xml")
- face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
- eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')
- def detect(img, idx):
- font_size = 1.2
- font_thickness = 3
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- faces = face_cascade.detectMultiScale(gray,
- scaleFactor=1.1,
- minNeighbors=3,
- minSize=(130,130),)
- for (x,y,w,h) in faces:
- roi = gray[y:y+h, x:x+w]
- eyes = eye_cascade.detectMultiScale(roi,
- scaleFactor=1.06,
- minNeighbors=5,
- minSize=(30,30),)
- if len(eyes)>=1:
- try:
- roi = cv2.resize(roi, (200,200), interpolation=cv2.INTER_LINEAR)
- params = model_LBPH.predict(roi)
- print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
- if params[1] < 80:
- img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),3)
- cv2.putText(img, names[params[0]], (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
- else:
- pass
- # img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
- # cv2.putText(img, 'Happy partner', (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
- except:
- pass
- # img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),3)
- # cv2.putText(img, 'Happy partner', (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, font_size, (255,0,0), font_thickness)
- print('Working with %s frame' % idx)
- return img
- input_file = 'twice_song.mp4'
- output_file = 'face_twice_song.avi'
- videoCapture = cv2.VideoCapture(input_file)
- fps = videoCapture.get(cv2.CAP_PROP_FPS)
- size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
- int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
- videoWriter = cv2.VideoWriter(output_file, cv2.VideoWriter_fourcc('I','4','2','0'), fps, size)
- success, frame = videoCapture.read()
- frame_counter = 1
- # plt.ion()
- while success:
- frame = detect(frame, frame_counter)
- videoWriter.write(frame)
- # plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
- # plt.pause(.01)
- # plt.cla() # Clear axis
- # plt.clf() # Clear figure
- success, frame = videoCapture.read()
- frame_counter += 1
- # plt.ioff()
- print("Done!!")
比較值得注意的是第 33行的 "if params[1] < 80:",這裡可以對精度設定條件,再決定要不要加上標籤。
Eigenfaces、Fisherfaces和 LBPH顯示出的數字對精度的定義不太一樣,這裡也需要注意一下。
本篇是針對 LBPH設定,至於要設定多少,我也不好拿捏,不過數字越低,則表示預測的可靠度越高。
matplotlib.pyplot的部分被著解掉了,因為實在太消耗資源了,處理速度慢太多了。
接著我把臉部偵測的條件降低,眼睛至少偵測到一隻,就進行辨識,若辨識不出來或不精準精度高過 80就不顯示。
利用這個辨識模型製作了下面兩部影片。
(實際捕捉時的參數不一定和上面的代碼一樣,辨識不同的素材時,多少做了些修改,所以用上面的代碼可能是某次調適時所設定的,不一定會得到一樣的結果。)
第一部是使用了相同訓練素材製作的 MV,影片中的精度還算高,當然也因為有對精度進行篩選。
第二部也是使用了相同的模型對沒有訓練過的影片進行辨識,但可能因為造型變化大,且鏡頭移動快速,結果慘不人睹。
其實嘗試辨識了五六部影片的,但有些因為臉部捕捉率太低,或是版權問題無法分享,
由於對 OpenCV的辨識系統沒有很了解,但下意識覺得有可能是 Overfitting之類的問題,或是訓練數據仍不夠或品質不佳等原因。
有機會再來用 Face Alignment的方式提高數據品質,還有機會的話也來做個用深度學習的辨識系統。
錯誤/修改: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))