快速实现你自己的人脸识别应用

67次阅读

[toc]

快速实现你自己的人脸识别应用

前言

本文代码基于python3+编写,参考face_recognition模块github实时识别的实例。

github链接在此

face_recongnition是在github上标星几十k的开源项目,能够以优秀的速度,简洁的命令行输入,快速获取你想要获得的人脸信息。

比如:识别图片中人脸的位置,通过对比提供的命令行参数指定的人脸图片信息,来以较高的精度识别出来,博主大三上学期的C#结课大作业就是通过C#做的管理界面来管理提供给Python脚本的图人脸信息,并进行识别标记来达到课堂快速签到的目的。

另外,face_recongnition还提供了几个函数,能够标记处人脸的关键位置,比如鼻子轮廓,眉毛,嘴唇,人脸轮廓等,(可以通过这个功能来实现实时妆容体验,当然恶搞也是可以的,博主暂时没试过)。

话不多说,先贴代码

安装使用的模块

代码中使用到的库都可以通过pip install module_name的方式来安装,简单编辑

pip install face_recognition

pip install opencv

pip install numpy

-------命令仅为示例,具体能否运行参考当前各包的安装方法,不过一般都是这个

代码

# -*- coding: UTF-8 -*-
from xml.dom.minidom import parse
import xml.dom.minidom

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

# 保存编码数据
known_face_encodings = []
known_face_names = []

# 编码读取到的图片
def FaceEncode(StuInfo, StuInfoSrc):
    for src in StuInfoSrc:
        my_image = face_recognition.load_image_file("E://pythonXML" + src[1:])
        my_face_encoding = face_recognition.face_encodings(my_image)[0]
        known_face_encodings.append(my_face_encoding)
    for name in StuInfo:
        known_face_names.append(name)

if __name__ == '__main__':
    # 加载配置文件
    DOMTree = parse("E://pythonXML//src//info.xml")
    # 获得元素树
    collection = DOMTree.documentElement
    # 根据person标签获得对应的元素组合
    body = collection.getElementsByTagName("person")
    StuInfo = []
    StuInImgSrc = []
    StuDic = []
    for stu in body:
        stu.setAttribute('here', '0')
        name = stu.getElementsByTagName("name")[0]
        # print("name: %s" % name.childNodes[0].data)
        src = stu.getElementsByTagName("imgsrc")[0]
        # print("imgsrc: %s" % src.childNodes[0].data)
        StuInfo.append(name.childNodes[0].data)
        # 将学生信息添加到一个字典列表中集中管理签到信息
        StuDic.append({'name': name.childNodes[0].data, 'IsHere': 0})
        StuInImgSrc.append(src.childNodes[0].data)
    # 显示读取信息,用于判断是否争取读取文件
    # print(StuInfo)
    # print(StuInImgSrc)
    # print(StuDic)
    # 打开摄像头,开始获得数据信息
    video_capture = cv2.VideoCapture(0)
    # 编码数据信息用于识别
    FaceEncode(StuInfo, StuInImgSrc)
    face_locations = []
    face_encodings = []
    face_names = []
    process_this_frame = True
    cnt = 0
    while True:
        # 读取一帧
        ret, frame = video_capture.read()

        # 将原帧信息缩小到四分之一便于处理
        small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)

        # BGR转换成RGB
        rgb_small_frame = small_frame[:, :, ::-1]

        # 只处理需要处理的帧信息
        if process_this_frame:
            # 显示出当前帧中所有识别到的人脸
            face_locations = face_recognition.face_locations(rgb_small_frame)
            face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)

            face_names = []
            for face_encoding in face_encodings:
                # 查看是否有数据库中的人脸信息
                matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
                name = "Unknown"

                face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
                best_match_index = np.argmin(face_distances)
                if matches[best_match_index]:
                    name = known_face_names[best_match_index]

                face_names.append(name)

        process_this_frame = not process_this_frame
        cv2charimg = 0
        # 绘制结果
        for (top, right, bottom, left), name in zip(face_locations, face_names):
            # 探测到人脸的区域是原图像的1/4,所以这里要还原出原大小
            top *= 4
            right *= 4
            bottom *= 4
            left *= 4
            index = 0
            while index < len(StuDic):
                stu = StuDic[index]['name']
                Ishere = StuDic[index]['IsHere']
                if stu == name and Ishere == 0:
                    print("%s 签到!" % name)
                    StuDic[index]['IsHere'] = 1
                index += 1
            # 围绕目标区域画选框
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 120, 255), 2)

            # 绘制标签 标注名字
            cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 130, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)

        # 显示区域
        cv2.imshow("video", frame)

        # Q:退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    print(StuDic)
    index2 = 0

    for stus in body:
        print(stus)
        while index2 < len(StuDic):
            stu = StuDic[index2]['name']
            Ishere = StuDic[index2]['IsHere']
            print(stu, Ishere)
            index2 += 1
            if Ishere == 1:
                stus.setAttribute('here', '1')
                break

    try:
        with open('E:/pythonXML/src/info.xml', 'w', encoding='UTF-8') as fh:
            # 4.writexml()第一个参数是目标文件对象,第二个参数是根节点的缩进格式,第三个参数是其他子节点的缩进格式,
            # 第四个参数制定了换行格式,第五个参数制定了xml内容的编码。
            DOMTree.writexml(fh, indent='', encoding='UTF-8')
            print('OK')
    except Exception as err:
        print('错误:{err}'.format(err=err))
    # 释放摄像头资源
    video_capture.release()
    cv2.destroyAllWindows()

