该项目将包括三个阶段:
人脸检测和数据收集
训练识别器
面部识别
在深入研究代码之前,让我们将电磁锁与Raspberry Pi连接。
电路图和说明
Raspberry Pi的GPIO引脚可以提供3.3V的输出,但电磁锁需要7-12V才能工作。因此,我们需要使用外部电源和继电器来操作锁定。
将继电器模块的VCC和GND连接到Raspberry Pi的5V和GND。然后将继电器模块的信号引脚连接到Raspberry Pi的GPIO 26.
在继电器模块的另一侧,将负极直流电源连接到电磁门锁的负极。将来自直流电源的正极连接到继电器模块的公共端,然后将常开模块从继电器模块连接到电磁门锁的正极。
面部识别电路图。
人脸检测的数据收集
第一个任务是收集我们要训练分类器的数据。我们将使用OpenCV预训练分类器编写一个python代码,每个人需要30个面。
OpenCV已经包含许多面部,眼睛,微笑等预先训练的分类器。我们要去的分类器使用将检测面部,并在GitHub上提供级联文件。
将此文件保存在工作目录中“haarcascade_frontalface_default.xml”。
代码演练
现在让我们编写代码。 首先,我们导入所需的包。
import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np
import os
import sys
然后我们初始化相机对象,这将允许我们使用Raspberry Pi相机。我们将分辨率设置为(640,480),帧速率设置为30 fps。
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30
PiRGBArray() 为我们提供了一个来自未编码RGB捕获的三维RGB数组(行,列,颜色)。 PiRGBArray的优势在于它能够将Raspberry Pi相机中的帧读取为NumPy阵列,使其与OpenCV兼容。它避免了从JPEG格式到OpenCV格式的转换,这会减慢我们的过程。
它需要两个参数:
相机对象
分辨率
rawCapture = PiRGBArray(camera, size=(640, 480))
加载用于检测面的级联文件。
face_cascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”);
接下来,我们要求用户输入名称。如果已存在具有该名称的目录,则它将以“名称已存在”进行响应并退出代码。如果没有此名称的目录,它将创建目录,图像将以此名称保存。
name = input(“What‘s his/her Name? ”)
dirName = “。/images/” + name
print(dirName)
if not os.path.exists(dirName):
os.makedirs(dirName)
print(“Directory Created”)
else:
print(“Name already exists”)
sys.exit()
为图像创建目录。
之后,我们使用 capture_continuous 从Raspberry Pi相机模块开始读取帧的功能。
c apture_continuous函数有三个参数:
rawCapture
我们想要读取每个帧的格式,因为OpenCV期望图像采用BGR格式而不是RGB格式,因此我们将格式指定为BGR。
use_video_port布尔值,使其为true意味着我们将流视为视频。
for frame in camera.capture_continuous(rawCapture, format=“bgr”, use_video_port=True):
一旦我们有了f rame,我们可以通过.array属性访问原始的NumPy数组。访问后,我们将此帧转换为灰度。
frame = frame.array
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
然后我们调用分类器函数来检测帧中的面。我们传递的第一个参数是灰度图像。第二个参数是指定在每个图像比例下图像尺寸减少多少的参数。第三个参数是一个参数,指定每个候选矩形应保留多少个邻居。数字越大,误报率越低。
faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
上面的函数给出了面部区域的矩形坐标。我们使用这些坐标从图像中提取面部并将其保存在我们在开始时创建的目录中。之后,我们展示了裁剪的面部并在原始框架上创建了一个矩形。
代码将收集30张图像。
for (x, y, w, h) in faces:
roiGray = gray[y:y+h, x:x+w]
fileName = dirName + “/” + name + str(count) + “.jpg”
cv2.imwrite(fileName, roiGray)
cv2.imshow(“face”, roiGray)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
count += 1
然后我们在上面显示原始帧输出窗口。 cv2.waitkey()是一个键盘绑定函数。它等待任何键盘事件的指定毫秒。它需要一个参数,这个参数是以毫秒为单位的时间。如果在该时间内按下该键,则程序将继续。传递0表示它将无限期地等待一个键。
cv2.imshow(’frame‘, frame)
key = cv2.waitKey(1)
然后我们通过在捕获之间调用truncate(0)来清除流以准备下一帧。
rawCapture.truncate(0)
人脸检测和数据收集工作现已完成。我们应该在新创建的目录中有30个图像。
我们的30张图片保存在新创建的目录中。
人脸检测完整代码
import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np
import os
import sys
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30
rawCapture = PiRGBArray(camera, size=(640, 480))
faceCascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)
name = input(“What’s his/her Name? ”)
dirName = “。/images/” + name
print(dirName)
if not os.path.exists(dirName):
os.makedirs(dirName)
print(“Directory Created”)
else:
print(“Name already exists”)
sys.exit()
count = 1
for frame in camera.capture_continuous(rawCapture, format=“bgr”, use_video_port=True):
if count 》 30:
break
frame = frame.array
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
for (x, y, w, h) in faces:
roiGray = gray[y:y+h, x:x+w]
fileName = dirName + “/” + name + str(count) + “.jpg”
cv2.imwrite(fileName, roiGray)
cv2.imshow(“face”, roiGray)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
count += 1
cv2.imshow(‘frame’, frame)
key = cv2.waitKey(1)
rawCapture.truncate(0)
if key == 27:
break
cv2.destroyAllWindows()
训练识别器
现在我们可以根据上一步收集的数据训练识别器。
我们将使用LBPH (LOCAL BINARY PATTERNS HISTOGRAMS)面部识别器,包含在OpenCV包中。我们将它加载到以下行:
recognizer = cv2.face.LBPHFaceRecognizer_create()
我们获取当前工作目录的路径,然后移动到图像目录所在的目录。
baseDir = os.path.dirname(os.path.abspath(__file__))
imageDir = os.path.join(baseDir, “images”)
《然后我们进入每个图像目录并查找图像。如果图像存在,我们将其转换为NumPy数组。
for root, dirs, files in os.walk(imageDir):
print(root, dirs, files)
for file in files:
print(file)
if file.endswith(“png”) or file.endswith(“jpg”):
path = os.path.join(root, file)
label = os.path.basename(root)
print(label)
if not label in labelIds:
labelIds[label] = currentId
print(labelIds)
currentId += 1
id_ = labelIds[label]
pilImage = Image.open(path).convert(“L”)
imageArray = np.array(pilImage, “uint8”)
之后,我们再次进行面部检测,以确保我们有正确的图像,然后我们准备训练数据。
faces = faceCascade.detectMultiScale(imageArray, scaleFactor=1.1, minNeighbors=5)
for (x, y, w, h) in faces:
roi = imageArray[y:y+h, x:x+w]
xTrain.append(roi)
yLabels.append(id_)
存储包含目录名称和标签ID的字典。
with open(“labels”, “wb”) as f:
pickle.dump(labelIds, f)
f.close()
现在训练数据并保存文件。
recognizer.train(xTrain, np.array(yLabels))
recognizer.save(“trainer.yml”)
此代码创建一个trainer.yml并标记我们在识别代码中使用的文件。
我们在识别代码中使用的trainer.yml文件。
培训识别器的完整代码
import os
import numpy as np
from PIL import Image
import cv2
import pickle
faceCascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)
recognizer = cv2.face.LBPHFaceRecognizer_create()
baseDir = os.path.dirname(os.path.abspath(__file__))
imageDir = os.path.join(baseDir, “images”)
currentId = 1
labelIds = {}
yLabels = []
xTrain = []
for root, dirs, files in os.walk(imageDir):
print(root, dirs, files)
for file in files:
print(file)
if file.endswith(“png”) or file.endswith(“jpg”):
path = os.path.join(root, file)
label = os.path.basename(root)
print(label)
if not label in labelIds:
labelIds[label] = currentId
print(labelIds)
currentId += 1
id_ = labelIds[label]
pilImage = Image.open(path).convert(“L”)
imageArray = np.array(pilImage, “uint8”)
faces = faceCascade.detectMultiScale(imageArray, scaleFactor=1.1, minNeighbors=5)
for (x, y, w, h) in faces:
roi = imageArray[y:y+h, x:x+w]
xTrain.append(roi)
yLabels.append(id_)
with open(“labels”, “wb”) as f:
pickle.dump(labelIds, f)
f.close()
recognizer.train(xTrain, np.array(yLabels))
recognizer.save(“trainer.yml”)
print(labelIds)
使用识别器进行面部识别
我们在上一节中设置的识别器现在可用于识别面部。它会给我们信心和标签ID(识别器对这场比赛的信心程度)。如果面部匹配,继电器将打开。
首先我们加载包含字典的pickle文件。
with open(‘labels’, ‘rb’) as f:
dicti = pickle.load(f)
f.close()
然后我们加载将检测面部的分类器以及预测面部和训练数据的识别器。
faceCascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read(“trainer.yml”)
我们读取帧,将其转换为灰度,并在图像中查找面。如果有任何面部,我们将提取面部区域并使用识别器识别图像。
识别器将为我们提供标签ID和置信度。
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
for (x, y, w, h) in faces:
roiGray = gray[y:y+h, x:x+w]
roiColor = frame[y:y+h, x:x+w]
id_, conf = recognizer.predict(roiGray)
我们在字典中查找分配给此标签ID的名称。
for name, value in dict.items():
if value == id_:
print(name)
然后检查我们是否有足够的信心打开门锁。如果信心小于70,门将打开。否则,它将保持关闭状态。
if conf 《= 70:
GPIO.output(relay_pin, 1)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, name + str(conf), (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)
else:
GPIO.output(relay_pin, 0)
在原始图像中创建一个矩形,并将该名称写在矩形的顶部。
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, name, (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)
随着代码的完成,设置已准备好进行面部识别!
训练识别器的完整代码
import cv2
from picamera.array import PiRGBArray
from picamera import PiCamera
import numpy as np
import pickle
import RPi.GPIO as GPIO
from time import sleep
relay_pin = [26]
GPIO.setmode(GPIO.BCM)
GPIO.setup(relay_pin, GPIO.OUT)
GPIO.output(relay_pin, 0)
with open(‘labels’, ‘rb’) as f:
dicti = pickle.load(f)
f.close()
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 30
rawCapture = PiRGBArray(camera, size=(640, 480))
faceCascade = cv2.CascadeClassifier(“haarcascade_frontalface_default.xml”)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read(“trainer.yml”)
font = cv2.FONT_HERSHEY_SIMPLEX
for frame in camera.capture_continuous(rawCapture, format=“bgr”, use_video_port=True):
frame = frame.array
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor = 1.5, minNeighbors = 5)
for (x, y, w, h) in faces:
roiGray = gray[y:y+h, x:x+w]
id_, conf = recognizer.predict(roiGray)
for name, value in dict.items():
if value == id_:
print(name)
if conf 《= 70:
GPIO.output(relay_pin, 1)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, name + str(conf), (x, y), font, 2, (0, 0 ,255), 2,cv2.LINE_AA)
else:
GPIO.output(relay_pin, 0)
cv2.imshow(‘frame’, frame)
key = cv2.waitKey(1)
rawCapture.truncate(0)
if key == 27:
break
cv2.destroyAllWindows()
最后的想法
这种人脸识别系统不是100%准确,但在良好的光线下效果很好条件。问题是它还可以检测图片中的面孔 - 例如,有人可以通过手机显示您的图片来解锁它。我们可以通过训练自己的级联分类器来改进这一点。
-
面部识别
+关注
关注
1文章
375浏览量
26623 -
树莓派
+关注
关注
116文章
1699浏览量
105548
发布评论请先 登录
相关推荐
评论