一.前言

1.本项目是基于face_recognition(底层为dlib机器学习库)开发的,需要具备一定的python基础,对使用到的相关库内置函数有简单的了解(无需深度了解),本项目内容仅为个人观点。(各位可在评论区表达自己看法,并指出错误,谢谢!

2.需要了解face_recognition,可去face_recognition官网,https://face-recognition.readthedocs.io/en/latest/modules.html

二.项目介绍

     这个是基于face_recognition(封装了dlib)实现的人脸识别项目,通过对已知人脸图片和未知人脸图片进行编码,把人脸转化为 128 维数字特征向量,不对比图片像素,只对比人脸特征的相似度,相似度达标就判定为同一个人,最终识别出人脸图片。

三.核心思路

1.构建已知人脸特征库

       遍历存放已知照片的文件夹,对每张照片:检测人脸 → 生成 128 维人脸特征 → 把特征和姓名 一一对应保存。

 2.处理待识别图片

         读取要识别的测试图,转换色彩空间,定位图中所有人脸的坐标位置,根据检测到的人脸位置,给测试图里的每张人脸,同样生成128 维特征向量。

3.特征匹配 + 结果标注

       拿测试人脸特征和人脸库里特征逐一比对,相似度(欧式距离)小于设定阈值(0.5)→匹配成功,对应人名都不匹配 → 标记为 unknown,最后给人脸画框、标注姓名,展示结果。

四.具体步骤(含代码)

1.项目目录(pycharm为例)

    大家可以按照我的格式来,也可以按照自己的。knowns_faces用于装已知人脸照片,图片命名为‘名字’+‘拓展名’,unknown——faces用于未知人脸照片,图片命名可以‘随意’+‘拓展名’,如下:

2.环境配置(强烈建议使用Anaconda或者miniconda去配置环境)

      python3.9及以上都可以,如果有Anaconda或者miniconda,去对应环境依次执行,conda install -c conda-forge dlib,pip install face_recognition opencv-python,库版本自行决定,建议使用清华源https://pypi.tuna.tsinghua.edu.cn/simple

3.导入相关库

import cv2                          #OpenCV,用于图像读取、显示、画框等
import face_recognition             #人脸识别库,封装了dlib的检测和编码功能
import os                           #操作系统接口,拼接文件路径和遍历文件夹
import resize as rs                 #调整显示图片大小
import read_chinese as rc           #中文路径读取图片
import Draw_chinese_text as Dr      #显示中文在图片上

     cv2,face_recognition,os库是配置的,resize,read_chinese,Draw_chinese_text是自己写的。

resize:

import cv2
def resize_display(img, max_width=800, max_height=600):
    h, w = img.shape[:2]
    scale = min(max_width / w, max_height / h, 1.0)  # 仅缩小,不放大
    if scale < 1.0:
        new_w = int(w * scale)
        new_h = int(h * scale)
        img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
    return img

     这个是用于控制输出图片大小的。

read_chinese:

import os
import cv2
import numpy as  np
def imread_chinese(path):
    if not os.path.exists(path):
        print(f"文件不存在: {path}")
        return None
    with open(path, 'rb') as f:
        data = np.frombuffer(f.read(), np.uint8)
    return cv2.imdecode(data, cv2.IMREAD_COLOR)

     这个可以读取含中文的文件路径。

Draw_chinese_text:

from PIL import ImageFont, ImageDraw, Image
import numpy as np
import cv2

def draw_chinese_text(img, text, position, font_path, font_size=30, color=(0, 255, 0)):
    
    #将OpenCV的BGR转为PillowRGB
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)
    font = ImageFont.truetype(font_path, font_size)
    #Pillow使用(R,G,B)颜色,所以要把BGR倒过来
    draw.text(position, text, font=font, fill=(color[2], color[1], color[0]))
    #转回OpenCV的BGR格式
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

     这个是用来在图片上显示中文的,如果直接显示会乱码。

4.对已知人脸照片进行处理

#===================对已知人脸照片进行处理==================
#存储每个人脸的128维特征向量
known_face_encodings = []

#存储对应的姓名
known_face_names = []
    known_face_encodings是用来储存已知人脸128维特征向量的,用于后续与未知人脸128维特征向量进行比对的,known_face_names则是用来储存对应姓名的,用于匹配成功之后所用的人名。
