【简陋Web应用2】人脸检测——基于Flask和PaddleHub

文章目录

  • 🚩 前言
  • 🌺 效果演示
  • 🥦 分析与设计
  • 🍉 实现
    • 🍬 1. 部署人脸检测模型
    • 🍭 2. 使用Flask构建app
      • 2.1 目录结构
      • 2.2 forms.py
      • 2.3 utils.py
      • 2.4 app.py
      • 2.5 index.html
  • 🥝 Bug(s)


🚩 前言

本次实现了一个在浏览器中运行的简陋的人脸检测功能,由于水平有限,这里使用表单上传图片,只能一次检测一张人脸。实现过程中遇到的主要问题是数据格式转换的问题。

🌺 清风莫追 🌺

csdn个人主页:https://blog.csdn.net/m0_63238256

🌺 效果演示

请添加图片描述

🥦 分析与设计

之前已经成功基于AI分词模型,构建了一个Web应用。套路大致相同,与本次任务的主要区别在于,本次传递的数据是图像而不是文本。图像数据会带来一个新的问题:

  • 图像的编码方式丰富,数据处理过程中需要进行一些数据格式转换

应用的逻辑大致如下:

  1. 用户通过表单从浏览器上传图像
  2. 将图像转发给人脸检测模型,得到人脸位置坐标
  3. 使用矩形框出图像中的人脸
  4. 浏览器显示结果

🍉 实现

🍬 1. 部署人脸检测模型

一行命令即可完成服务化部署(你需要先安装PaddleHub库),pyramidbox_lite_mobile是一个预训练的人脸检测模型。

hub serving start -m pyramidbox_lite_mobile

你可以使用下面的代码(来自PaddleHub的文档,记得修改未你自己的图片存放路径),测试接口

# coding: utf8
import requests
import json
import cv2
import base64


def cv2_to_base64(image):
    data = cv2.imencode('.jpg', image)[1]
    return base64.b64encode(data.tostring()).decode('utf8')


if __name__ == '__main__':
    # 获取图片的base64编码格式 (记得修改你自己的图片存放路径)
    img1 = cv2_to_base64(cv2.imread("./static/Aaron_Peirsol_0001.jpg"))
    img2 = cv2_to_base64(cv2.imread("./static/Aaron_Peirsol_0002.jpg"))
    data = {'images': [img1, img2]}
    # 指定content-type
    headers = {"Content-type": "application/json"}
    # 发送HTTP请求
    url = "http://127.0.0.1:8866/predict/pyramidbox_lite_mobile"
    r = requests.post(url=url, headers=headers, data=json.dumps(data))

    # 打印预测结果
    print(r.json())

🍭 2. 使用Flask构建app

2.1 目录结构

- templates
	- index.html
- app.py
- forms.py
- utils.py

其中utils.py封装了一些简单的函数。

2.2 forms.py

下面定义了一个表单,它只有一个字段face_img,用于上传待检测的人脸图片。validatiors中描述了很多message,在上传的表单不满足约束时,可在html模板中通过{{ form.face_img.erros }}获取相关的message信息。

from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileRequired, FileSize, FileField

class ImageForm(FlaskForm):
    face_img = FileField("face_img", 
        validators=[
            FileRequired(message="不能为空"),
            FileAllowed(['jpg', 'png'], message="仅支持jpg/png格式"),
            FileSize(max_size=2048000, message="图片不能大于2Mb")
        ],
        description="图片不能大于2Mb,仅支持jpg/png格式"
    )

2.3 utils.py

封装了三个简单的函数,但在app.py中只使用了cv2_to_base64()

import base64
import numpy as np
import cv2


def base64_to_cv2(img: str):
    # base64 -> 二进制 -> ndarray -> cv2
    # 解码为二进制数据
    img_codes = base64.b64decode(img)
    img_np = np.frombuffer(img_codes, np.uint8)
    img_cv2 = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
    return img_cv2


def cv2_to_base64(image):
    data = cv2.imencode('.jpg', image)[1]
    return base64.b64encode(data.tostring()).decode('utf8')


