专栏首页CSDN博客基于PaddlePaddle实现的密度估计模型CrowdNet
原创

基于PaddlePaddle实现的密度估计模型CrowdNet

原文博客:Doi技术团队 链接地址:https://blog.doiduoyi.com/authors/1584446358138 初心:记录优秀的Doi技术团队学习经历 本文链接:基于PaddlePaddle实现的密度估计模型CrowdNet

前言

CrowdNet模型是2016年提出的人流密度估计模型,论文为《CrowdNet: A Deep Convolutional Network for DenseCrowd Counting》,CrowdNet模型主要有深层卷积神经网络和浅层卷积神经组成,通过输入原始图像和高斯滤波器得到的密度图进行训练,最终得到的模型估计图像中的行人的数量。当然这不仅仅可以用于人流密度估计,理论上其他的动物等等的密度估计应该也可以。

项目开源地址: https://github.com/yeyupiaoling/PaddlePaddle-CrowdNet.git

本项目开发环境为:

  • Windows 10
  • Python 3.7
  • PaddlePaddle 2.0.0a0

CrowdNet模型实现

以下是CrowdNet模型的结构图,从结构图中可以看出,CrowdNet模型是深层卷积网络(Deep Network)和浅层卷积网络(Shallow Network)组成,两组网络通过拼接成一个网络,接着输入到一个卷积核数量和大小都是1的卷积层,最后通过插值方式得到一个密度图数据,通过统计这个密度就可以得到估计人数

在这里插入图片描述

在PaddlePaddle中,通过以下代码判断即可实现上面的CrowdNet模型,在深层卷积网络和浅层卷积网络的卷积层都使用conv_bn卷积层,这个是通过把卷积层和batch_norm组合在一起的。在本项目中,输入的图像大小3, 640, 480,密度图大小为1, 80, 60,所以深层卷积网络输出的shape为512, 80, 60,浅层神经网络的输出为24, 80, 60。两个网络的输出通过fluid.layers.concat()接口进行拼接,拼接后输入到fluid.layers.conv2d(),最后通过fluid.layers.resize_bilinear() 双向性插值法输出一个密度图,最后使用的fluid.layers.reduce_sum()是为了方便在预测时直接输出估计人数。

def deep_network(img):
    x = img
    x = conv_bn(input=x, num_filters=64, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=64, filter_size=3, padding=1, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=2, pool_stride=2)
    x = fluid.layers.dropout(x=x, dropout_prob=0.25)
    x = conv_bn(input=x, num_filters=128, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=128, filter_size=3, padding=1, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=2, pool_stride=2)
    x = fluid.layers.dropout(x=x, dropout_prob=0.25)
    x = conv_bn(input=x, num_filters=256, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=256, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=256, filter_size=3, padding=1, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=2, pool_stride=2)
    x = fluid.layers.dropout(x=x, dropout_prob=0.5)
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=3, pool_stride=1, pool_padding=1)
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1, act='relu')
    x = conv_bn(input=x, num_filters=512, filter_size=3, padding=1)
    x = fluid.layers.dropout(x=x, dropout_prob=0.5)
    return x


def shallow_network(img):
    x = img
    x = conv_bn(input=x, num_filters=24, filter_size=5, padding=3, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=5, pool_type='avg', pool_stride=2)
    x = conv_bn(input=x, num_filters=24, filter_size=5, padding=3, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=5, pool_type='avg', pool_stride=2)
    x = conv_bn(input=x, num_filters=24, filter_size=5, padding=4, act='relu')
    x = fluid.layers.pool2d(input=x, pool_size=5, pool_type='avg', pool_stride=2)
    return x

# 创建CrowdNet网络模型
net_out1 = deep_network(images)
net_out2 = shallow_network(images)
concat_out = fluid.layers.concat([net_out1, net_out2], axis=1)
conv_end = fluid.layers.conv2d(input=concat_out, num_filters=1, filter_size=1)
# 双向性插值
map_out = fluid.layers.resize_bilinear(conv_end, out_shape=(80, 60))
# 避开Batch维度求和
sum_ = fluid.layers.reduce_sum(map_out, dim=[1, 2, 3])
sum_ = fluid.layers.reshape(sum_, [-1, 1])

通过上面实现的CrowdNet模型,它的结构如下图所示:

在这里插入图片描述

训练模型