#已知人脸照片的存放文件夹
known_faces_dir = r"known_faces"
#遍历文件夹内所有文件
for filename in os.listdir(known_faces_dir):
    #常见图片格式
    if filename.lower().endswith(('.jpg', '.png')):
        #拼接完整路径
        img_path = os.path.join(known_faces_dir, filename)
        #face_recognition加载图片
        img = face_recognition.load_image_file(img_path)
        #提取每张图里的人脸编码
        encodings = face_recognition.face_encodings(img)
        #检测到了人脸
        if encodings:
            #保存脸的编码
            known_face_encodings.append(encodings[0])
            #文件名去掉扩展名后作为人名
            known_face_names.append(os.path.splitext(filename)[0])
    os.listdir(known_faces_dir)列出 known_faces_dir这个文件夹里的所有文件和子文件夹名称,返回一个列表,通过for循环去遍历这个列表,endswith(('.jpg', '.png'))则是用来检查是否以jpg.png结尾的,因为filename路径并不完整,因此需要用join拼接一下路径,之后需要加载图片,并且给人脸生成128维特征向量,如果存在人脸,我们就储存人脸的特征向量到known_face_encodings,之所以是encodings[0],是因为face_recognition.face_encodings(img)会对传入的图片进行人脸检测(返回值是列表),然后为检测到的每一张脸生成一个128维的特征向量,也就是说,当一张图片只有一个人脸的时候,就会只有一个人脸编码,当一张图片有两个人的时候就会有两个人的人脸编码,我提供的每张图片只有一张人脸,因此也就只有一个128维的特征向量,因此encodings这个列表只有一个特征向量,之后用os.path.splitext()[0]获取人名。

5.对未知人脸照片进行处理

#===================对未知人脸照片进行处理==================
#测试图片路径
test_path = r"unknown_faces/彭于晏.png"

#调用自定义函数读取图片
test_img = rc.imread_chinese(test_path)

#当图片读取失败
if test_img is None:
    print("无法读取测试图片,程序退出")
    exit()

       这一段主要是用来读取图片的。

#将BGR转换为RGB(face_recognition需要RGB)
test_img1 = cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB)

      颜色转换。

#定位所有人脸的位置(返回列表(top, right, bottom, left))
face_locations = face_recognition.face_locations(test_img1)

#根据位置提取每张人脸的128维特征编码
face_encodings = face_recognition.face_encodings(test_img1, face_locations)

     用来对未知人脸生成128维特征向量,之所以要定位所有人脸位置,是因为一张图片,可能不止有一张人脸,有人可能不太理解列表(top, right, bottom, left),这里给大家展示一下:

6.已知与未知进行对比

#将每张人脸的位置和编码打包,逐个处理
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
    #与已知人脸库比对,返回布尔值列表,表示与每个已知人的相似情况
    matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance=0.5)
    #默认标为“未知”
    name = "Unknown"
    #当匹配成功
    if True in matches:
        #找到第一个匹配的索引
        first_match_index = matches.index(True)
        #取出对应的人名
        name = known_face_names[first_match_index]
    zip(face_locations, face_encodings)把位置信息和特征编码“配对”起来,然后逐个人脸进行处理face_recognition.compare_faces则是用于未知和已知进行对比,当小于阈值时,就是说明相似度高,之所以要小于,是因为相似度比对,实际上就是计算欧式距离,距离自然是越近越好,越近相似度越高,之后通过索引去找寻名字。

7.画框和显示名字

 #画框
    cv2.rectangle(test_img, (left, top), (right, bottom), (0, 255, 0), 2)

    #显示名字
    font_path = "C:/Windows/Fonts/msyh.ttc"  # 微软雅黑,确保路径正确
    test_img = Dr.draw_chinese_text(test_img, name, (left, top - 30), font_path, font_size=25, color=(0, 255, 0))

     在人脸图片上,画框和显示名字,font_path是字体的路径。

8.剩余操作

#调整图片尺寸
test_img = rs.resize_display(test_img, max_width=800, max_height=600)
#显示图片
cv2.imshow('Face Recognition', test_img)
# 等待用户按下任意键,退出
cv2.waitKey(0)
#销毁窗口
cv2.destroyAllWindows()

修改图片大小,之后显示图片,用户任意按键则退出,最后再销毁窗口,释放资源。

五.效果展示

单人:

多人:

总体效果还是不错的。

六.完整代码

1.主程序