# 显示cv2格式的图像 --> 开发过程中测试图像是否正常时使用
def cv2_show(img_cv2):
    cv2.imshow('img', img_cv2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

2.4 app.py

:如果以后数据在转换的过程中究竟变成了什么格式,那就把它们打印出来看看叭!例如print(data, type(data))

主要的逻辑就在这里了,图像主要经历了三种类型的格式:

  • 文件对象:从前端表单返回的图像文件的格式。
  • cv2:opencv的图像格式,是一个numpyndarray数组。
  • str:base64编码格式的字符串;是作为模型输入,和在前端显示图像的格式。

数据格式的变化流程大致如下图:

在这里插入图片描述

# 注:在推理前将图像缩放到指定的尺寸,即能提升速度,有时也能提升精度(实测像素太高时识别效果也不好)
from flask import Flask, render_template, request
import requests
from forms import ImageForm
import cv2
import numpy as np
import json
import time
from utils import cv2_to_base64


app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key_here'


@app.route('/', methods=['GET', 'POST'])
def predict():
    form = ImageForm()

    if form.validate_on_submit():

        # 1. 从前端表单获取图像文件
        file = form.face_img.data  # <class 'werkzeug.datastructures.FileStorage'>
        file_content = file.read()  # <class 'bytes'>

        # 2. 图像文件转cv2, 并缩放到指定尺寸 --> 尺寸太大或太小,识别精度都会变差
        img_cv2 = np.asarray(bytearray(file_content), dtype=np.uint8)  # (len,)
        img_cv2 = cv2.imdecode(img_cv2, cv2.IMREAD_COLOR)  # (w, h, c)
        img_cv2 = cv2.resize(img_cv2, (250, 250), interpolation=cv2.INTER_LINEAR)

        # 3. cv2转str(base64)
        img_base64 = cv2_to_base64(img_cv2)

        # 4. str(base64)输入模型 --> json --> 人脸框坐标
        data = {'images': [img_base64]}
        headers = {"Content-type": "application/json"}
        url = "http://127.0.0.1:8866/predict/pyramidbox_lite_mobile"

        start_time = time.time()
        r = requests.post(url=url, headers=headers, data=json.dumps(data))
        use_time = time.time() - start_time

        rectangle = r.json()['results'][0]['data'][0]  # 一张图片 --> dict{confidence, left, top, right, bottom}

        # 5. cv2,json --> 画矩形 --> cv2
        cv2.rectangle(
            img_cv2, 
            (rectangle['left'], rectangle['top']),
            (rectangle['right'], rectangle['bottom']),
            (255, 0, 0),  # 蓝色
            thickness=2)

        # 6. cv2转str(base64)
        img_base64 = cv2_to_base64(img_cv2)

        # 7. str(base64) 返回到前端
        return render_template(
            'index.html', form=form, img_base64=img_base64, 
            confidence=rectangle['confidence'], use_time=use_time)

    return render_template('index.html', form=form)

if __name__ == '__main__':
    app.run(debug=True, port=5000)

2.5 index.html

视图模板,也是十分简陋。

<h1>试试人脸检测</h1>


<!-- 1. 上传图像的表单 -->
<form action="" method="post" class="mt-4" enctype="multipart/form-data">
    <!-- csrf这一句好像可以没啥用 -->
    {{ form.csrf_token }}
    {{ form.face_img() }}
    <input type="submit" value="Submit">
</form>

<!-- 2. 显示检测结果 -->
{% if img_base64 %}
    <img src="data:image/jpeg;base64, {{ img_base64 }}" width="250" height="250">
    <p>置信度: {{ confidence }}</p>
    <p>推理耗时(秒): {{ use_time }}</p>
{% endif %}

<!-- 3. 显示错误信息 -->
{% if form.face_img.errors %}
    <div class="alert alert-danger">
        {% for error in form.face_img.errors %}
            {{ error }}
        {% endfor %}
    </div>
{% endif %}

🥝 Bug(s)

1、后端接收不到上传的图片

使用表单的模板代码如下:

<form action="" method="post" class="mt-4">
    <!-- csrf这一句好像可以删掉 -->
    {{ form.csrf_token }}
    {{ form.face_img() }}
    <input type="submit" value="Submit">
</form>

解决:在 Flask 中处理文件上传时,需要<form>中添加 enctype="multipart/form-data" 属性,这样浏览器才能正确识别上传的文件数据。

2、数据格式转换晕头转向

app.py中,我最初对于图像格式的转换十分懵圈,想整理下思路,结果却如下图,还是很乱。经过多次重构,才变成了 2.5 app.py 那里显示的图。

重构还是挺有用的!有时代码经过重构也会变得清晰。

在这里插入图片描述


原文链接:https://cfeng.blog.csdn.net/article/details/129636071

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2796.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

V2G模式下含分布式能源网优化运行研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&#x1f381; 目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &am…

手写一个简单的RPC框架

学习RPC框架&#xff0c;由繁化简&#xff0c;了解其本质原理 文章目录项目简介什么是RPC&#xff1f;项目模块项目代码common模块client模块server模块framework模块测试项目简介 什么是RPC&#xff1f; RPC&#xff08;Remote Procedure Call&#xff09;即远程过程调用&am…

Cursor:GPT-4 驱动的强大代码编辑器

Cursor &#xff08;https://www.cursor.so/&#xff09;是 GPT-4 驱动的一款强大代码编辑器&#xff0c;可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜&#xff0c;如下…

SWA Object Detection随机权重平均【论文+代码】

随机权重平均摘要IntroductionSWA实验部分消融实验摘要 您想在不增加推断成本和不改变检测器的情况下提高对象检测器的1.0 AP吗&#xff1f;让我们告诉您一个这样的秘方。这个秘方令人惊讶地简单&#xff1a;使用循环学习率训练您的检测器额外的12个epoches&#xff0c;然后将…

最强的Python可视化神器,你有用过么?

数据分析离不开数据可视化&#xff0c;我们最常用的就是Pandas&#xff0c;Matplotlib&#xff0c;Pyecharts当然还有Tableau&#xff0c;看到一篇文章介绍Plotly制图后我也跃跃欲试&#xff0c;查看了相关资料开始尝试用它制图。 1、Plotly Plotly是一款用来做数据分析和可视…

【数据结构】Java实现队列与循环队列

目录 1. 概念 2. 队列的使用 3. 自己动手实现队列 3.1 MyQueue接口 3.2 LinkedQueue类 3.3 入队列 3.4 出队列 3.5 获取队头元素 3.6 获取队列中有效元素个数与检测队列是否为空 3.7 toString方法 4. 整体实现 4.1 LinkedQueue类 4.2 Test类 4.3 测试结果 5. 循…

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)

