背景
源於實習公司的人臉識別打卡系統,完成之前的項目後正好沒有事情幹,於是想到了這個,公司的這個打卡系統操作流程是這樣的,首先用手機把你的人臉錄進去,要求繞頭半圈,也就是右臉,正臉,左臉,然後你再去攝像頭那裏,識別到你後就會幫你把門打開,順便幫你在釘釘上打卡。
功能
我做的是簡易版,實現了這個打卡系統的主要功能,能完成:信息錄入,正臉識別,開門關門,名字與時間的保存。
硬件
樹莓派一個,攝像頭一個,顯示屏一個
效果
並沒有打馬賽克,文件大小還被限制了,大家將就着看,
這是在電腦上的效果:
在樹莓派上效果:
打卡記錄:
看起來效果還是不錯的
源碼
源碼使用的電腦上的源碼,其實是差不多的,路徑不一樣而已
如果樹莓派打不開攝像頭,參考這個:
https://www.jianshu.com/p/5653b2b7248c
代碼註釋很全,不詳細解釋
攝像頭測試代碼
import cv2
capCamera = cv2.VideoCapture(0)
if(not capCamera.isOpened()):
print("can't open this camera")
exit(0)
capCamera.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
capCamera.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
while(True):
# handle for the camera
ret, frame = capCamera.read()
if ret == True:
cv2.imshow('camera',frame)
else:
break
# handle for the video
# handle for exit
if (cv2.waitKey(1)) == ord('q'):
break
capCamera.release()
cv2.destroyAllWindows()
錄入信息
import cv2
import os
#config
add_name = 'xiaoming'#要錄入的人名
target_dir = './pic_dir/{}/'.format(add_name)
if os.path.exists(target_dir) is False:
os.makedirs(target_dir)
def generate():
face_cascade = cv2.CascadeClassifier('.\cascades\haarcascade_frontalface_default.xml')
#打開攝像頭
camera = cv2.VideoCapture(0)
forword_count = 0
#正臉採集,一共20張圖片
while (forword_count <= 20):
ret, frame = camera.read()
#轉化爲灰度圖像,用來檢測人臉
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
#畫出預測框
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
f = cv2.resize(gray[y:y + h, x:x + w], (200, 200))
#保存錄入的圖片
cv2.imwrite('./pic_dir/{0}/{1}.png'.format(add_name, forword_count), f)
print(forword_count)
forword_count += 1
#展示圖片
cv2.imshow("camera", frame)
#一秒鐘24幀
if cv2.waitKey(1000 // 24) & 0xff == ord("q"):
break
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
generate()
人臉識別
import os
import sys
import cv2
import numpy as np
import time
def change_door(open_later_time,isOpen,new_face_position):
if len(new_face_position) > 0 and isOpen == False:
print('打開')
isOpen = True
if len(new_face_position) == 0 and isOpen == True:
open_later_time += 1
else:
open_later_time = 0
if open_later_time == 100:
open_later_time = 0
print('關閉')
isOpen = False
return open_later_time,isOpen,new_face_position
def read_images(path, sz=None):#給一個地址,返回訓練集
c = 0
X, Y = [], []
names = []
for dirname, dirnames, filenames in os.walk(path):#目錄,子目錄,子文件(只限一層目錄)
for subdirname in dirnames:
names.append(subdirname)
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):#遍歷每個名字
try:
if (filename == ".directory"):
continue
filepath = os.path.join(subject_path, filename)
im = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
if (im is None):
print("image " + filepath + " is none")
else:
print(filepath)
if (sz is not None):
im = cv2.resize(im, (200, 200))
X.append(np.asarray(im, dtype=np.uint8))
Y.append(c)
except IOError:
print("I/O error({0}): {1}".format(IOError.errno, IOError.strerror))
except:
print("Unexpected error:", sys.exc_info()[0])
raise
print(c)
c = c + 1
print(Y)
print(names)
return [X, Y], names
def face_rec():
image_dir = './pic_dir_1'
isOpen = False
open_later_time = 0
[X, Y] , names = read_images(image_dir)
Y = np.asarray(Y, dtype=np.int32)
model = cv2.face.LBPHFaceRecognizer_create()
model.train(np.asarray(X), np.asarray(Y))
camera = cv2.VideoCapture(0)
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 350)
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
re_count = 0
old_face_position = {}#用來繪製預測框
new_face_position = {}#用來收集新數據
while (True):
#print(old_face_position)
#print(new_face_position)
re_count += 1
read, img = camera.read()
faces = face_cascade.detectMultiScale(img, scaleFactor =1.3, minNeighbors=5)
#print('{}的類型{}'.format(faces, type(faces)))
for (x, y, w, h) in faces:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
roi = gray[x:x + w, y:y + h]
try:
roi = cv2.resize(roi, (200, 200), interpolation=cv2.INTER_LINEAR)
#print(roi.shape)
params = model.predict(roi)
#print("Label: %s, Confidence: %.2f" % (params[0], params[1]))
new_face_position[names[params[0]]] = (x, y, w, h)
except:
continue
#優化用戶體驗
#採集三幀的人臉識別信息,將預測框畫出,預測框三幀一刷新,防止預測框頻繁抖動的現象
if re_count == 3:
re_count = 0
#print(new_face_position)
if len(new_face_position) > 0:
for key in new_face_position.keys():
(x, y, w, h) = new_face_position[key]
if old_face_position.__contains__(key) is False:
img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.putText(img, key, (x, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
old_face_position[key] = (x, y, w, h)
else:
(o_x, o_y, o_w, o_h) = new_face_position[key]
if abs((o_x-x)) <= 5 and abs((o_y-y)) <= 5:
img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.putText(img, key, (x, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
old_face_position[key] = (x, y, w, h)
else:
old_face_position = {}
new_face_position = {}
else:
for key in old_face_position.keys():
(o_x, o_y, o_w, o_h) = old_face_position[key]
img = cv2.rectangle(img, (o_x, o_y), (o_x + o_w, o_y + o_h), (255, 0, 0), 2)
cv2.putText(img, key, (o_x, o_y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
#開關門模擬和保存打卡信息
#如果檢測到人,並且門沒有開,則打開門,並且錄入信息
if len(new_face_position) > 0 and isOpen == False:
print('開門')
#保存打卡信息
t = time.strftime('%Y.%m.%d %H:%M:%S', time.localtime(time.time()))
with open('jilu.txt', 'a') as file:
file.writelines('{},{}\n'.format(new_face_position.keys(),t))
isOpen = True
#如果沒有檢測到人,並且門開了,計數器+1,否則計數器爲0
if len(new_face_position) == 0 and isOpen == True:
open_later_time += 1
else:
open_later_time = 0
#當計數器爲100的時候關門
if open_later_time == 100:
print('關門')
isOpen = False
open_later_time = 0
cv2.imshow("camera", img)
#樹莓派最好將幀數設爲最大,不然看起來不舒服
if cv2.waitKey(1000 // 25) & 0xff == ord("q"):
#if cv2.waitKey(1000 // 25) & 0xff == ord("q"):
break
cv2.destroyAllWindows()
if __name__ == "__main__":
face_rec()
結論
在電腦上運行的很流暢,在樹莓派上運行的話因樹莓派而異,能明顯感覺到幀數下降,不過基本功能還是能完成的