原來opencv也能"訓練"人臉辨識

 原來opencv也能"訓練"人臉辨識



有玩過opencv的人都知道可套用haarcascades特徵檔(xml),當時我就一直在想
這個配合opencv用起來很方便,但要如何去訓練出xml,剛好查谷歌大神時翻到
這個主題,當然要來練一下才行。

#順便附上官方的可套用xml檔

會用到的程式碼都放在github上面

https://github.com/blairan/openCVex/tree/main/trainFaceDector

1.先安裝必要的程式庫

#opencv和opencv-contrib-python版本對照

因為opencv4.x版後有申請項目專利,所以和opencv-contrib-python這個非官方庫後面的版本不相容,尤其cv2.face這個功能這次主題會用到的就可能會無效,所以選擇好版本,避開坑洞。
此次我採用以下版本,親測可用。
最好使用Anacoda的evn來安裝

1-1
pip install opencv-python==4.5.5.62
pip install opencv-contrib-python==4.5.5.62
pip install pillow

1-2
下載haarcascade_frontalface_default.xml,和py文件放同個目錄

1-3
在主文件目錄下新增imgs和train兩個目錄
-imgs目錄用來存放要訓練的人臉感興趣的特徵檔
-train目錄用來存放由imgs提供的照片做訓練,最後生成.xml


2.採集人臉

使用說明:
執行時,會在終端機等待輸入id,例如輸入1,檔案名稱會以user.1.0.jpg/user.1.1.jpg這樣排列下去,那輸入2,就會生成user.2.0..jpg/user.2.1.jpg,一次採集會以30張為基準。照片存在會在imgs資料夾

程式碼
import cv2

cap=cv2.VideoCapture(0)
face_dector=cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
id=input("\n請輸入id:")
print("\n攝影機初始化,請稍候")
count=0
while True:
_, img=cap.read()
img=cv2.flip(img,1)
gray=cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
faces=face_dector.detectMultiScale(gray,1.3,5)
if len(faces)>0:
for (x,y,w,h) in faces:
cv2.rectangle(img, (x,y),(x+w, y+h),(0,255,0),3)
cv2.imwrite("faceImgs/user."+str(id)+"."+str(count)+".jpg",gray[y:y+h, x:x+w])
count+=1
cv2.imshow("face_detection", img)
s=cv2.waitKey(100)
if s==ord('q'):
break
elif count>30:
break
cap.release()
cv2.destroyAllWindows()

3.訓練數據生成xml文件

執行完畢後可以看到train資料夾裡已生成一個train.xml檔
import cv2
import numpy as np
from PIL import Image
import os

path="faceImgs"
recognizer=cv2.face.LBPHFaceRecognizer_create()
detector=cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
def getFaceAndLabel(path):
faceSamples=[]
ids=[]
images=[os.path.join(path,i) for i in os.listdir(path)]
for image in images:
PIL_image=Image.open(image).convert('L') #轉成灰階
img_arr=np.array(PIL_image,'uint8')
id=int(os.path.split(image)[-1].split(".")[1])
faces=detector.detectMultiScale(img_arr)
for (x,y,w,h) in faces:
faceSamples.append(img_arr[y:y+h, x:x+w])
ids.append(id)
return faceSamples, ids

print("\n影像辨識中....")
face, id = getFaceAndLabel(path)
recognizer.train(face, np.array(id))
recognizer.write('train/train.yml') #儲存訓練結果
print("\n訓練出{0}張臉".format(len(np.unique(id))))

4.使用訓練出來的xml辨識人臉並標示出姓名

# 人脸识别
# coding=utf-8
import cv2
import numpy
from PIL import Image, ImageDraw, ImageFont

# 解决cv2.putText绘制中文乱码
def cv2ImgAddText(img2, text, left, top, textColor=(0, 0, 255), textSize=20):
if isinstance(img2, numpy.ndarray): # 判断是否OpenCV图片类型
img2 = Image.fromarray(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
# 创建一个可以在给定图像上绘图的对象
draw = ImageDraw.Draw(img2)
# 字体的格式
fontStyle = ImageFont.truetype(r"C:\WINDOWS\FONTS\MSYH.TTC", textSize, encoding="utf-8")
# 绘制文本
draw.text((left, top), text, textColor, font=fontStyle)
# 转换回OpenCV格式
return cv2.cvtColor(numpy.asarray(img2), cv2.COLOR_RGB2BGR)


recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('train/train.yml')
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)
font = cv2.FONT_HERSHEY_SIMPLEX

num = 0
names = ['邱定凱']
cam = cv2.VideoCapture(0)
minW = 0.1*cam.get(3)
minH = 0.1*cam.get(4)

while True:
ret, img = cam.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(int(minW), int(minH))
)

for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
num, confidence = recognizer.predict(gray[y:y + h, x:x + w])

if confidence < 100:
name = names[num]
# confidence = "{0}%".format(round(100 - confidence))
# confidence = format(round(100 - confidence))
if name=='邱定凱':
print("定凱,歡迎您!")
else:
name = "unknown"
# confidence = "{0}%".format(round(100 - confidence))
# confidence = format(round(100 - confidence))

# 解决cv2.putText绘制中文乱码
img = cv2ImgAddText(img, name, x + 5, y - 30)
# cv2.putText(img, name, (x + 5, y - 5), font, 1, (0, 0, 255), 1) 无法显示中文
# cv2.putText(img, str(confidence.encode('utf-8')), (x+5, y+h-5), font, 1, (0, 0, 0), 1)

cv2.imshow('camera', img)
k = cv2.waitKey(5)
if k == ord('q'):
break

cam.release()
cv2.destroyAllWindows()







留言

這個網誌中的熱門文章

opencv讀取圖片和視訊頭