【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和&#xff0c;本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下&#xff1a; 在&l…

【进阶数据结构】——红黑树

&#x1f308;感谢阅读East-sunrise学习分享——[进阶数据结构]红黑树 博主水平有限&#xff0c;如有差错&#xff0c;欢迎斧正&#x1f64f;感谢有你 码字不易&#xff0c;若有收获&#xff0c;期待你的点赞关注&#x1f499;我们一起进步&#x1f680; &#x1f308;我们上一…

SpringCloud之 LoadBalancer和Feign负载均衡

文章目录LoadBalancer 负载均衡一、LoadBalanced 负载均衡&#x1f33d;①观察负载均衡现象&#x1f33d;②LoadBalanced 源码剖析二、自定义负载均衡三、OpenFeign 实现负载均衡&#x1f346;①添加依赖&#x1f346;②启动类添加 EnableFeignClients&#x1f346;③创建客户端…

MySQL的COUNT语句,竟然都能被面试官虐的这么惨!?

关于数据库中行数统计&#xff0c;无论是MySQL还是Oracle&#xff0c;都有一个函数可以使用&#xff0c;那就是COUNT 但是&#xff0c;就是这个常用的COUNT函数&#xff0c;却暗藏着很多玄机&#xff0c;尤其是在面试的时候&#xff0c;一不小心就会被虐。不信的话请尝试回答下…

一文了解Jackson注解@JsonFormat及失效解决

背景 项目中使用WRITE_DATES_AS_TIMESTAMPS: true转换日期格式为时间戳未生效。如下&#xff1a; spring:jackson:time-zone: Asia/Shanghaiserialization:WRITE_DATES_AS_TIMESTAMPS: true尝试是否关于时间的注解是否会生效&#xff0c;使用JsonForma和JsonFiled均失效。 常…

【Docker】CAdvisor+InfluxDB+Granfana容器监控

文章目录原生命令 docker stats容器监控3剑客CIGCAdvisorInfluxDBGranfanacompose容器编排&#xff0c;一套带走新建目录新建3件套组合的 docker-compose.yml检查配置&#xff0c;有问题才有输出 docker-compose config -q启动docker-compose文件 docker-compose up -d测试浏览…

HTML5 Canvas

HTML5 Canvas <canvas>元素是HTML5中的新元素&#xff0c;通过使用该元素&#xff0c;你可以在网页中绘制所需的图形。 标签定义图形&#xff0c;比如图表和其他图像&#xff0c;您必须使用脚本来绘制图形。在画布上&#xff08;Canvas&#xff09;画一个红色矩形&#…

Java基础知识之HashMap的使用

一、HashMap介绍 HashMap是Map接口的一个实现类&#xff08;HashMap实现了Map的接口&#xff09;&#xff0c;它具有Map的特点。HashMap的底层是哈希表结构。 Map是用于保存具有映射关系的数据集合&#xff0c;它具有双列存储的特点&#xff0c;即一次必须添加两个元素&#xf…

5个高清/4K视频素材网站,免费下载。

本期跟大家分享5个超好用的视频素材网站&#xff0c;4K质量&#xff0c;免费可商用。 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库主要提供设计素材为主&#xff0c;自媒体相关素材也很多&#xff0c;像商用图片、背景图、视频素材、音频素材都很齐…

算法刷题总结 (二) 回溯与深广搜算法

算法总结2 回溯与深广搜算法一、理解回溯算法1.1、回溯的概念1.2、回溯法的效率1.3、回溯法问题分类1.4、回溯法的做题步骤二、经典问题2.1、组合问题2.1.1、77. 组合 - 值不重复2.1.2、216.组合总和III - 值不重复且等于目标值2.1.3、17. 电话号码的字母组合 - 双层回溯2.1.4、…

KafKa知识汇总

前言 汇总相关知识 Kafka快速实战与基本原理详解

LeetCode:215. 数组中的第K个最大元素

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;215. 数组中的第K个最大元素 题目描述&#xff1a;给定整数数组nums和整…

Django 之 Cookie 和 Session

3. Cookie 和 Session 会话 因为 HTTP 协议是无状态的&#xff0c;每次浏览器请求 request都是无状态的&#xff0c;后台服务器无法识别当前请求与上一次请求及之后请求是否为同一用户。 对于静态网站来说无所谓&#xff08;所有用户看到的都是一样的&#xff09;&#xff0c…

【C++】引用详细解析

目录引用的概念引用的用法引用的特性常引用&#xff08;涉及权限的放大与缩小&#xff09;引用的使用场景**作参数****作返回值**正确使用引用返回传值、传引用效率比较引用和指针的区别引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0…