【人工智能学习之人脸识别】

【人工智能学习之人脸识别】

人脸识别思路

因为人脸数据很不好找,训练也比较麻烦,所以我们可以直接使用现成的模型权重。

思路:人脸检测->特征提取->注册/识别

人脸检测可以选择又快又小效果还很棒的DBface克隆链接:DBFace

特征提取我选择的DeepFace中的Facenet512,读者也可以换成其它的模型或者自己训练的模型都可以,这里只做特征提取,所以我直接使用训练好的DeepFace中的Facenet512

有了识别网络和特征网络之后就只剩下后处理部分,我们可以设置待检测区域,只有单张人脸进入该区域,识别或注册程序才会启动。注册需要采集上下左右转头的人脸图像,登录则需要将检测区的人脸与特征库进行比对(比对方法可以采取欧氏距离或者余弦相似度)。

人脸识别代码与讲解

因为借用别人以及训练好的模型,直接导入DBFace和DeepFace使用即可。

不过首先你必须明白DBFace侦测网络返回的各个结果是什么意思,温馨提示:可以查看DBface的common.py

common.py中的drawbbox中对bbox的使用有很强的参考性,它会让你明白返回的x, y, r, b分别是什么,以及你如何按照你的想法调整。

python 复制代码
def drawbbox(image, bbox, color=None, thickness=2, textcolor=(0, 0, 0), landmarkcolor=(0, 0, 255)):

    if color is None:
        color = randcolor(bbox.label)

    #text = f"{bbox.label} {bbox.score:.2f}"
    text = f"{bbox.score:.2f}"
    x, y, r, b = intv(bbox.box)
    w = r - x + 1
    h = b - y + 1

    cv2.rectangle(image, (x, y, r-x+1, b-y+1), color, thickness, 16)

    border = thickness / 2
    pos = (x + 3, y - 5)
    cv2.rectangle(image, intv(x - border, y - 21, w + thickness, 21), color, -1, 16)
    cv2.putText(image, text, pos, 0, 0.5, textcolor, 1, 16)

    if bbox.haslandmark:
        for i in range(len(bbox.landmark)):
            x, y = bbox.landmark[i][:2]
            cv2.circle(image, intv(x, y), 3, landmarkcolor, -1, 16)

以及DeepFace中特征部分存储在哪一个字段(温馨提示:img_representation[0]['embedding'])。

我的代码:

python 复制代码
import main
import common
import numpy as np
import torch
import torch.nn.functional as F
import torch.nn as nn
from PIL import Image
from torchvision import transforms
import cv2
from model.DBFace import DBFace
from deepface import DeepFace
import ast
import math

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((160, 160), antialias=True),
])
FEATURE_TXT_PATH = 'datas/feature.txt'
models = ["VGG-Face", "Facenet", "Facenet512", "OpenFace", "DeepFace", "DeepID", "ArcFace", "Dlib"]
# 候选区域
Center = [320,240]
x1 = 160
y1 = 80
x2 = 480
y2 = 400

def preprocess_image(image_path):
    image = Image.open(image_path).convert('RGB')
    image = transform(image)
    return image.unsqueeze(0)


