简介
本文将介绍一种基于LBPH(Local Binary Pattern Histograms)设计方法的人脸识别系统,使用了OpenCV库和深度学习模型的集成。LBPH方法以其对纹理特征的强大提取能力而在人脸识别领域备受青睐。通过本系统,我们不仅可以实时通过摄像头采集用户信息,还可以通过导入相片的方式来添加用户数据。
OpenCV(Open Source Computer Vision Library)是一个用于计算机视觉任务的开源库,它包含了丰富的函数和工具,可以用于图像处理、模式识别、物体检测等方面。它的跨平台性使得它在许多不同的操作系统上都能够运行,为我们提供了一个强大的工具箱来处理图像数据。
LBPH算法是一种基于局部二值模式(Local Binary Pattern)的人脸识别方法。它通过将像素点与周围像素点进行比较,提取局部纹理特征,从而实现对人脸的准确识别。相较于其他方法,LBPH算法对于光照变化和部分遮挡具有较强的鲁棒性,因此在实际应用中得到了广泛的使用。
LBPH原理图
人脸识别一共分为四步,本文将按照以下步骤来进行讲解:
- 人脸采集
- 数据预处理
- 特征提取(模型训练)
- 匹配识别
实现流程
人脸采集
摄像头采集
首先,通过OpenCV的库打开电脑摄像头,并输入录入信息者的用户名。
# 调用笔记本内置摄像头,参数为0,如果有其他的摄像头可以调整参数为1,2
cap = cv2.VideoCapture(0)
# 调用人脸分类器,要根据实际路径调整3
face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml') # 输入自己haar模型所在地址
# sampleNum用来计数样本数目
count = 0
# 用户名
name = 'fen' # 输入你的用户名
随后,为新用户创建一个存放照片的文件夹和对应的ID。
# 创建新的用户文件夹和用户ID
def create_incremental_folder():
folder_name = 1
root_folder = "./data"
while True:
# 检查文件夹是否已存在
folder_path = os.path.join(root_folder, f"User{folder_name}")
if not os.path.exists(folder_path):
os.mkdir(folder_path)
break
else:
folder_name += 1
return folder_path, folder_name
# 为即将录入的脸标记一个id
folder_path, face_id = create_incremental_folder()
随后,打开摄像头,连续捕捉50张图像,并通过haar人脸识别器框选出人脸位置图像,截取后后保存到对应的文件夹内。
while True:
# 从摄像头读取图片
success, img = cap.read()
# 转为灰度图片,减少程序符合,提高识别度
if success is True:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
else:
break
# 检测人脸,将每一帧摄像头记录的数据带入OpenCv中,让Classifier判断人脸
# 其中gray为要检测的灰度图像,1.3为每次图像尺寸减小的比例,5为minNeighbors
faces = face_detector.detectMultiScale(gray, 1.3, 5)
# 框选人脸,for循环保证一个能检测的实时动态视频流
for (x, y, w, h) in faces:
# xy为左上角的坐标,w为宽,h为高,用rectangle为人脸标记画框
cv2.rectangle(img, (x, y), (x + w, y + w), (255, 0, 0))
# 成功框选则样本数增加
count += 1
print(count)
# 保存图像,把灰度图片看成二维数组来检测人脸区域
# (这里是建立了data的文件夹,当然也可以设置为其他路径或者调用数据库)
cv2.imwrite("{}/User.".format(folder_path) + str(face_id) + '.' + str(count) + '.jpg', gray[y:y + h, x:x + w])
# 显示图片
cv2.imshow('image', img)
# 保持画面的连续。waitkey方法可以绑定按键保证画面的收放,通过q键退出摄像
k = cv2.waitKey(1)
if k == '27':
break
# 或者得到800个样本后退出摄像,这里可以根据实际情况修改数据量,实际测试后800张的效果是比较理想的
elif count >= 50:
break
图片存储完成后,将用户ID和对应的用户名写入到csv文件中。
def csv_take(face_id, name):
csv_file = './csv/user.csv' # CSV 文件路径
input_data = [['{}'.format(face_id), "{}".format(name)]] # 输入数据,示例为编号为1的数据
# 读取 CSV 文件中已有的数据
existing_data = []
with open(csv_file, 'r') as file:
reader = cs.reader(file)
for row in reader:
existing_data.append(row)
# 根据编号找到对应的行索引
row_index = -1
for i, row in enumerate(existing_data):
if i + 1 == int(input_data[0][0]):
row_index = i
break
# 如果找到对应的行索引,则覆盖数据
if row_index != -1:
existing_data[row_index] = input_data[0]
else:
existing_data.append(input_data[0])
# 写入更新后的数据到 CSV 文件
with open(csv_file, 'w', newline='') as file:
writer = cs.writer(file)
writer.writerows(existing_data)
print("数据已成功写入到 CSV 文件!")
照片导入
该方法与摄像头导入类似,首先选择要导入的图片,其次输入用户名。
# 为即将录入的脸标记一个id
folder_path, face_id = create_incremental_folder()
# 指定原始图像路径和目标文件夹路径
image_path ='./image/3.jpg' # 原始图像路径
output_folder = '{}'.format(folder_path) # 目标文件夹路径
name = 'You'
face_detector = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml') # 待更改
count = 0
将图片进行灰度化处理后再截取图像,然后再把一张图片复制到20张。
# 扩充数据集到 20 张图像
for i in range(19):
augmented_image = random_rotate_image(img_tem)
augmented_image = flip_image(augmented_image)
augmented_image = adjust_contrast(augmented_image)
augmented_image = random_scale_image(augmented_image)
count += 1
# 保存增强后的图像到目标文件夹
cv2.imwrite("{}/User.".format(folder_path) + str(face_id) + '.' + '1' + '.' + '.jpg', img_tem)
cv2.imwrite("{}/User.".format(folder_path) + str(face_id) + '.' + '{}'.format(count+1) + '.' + '.jpg', augmented_image)
然后再对20张图片进行随机的数据增强,经过处理后,就能得到同一个人20张不一样的照片,可以提高模型的拟合度,识别的时候效果更好。随后,将用户ID和用户名存储到CSV文件中,代码同上。至此,数据采集就做完了。
数据预处理
数据增强
其中数据增强包含了四种方法,分别为:
- 随机旋转图像
- 50%概率触发水平翻转图像
- 调整图像对比度
- 随机缩放图像
# 数据增强方法1:随机旋转图像
def random_rotate_image(image):
angle = random.randint(0, 360) # 随机生成旋转角度
rows, cols = image.shape
rotation_matrix = cv2.getRotationMatrix2D((cols/2, rows/2), angle, 1)
rotated_image = cv2.warpAffine(image, rotation_matrix, (cols, rows))
return rotated_image
# 数据增强方法2:50% 概率触发水平翻转图像
def flip_image(image):
if random.random() < 0.5: # 50% 的概率触发水平翻转
flipped_image = cv2.flip(image, 1)
else:
flipped_image = image
return flipped_image
# 数据增强方法3:调整图像对比度
def adjust_contrast(image):
alpha = random.uniform(0.5, 1.5) # 随机生成对比度调整系数
adjusted_image = cv2.convertScaleAbs(image, alpha=alpha)
return adjusted_image
# 数据增强方法4:随机缩放图像
def random_scale_image(image):
scale_factor = random.uniform(0.5, 1.5) # 随机生成缩放比例
rows, cols = image.shape
scaled_image = cv2.resize(image, (int(cols*scale_factor), int(rows*scale_factor)))
return scaled_image
转换数据格式
为了能让机器能够处理数据,需要分别将图片和ID处理成一个列表。
# 创建一个函数,用于从数据集文件夹中获取训练图片,并获取id
# 注意图片的命名格式为User.id.sampleNum
def get_images_and_labels(path):
image_paths = [os.path.join(path, f) for f in os.listdir(path)]
# 新建连个list用于存放
face_samples = []
ids = []
# 遍历图片路径,导入图片和id添加到list中
for image_path in image_paths:
# 通过图片路径将其转换为灰度图片
img = Image.open(image_path).convert('L')
# 将图片转化为数组
img_np = np.array(img, 'uint8')
if os.path.split(image_path)[-1].split(".")[-1] != 'jpg':
continue
# 为了获取id,将图片和路径分裂并获取
id = int(os.path.split(image_path)[-1].split(".")[1])
faces = detector.detectMultiScale(img_np)
# 将获取的图片和id添加到list中
for (x, y, w, h) in faces:
face_samples.append(img_np[y:y + h, x:x + w])
ids.append(id)
return face_samples, ids
特征提取(模型训练)
由于OpenCV提供了已经封装好的函数,我们直接调用LBPH算法的函数即可,通过将特征值和标签值输入模型之后便可以开始训练了,训练完后的模型会保存在weigths文件夹中。
# 初始化识别的方法
recog = cv2.face.LBPHFaceRecognizer_create()
# 获取图片和ID的列表
faces, ids = get_images_and_labels(path)
# 训练模型
recog.train(faces, np.array(ids))
# 保存模型
recog.save('./weights/model.yml'.format())
匹配识别
再识别界面,我们首先需要加载我们的标签文件,就是一开始说的那个csv文件,这个文件里存放了每个ID对应的用户名,由于模型再识别到人脸后不会直接返回用户名,只会返回一个ID编号,所以我们要通过这个ID来找到对应的用户名并将它写在界面上。
# 读取 CSV 文件中指定列的数据
with open(csv_file, 'r') as file:
reader = cs.reader(file)
for row in reader:
if len(row) > column_index:
data_array.append(row[column_index])
# 设置好与ID号码对应的用户名,如下,如0对应的就是初始
new_element = 'None' # 要添加的新元素
data_array.insert(0, new_element) # 在数组第一位插入新元素
names = data_array
运行程序会打开电脑的摄像头,然后对人脸进行识别,识别到人脸之后会输出用户数据并且将数据写在识别框旁边,代码如下:
while True:
ret, img = cam.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 识别人脸
faces = face_cascade.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)
idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w])
# 计算出一个检验结果
if confidence < 100:
idum = names[idnum]
confidence = "{0}%".format(round(confidence))
else:
idum = "unknown"
confidence = "{0}%".format(round(100 - confidence))
# 输出检验结果以及用户名
cv2img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同
pilimg = Image.fromarray(cv2img)
draw = ImageDraw.Draw(pilimg) # 图片上打印
font = ImageFont.truetype("./simhei.ttf", 30, encoding="utf-8") # 参数1:字体文件路径,参数2:字体大小
draw.text((x + 5, y - 30), str(idum), (255, 0, 0), font=font)
draw.text((x + 5, y + h - 30), str(confidence), (255, 0, 0), font=font)
img = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR) # PIL图片转cv2 图片
# 展示结果
cv2.imshow('camera', img)
k = cv2.waitKey(int(1000 / fps))
if k == 27:
break
也可以在此处输入视频的地址,对视频进行实时人脸识别。
# 打开视频文件或者使用摄像头
video = 0
# video = './video/test1.mp4'
效果实测(别骂了别骂了,由于博主使用电脑摄像头对着手机拍的,所以很糊Q_Q)
运行环境
以下是我的python版本以及所用的主要库的版本,文件里会有我用到的所有库,可以直接安装。
python==3.9.13
opencv-contrib-python==4.7.0.72
opencv-python==4.7.0.72
numpy==1.22.0
Pillow==9.5.0
资料链接
百度网盘链接: https://pan.baidu.com/s/1FRTg95SRnOuL2NFnaPlWfA?pwd=brxf 提取码: brxf
感谢您阅读本文,如果对您有帮助希望能点个赞点个收藏,期待在未来的技术发展中与您共同前行!