本项目使用的是百度公开的一个人流密度数据集,数据集下载链接:https://aistudio.baidu.com/aistudio/datasetdetail/1917,下载之后,执行下面操作:

  • train.json文件存放在data目录
  • test_new.zip解压到data目录
  • train_new.zip解压到data目录

本项目提供了一个脚本create_list.py可以把百度公开的数据集数据标准文件生成本项目所需要的标注格式,通过执行脚本可以生成类似以下格式的数据列表,每一行的前面是图像路径,后面的是人的坐标点,中间用制表符\t分开。如果开发者要训练自己的数据集,将图像标注数据生成以下格式即可。

data/train/4c93da45f7dc854a31a4f75b1ee30056.jpg	[(171, 200), (365, 144), (306, 155), (451, 204), (436, 252), (600, 235)]
data/train/3a8c1ed636145f23e2c5eafce3863bb2.jpg	[(788, 205), (408, 250), (115, 233), (160, 261), (226, 225), (329, 161)]
data/train/075ed038030094f43f5e7b902d41d223.jpg	[(892, 646), (826, 763), (845, 75), (896, 260), (773, 752)]

模型的输入标签是一个密度图,那么如何通过标注数据生成一个密度图的,下面就来简单介绍一下。其实就是一些不同核的高斯滤波器生成的,得到的一个比输入图像小8倍的密度图。

import json
import numpy as np
import scipy
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib import cm as CM
import scipy
import scipy.spatial
from PIL import Image
from scipy.ndimage.filters import gaussian_filter
import os

# 图片预处理
def picture_opt(img, ann):
    # 缩放的图像大小
    train_img_size = (640, 480)
    gt = []
    size_x, size_y = img.size
    img = img.resize(train_img_size, Image.ANTIALIAS)

    for b_l in range(len(ann)):
        x = ann[b_l][0]
        y = ann[b_l][1]
        x = (x * train_img_size[0] / size_x) / 8
        y = (y * train_img_size[1] / size_y) / 8
        gt.append((x, y))

    img = np.array(img) / 255.0
    return img, gt

# 高斯滤波
def gaussian_filter_density(gt):
    density = np.zeros(gt.shape, dtype=np.float32)
    gt_count = np.count_nonzero(gt)
    if gt_count == 0:
        return density
    pts = np.array(list(zip(np.nonzero(gt)[1].ravel(), np.nonzero(gt)[0].ravel())))
    tree = scipy.spatial.KDTree(pts.copy(), leafsize=2048)
    distances, locations = tree.query(pts, k=4)
    for i, pt in enumerate(pts):
        pt2d = np.zeros(gt.shape, dtype=np.float32)
        pt2d[pt[1], pt[0]] = 1.
        if gt_count > 1:
            sigma = (distances[i][1] + distances[i][2] + distances[i][3]) * 0.1
        else:
            sigma = np.average(np.array(gt.shape)) / 2. / 2.
        density += scipy.ndimage.filters.gaussian_filter(pt2d, sigma, mode='constant')
    return density


# 密度图处理
def ground(img, gt):
    imgs = img
    x = imgs.shape[0] / 8
    y = imgs.shape[1] / 8
    k = np.zeros((int(x), int(y)))
    for i in range(0, len(gt)):
        if int(gt[i][1]) < int(x) and int(gt[i][0]) < int(y):
            k[int(gt[i][1]), int(gt[i][0])] = 1
    img_sum = np.sum(k)
    k = gaussian_filter_density(k)
    return k, img_sum

读取一张图片,并经过缩放预处理,在这里图像没有经过装置,但是在训练过程中需要对图像执行装置im.transpose()操作,这样才符合PaddlePaddle的输入格式。

