基於opencv和pillow實現人臉識別系統(附demo)

本文不涉及分類器、訓練識別器等算法原理,僅包含對其應用(未來我也會寫自己對機器學習算法原理的一些觀點和瞭解)
首先我們需要知道的是利用現有框架做一個人臉識別系統並不難,然後就開始我們的系統開發吧。
我們的系統主要分為三個部分,然後我還會提出對補獲圖片不能添加中文的解決方案。我們需要完成的任務:1.人臉檢測和數據收集2.訓練識別器3.人臉識別和顯示

在讀此篇文章之前我相信你已經做瞭python環境部署和opencv模塊的下載安裝工作,現在我們還需要的模塊是pillow(樹莓派默認帶有此模塊,但如果你用的是win系統可能還需要另外安裝,在終端輸入pip install pillow即可),和opencv-contrib模塊,cv2的face模塊包含在內(當然我的Linux系統的樹莓派貌似仍然默認包含瞭此模塊,所以如果你是用的pc可能需要另外下載),以及最基本的numpy模塊。

在開始寫代碼之前我們首先需要在當前運行目錄中添加兩個文件夾,dataset用於存放捕獲到的人臉圖像,方便後面訓練識別器,trainer文件夾則存放瞭訓練結果

一。人臉檢測和數據收集

#數據采集
cam = cv2.VideoCapture(0)#補獲圖片
cam.set(3, 640) # set video width
cam.set(4, 480) # set video height
face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')#導入分類器
# For each person, enter one numeric face id
face_id = input('\n 輸入用戶id')
print("\n 數據采集中,請正視攝像頭輕微扭轉")
# Initialize individual sampling face count
count = 0
while(True):
 ret, img = cam.read()#ret為是否成功讀取,是一個佈爾值
 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#轉化為灰度圖
 faces = face_detector.detectMultiScale(gray, 1.3, 5,minSize=(100,100))
 for (x,y,w,h) in faces:#此處faces是一個array數組或空的元組,原因我後面會分析
  cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)
  count += 1
 # Save the captured image into the datasets folder
  cv2.imwrite("dataset/User." + str(face_id) + '.' + str(count) + ".jpg", gray[y:y+h,x:x+w])
 cv2.imshow('image', img)
 k = cv2.waitKey(100) & 0xff # Press 'ESC' for exiting video
 if k == 27:
  break
 elif count >= 10: # Take 10 face sample and stop video
  break
# Do a bit of cleanup
print("\n [INFO] Exiting Program and cleanup stuff")
cam.release()
cv2.destroyAllWindows()

在這一部分中我們完成瞭人臉的補獲,並將其保存在瞭我們建立的dataset文件夾,並將每一個人的數據用特定的id表述,這樣我們就能訓練能識別不同人臉的識別器。cam.set這一步中第二個參數是圖像的分辨率,640×480是opencv的默認分辨率,但其仍支持800×600且最大支持1280乘1024像素,即使你的攝像頭最大允許分辨率遠大於這個值,opencv貌似仍不會允許你使用。之後就是haar級聯分類器的導入,在配置過程中,我們已經下載瞭opencv自帶的分類器,你隻需要在文件管理器中查找haarcascade_frontalface_default.xml這個文件即可,在這個文件所在的文件夾中有許多分類器,當然如果你要識別例如蘋果香蕉等物體你可能需要訓練新的分類器(這也很容易做到),本文討論的是人臉識別,因此這裡不再贅述,你可以選擇用其絕對路徑導入,當然你也可以像我一樣將其復制到你當前目錄中。然後進入循環,圖片的讀取->轉灰度圖,然後是使用你已經導入的分類器識別人臉並將其用方框標出,然後將方框內的圖片儲存入dataset文件夾中。值得一提的是,因為分類器的算法是很慢的,所以分類器本身就有減幀處理(即使這樣我的樹莓派帶人臉識別系統仍然很吃力),所以faces應該是大部分時間都是空的元組,小部分時間是讀取到的array數組,因此你需要特別註意縮進問題,不要讓分類器本身的減幀影響你視頻讀取並顯示的幀數。

二.訓練識別器

