专栏首页CSDN博客基于Pyramidbox实现的大规模人脸检测
原创

基于Pyramidbox实现的大规模人脸检测

原文博客:Doi技术团队 链接地址:https://blog.doiduoyi.com/authors/1584446358138 初心:记录优秀的Doi技术团队学习经历 本文链接:基于Pyramidbox实现的大规模人脸检测

前言

今天来水一片文章,基于开源的Pyramidbox大规模人脸检测编写的PaddlePaddle教程,为了方便训练预测,本教程做了一定的修改。这个模型虽然大,但是符合大规模人群中也可以准确地检测到人脸,就是遮挡比较严重也能正确检测。

本教程源码:https://resource.doiduoyi.com/#2mgg861

PyramidBox 是一种基于SSD的单阶段人脸检测器,它利用上下文信息解决困难人脸的检测问题。如下图所示,PyramidBox在六个尺度的特征图上进行不同层级的预测。该工作主要包括以下模块:LFPN、Pyramid Anchors、CPM、Data-anchor-sampling。

在这里插入图片描述

LFPN: LFPN全称Low-level Feature Pyramid Networks, 在检测任务中,LFPN可以充分结合高层次的包含更多上下文的特征和低层次的包含更多纹理的特征。高层级特征被用于检测尺寸较大的人脸,而低层级特征被用于检测尺寸较小的人脸。为了将高层级特征整合到高分辨率的低层级特征上,我们从中间层开始做自上而下的融合,构建Low-level FPN。

Pyramid Anchors: 该算法使用半监督解决方案来生成与人脸检测相关的具有语义的近似标签,提出基于anchor的语境辅助方法,它引入有监督的信息来学习较小的、模糊的和部分遮挡的人脸的语境特征。使用者可以根据标注的人脸标签,按照一定的比例扩充,得到头部的标签(上下左右各扩充1/2)和人体的标签(可自定义扩充比例)。

CPM: CPM全称Context-sensitive Predict Module, 本方法设计了一种上下文敏感结构(CPM)来提高预测网络的表达能力。

Data-anchor-sampling: 设计了一种新的采样方法,称作Data-anchor-sampling,该方法可以增加训练样本在不同尺度上的多样性。该方法改变训练样本的分布,重点关注较小的人脸。

下面这张图可以体现Pyramidbox在大规模人群中人脸检测的强大,不知道你信不信,反正我信了。

在这里插入图片描述

训练

首先下载数据集,下载链接如下,把他们下载解压到项目根目录下的data目录中。

https://share.weiyun.com/5WjCBWV
https://share.weiyun.com/5ot9Qv1
https://share.weiyun.com/5vSUomP
http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/support/bbx_annotation/wider_face_split.zip

该数据集解压之后的结构是这样的,检测你的路径是否正确了。

data
|-- download.sh
|-- wider_face_split
|   |-- readme.txt
|   |-- wider_face_train_bbx_gt.txt
|   |-- wider_face_val_bbx_gt.txt
|   `-- ...
|-- WIDER_train
|   `-- images
|       |-- 0--Parade
|       ...
|       `-- 9--Press_Conference
`-- WIDER_val
    `-- images
        |-- 0--Parade
        ...
        `-- 9--Press_Conference

然后是下载预训练模型,下载链接如下,把预训练模型解压到项目的根目录下。

http://paddlemodels.bj.bcebos.com/vgg_ilsvrc_16_fc_reduced.tar.gz

最后直接执行train.py就可以了,模型比较大,如何显存不足,可以设置batch_size小一点。该模型支持多卡训练,可以通过设置export CUDA_VISIBLE_DEVICES=0,1,2,3指定使用的GPU,并设置参数num_devices想要使用的GPU数量。

多少的读者应该也是使用Windows训练的吧,笔者也是,但Windows下PaddlePaddle不支持多线程读取数据,所以参数use_multiprocess需要设置为False。

训练保存的模型存放在output目录中。

预测

上面训练保存的或者下载的模型都是是持久化参数,这里说一下,官方提供的PyramidBox模型下载地址为:http://paddlemodels.bj.bcebos.com/PyramidBox_WiderFace.tar.gz。这些持久化参数在预测中非常不方便,以下载的模型为例,解压下载的模型到根目录,下面写一段代码把这些持久化参数转换预测模型,预测参数会保存在pyramidbox_model中,只有modelparams两个文件。