# 读取数据列表
with open('data/data_list.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()

line = lines[50]
img_path, gt = line.replace('\n', '').split('\t')
gt = eval(gt)
img = Image.open(img_path)
im, gt = picture_opt(img, gt)

print(im.shape)
plt.imshow(im)
在这里插入图片描述

通过ground()函数将上面的图片生成一个密度图,密度图结果如下图所示。注意在输入PaddlePaddle的密度图是要经过装置的,因为图像的数据的输入是装置的,所以密度图也得装置。

k, img_sum = ground(im, gt)
groundtruth = np.asarray(k)
groundtruth = groundtruth.astype('float32')

print("实际人数:", img_sum)
print("密度图人数:", np.sum(groundtruth))
print("密度图大小:", groundtruth.shape)

plt.imshow(groundtruth,cmap=CM.jet)
在这里插入图片描述

训练程序

以下为train.py的代码,在训练中使用了平方差损失函数,其中损失值乘以6e5是为了不让输出的损失值太小。

loss = fluid.layers.square_error_cost(input=map_out, label=label) * 6e5
loss = fluid.layers.mean(loss)

为了加快数据的读取,这里使用了异步数据读取方式,可以一边训练一边读取下一步batch的数据。

py_reader = fluid.io.PyReader(feed_list=[images, label, img_num],
                              capacity=32,
                              iterable=True,
                              return_list=False)
py_reader.decorate_sample_list_generator(paddle.batch(reader.train_reader(data_list_file), batch_size=BATCH_SIZE),
                                         places=fluid.core.CPUPlace())

在训练前加上一个加载预训练模型的方法,如果之前的模型存在,就加载该模型,接着上一次的训练结果继续训练。

if PERSISTABLES_MODEL_PATH is not None and os.path.exists(PERSISTABLES_MODEL_PATH):
    def if_exist(var):
        if os.path.exists(os.path.join(PERSISTABLES_MODEL_PATH, var.name)):
            print('loaded: %s' % var.name)
        return os.path.exists(os.path.join(PERSISTABLES_MODEL_PATH, var.name))


    fluid.io.load_vars(exe, PERSISTABLES_MODEL_PATH, main_program=fluid.default_main_program(), predicate=if_exist)

在执行训练前需要留意以下几个参数,需要根据自己的实际情况修改。当然如果开发者都是按照上面的操作,这里基本上不需要修改,但是BATCH_SIZE可能要修改一下,因为这个模型比较大,如何显存小的可能还有修改,以下是笔者在8G显存的环境下设置的。

# 是否使用GPU
USE_CUDA = True
# 模型参数保存路径
PERSISTABLES_MODEL_PATH = 'persistables_model/'
# 预测模型保存路径
INFER_MODEL = 'infer_model/'
# 训练轮数
EPOCHS_SUM = 800
# Batch大小
BATCH_SIZE = 6
# 图像列表路径
data_list_file = 'data/data_list.txt'

最后执行python train.py开始训练模型。

预测

最通过执行infer.py可以把data/test/目录下的图像都进行预测,结果写入到results.csv文件中。

下面介绍预测的大概方式,通过加载训练过程中保存的预测模型,得到一个预测程序。

import matplotlib.pyplot as plt
from matplotlib import cm as CM
import os
import numpy as np
import paddle.fluid as fluid
from PIL import Image

# 是否使用GPU
USE_CUDA = True
INFER_MODEL = 'infer_model/'

place = fluid.CUDAPlace(0) if USE_CUDA else fluid.CPUPlace()
exe = fluid.Executor(place)

[inference_program,
 feed_target_names,
 fetch_targets] = fluid.io.load_inference_model(INFER_MODEL, exe)

读取一张待预测的图片。

image_path = "data/test/00bdc7546131db72333c3e0ac9cf5478.jpg"
test_img = Image.open(image_path)
plt.imshow(test_img)
在这里插入图片描述

通过对图像进行预处理,输入到预测程序中,预测的结果有两个,第一个是密度图,第二个是估计人数,因为输出的估计是估计人数是一个带小数的值,所以要进行四舍五入。其实对密度图求和也是能够得到估计人数的。因为PaddlePaddle输出的密度图是经过转置的,所以在显示时需要再一次执行转置才能正常显示。

test_img = test_img.resize((640, 480), Image.ANTIALIAS)
test_im = np.array(test_img) / 255.0
test_im = test_im.transpose().reshape(1, 3, 640, 480).astype('float32')

results = exe.run(program=inference_program,
                    feed={feed_target_names[0]: test_im},
                    fetch_list=fetch_targets)
density, quantity = results[0], results[1]
q = int(abs(quantity) + 0.5)

print("预测人数:", q)
plt.imshow(density[0][0].T,cmap=CM.jet)
在这里插入图片描述

模型下载

模型名称

所用数据集

下载地址

预训练模型

常规赛-人流密度预测数据集

预测模型

常规赛-人流密度预测数据集

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基于PaddlePaddle实现的密度估计模型CrowdNet

    CrowdNet模型是2016年提出的人流密度估计模型,论文为《CrowdNet: A Deep Convolutional Network for Dense...

    夜雨飘零
  • PaddleWeekly | 假期必备!视频版魔法换天小工具开源

    开源发展至今,越来越多的开发者共享免费代码的同时,也将自己的项目和代码大方骄傲地分享出来。使用者自由的获得项目成果,贡献者找到成就和价值,然后,更多的开发者加入...

    用户1386409
  • 进展 | 密集人群分布检测与计数

    高密度人群聚集容易发生各种意外事件、所以监控与分析高密度人群,防止意外事件发生,具有重要的现实意义,分析高密度人群其中一个最重要的参考就是人群数量、评估聚集人群...

    OpenCV学堂
  • 基于PaddlePaddle实现的目标检测模型SSD

    SSD,全称Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一种目标检测算法,截至目前是主要的检测框架之...

    夜雨飘零
  • 基于PaddlePaddle实现的目标检测模型SSD

    SSD,全称Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一种目标检测算法,截至目前是主要的检测框架之...

    夜雨飘零
  • 人群密度估计--CrowdNet: A Deep Convolutional Network for Dense Crowd Counting

    CrowdNet: A Deep Convolutional Network for Dense Crowd Counting published in ...

    用户1148525
  • 根植于工业级大规模深度学习应用场景的PaddlePaddle

    2018年11月,英特尔人工智能大会(AIDC 2018)在北京国贸酒店举行,这也是英特尔首次专门面向开发者和和技术社区的人工智能大会。PaddlePaddle...

    用户1386409
  • 【词向量】 噪声对比估计加速词向量训练

    导语 PaddlePaddle提供了丰富的运算单元,帮助大家以模块化的方式构建起千变万化的深度学习模型来解决不同的应用问题。这里,我们针对常见的机器学习任务,提...

    用户1386409
  • 基于PaddlePaddle语音识别模型

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

    夜雨飘零
  • 快到没朋友的YOLO v3有了PaddlePaddle 预训练模型

    YOLO作为目标检测领域的创新技术,一经推出就受到开发者的广泛关注。值得一提的是,基于百度自研的开源深度学习平台PaddlePaddle的YOLO v3实现,参...

    机器之心
  • 快到没朋友的YOLO v3有了PaddlePaddle实现

    YOLO作为目标检测领域的创新技术,一经推出就受到开发者的广泛关注。值得一提的是,基于百度自研的开源深度学习平台PaddlePaddle的YOLO v3实现,参...

    用户1386409
  • PaddlePaddle重磅升级,Paddle Fluid v1.4版本发布

    继上个版本发布后,PaddlePaddle添加了很多新的特性和工具组件,目前已发展为集核心框架、工具组件和服务平台为一体的端到端开源深度学习平台。

    用户1386409
  • 百度PaddlePaddle开源视频分类模型Attention Cluster,曾夺挑战赛冠军

    视频分类问题在视频标签、监控、自动驾驶等领域有着广泛的应用,但它同时也是计算机视觉领域面临的一项重要挑战之一。

    机器之心
  • 薅百度GPU羊毛!PaddlePaddle大升级,比Google更懂中文,打响AI开发者争夺战

    深度学习已经推动人工智能进入工业大生产阶段,而深度学习框架则是智能时代的操作系统。

    AI科技大本营
  • 基于PaddlePaddle搭建工业级ICNET应用 预测速度超TensorFlow 20%

    提起ICNET,就不得不说说ICNET构建的初衷-解决图像语义分割在实时应用中的挑战。图像语义分割(semantic segmentation)是结合了图像分类...

    用户1386409
  • 服务全球AI开发者,University AI携手百度AI为你推开现代人工智能大门

    用户1107453
  • 强烈推荐 | 飞桨最全面的工具组件详解

    深度学习技术已经具备了很强的通用性,正在推动人工智能进入工业大生产阶段。飞桨(PaddlePaddle)是百度自研的开源深度学习平台,有全面的官方支持的工业级应...

    用户1386409
  • 专栏 | 云脑科技-实习僧文本匹配模型及基于百度PaddlePaddle的应用

    机器之心
  • 强力推荐!飞桨产业级PaddleCV最新全景图

    导读:PaddleCV是飞桨开源的产业级CV工具与预训练模型集,提供了依托于百度实际产品打磨,能够极大地方便 CV 研究者和工程师快速应用。使用者可以使用Pad...

    用户1386409

扫码关注云+社区

领取腾讯云代金券