对比原版代码

原版代码示例在此

import face_recognition
import cv2
import numpy as np

#...省略了原版注释...#

video_capture = cv2.VideoCapture(0)

# Load a sample picture and learn how to recognize it.
obama_image = face_recognition.load_image_file("obama.jpg")
obama_face_encoding = face_recognition.face_encodings(obama_image)[0]

# Load a second sample picture and learn how to recognize it.
biden_image = face_recognition.load_image_file("biden.jpg")
biden_face_encoding = face_recognition.face_encodings(biden_image)[0]

# Create arrays of known face encodings and their names
known_face_encodings = [
    obama_face_encoding,
    biden_face_encoding
]
known_face_names = [
    "Barack Obama",
    "Joe Biden"
]

# Initialize some variables
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True

while True:
    # Grab a single frame of video
    ret, frame = video_capture.read()

    # Resize frame of video to 1/4 size for faster face recognition processing
    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)

    # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
    rgb_small_frame = small_frame[:, :, ::-1]

    # Only process every other frame of video to save time
    if process_this_frame:
        # Find all the faces and face encodings in the current frame of video
        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)

        face_names = []
        for face_encoding in face_encodings:
            # See if the face is a match for the known face(s)
            matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
            name = "Unknown"

            # # If a match was found in known_face_encodings, just use the first one.
            # if True in matches:
            #     first_match_index = matches.index(True)
            #     name = known_face_names[first_match_index]

            # Or instead, use the known face with the smallest distance to the new face
            face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
            best_match_index = np.argmin(face_distances)
            if matches[best_match_index]:
                name = known_face_names[best_match_index]

            face_names.append(name)

    process_this_frame = not process_this_frame

    # Display the results
    for (top, right, bottom, left), name in zip(face_locations, face_names):
        # Scale back up face locations since the frame we detected in was scaled to 1/4 size
        top *= 4
        right *= 4
        bottom *= 4
        left *= 4

        # Draw a box around the face
        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)

        # Draw a label with a name below the face
        cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)

    # Display the resulting image
    cv2.imshow('Video', frame)

    # Hit 'q' on the keyboard to quit!
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release handle to the webcam
video_capture.release()
cv2.destroyAllWindows()
  1. 通过不修改脚本代码的方式来动态添加删除信息

    • 原版示例通过

      obama_image = face_recognition.load_image_file("obama.jpg")
      obama_face_encoding = face_recognition.face_encodings(obama_image)[0]
      
      biden_image = face_recognition.load_image_file("biden.jpg")
      biden_face_encoding = face_recognition.face_encodings(biden_image)[0]

      来添加需要识别的人像图片路径,以及人像名字,在需要动态添删信息的时候必然不能直接去修改源代码脚本,所以我们需要将图片信息单独分出来一个文件,因此博主选择了xml,在这

      可以了解一下xml,XML 指可扩展标记语言(eXtensible Markup Language), 被设计用来传输和存储数据,重要且简单易学,xml里面除部分保留标签外,用户完全可以自定义自己的标签,让用户更加灵活的使用并传输数据。

    • 自定义xml文件

      
      
       张三
       .//img//demo.jpg
       .2021 李四 .//img//diao.png 2022 王五 .//img//kai.jpg 2023 
      • Xml文件需要一个根标签,如<node></node>,<root></root>等等
      • 其余的标签都要写在根标签内,同类标签并列,这很容易理解。
      • python可以通过xml.dom来解析xml文件,在内存中,python将xml文件读取并解析成一个树状结构,提供了很多方法来读取或者创建写入方法,这可以参考xml.dom官方文档。
  2. 操作并读取xml文件信息,得到图片信息并进行编码

    DOMTree = parse("E://pythonXML//src//info.xml")
    # 获得元素树
    collection = DOMTree.documentElement
    # 根据person标签获得对应的元素组合
    body = collection.getElementsByTagName("person")

    这里的路径可以参考你自己的文件路径,博主这里使用的绝对路径,也可以使用相对路径。

    最后一步返回的是一个可以遍历的集合,这部分python知识读者自行查阅即可,可以参考菜鸟教程

  3. 对图片进行编码,传入识别函数开始分析并返回人脸位置信息

    这一部分参考原版示例代码,可以将编码部分重构成你自己的函数,方便调用

  4. 好了,我们开始愉快的识别吧

    博主这一部分代码参考原版代码并修改了一部分,在控制台实时输出当前识别到的人脸信息并对比xml文件提供的信息,来查看某人是否已经通过识别,读者可以参考注释自行修改。

    注意:这里的识别并不需要识别出人脸的运动轨迹,因此并不需要一帧一帧的来识别,只需要处理当前帧处理完瞬间输入的帧信息,以求达到较好的实时效果。

    一定要在结束之后释放摄像头资源!!!!

写在最后

​ 博主目前使用的代码启动速度以及结束之后释放资源十分缓慢,不排除是没有使用多线程来加速,也有可能是face_recognition模块在内存中加载了大量模型文件以及图片编码信息,这一部分还要多多研究,也欢迎与博主进行交流。

liubobo
版权声明:本站原创文章,由 liubobo2021-11-12发表,共计7600字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。