import paddle.fluid as fluid
from pyramidbox import PyramidBox

use_gpu = True
model_dir = 'PyramidBox_WiderFace'
save_infer_model_path = 'pyramidbox_model'

place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
exe = fluid.Executor(place)
main_program = fluid.Program()
startup_program = fluid.Program()
image_shape = [3, 1024, 1024]
with fluid.program_guard(main_program, startup_program):
    network = PyramidBox(
        data_shape=image_shape,
        sub_network=True,
        is_infer=True)
    infer_program, nmsed_out = network.infer(main_program)
    fetches = [nmsed_out]
    fluid.io.load_persistables(exe, model_dir, main_program=infer_program)
    # save model and program
    fluid.io.save_inference_model(save_infer_model_path,
                                  ['image'], [nmsed_out], exe, main_program=infer_program,
                                  model_filename='model', params_filename='params')

接下来就是预测,编写infer.py代码,创建执行器并加载Pyramidbox模型,加载的就是上一步转换的预测模型。

import time
import cv2
import numpy as np
import paddle.fluid as fluid
from PIL import Image
from PIL import ImageDraw

use_gpu = True
# 创建执行器
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

# 预测模型路径
save_path = 'pyramidbox_model/'
[infer_program,
 feeded_var_names,
 target_var] = fluid.io.load_inference_model(dirname=save_path,
                                             executor=exe,
                                             model_filename='model',
                                             params_filename='params')

该函数获取变换图片到一定范围的尺度, 通过这个尺度改变输入图片的大小。

def get_shrink(height, width):
    max_shrink_v1 = (0x7fffffff / 577.0 / (height * width)) ** 0.5
    max_shrink_v2 = ((678 * 1024 * 2.0 * 2.0) / (height * width)) ** 0.5

    def get_round(x, loc):
        str_x = str(x)
        if '.' in str_x:
            str_before, str_after = str_x.split('.')
            len_after = len(str_after)
            if len_after >= 3:
                str_final = str_before + '.' + str_after[0:loc]
                return float(str_final)
            else:
                return x

    max_shrink = get_round(min(max_shrink_v1, max_shrink_v2), 2) - 0.3
    if 1.5 <= max_shrink < 2:
        max_shrink = max_shrink - 0.1
    elif 2 <= max_shrink < 3:
        max_shrink = max_shrink - 0.2
    elif 3 <= max_shrink < 4:
        max_shrink = max_shrink - 0.3
    elif 4 <= max_shrink < 5:
        max_shrink = max_shrink - 0.4
    elif max_shrink >= 5:
        max_shrink = max_shrink - 0.5

    shrink = max_shrink if max_shrink < 1 else 1
    return shrink, max_shrink

编写一个可以显示预测图像的函数,在图像中画上预测的框,并显示在桌面。

def draw_image(img_path, bboxes):
    image = Image.open(img_path)
    draw = ImageDraw.Draw(image)
    for i in range(len(bboxes)):
        xmin, ymin, xmax, ymax = bboxes[i]
        (left, right, top, bottom) = (xmin, xmax, ymin, ymax)
        draw.line([(left, top), (left, bottom), (right, bottom), (right, top), (left, top)], width=4, fill='red')

    # 显示图像
    cv2.imshow('result image', cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR))
    cv2.waitKey(1)

该函数为使用模型检测人脸,该函数包括了图像预处理,首先是要把图片从HWC转化为CHW,然后PIL打开图片是RBG的,但训练的时候是用的是BGR,所以要转换为BGR,最后减去均值和乘缩放值。

def detect_face(image, shrink):
    image_shape = [3, image.size[1], image.size[0]]
    if shrink != 1:
        h, w = int(image_shape[1] * shrink), int(image_shape[2] * shrink)
        image = image.resize((w, h), Image.ANTIALIAS)
        image_shape = [3, h, w]

    img = np.array(image)
    print(img.shape)
    # HWC to CHW
    if len(img.shape) == 3:
        img = np.swapaxes(img, 1, 2)
        img = np.swapaxes(img, 1, 0)
    print(img.shape)
    # RBG to BGR
    img = img[[2, 1, 0], :, :]
    mean = [104., 117., 123.]
    scale = 0.007843
    img = img.astype('float32')
    img -= np.array(mean)[:, np.newaxis, np.newaxis].astype('float32')
    img = img * scale
    img = [img]
    img = np.array(img)

    detection, = exe.run(infer_program,
                         feed={feeded_var_names[0]: img},
                         fetch_list=target_var,
                         return_numpy=False)
    detection = np.array(detection)
    # layout: xmin, ymin, xmax. ymax, score
    if np.prod(detection.shape) == 1:
        print("No face detected")
        return np.array([[0, 0, 0, 0, 0]])
    det_conf = detection[:, 1]
    det_xmin = image_shape[2] * detection[:, 2] / shrink
    det_ymin = image_shape[1] * detection[:, 3] / shrink
    det_xmax = image_shape[2] * detection[:, 4] / shrink
    det_ymax = image_shape[1] * detection[:, 5] / shrink

    det = np.column_stack((det_xmin, det_ymin, det_xmax, det_ymax, det_conf))
    return det