import cv2
#訓練器
import numpy as np
from PIL import Image
import os
# Path for face image database
path = 'dataset'
recognizer = cv2.face.LBPHFaceRecognizer_create()#識別器的導入
detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
# function to get the images and label data
def getImagesAndLabels(path):
 imagePaths = [os.path.join(path,f) for f in os.listdir(path)]#在這裡os模塊可以幫助我們很好的建立路徑,建議可以先查看一下相關函數的使用方法。
 faceSamples=[]
 ids = []
 for imagePath in imagePaths:
  PIL_img = Image.open(imagePath).convert('L') #轉化為灰度圖
  img_numpy = np.array(PIL_img,'uint8')#轉化為數組
  id = int(os.path.split(imagePath)[-1].split(".")[1])
  faces = detector.detectMultiScale(img_numpy)
  for (x,y,w,h) in faces:
   faceSamples.append(img_numpy[y:y+h,x:x+w])
   ids.append(id)
 return faceSamples,ids
print ("\n 訓練數據中,請稍後")
faces,ids = getImagesAndLabels(path)
recognizer.train(faces, np.array(ids))
# Save the model into trainer/trainer.yml
recognizer.write('trainer/trainer.yml') # recognizer.save() worked on Mac, but not on Pi
# Print the numer of faces trained and end program
print("\n {0} 張臉訓練完畢. 程序關閉".format(len(np.unique(ids))))

在這一步我們需要對識別器按照不同id分別訓練並保存結果,並將結果匯總寫入trainer文件夾中命名為trainer.yml。這個文件裡就是訓練好的識別器。

三.人臉識別和顯示

# -*- coding: UTF-8 -*-
#識別器
import cv2
import numpy as np
import os
from PIL import Image, ImageFont, ImageDraw
path_to_ttf = 'C:\Windows\Fonts\Microsoft YaHei UI\msyh.ttc'#ttc文件是支持漢語的字體,稍後我會說明。
font1= ImageFont.truetype(path_to_ttf, size=20)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('trainer/trainer.yml')#讀取識別器
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath);
font = cv2.FONT_HERSHEY_SIMPLEX
#iniciate id counter
id = 0
# names related to ids: example ==> Marcelo: id=1, etc
names = ['None', '段林晨', 'Paula', 'Ilza', 'Z', 'W']#因為我們不會在人臉識別時隻顯示你的代號而是要顯示你的具體信息。
cam = cv2.VideoCapture(0)
cam.set(3, 640) # set video widht
cam.set(4, 480) # set video height
#最小識別的臉的大小,在識別過程中,我們不會捕獲到距離你很遠的街上路人的信息,這會導致很多問題,因此我們隻需要識別想參與識別的人,而設置最小人臉識別大小可以規避這一點
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)
  id, confidence = recognizer.predict(gray[y:y+h,x:x+w])
 # Check if confidence is less them 100 ==> "0" is perfect match
  if (confidence < 100):
   id = names[id]
   confidence = " {0}%".format(round(100 - confidence))#confidence是置信度指數,等於100-概率,相信大傢的概率統計一定比我優秀
  else:
   id = "未識別"
   confidence = " {0}%".format(round(100 - confidence))
  img=Image.fromarray(img)
  draw = ImageDraw.Draw(img)
  draw.text(xy=(x+5,y-5), text=str(id), font=font1,fill=(255,255,255))
  img=np.array(img)
  cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1)
 cv2.imshow('camera',img)
 k = cv2.waitKey(10) & 0xff # Press 'ESC' for exiting video
 if k == 27:
  break
# Do a bit of cleanup
print("\n [INFO] Exiting Program and cleanup stuff")
cam.release()
cv2.destroyAllWindows()

在這一步中,很遺憾的是cv2.putText函數並不支持漢語的應用,即你不能通過這個函數將漢語姓名顯示在視頻中,雖然你可能會有英文名或幹脆用漢語拼音,但這個問題我們必須要解決。因此我們在這裡引入瞭pillow模塊,我們隻需要使用img.draw功能在圖片上先打出你的姓名,再進行cv2.putText函數就能很好的解決這個問題,但是比較麻煩的是這兩個函數涉及到的圖片類型是不一樣的,因此我在代碼中對img和array圖像進行瞭轉換,最終完成瞭人臉識別系統。

到此這篇關於基於opencv和pillow實現人臉識別系統(附demo)的文章就介紹到這瞭,更多相關opencv pillow實現人臉識別內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: