基于Django+Bootstrap+深度学习 构建商业级人脸识别系统(代码开源)

目录

一、概述

[1.1 人脸识别](#1.1 人脸识别)

[1.2 Django和Bootstrap](#1.2 Django和Bootstrap)

[1.3 本文任务流程](#1.3 本文任务流程)

二、开发

[2.1 环境安装](#2.1 环境安装)

[2.2 创建项目](#2.2 创建项目)

[2.2.1 创建Django项目](#2.2.1 创建Django项目)

[2.2.2 集成Bootstrap](#2.2.2 集成Bootstrap)

[2.3 开发人物管理模块](#2.3 开发人物管理模块)

[2.3.1 创建人物模型](#2.3.1 创建人物模型)

[2.3.2 在后台系统中添加人物](#2.3.2 在后台系统中添加人物)

[2.3.3 人物显示功能](#2.3.3 人物显示功能)

[2.3.4 人物添加功能](#2.3.4 人物添加功能)

[2.3.5 人物删除功能](#2.3.5 人物删除功能)

[2.3.6 人物编辑功能](#2.3.6 人物编辑功能)

[2.4 人脸检测](#2.4 人脸检测)

[2.4.1 安装和配置Dlib库](#2.4.1 安装和配置Dlib库)

[2.4.2 修改人物添加和人物编辑模块](#2.4.2 修改人物添加和人物编辑模块)

[2.5 特征提取](#2.5 特征提取)

[2.5.1 安装和配置fastdeploy库](#2.5.1 安装和配置fastdeploy库)

[2.5.2 修改人物添加和人物编辑模块](#2.5.2 修改人物添加和人物编辑模块)

[2.6 人脸比对识别](#2.6 人脸比对识别)

[2.6.1 前端](#2.6.1 前端)

[2.6.2 后端](#2.6.2 后端)

三、优化建议


本项目完整代码链接

一、概述

1.1 人脸识别

人脸识别是一种基于人脸特征进行身份识别的生物识别技术,广泛应用于安全监控、身份验证、智能设备解锁等领域。其核心是通过计算机视觉和机器学习算法,从图像或视频中提取人脸特征,并与数据库中的已知人脸进行比对,从而实现身份识别。

人脸识别技术主要包括以下几个步骤:首先是人脸检测,通过算法定位图像中的人脸区域;其次是特征提取,如卷积神经网络CNN,提取人脸的独特特征,如五官位置、轮廓等;最后是特征匹配,将提取的特征与数据库中的特征进行比对,判断是否为同一人。

  1. 人脸检测:从图像或视频中定位人脸的位置。

  2. 特征提取:从检测到的人脸中提取独特的特征向量。

  3. 特征比对:将提取的特征向量与数据库中的人脸特征进行比对,判断是否为同一个人。

近年来,随着深度学习技术的快速发展,人脸识别的准确率和效率大幅提升,尤其在复杂光照、姿态变化等场景下表现优异。

本文采用DLib库实现人脸检测,具有高精度和鲁棒性,能够有效应对复杂场景;使用ArcFace算法模型提取人脸特征,通过角度间隔优化特征分布,显著提升类内紧凑性和类间可分性,适用于高精度识别任务;采用余弦度量进行特征比对,计算简单高效,能够快速完成人脸匹配。整体方案结合了成熟的人脸检测技术、先进的深度学习模型和高效的相似度度量方法,具有高准确性、强鲁棒性和良好的实用性,适用于多种人脸识别场景。

1.2 Django和Bootstrap

  • Django:一个基于Python的高级Web框架,支持快速开发安全和可维护的网站。Django提供了强大的ORM、模板引擎和内置的管理后台,适合构建复杂的Web应用。

  • Bootstrap:一个流行的前端框架,提供了丰富的CSS和JavaScript组件,能够快速构建响应式、美观的网页界面。

使用 Django+Bootstrap 开发人脸识别系统有诸多好处:Django 具有强大的 ORM、内置的用户认证等功能,能提高开发效率,其丰富插件和工具可快速搭建系统架构;Bootstrap 提供了大量美观的 CSS 样式和 JavaScript 插件,能轻松实现响应式布局,确保系统在不同设备上有良好的显示效果,提升用户体验;两者结合还便于团队协作开发,代码结构更清晰,后期维护和扩展也更加容易,可大大缩短项目开发周期。

1.3 本文任务流程

本文将介绍如何使用Django+Bootstrap构建一个人脸识别系统,具体流程如下:

  1. 安装必要的开发环境。

  2. 创建Django项目并配置相关设置。

  3. 开发人脸库管理模块,支持人脸的录入、删除和查询。

  4. 开发人脸比对模块,使用深度学习模型进行人脸识别。

二、开发

2.1 环境安装

首先,确保系统中已安装Python 3.8及以上版本。

接下来安装Django:

bash 复制代码
pip install django -i https://pypi.tuna.tsinghua.edu.cn/simple

本文安装的是Django==5.1.6版本。

2.2 创建项目

2.2.1 创建Django项目

使用下面的代码创建一个名为FaceRec的项目:

bash 复制代码
django-admin startproject FaceRec

然后cd到FaceRec根目录下(后面所有命令都在FaceRec项目根目录下操作),使用下面的命令创建一个应用:

bash 复制代码
cd FaceRec
python manage.py startapp app

打开FaceRec/settings.py文件,找到INSTALLED_APPS字段,添加一行代码如下:

python 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app', # 添加新应用
]

并且修改ALLOWED_HOSTS字段如下:

bash 复制代码
ALLOWED_HOSTS = ['*',]

在项目根目录创建一个templates文件夹,然后在该文件夹中创建一个基本的HTML文件home.html,代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>人脸识别系统</title>
</head>
<body>
    <h1>人脸识别</h1>
</body>
</html>

为了能够让Django项目找到刚创建的templates文件夹,需要找到FaceRec.settings.py文件的TEMPLATES字段,修改代码如下:

python 复制代码
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 添加templates目录
        'DIRS': [BASE_DIR / 'templates'],
        #'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

接下来修改app/views.py文件,代码如下:

python 复制代码
from django.shortcuts import render

# Create your views here.
def home(request):
    '''首页'''
    return render(request, 'home.html')

打开FaceRec/urls.py文件,为"首页"页面添加访问路由,具体编辑代码如下:

python 复制代码
from django.contrib import admin
from django.urls import path
import app.views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', app.views.home, name='home'),  # 首页
]

保存所有修改的文件后,使用下面的命令启动:

bash 复制代码
python manage.py runserver

运行成功后,使用浏览器访问网址http://127.0.0.1:8000,正常效果如下图所示:

2.2.2 集成Bootstrap

为了方便后续制作界面,可以在Django项目中集成现成的前端组件:Bootstrap。下载链接如下:

https://download.csdn.net/download/qianbin3200896/90200781

首先在FaceRec根目录下创建一个名为static的文件夹,然后把下载的Bootstrap包中的css和js文件夹拷贝到static中。目录结构如下:

修改FaceRec/settings.py文件,在文件末尾添加代码如下:

python 复制代码
# 集成本地static文件夹
STATICFILES_DIRS = [
    BASE_DIR / "static",
]

通过上述代码设置,可以将根目录下的static文件夹资源准确被Django项目索引到。

重新修改home.html文件,并对页面进行基本设置,完整代码如下:

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <title>人脸识别系统</title>
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" type="image/x - icon">
    <!-- 引入Bootstrap的CSS和JS文件 -->
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
</head>

<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand">人脸识别系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse justify-content-center" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item mx-5">
                        <a class="nav-link active" aria-current="page" href="#">在线识别</a>
                    </li>
                    <li class="nav-item mx-5">
                        <a class="nav-link" href="#">人物管理</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 广告横幅 -->
    <div class="jumbotron jumbotron-fluid">
        <div class="container">
            <h1 class="display-4">欢迎使用人脸识别平台</h1>
            <p class="lead">Welcome to the Face Recognition Platform</p>
            <a href="#" class="btn btn-light btn-lg">在线识别</a>
        </div>
    </div>

    <!-- 使用说明 -->
    <div class="container my-5">
        <h2 class="text-center">使用说明</h2>
        <ol class="list-group list-group-numbered">
            <li class="list-group-item">点击导航栏中的"在线识别",弹出人脸识别页面。</li>
            <li class="list-group-item">上传包含人脸的图片,系统将自动进行识别。</li>
            <li class="list-group-item">识别结果将在页面上显示,包括人脸信息和相似度。</li>
            <li class="list-group-item">若需要管理人物信息,可点击"人物管理",进行添加、删除或修改操作。</li>
        </ol>
    </div>

    
    <!-- 底部 -->
    <footer class="navbar navbar-expand-sm navbar-dark bg-dark">
        <div class="container-fluid justify-content-center">
            <p class="navbar-text custom-navbar-text" style="line-height: 0.1; padding: 0.5rem 0; margin: 0;">
                YM&nbsp;人脸识别平台 &nbsp;&nbsp;版本:1.0</p>
        </div>
    </footer>
</body>

</html>

头部部分相关代码解释:

  • {% load static %}:加载 Django 的静态文件标签库。
  • <!DOCTYPE html>:声明文档类型为 HTML5。
  • <title>:设置网页标题。
  • <link rel="shortcut icon">:设置网页的图标。
  • 引入 Bootstrap 的 CSS 和自定义 CSS 文件,以及 Bootstrap 和 Popper 的 JavaScript 文件。

导航栏部分代码解释:

  • 使用 Bootstrap 的导航栏组件,包含"在线识别"和"人物管理"两个导航链接。

广告横幅部分代码解释:

  • 使用 Bootstrap 的 jumbotron 组件展示广告横幅,包含标题、副标题和"在线识别按钮。

使用说明部分代码解释:

  • 使用 Bootstrap 的列表组展示使用说明的步骤。

底部代码解释:

  • 使用 Bootstrap 的导航栏组件作为底部,显示平台名称和版本号。

除此以外,在static/css文件夹中新建一个style.css的样式文件,用于设置部分内容的样式,代码如下:

css 复制代码
body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

footer {
    margin-top: auto;
    position: fixed;
    bottom: 0;
    width: 100%;
}

.custom-navbar-text {
    padding-top: 0.0rem;
    padding-bottom: 0.0rem;
}

.jumbotron {
    background-color: #007bff;
    background-size: cover;
    background-position: center;
    color: white;
    text-align: center;
    padding: 100px 0;
}

保存所有修改后重新运行项目,效果如下:

到这里,完成了基本页面的设计开发。

2.3 开发人物管理模块

2.3.1 创建人物模型

在 app/models.py 文件中创建一个 Person 模型,包含中文名、修改时间和详情描述这几个字段。以下是具体的代码:

python 复制代码
from django.db import models
from django.utils import timezone

class Person(models.Model):
    """
    人物模型,用于存储人物的相关信息。
    """
    chinese_name = models.CharField(max_length=200, verbose_name='中文名')
    modified_time = models.DateTimeField(default=timezone.now, verbose_name='修改时间')
    description = models.TextField(blank=True, null=True, verbose_name='详情描述')
    photo = models.ImageField(upload_to='person_photos/',blank=True, null=True, verbose_name='照片')

    class Meta:
        verbose_name = '人物'
        verbose_name_plural = '人物'

    def __str__(self):
        return self.chinese_name

代码解释:

  1. 导入必要的模块

    • models:Django 模型的基类。
    • timezone:用于处理时间相关的操作。
  2. 定义 Person 模型

    • chinese_name:使用 CharField 存储人物的中文名,最大长度为 100。
    • modified_time:使用 DateTimeField 存储人物信息的修改时间,默认值为当前时间。
    • description:使用 TextField 存储人物的详情描述,允许为空。
    • photo:使用ImageField存储人物照片。
  3. 定义 Meta

    • verbose_name:设置模型在后台管理系统中的单数显示名称为"人物"。
    • verbose_name_plural:设置模型在后台管理系统中的复数显示名称为"人物"。
  4. 定义 __str__ 方法

    • 返回人物的中文名,用于在后台管理系统中显示人物信息。

创建完模型后,需要运行以下命令来迁移数据库:

bash 复制代码
python manage.py makemigrations
python manage.py migrate

完成数据库迁移后,需要为图片等媒体资源的存储路径进行设置。具体的,打开FaceRec/settings.py文件,在文件尾添加代码如下:

python 复制代码
# 媒体文件存储的根目录
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

然后修改FaceRec/urls.py文件,在文件尾添加代码如下:

python 复制代码
# 媒体资源路径设置
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

到这里,就完成了人物模型的创建。

2.3.2 在后台系统中添加人物

首先在django中创建超级管理员,这样方便后续添加测试样例:

bash 复制代码
python manage.py createsuperuser

然后按照英文提示依次输入用户名、邮箱和密码。

接下来可以在app/admin.py文件中将Person注册到后台管理系统,代码如下:

python 复制代码
from django.contrib import admin
from .models import Person

# 注册 Person 模型到后台管理系统
admin.site.register(Person)

保存所有修改后,重新启动项目,然后访问 http://127.0.0.1:8000/admin

在弹出的后台管理界面中输入超级管理员账户和密码进行登录。进入主界面后,在人物模型里面添加几个人物信息(尤其是照片),如下图所示:

此处不用添加太多,先添加4条数据用于后面界面展示即可。

2.3.3 人物显示功能

首先编辑app/views.py文件,添加代码如下:

python 复制代码
from app.models import Person
def show_persons(request):
    '''显示人员列表'''
    # 提取所有人员
    persons = Person.objects.all()
    return render(request, 'showpersons.html',{'persons':persons})

上述代码将数据库中的所有人像提取出来,并渲染到showpersons.html页面中。

接下来添加路由。打开FaceRec/urls.py文件,添加路由如下:

python 复制代码
urlpatterns = [
    ### 其他路由
    path('showpersons/', app.views.show_persons, name='show_persons'),  # 显示人员列表
]

在templates文件夹中,新建页面文件showpersons.html,其完整内容如下:

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <title>人脸识别系统</title>
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" type="image/x - icon">
    <!-- 引入Bootstrap的CSS和JS文件 -->
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
</head>

<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand">人脸识别系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse justify-content-center" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item mx-5">
                        <a class="nav-link" aria-current="page" href="{% url 'home' %}">在线识别</a>
                    </li>
                    <li class="nav-item mx-5">
                        <a class="nav-link active" href="{% url 'show_persons' %}">人物管理</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 内容区域 -->
    <div class="container my-5">
        <div class="row row-cols-1 row-cols-md-5 g-4">
            {% for person in persons %}
            <div class="col">
                <div class="card">
                    {% if person.photo %}
                    <img src="{{ person.photo.url }}" class="card-img-top" alt="{{ person.chinese_name }}">
                    {% else %}
                    <img src="{% static 'images/default_person.jpg' %}" class="card-img-top" alt="{{ person.chinese_name }}">
                    {% endif %}
                    <div class="card-body">
                        <h5 class="card-title">{{ person.chinese_name }}</h5>
                    </div>
                    <div class="card-footer">
                        <a href="#" class="btn btn-primary">编辑</a>
                        <a href="#" class="btn btn-danger">删除</a>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>

    <!-- 底部 -->
    <footer class="navbar navbar-expand-sm navbar-dark bg-dark">
        <div class="container-fluid justify-content-center">
            <p class="navbar-text custom-navbar-text" style="line-height: 0.1; padding: 0.5rem 0; margin: 0;">
                YM&nbsp;人脸识别平台 &nbsp;&nbsp;版本:1.0</p>
        </div>
    </footer>
</body>

</html>

这里,同步修改home.html页面中的href设置。

保存所有修改后,重新运行项目,效果图如下:

2.3.4 人物添加功能

在showpersons.html页面的导航栏下面添加一个按钮,用于添加"人物",同时添加一个消息显示部件,详细内容如下:

html 复制代码
<!-- 消息提示框 -->
    {% if messages %}
        {% for message in messages %}
            <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
                {{ message }}
                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            </div>
        {% endfor %}
    {% endif %}

    <!-- 添加人物按钮 -->
    <div class="container my-3">
        <div class="d-flex justify-content-between align-items-center">
            <a href="{% url 'add_person' %}" class="btn btn-primary">添加人物</a>     
        </div>
    </div>

然后在templates文件夹中添加一个页面add_person.html用于提交人物信息,代码如下:

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <title>添加人物</title>
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" type="image/x - icon">
    <!-- 引入Bootstrap的CSS和JS文件 -->
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
</head>

<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand">人脸识别系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse justify-content-center" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item mx-5">
                        <a class="nav-link" aria-current="page" href="{% url 'home' %}">在线识别</a>
                    </li>
                    <li class="nav-item mx-5">
                        <a class="nav-link active" href="{% url 'show_persons' %}">人物管理</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 内容区域 -->
    <div class="container my-5">
        <h2>添加人物</h2>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <div class="mb-3">
                <label for="chinese_name" class="form-label">中文名</label>
                <input type="text" class="form-control" id="chinese_name" name="chinese_name" required>
            </div>
            <div class="mb-3">
                <label for="photo" class="form-label">照片</label>
                <input type="file" class="form-control" id="photo" name="photo">
            </div>
            <div class="mb-3">
                <label for="details" class="form-label">详细信息</label>
                <textarea class="form-control" id="details" name="details"></textarea>
            </div>
            <button type="submit" class="btn btn-primary">提交</button>
        </form>
    </div>

       
    <!-- 底部 -->
    <footer class="navbar navbar-expand-sm navbar-dark bg-dark">
        <div class="container-fluid justify-content-center">
            <p class="navbar-text custom-navbar-text" style="line-height: 0.1; padding: 0.5rem 0; margin: 0;">
                YM&nbsp;人脸识别平台 &nbsp;&nbsp;版本:1.0</p>
        </div>
    </footer>
</body>

</html>

接下来,我们需要修改视图函数,以处理表单提交并保存数据到数据库。具体的,编辑app/views.py文件,添加代码如下:

python 复制代码
from django.shortcuts import redirect
from django.contrib import messages

def add_person(request):
    if request.method == 'POST':
        chinese_name = request.POST.get('chinese_name')
        photo = request.FILES.get('photo')
        details = request.POST.get('details')

        person = Person(chinese_name=chinese_name, photo=photo, description=details)
        person.save()

        messages.success(request, '人物添加成功!')
        return redirect('show_persons')
    return render(request, 'add_person.html')

最后在FaceRec/urls.py文件中添加对应的路由:

html 复制代码
urlpatterns = [
    # 其他路由
    path('add_person/', app.views.add_person, name='add_person'),  # 增加人员
]

保存所有修改后,重新运行项目,效果如下:

2.3.5 人物删除功能

修改showpersons.html文件中关于人物删除的代码,具体修改如下:

html 复制代码
<a href="{% url 'delete_person' person.id %}" class="btn btn-danger" onclick="return confirm('确定要删除该人物数据吗?')">删除</a>

然后在app/views.py文件中添加对应的删除代码:

python 复制代码
def delete_person(request, person_id):
    try:
        person = Person.objects.get(id=person_id)
        person.delete()
        messages.success(request, '人物删除成功!')
    except Person.DoesNotExist:
        messages.error(request, '未找到该人物数据!')
    return redirect('show_persons')

最后,修改FaceRec/urls.py文件,添加路由:

python 复制代码
urlpatterns = [
    # 其他路由
    path('delete_person/<int:person_id>/', app.views.delete_person, name='delete_person'),  # 删除人员
]

保存所有修改后,重新运行项目,单击某位人物上的删除按钮,效果如下:

2.3.6 人物编辑功能

修改 showpersons.html 页面,为编辑按钮添加链接,指向编辑视图,代码如下:

html 复制代码
<a href="{% url 'edit_person' person.id %}" class="btn btn-primary">编辑</a>

然后在templates文件夹中新建一个人物编辑页面edit_person.html,其完整代码如下:

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <title>编辑人物</title>
    <link rel="shortcut icon" href="{% static 'images/favicon.ico' %}" type="image/x - icon">
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
</head>

<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
            <a class="navbar-brand">人脸识别系统</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
                aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse justify-content-center" id="navbarNav">
                <ul class="navbar-nav">
                    <li class="nav-item mx-5">
                        <a class="nav-link" aria-current="page" href="{% url 'home' %}">在线识别</a>
                    </li>
                    <li class="nav-item mx-5">
                        <a class="nav-link" href="{% url 'show_persons' %}">人物管理</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 内容区域 -->
    <div class="container my-5">
        <h2>编辑人物</h2>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <div class="mb-3">
                <label for="chinese_name" class="form-label">中文名</label>
                <input type="text" class="form-control" id="chinese_name" name="chinese_name" value="{{ person.chinese_name }}" required>
            </div>
            <div class="mb-3">
                <label for="photo" class="form-label">照片</label>
                {% if person.photo %}
                    <img src="{{ person.photo.url }}" alt="{{ person.chinese_name }}" class="img-thumbnail mb-2" style="max-width: 200px;">
                {% endif %}
                <input type="file" class="form-control" id="photo" name="photo">
            </div>
            <div class="mb-3">
                <label for="details" class="form-label">详细信息</label>
                <textarea class="form-control" id="details" name="details">{{ person.details }}</textarea>
            </div>
            <button type="submit" class="btn btn-primary">提交</button>
        </form>
    </div>

    <!-- 底部 -->
    <footer class="navbar navbar-expand-sm navbar-dark bg-dark">
        <div class="container-fluid justify-content-center">
            <p class="navbar-text custom-navbar-text" style="line-height: 0.1; padding: 0.5rem 0; margin: 0;">
                YM&nbsp;人脸识别平台 &nbsp;&nbsp;版本:1.0</p>
        </div>
    </footer>
</body>

</html>

添加编辑视图函数,在 app/views.py 中添加编辑视图函数,用于处理编辑页面的显示和表单提交:

python 复制代码
def edit_person(request, person_id):
    try:
        person = Person.objects.get(id=person_id)
        if request.method == 'POST':
            chinese_name = request.POST.get('chinese_name')
            photo = request.FILES.get('photo')
            details = request.POST.get('details')

            person.chinese_name = chinese_name
            if photo:
                person.photo = photo
            person.details = details
            person.save()

            messages.success(request, '人物信息修改成功!')
            return redirect('show_persons')

        return render(request, 'edit_person.html', {'person': person})
    except Person.DoesNotExist:
        messages.error(request, '未找到该人物数据!')
        return redirect('show_persons')

最后配置 URL。在 FaceRec/urls.py 中添加编辑操作的 URL 配置:

python 复制代码
urlpatterns = [
    # 其他路由
    path('edit_person/<int:person_id>/', app.views.edit_person, name='edit_person'),  # 编辑人员
]

保存所有修改后,重新运行项目,单击某位人物上的编辑按钮,效果如下:

到这里,就完成了人物的增删改查功能。下面将在此基础上,将人脸检测、特征提取、人脸比对功能嵌入到上述各个模块中。

2.4 人脸检测

前面在新建人物时没有对上照的照片进行处理,考虑到本文的人脸识别任务,对上传的照片只需要保留人脸区域即可,其余部分不需要,这样也可以避免背景的干扰,提高后续人脸比对准确率。

具体的,本文将使用dlib库完成人脸检测。Dlib 库是强大的机器学习工具包,其人脸检测功能出色,能精准定位人脸位置,速度较快且鲁棒性好,可适应不同光照、姿态。支持多角度检测,在安防、图像识别等领域广泛应用。

2.4.1 安装和配置Dlib库

首先安装Dlib和opencv库:

bash 复制代码
pip install dlib opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

由于Dlib库需要本地编译,安装时间比较长(大概10分钟)。

然后下载Dlib人脸检测模型的模型文件,下载网址:

http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

下载完成后进行解压,将解压后的文件shape_predictor_68_face_landmarks.dat拷贝到项目根目录下,完整目录如下:

2.4.2 修改人物添加和人物编辑模块

在新建人物或编辑人物时,对上传的照片使用dlib进行人脸检测,提取人脸区域作为photo字段。

编辑app/views.py文件,添加代码如下:

python 复制代码
import dlib
import cv2
import numpy as np
import tempfile
import os
import uuid
from django.core.files import File

# 初始化 dlib 的人脸检测器和形状预测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')  # 请根据实际路径修改

def detect_and_extract_face(image):
    """
    使用 dlib 检测人脸并提取人脸区域
    :param image: 输入的图像(OpenCV 格式)
    :return: 提取的人脸区域图像,如果未检测到人脸则返回 None
    """
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = detector(gray)
    if len(faces) > 0:
        # 提取面积最大的人脸
        face = max(faces, key=lambda rect: rect.width() * rect.height())
        (x, y, w, h) = (face.left(), face.top(), face.width(), face.height())
        starty = y-int(1.0/3*h)
        if y-int(1.0/3*h) < 0 :
            starty = 0    
        face_image = image[starty:y+h, x:x+w]
        return face_image
    return None

然后修改add_person()和edit_person()函数,代码如下:

python 复制代码
def add_person(request):
    if request.method == 'POST':
        chinese_name = request.POST.get('chinese_name')
        photo = request.FILES.get('photo')
        details = request.POST.get('details')

        if photo:
            # 读取上传的照片
            file_bytes = np.asarray(bytearray(photo.read()), dtype=np.uint8)
            img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
            if img is None:
                messages.error(request, '无效的照片文件,请上传有效的照片。')
                return render(request, 'add_person.html')
            # 检测并提取人脸
            face_image = detect_and_extract_face(img)
            if face_image is not None:
                # 创建临时的随机文件名
                random_filename = f"{uuid.uuid4().hex}.jpg"
                temp_file_path = os.path.join(tempfile.gettempdir(), random_filename)
                cv2.imwrite(temp_file_path, face_image)
                with open(temp_file_path, 'rb') as f:
                    # 将 BufferedReader 对象包装成 Django 的 File 对象
                    photo = File(f)
                    # 创建 Person 对象
                    person = Person(chinese_name=chinese_name, description=details)
                    person.photo.save(random_filename, photo, save=False)
                # 删除临时文件
                os.remove(temp_file_path)
            else:
                messages.error(request, '未检测到人脸,请上传包含人脸的照片。')
                return render(request, 'add_person.html')
        else:
            # 如果没有上传照片,也创建 Person 对象
            person = Person(chinese_name=chinese_name, description=details)

        person.save()
        messages.success(request, '人物添加成功!')
        return redirect('show_persons')

    return render(request, 'add_person.html')


def edit_person(request, person_id):
    try:
        person = Person.objects.get(id=person_id)
        if request.method == 'POST':
            chinese_name = request.POST.get('chinese_name')
            photo = request.FILES.get('photo')
            details = request.POST.get('details')

            person.chinese_name = chinese_name
            if photo:
                # 读取上传的照片
                file_bytes = np.asarray(bytearray(photo.read()), dtype=np.uint8)
                img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
                if img is None:
                    messages.error(request, '无效的照片文件,请上传有效的照片。')
                    return render(request, 'edit_person.html', {'person': person})
                # 检测并提取人脸
                face_image = detect_and_extract_face(img)
                if face_image is not None:
                    # 创建临时的随机文件名
                    random_filename = f"{uuid.uuid4().hex}.jpg"
                    temp_file_path = os.path.join(tempfile.gettempdir(), random_filename)
                    cv2.imwrite(temp_file_path, face_image)
                    with open(temp_file_path, 'rb') as f:
                        # 将 BufferedReader 对象包装成 Django 的 File 对象
                        photo = File(f)
                        person.photo.save(random_filename, photo, save=False)
                    # 删除临时文件
                    os.remove(temp_file_path)
                else:
                    messages.error(request, '未检测到人脸,请上传包含人脸的照片。')
                    return render(request, 'edit_person.html', {'person': person})

            person.description = details
            person.save()

            messages.success(request, '人物信息修改成功!')
            return redirect('show_persons')

        return render(request, 'edit_person.html', {'person': person})
    except Person.DoesNotExist:
        messages.error(request, '信息修改失败!')
        return redirect('show_persons')

保存所有修改后,重新运行项目,将所有人物进行编辑,重新选择照片并提交,效果如下:

可以看到,当前人物只保留了面部特征,其余部分已全部去除。

2.5 特征提取

本文使用**insightface**开源库中的arcface算法来完成人脸特征提取。该算法会根据输入的人脸图像计算得到一个512维的特征向量,这个特征向量较好的保留了人物身份属性,可用于后续的快速人脸比对。

为了方便使用,本文直接使用封装好的fastdeploy库进行调用。FastDeploy 是百度飞桨推出的全场景 AI 推理部署工具。它打通模型训练到部署全流程,支持超百种预训练模型,其中就包括arcface模型。具备多硬件多后端适配能力,如 CPU、GPU 及昇腾等,能灵活选择推理引擎。部署简单高效,广泛用于视觉、语音、文本等多领域。

2.5.1 安装和配置fastdeploy库

本文使用CPU进行推理即可,因此安装fastdeploy的cpu版本。使用下面的命令进行安装:

bash 复制代码
pip install numpy opencv-python fastdeploy-python -i https://pypi.tuna.tsinghua.edu.cn/simple

然后下载人脸识别特征提取模型(该模型文件260M左右):

bash 复制代码
wget https://bj.bcebos.com/paddlehub/fastdeploy/ms1mv3_arcface_r100.onnx

下载完成后,将其放置在FaceRec项目根目录下。

修改app/views.py文件,添加人脸特征提取代码:

python 复制代码
# 初始化人脸识别模型
import fastdeploy as fd
option = fd.RuntimeOption()
option.use_cpu()
model = fd.vision.faceid.ArcFace("ms1mv3_arcface_r100.onnx", runtime_option=option)

def get_embedding(face_img):
    '''提取人脸特征向量(512维),并进行归一化'''
    face_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) # 去除颜色
    face_img = cv2.cvtColor(face_img, cv2.COLOR_GRAY2BGR)
    result = model.predict(face_img)
    embedding = result.embedding
    embedding = np.array(embedding)
    embedding = embedding / np.linalg.norm(embedding)
    return embedding

2.5.2 修改人物添加和人物编辑模块

修改app/models.py文件中Person模型的定义,添加一个字段用于存储每个人脸的特征向量:

python 复制代码
embedding = models.BinaryField(blank=True, null=True)  # 存储人脸特征向量

保存修改后,更新数据库模型:

bash 复制代码
python manage.py makemigrations
python manage.py migrate

接下来修改app/views.py文件中的add_person()和edit_person()函数:

python 复制代码
def add_person(request):
    if request.method == 'POST':
        chinese_name = request.POST.get('chinese_name')
        photo = request.FILES.get('photo')
        details = request.POST.get('details')

        if photo:
            # 读取上传的照片
            file_bytes = np.asarray(bytearray(photo.read()), dtype=np.uint8)
            img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
            if img is None:
                messages.error(request, '无效的照片文件,请上传有效的照片。')
                return render(request, 'add_person.html')
            # 检测并提取人脸
            face_image = detect_and_extract_face(img)
            # 提取人脸特征向量
            embedding = get_embedding(face_image)
            # 将特征向量转换为二进制格式
            embedding_binary = embedding.tobytes()
            if face_image is not None:
                # 创建临时的随机文件名
                random_filename = f"{uuid.uuid4().hex}.jpg"
                temp_file_path = os.path.join(tempfile.gettempdir(), random_filename)
                cv2.imwrite(temp_file_path, face_image)
                with open(temp_file_path, 'rb') as f:
                    # 将 BufferedReader 对象包装成 Django 的 File 对象
                    photo = File(f)
                    # 创建 Person 对象
                    person = Person(chinese_name=chinese_name, description=details,embedding=embedding_binary)
                    person.photo.save(random_filename, photo, save=False)
                # 删除临时文件
                os.remove(temp_file_path)
            else:
                messages.error(request, '未检测到人脸,请上传包含人脸的照片。')
                return render(request, 'add_person.html')
        else:
            # 如果没有上传照片,也创建 Person 对象
            person = Person(chinese_name=chinese_name, description=details)

        person.save()
        messages.success(request, '人物添加成功!')
        return redirect('show_persons')

    return render(request, 'add_person.html')


def edit_person(request, person_id):
    try:
        person = Person.objects.get(id=person_id)
        if request.method == 'POST':
            chinese_name = request.POST.get('chinese_name')
            photo = request.FILES.get('photo')
            details = request.POST.get('details')

            person.chinese_name = chinese_name
            if photo:
                # 读取上传的照片
                file_bytes = np.asarray(bytearray(photo.read()), dtype=np.uint8)
                img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
                if img is None:
                    messages.error(request, '无效的照片文件,请上传有效的照片。')
                    return render(request, 'edit_person.html', {'person': person})
                # 检测并提取人脸
                face_image = detect_and_extract_face(img)
                # 提取人脸特征向量
                embedding = get_embedding(face_image)
                # 将特征向量转换为二进制格式
                embedding_binary = embedding.tobytes()
                if face_image is not None:
                    # 创建临时的随机文件名
                    random_filename = f"{uuid.uuid4().hex}.jpg"
                    temp_file_path = os.path.join(tempfile.gettempdir(), random_filename)
                    cv2.imwrite(temp_file_path, face_image)
                    with open(temp_file_path, 'rb') as f:
                        # 将 BufferedReader 对象包装成 Django 的 File 对象
                        photo = File(f)
                        person.embedding = embedding_binary
                        person.photo.save(random_filename, photo ,save=False)
                    # 删除临时文件
                    os.remove(temp_file_path)
                else:
                    messages.error(request, '未检测到人脸,请上传包含人脸的照片。')
                    return render(request, 'edit_person.html', {'person': person})

            person.description = details
            person.save()

            messages.success(request, '人物信息修改成功!')
            return redirect('show_persons')

        return render(request, 'edit_person.html', {'person': person})
    except Person.DoesNotExist:
        messages.error(request, '信息修改失败!')
        return redirect('show_persons')

保存所有修改后,启动系统,重新修改并上传每个人物照片,此时每个人物的特征向量会被计算并且保存到数据库中用于后续识别。

2.6 人脸比对识别

本小节开发在线人脸比对功能。当用户单击home.html广告横幅中的在线识别按钮时,弹出模态对话框,在该对话框中,用户单击浏览按钮,从本地选择一张图片并显示到对话框上,然后用户单击提交,可以将图像传递给django后端,django后端提取该照片的人脸特征并归一化,然后从数据库中检索所有人像的人脸特征embedding,计算cos相似度,然后返回最相似的图片的中文名和相似度值。

2.6.1 前端

首先修改home.html文件,针对广告横幅中的在线识别按钮,修改如下:

html 复制代码
<a href="#" class="btn btn-light btn-lg" data-bs-toggle="modal" data-bs-target="#upload-modal">在线识别</a>

然后在"使用说明"下面添加在线人脸识别模态对话框:

html 复制代码
<!-- 在线人脸识别模态框 -->
    <div class="modal fade" id="upload-modal" tabindex="-1" aria-labelledby="upload-modal-label" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="upload-modal-label">上传图片进行人脸识别</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form id="upload-form" enctype="multipart/form-data">
                        <div class="mb-3">
                            <label for="image-upload" class="form-label">选择图片</label>
                            <input class="form-control" type="file" id="image-upload" name="image" onchange="showImage(this)">
                        </div>
                        <img id="selected-image" src="#" alt="选择的图片" style="max-width: 100%;">
                    </form>
                    <!-- 结果显示区域,初始隐藏 -->
                    <div id="result-area" style="display: none;">
                        <p>最相似的人物: <span id="result-name"></span></p>
                        <p>相似度: <span id="result-similarity"></span></p>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
                    <button type="button" class="btn btn-primary" onclick="submitForm()">提交</button>
                </div>
            </div>
        </div>
    </div>

最后添加相关的js代码:

html 复制代码
<script>
        function showImage(input) {
            if (input.files && input.files[0]) {
                var reader = new FileReader();

                reader.onload = function (e) {
                    document.getElementById('selected-image').src = e.target.result;
                }

                reader.readAsDataURL(input.files[0]);
            }
        }

        function submitForm() {
            var form = document.getElementById('upload-form');
            var formData = new FormData(form);

            fetch('{% url "face_recognition" %}', {
                method: 'POST',
                body: formData,
                headers: {
                    'X-CSRFToken': '{{ csrf_token }}'
                }
            })
           .then(response => response.json())
           .then(data => {
                document.getElementById('result-name').textContent = data.name;
                document.getElementById('result-similarity').textContent = data.similarity;
                // 显示结果区域
                document.getElementById('result-area').style.display = 'block';
            })
           .catch(error => console.error('Error:', error));
        }
    </script>

2.6.2 后端

修改urls.py文件,添加路由:

python 复制代码
urlpatterns = [
    # 其他路由
    path('face_recognition/', app.views.face_recognition, name='face_recognition'), # 人脸在线识别
]

修改views.py文件,添加代码如下:

python 复制代码
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse

@csrf_exempt
def face_recognition(request):
    if request.method == 'POST' and request.FILES.get('image'):
        image = request.FILES['image']
        file_bytes = np.asarray(bytearray(image.read()), dtype=np.uint8)
        img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
        if img is None:
            return JsonResponse({'name': '无效的照片文件','similarity': '0.00%'})
        # 人脸检测并提取特征
        face_image = detect_and_extract_face(img)
        if face_image is None:
            return JsonResponse({'name': '未检测到人脸','similarity': '0.00%'})
        # 提取人脸特征向量
        embedding = get_embedding(face_image)

        # 从数据库中检索所有人像的人脸特征 embedding
        persons = Person.objects.all()
        max_similarity = -1
        most_similar_person = None

        for person in persons:
            if person.embedding:
                # 将二进制数据转换为 numpy 数组
                db_embedding = np.frombuffer(person.embedding, dtype=np.float64)
                # 计算余弦相似度
                similarity = np.dot(db_embedding, embedding)
                if similarity > max_similarity:
                    max_similarity = similarity
                    most_similar_person = person

        if most_similar_person:
            result = {
                'name': most_similar_person.chinese_name,
                'similarity': f"{max_similarity * 100:.2f}%"
            }
        else:
            result = {
                'name': '未找到相似人物',
                'similarity': '0.00%'
            }

        return JsonResponse(result)
        
    return JsonResponse({'name': '无效请求', 'similarity': '0.00%'})

保存所有修改后,运行项目。上传一张新的人脸照片,效果如下:

三、优化建议

前面已经完成了完整的人脸识别系统建设,下面给出后续可以优化的方向:

(1)可以在Django程序启动时,读取所有人物的特征向量到FAISS向量库中,后续比对可以在该向量库中完成,提高比对速度。该方案尤其针对人物数较多的场景合适。

(2)如果有GPU的设备,可以使用GPU推理,进一步加快人脸特征计算速度。

最后说一下,想要深入、系统学习Python Web/Django的小伙伴们可以参考我的实体书籍《Python Web开发从入门到实战 (Django+Bootstrap)》清华大学出版社,该书今年会出版第二版,感谢关注。

相关推荐
明月清风徐徐12 分钟前
Miniconda + VSCode 的Python环境搭建
ide·vscode·python
笨鸟笃行25 分钟前
爬虫第七篇数据爬取及解析
开发语言·爬虫·python
java1234_小锋31 分钟前
一周学会Flask3 Python Web开发-response响应格式
开发语言·python·flask·flask3
大数据追光猿32 分钟前
Python中的Flask深入认知&搭建前端页面?
前端·css·python·前端框架·flask·html5
java1234_小锋33 分钟前
一周学会Flask3 Python Web开发-flask3模块化blueprint配置
开发语言·python·flask·flask3
莫忘初心丶35 分钟前
python flask 使用教程 快速搭建一个 Web 应用
前端·python·flask
不爱学英文的码字机器1 小时前
Python爬虫实战:从零到一构建数据采集系统
开发语言·爬虫·python
鹿鸣悠悠1 小时前
Python 类和对象详解
开发语言·python
laocooon5238578861 小时前
用Python实现的双向链表类,包含了头插、尾插、归并排序等功能
开发语言·python
百锦再2 小时前
在Linux上创建一个Docker容器并在其中执行Python脚本
linux·python·docker