最后传一张图片的路径,预测并展示检测后的图像。通过设置阈值confs_threshold过滤掉得分比较低的人脸框。

def infer(image_path, confs_threshold):
    if True:
        image = Image.open(image_path)
        if image.mode == 'L':
            image = image.convert('RGB')
        shrink, max_shrink = get_shrink(image.size[1], image.size[0])

        start = time.time()
        det0 = detect_face(image, shrink)
        dets = det0
        end = time.time()
        print("预测时间: %f" % (end - start))

        keep_index = np.where(dets[:, 4] >= confs_threshold)[0]
        dets = dets[keep_index, :]
        draw_image(image_path, dets[:, 0:4])


if __name__ == '__main__':
    confs_threshold = 0.15
    image_path = 'dataset/images/0acc15e8965111eab2edc8ff285a4318.jpg'
    infer(image_path, confs_threshold)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

以下是本程序执行之后的效果图:

在这里插入图片描述

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《PaddlePaddle从入门到炼丹》十三——自定义图像数生成

    我们在第六章介绍了生成对抗网络,并使用生成对抗网络训练mnist数据集,生成手写数字图片。那么本章我们将使用对抗生成网络训练我们自己的图片数据集,并生成图片。在...

    夜雨飘零
  • 《PaddlePaddle从入门到炼丹》六——生成对抗网络

    我们上一章使用MNIST数据集进行训练,获得一个可以分类手写字体的模型。如果我们数据集的数量不够,不足于让模型收敛,最直接的是增加数据集。但是我们收集数据并进行...

    夜雨飘零
  • 基于PaddlePaddle语音识别模型

    本项目是基于PaddlePaddle的DeepSpeech项目修改的,方便训练中文自定义数据集。

    夜雨飘零
  • python的图像处理模块

    除了opencv专门用来进行图像处理,可以进行像素级、特征级、语义级、应用级的图像处理外,python中还有其他库用来进行简单的图像处理,比如图像的读入和保存、...

    于小勇
  • 【多进程】php多进程编程

    php实现多进程需要安装pcntl模块,这个模块是php官方提供的,所以我们可以在PHP源码中找到,下载 php7.3.7 源码并解压到 /home 目录下,...

    码缘
  • thinkphp常用配置config

    下载解压 ThinkPHP 3.2.3,在默认的应用 Application(./Application) 中,包含一个默认的模块 Home(./Applica...

    公众号php_pachong
  • 【不戳后悔】大数据教你史上最快升职方法!

    近日,LinkedIn领英在对拥有200员工以上的公司内的总监职位进行透彻研究后,得到了这份严肃又不失活泼的升职总监指南,不想当总监的小兵不是好民工,今日搬砖的...

    CDA数据分析师
  • .NET4.0下网站应用程序用UrlRewriter.dll重写无后缀路径 (在IIS7.5中的配置方法)

    接上一篇  .NET4.0下网站应用程序用UrlRewriter.dll重写无后缀路径 在IIS中新建网站(端口号8111)       直接运行http://...

    Porschev
  • 干货 | 机器学习算法在饿了么供需平衡系统中的应用

    作者简介 陈宁,饿了么人工智能与策略部高级算法专家,负责供需平衡系统的算法与研发工作。获新加坡南洋理工大学计算机博士学位,研究方向包括:数据挖掘,机器学习,自然...

    携程技术
  • Android添加ButterKnife时报错Error:(2, 0) Cannot add extension with name 'android'的解决办法

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对ZaLou.Cn的支持。如果你想了解更多相关内容请查看下面相关...

    砸漏

扫码关注云+社区

领取腾讯云代金券