import cv2                          #OpenCV,用于图像读取、显示、画框等
import face_recognition             #人脸识别库,封装了dlib的检测和编码功能
import os                           #操作系统接口,拼接文件路径和遍历文件夹
import resize as rs                 #调整显示图片大小
import read_chinese as rc           #中文路径读取图片
import Draw_chinese_text as Dr      #显示中文在图片上

#===================对已知人脸照片进行处理==================
#存储每个人脸的128维特征向量
known_face_encodings = []

#存储对应的姓名
known_face_names = []

#已知人脸照片的存放文件夹
known_faces_dir = r"D:\学习\人脸识别\known_faces"
#遍历文件夹内所有文件
for filename in os.listdir(known_faces_dir):
    #常见图片格式
    if filename.lower().endswith(('.jpg', '.png')):
        #拼接完整路径
        img_path = os.path.join(known_faces_dir, filename)
        #face_recognition加载图片
        img = face_recognition.load_image_file(img_path)
        #提取每张图里的人脸编码
        encodings = face_recognition.face_encodings(img)
        #检测到了人脸
        if encodings:
            #保存脸的编码
            known_face_encodings.append(encodings[0])
            #文件名去掉扩展名后作为人名
            known_face_names.append(os.path.splitext(filename)[0])


#输出已录入的所有人名,方便核对
print("已录入:", known_face_names)

#===================对未知人脸照片进行处理==================
#测试图片路径
test_path = r"unknown_faces/周杰伦和邓紫棋.png"

#调用自定义函数读取图片
test_img = rc.imread_chinese(test_path)

#当图片读取失败
if test_img is None:
    print("无法读取测试图片,程序退出")
    exit()

#将BGR转换为RGB(face_recognition需要RGB)
test_img1 = cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB)

#定位所有人脸的位置(返回列表(top, right, bottom, left))
face_locations = face_recognition.face_locations(test_img1)

#根据位置提取每张人脸的128维特征编码
face_encodings = face_recognition.face_encodings(test_img1, face_locations)

#将每张人脸的位置和编码打包,逐个处理
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
    #与已知人脸库比对,返回布尔值列表,表示与每个已知人的相似情况
    matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance=0.5)
    #默认标为“未知”
    name = "Unknown"
    #当匹配成功
    if True in matches:
        #找到第一个匹配的索引
        first_match_index = matches.index(True)
        #取出对应的人名
        name = known_face_names[first_match_index]

    #画框
    cv2.rectangle(test_img, (left, top), (right, bottom), (0, 255, 0), 2)

    #显示名字
    font_path = "C:/Windows/Fonts/msyh.ttc"  # 微软雅黑,确保路径正确
    test_img = Dr.draw_chinese_text(test_img, name, (left, top - 30), font_path, font_size=25, color=(0, 255, 0))


#调整图片尺寸
test_img = rs.resize_display(test_img, max_width=800, max_height=600)
#显示图片
cv2.imshow('Face Recognition', test_img)
# 等待用户按下任意键,退出
cv2.waitKey(0)
#销毁窗口
cv2.destroyAllWindows()

2.resize

import cv2
def resize_display(img, max_width=800, max_height=600):
    h, w = img.shape[:2]
    scale = min(max_width / w, max_height / h, 1.0)  # 仅缩小,不放大
    if scale < 1.0:
        new_w = int(w * scale)
        new_h = int(h * scale)
        img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
    return img

3.read_chinese

import os
import cv2
import numpy as  np
def imread_chinese(path):
    if not os.path.exists(path):
        print(f"文件不存在: {path}")
        return None
    with open(path, 'rb') as f:
        data = np.frombuffer(f.read(), np.uint8)
    return cv2.imdecode(data, cv2.IMREAD_COLOR)

4.Draw_chinese_text

from PIL import ImageFont, ImageDraw, Image
import numpy as np
import cv2

def draw_chinese_text(img, text, position, font_path, font_size=30, color=(0, 255, 0)):
  
    #将OpenCV的BGR转为PillowRGB
    img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)
    font = ImageFont.truetype(font_path, font_size)
    #Pillow使用(R,G,B)颜色,所以要把BGR倒过来
    draw.text(position, text, font=font, fill=(color[2], color[1], color[0]))
    #转回OpenCV的BGR格式
    return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

七.补充

本项目代码中的参数,需根据具体情况进行调整,代码内容仅供参考。需要图片的,可以私聊

,或者去网上也可以找到。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