class Face_recognition(nn.Module):
    def __init__(self):
        super().__init__()
        self.DeepFace = DeepFace.build_model(model_name="Facenet512")
        self.DBFace = DBFace().eval().cuda()
        self.DBFace.load("model/dbface.pth")
        try:
            self.features = self.txt_read()
        except:
            print('没有找到特征文件!')

    def verify_faces(self, img, log_features):
        for log_feature in log_features:
            img_representation = DeepFace.represent(img, "Facenet512", enforce_detection=False)
            target_face_feature = img_representation[0]['embedding']
            feature1 = np.array(target_face_feature)
            feature2 = np.array(log_feature)
            # 计算 L2 距离
            distance = np.linalg.norm(feature1 - feature2)
            # 计算余弦相似度
            # cosine_similarity = np.dot(target_face_feature, log_feature) / (np.linalg.norm(target_face_feature) * np.linalg.norm(log_feature))
            if distance < 0.4:
                return True
            else:
                continue
        return False

    def face_log(self, img):
        img_representation = DeepFace.represent(img, "Facenet512", enforce_detection=False)
        face_feature = img_representation[0]['embedding']
        with open(FEATURE_TXT_PATH, 'a') as file:
            file.write(str(face_feature) + '\n')
            print('注册完成!')

    def txt_read(self):
        all_feature = []
        with open(FEATURE_TXT_PATH, 'r') as file:
            features = file.readlines()
            for feature in features:
                all_feature.append(ast.literal_eval(feature))
        return all_feature

    def face_detect_log(self):
        left = True
        right = True
        center = True
        up = True
        down = True
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        ok, frame = cap.read()
        img = frame.copy()
        while ok:
            objs = main.detect(self.DBFace, frame)
            for obj in objs:
                common.drawbbox(frame, obj)
                if len(objs) > 1:
                    print("识别到多张人脸,仅支持单人脸识别认证!")
                    cv2.putText(frame, 'Multiple faces detected, only single face recognition supported!', (x1-160, y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=[0, 0, 255])
                else:
                    for idx, land in enumerate(obj.landmark):
                        x = int(land[0])
                        y = int(land[1])
                        cv2.putText(frame, f'{idx}', (x, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=[0, 0, 255])

                    # 后处理
                    if self.set_head_in_rang(frame,obj):
                        creen = self.creen_face(img, obj)
                        if up:
                            cv2.putText(frame,'Please up',(x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=[255, 0, 0])
                            if self.turn_head_up(obj):
                                self.face_log(creen)
                                up = False

                        if down and up == False:
                            cv2.putText(frame, 'Please down', (x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=[255, 0, 0])
                            if self.turn_head_down(obj):
                                self.face_log(creen)
                                down = False

                        if left and up == False and down == False:
                            cv2.putText(frame, 'Please left', (x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                                        fontScale=1, color=[255, 0, 0])
                            if self.turn_head_left(obj):
                                self.face_log(creen)
                                left = False

                        if right and up == False and down == False and left == False:
                            cv2.putText(frame, 'Please right', (x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                                        fontScale=1, color=[255, 0, 0])
                            if self.turn_head_right(obj):
                                self.face_log(creen)
                                right = False

                        if center and up == False and down == False and left == False and right == False:
                            cv2.putText(frame, 'Please place the face in the center', (x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                                        fontScale=1, color=[255, 0, 0])
                            if self.turn_head_center(obj):
                                self.face_log(creen)
                                center = False

                    if up == False and down == False and left == False and right == False and center == False:
                        cv2.putText(frame, 'Registration completed!', (x1-80,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
                                    color=[0, 0, 255])
                        return True

            cv2.imshow("demo DBFace", frame)
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                break
            ok, frame = cap.read()
            img = frame.copy()

        cap.release()
        cv2.destroyAllWindows()

    def set_head_in_rang(self,image,bbox):
        top_left = (x1, y1)
        bottom_right = (x2, y2)

        x, y, r, b = bbox.box

        cv2.rectangle(image, top_left, bottom_right, color=(255, 0, 0), thickness=3)
        if y >= y1 and b <= y2 and x >= x1 and r <= x2:
            return True
        else:
            cv2.putText(image,'Please place the face in the detection area.',(x1-160,y1-40), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=[255, 0, 0])
        return False

    def turn_head_up(self, obj):
        if math.fabs(obj.landmark[2][1] - obj.box[1]) / obj.height < 0.35:
            print('人脸已上转')
            return True
        else:
            print('请向上转头')
            return False

    def turn_head_down(self, obj):
        if math.fabs(obj.landmark[2][1] - obj.box[1]) / obj.height > 0.65:
            print('人脸已下转')
            return True
        else:
            print('请向下转头')
            return False

    def turn_head_left(self, obj):
        if math.fabs(obj.landmark[2][0] - obj.box[0]) / obj.width < 0.35:
            print('人脸已左转')
            return True
        else:
            print('请向左转头')
            return False

    def turn_head_right(self, obj):
        if math.fabs(obj.landmark[2][0] - obj.box[2]) / obj.width < 0.35:
            print('人脸已右转')
            return True
        else:
            print('请向右转头')
            return False

    def turn_head_center(self, obj):
        if math.sqrt(math.pow(obj.landmark[2][0] - obj.center[0],2) + math.pow(obj.landmark[2][1] - obj.center[1],2)) < 5:
            print('人脸已转正')
            return True
        else:
            print('请转正人脸,并置于检测框中心')
            return False

    def face_detect_check(self):
        config = False
        cap = cv2.VideoCapture('datas/test4.jpg') # 摄像头:0
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        ok, frame = cap.read()
        img = frame.copy()
        while ok:
            objs = main.detect(self.DBFace, frame)
            for obj in objs:
                common.drawbbox(frame, obj)
                if len(objs) > 1:
                    print("识别到多张人脸,仅支持单人脸识别认证!")
                else:
                    creen = self.creen_face(img, obj)
                    for idx, land in enumerate(obj.landmark):
                        x = int(land[0])
                        y = int(land[1])
                        cv2.putText(frame, f'{idx}', (x, y), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
                                    color=[0, 0, 255])
                    if self.verify_faces(creen, self.features):
                        config = True
                        print('人脸识别成功!!!')
                    else:
                        print('人脸识别失败!')


            cv2.imshow("demo DBFace", frame)
            key = cv2.waitKey(0) & 0xFF
            if key == ord('q') or config:
                break
            ok, frame = cap.read()
            img = frame.copy()

        cap.release()
        cv2.destroyAllWindows()

    def creen_face(self, img, obj):
        x, y, r, b = obj.box
        creen = img[int(y):int(b + 1),int(x):int(r + 1), :]
        cv2.imshow('creen',creen)

        # 计算两眼之间的斜率
        dx = obj.landmark[1][0] - obj.landmark[0][0]
        dy = obj.landmark[1][1] - obj.landmark[0][1]
        angle = np.degrees(np.arctan2(dy, dx))
        # 假设 image 是你要摆正的人脸图像
        aligned_face = align_face(creen, angle)
        cv2.imshow('aligned_face',aligned_face)
        return creen

def align_face(image, angle):
    # 获取图像的尺寸
    (h, w) = image.shape[:2]
    # 创建旋转矩阵
    center = (w // 2, h // 2)
    M = cv2.getRotationMatrix2D(center, angle, 1.0)

    # 执行仿射变换
    aligned = cv2.warpAffine(image, M, (w, h),
                             flags=cv2.INTER_CUBIC,
                             borderMode=cv2.BORDER_CONSTANT,
                             borderValue=(0, 0, 0))

    # 创建一个正方形底图
    region_mask = np.zeros((h, h, 3), dtype=np.uint8)
    # 计算region图片在底图上的起始位置
    start_x = (h - w) // 2
    start_y = (h - h) // 2

    # 使用NumPy索引将region图片放置到底图上
    region_mask[start_y:start_y + h, start_x:start_x + w, :] = aligned

    return region_mask

def onnx_dbface(output_path="dbface.onnx"):
    # 实例化DBFace模型并加载权重
    face_detector = DBFace().eval()
    face_detector.load("model/dbface.pth")

    # 设置模型输入的动态轴,这里我们假设输入图片大小为640x640
    dynamic_axes = {'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}

    # 设置一个随机的或真实的输入张量,用于导出模型
    dummy_input = torch.randn(1, 3, 640, 640)

    # 导出模型到ONNX格式
    torch.onnx.export(face_detector,
                      dummy_input,
                      output_path,
                      verbose=False,
                      opset_version=11,
                      input_names=['input'],
                      output_names=['output'],
                      dynamic_axes=dynamic_axes)

if __name__ == "__main__":
    Face = Face_recognition()

    if Face.face_detect_log():
        print('注册完毕!!!')
    # if Face.face_detect_check():
    #     print('人脸识别成功!!!')



    # img = cv2.imread('datas/test4.jpg')
    # if Face.verify_faces(img, Face.features):
    #     print('人脸认证通过')
    # else:
    #     print('人脸认证未通过')

    # onnx_dbface()

效果图



相关推荐
拓端研究室1 小时前
【专题】2024年悦己生活消费洞察报告汇总PDF洞察(附原数据表)
人工智能
月眠老师1 小时前
拓展AI超级智能后的人类生活场景
人工智能·生活
是十一月末2 小时前
Opencv实现图片的边界填充和阈值处理
人工智能·python·opencv·计算机视觉
机智的叉烧2 小时前
前沿重器[57] | sigir24:大模型推荐系统的文本ID对齐学习
人工智能·学习·机器学习
凳子花❀2 小时前
强化学习与深度学习以及相关芯片之间的区别
人工智能·深度学习·神经网络·ai·强化学习
量子-Alex3 小时前
【多模态聚类】用于无标记视频自监督学习的多模态聚类网络
学习·音视频·聚类
吉大一菜鸡3 小时前
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
笔记·学习·fpga开发
泰迪智能科技014 小时前
高校深度学习视觉应用平台产品介绍
人工智能·深度学习
盛派网络小助手4 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
Eric.Lee20215 小时前
Paddle OCR 中英文检测识别 - python 实现
人工智能·opencv·计算机视觉·ocr检测