PyTorch深度学习实战——人群计数

PyTorch深度学习实战——人群计数

    • 0. 前言
    • 1. 人群计数
      • 1.1 基本概念
      • 1.2 CRSNet 架构
    • 2. 使用 CSRNet 实现人群计数
      • 2.1 模型分析
      • 2.2 数据集分析
      • 2.3 模型构建与训练
    • 相关链接

0. 前言

人群计数是指通过图像或视频分析技术,对给定场景中的人群数量进行估计和统计的过程。人群计数在城市交通监控、公共安全、活动管理等领域具有广泛的应用。例如,在城市交通管理中,可以通过人群计数来评估交通拥堵情况;在公共安全中,可以利用人群计数来监测人员密集区域,及时发现异常情况。本节中,将介绍人群计数的基本概念,并基于 CSRNet 构建人群计数模型。

1. 人群计数

1.1 基本概念

人群计数指的是在一个给定场景中,通过计算机视觉和图像分析技术对人群数量进行精确计数,即估计图像中的人数。目前已经出现了很多不同的方法进行人群计数,例如使用深度学习模型来进行人群检测和跟踪,使用人工智能算法来预测人群密度和人数,以及使用传感器网络来获取实时人流信息等:

  • 单目标人群计数:单目标人群计数是指在给定的图像或视频中,对所有的个体进行逐一检测,然后对其进行计数,这种方法需要先使用目标检测算法(如基于深度学习的目标检测算法)检测出每个人的位置,然后进行数量统计
  • 密度估计人群计数:密度估计人群计数是指通过估计人群的密度分布来推断人群数量,该方法通常基于图像中人群的特征,如头部、肩膀等,通过密度估计算法(如核密度估计、高斯过程回归等)对图像中的人群密度进行建模,从而得到人群数量的估计结果

在构建模型来执行人群计数之前,我们首先了解可用的数据集和模型架构。为了训练能够预测图像中人数的模型,使用的数据集图像中,应该包括所有出现在图像中的人物头部的中心位置。输入图像样本中每个人物头部中心位置构成的图像非常稀疏,图像中使用 N 个白色像素表示图像中的 N 个人。为了便于观察,将标注(稀疏)图像转换为表示图像该区域中的人数的热力图:

标注图像
放大图像左上角,便于观察最终输入-输出对:

示例图像
在上图中,当两个人距离较近时,像素强度很高;当一个人远离其他人时,此人对应的像素密度分布更均匀,且像素强度较低。基本上,按照像素值的总和等于图像中存在的人数生成热力图。

1.2 CRSNet 架构

我们已经了解了人群计数模型需要接收的输入图像、表示图像中人物头部中心位置的稀疏图,以及处理后得到真实输出热力图。接下来,我们将利用 CSRNet 预测图像中的人数。CRSNet 模型架构如下:

CRSNet

在模型架构中,先将图像通过标准 VGG-16 主干网络,然后再通过四个额外的卷积层。接下来,可以使用四种不同的卷积配置(在本节中,我们采取第一种配置),最后通过 1 x 1 x 1 卷积层。
模型使用均方误差 (Mean-Square Error, MSE) 函数,并最小化损失值以学习最佳权重,同时使用平均绝对误差 (Mean Absolute Error, MAE) 跟踪实际人群计数。
需要注意的是,该架构的使用空洞卷积 (dilated convolution) 替代了普通卷积。空洞卷积计算方法如下所示:

空洞卷积
在上图中,第一张图中黄色部分表示普通的卷积核,第二张和第三张中黄色部分图表示扩张卷积核(或空洞卷积核),它们在各个像素之间有间隙,这样可以在不增加卷积核参数数量的情况下,增加感受野,从而提高模型的性能。因为模型需要了解给定人附近的人数,以便估计与此人相对应的像素密度,使用扩张卷积核(有 9 个参数)非普通卷积核(有 49 个参数),能够以更少的参数捕获更多信息。

2. 使用 CSRNet 实现人群计数

2.1 模型分析

在本节中,我们使用 PyTorch 实现 CSRNet 模型以执行人群计数。 在实现人群计数模型之前,首先总结模型实现策略,以对模型有全面的了解:

  1. 导入相关库和数据集
  2. 由于本节所用数据集已经将人物头部的中心位置图像转换为基于高斯滤波器密度的分布,因此无需再次进行转换
  3. 使用神经网络映射输入图像和输出高斯密度图像
  4. 定义函数执行空洞卷积
  5. 定义网络模型,并在批数据上训练模型最小化 MSE 损失

2.2 数据集分析

在本节中,我们使用 Shanghaitech with People Density Map 数据集构建人群计数模型,该数据集是一个用于人群计数研究的数据集,主要用于评估和训练人群计数算法。数据集由两个子数据集组成:Part_APart_BPart_A 包含人群密集场景的图像,而 Part_B 包含人群稀疏场景的图像,每个子数据集都有大约 300 张高分辨率的图像。
对于每张图像,数据集提供了人工标注的人群数量以及相应的人群密度热力图,人群密度热力图是一种灰度图,它通过将每个像素点的值设为该位置上的人群密度来表示人群的分布情况。该数据集可用于训练和评估各种人群计数算法,包括基于密度的方法、检测和回归方法等。可以在 Kaggle 下载 Shanghaitech with People Density Map 数据集,并解压缩。

2.3 模型构建与训练

接下来,使用 PyTorch 实现以上模型策略。

(1) 导入相关库并下载数据集:

import h5py
from scipy import io
from glob import glob
import torch
from torch import optim
from torch.utils.data import DataLoader, Dataset
import cv2
import random
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import cm as c

定义图像 (image_folder)、目标输出 (gt_folder) 和热力图文件夹 (heatmap_folder) 位置:

part_A = glob('archive/shanghaitech_with_people_density_map/ShanghaiTech/part_A/train_data/*')
image_folder = 'archive/shanghaitech_with_people_density_map/ShanghaiTech/part_A/train_data/images/'
heatmap_folder = 'archive/shanghaitech_with_people_density_map/ShanghaiTech/part_A/train_data/ground-truth-h5/'
gt_folder = 'archive/shanghaitech_with_people_density_map/ShanghaiTech/part_A/train_data/ground-truth/'

(2) 定义训练、验证数据集和数据加载器:

device = 'cuda' if torch.cuda.is_available() else 'cpu'class Crowds(Dataset):def __init__(self, stems):self.stems = stemsdef __len__(self):return len(self.stems)def __getitem__(self, ix):_stem = self.stems[ix]image_path = f'{image_folder}/{_stem}.jpg'heatmap_path = f'{heatmap_folder}/{_stem}.h5'gt_path = f'{gt_folder}/GT_{_stem}.mat'pts = io.loadmat(gt_path)pts = len(pts['image_info'][0,0][0,0][0])image = cv2.imread(image_path, 1)h, w, _ = image.shapewith h5py.File(heatmap_path, 'r') as hf:gt = hf['density'][:]gt = cv2.resize(gt, (int(w/8), int(h/8)))*64# gt = resize(gt, 1/8)*64return image.copy(), gt.copy(), ptsdef collate_fn(self, batch):ims, gts, pts = list(zip(*batch))ims = torch.cat([torch.tensor(im)[None].float() for im in ims]).to(device).permute(0,3,1,2)# print(ims.shape)gts = torch.cat([torch.tensor(gt)[None].float() for gt in gts]).to(device)# .permute(0,3,2,1)return ims, gts, torch.tensor(pts).to(device)def choose(self):return self[random.randint(len(self))]def stems(split):items_new = [item.split('/')[-1] for item in split]items = [item.split('.')[0] for item in items_new]return itemsfrom sklearn.model_selection import train_test_split
# print(stems(glob(image_folder)))
trn_stems, val_stems = train_test_split(stems(glob(f'{image_folder}/*.jpg')), random_state=10)trn_ds = Crowds(trn_stems)
val_ds = Crowds(val_stems)trn_dl = DataLoader(trn_ds, batch_size=1, shuffle=True, collate_fn=trn_ds.collate_fn)
val_dl = DataLoader(val_ds, batch_size=1, shuffle=True, collate_fn=val_ds.collate_fn)

调整人群热力图的大小,因为网络的输出尺寸为原始图像尺寸的 1/8,因此我们通过将目标输出图乘以 64,以使图像像素的总和将按比例缩放回原始人群计数。

(3) 定义网络架构.

定义执行空洞卷积的函数 make_layers()

import torch.nn as nn
import torch
from torchvision import modelsdef make_layers(cfg, in_channels = 3,batch_norm=False,dilation = False):if dilation:d_rate = 2else:d_rate = 1layers = []for v in cfg:if v == 'M':layers += [nn.MaxPool2d(kernel_size=2, stride=2)]else:conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=d_rate, dilation=d_rate)if batch_norm:layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]else:layers += [conv2d, nn.ReLU(inplace=True)]in_channels = vreturn nn.Sequential(*layers)

定义网络架构 CSRNet

class CSRNet(nn.Module):def __init__(self, load_weights=False):super(CSRNet, self).__init__()self.seen = 0self.frontend_feat = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512]self.backend_feat = [512, 512, 512, 256, 128, 64]self.frontend = make_layers(self.frontend_feat)self.backend = make_layers(self.backend_feat,in_channels = 512,dilation = True)self.output_layer = nn.Conv2d(64, 1, kernel_size=1)if not load_weights:mod = models.vgg16(pretrained = True)self._initialize_weights()items = list(self.frontend.state_dict().items())_items = list(mod.state_dict().items())for i in range(len(self.frontend.state_dict().items())):items[i][1].data[:] = _items[i][1].data[:]def forward(self,x):x = self.frontend(x)x = self.backend(x)x = self.output_layer(x)return xdef _initialize_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.normal_(m.weight, std=0.01)if m.bias is not None:nn.init.constant_(m.bias, 0)elif isinstance(m, nn.BatchNorm2d):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)

(4) 定义网络训练和验证函数:

def train_batch(model, data, optimizer, criterion):model.train()optimizer.zero_grad()ims, gts, pts = data_gts = model(ims).squeeze(1)# print(gts.shape)# print(_gts.shape)loss = criterion(_gts, gts)loss.backward()optimizer.step()pts_loss = nn.L1Loss()(_gts.sum(), gts.sum())return loss.item(), pts_loss.item()@torch.no_grad()
def validate_batch(model, data, criterion):model.eval()ims, gts, pts = data_gts = model(ims)loss = criterion(_gts, gts)pts_loss = nn.L1Loss()(_gts.sum(), gts.sum())return loss.item(), pts_loss.item()

(5) 训练模型:

model = CSRNet().to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=1e-6)
n_epochs = 40train_loss_epochs = []
val_loss_epochs = []
for ex in range(n_epochs):N = len(trn_dl)trn_loss = []val_loss = []for bx, data in enumerate(trn_dl):loss, pts_loss = train_batch(model, data, optimizer, criterion)pos = (ex + (bx+1)/N)trn_loss.append(loss)train_loss_epochs.append(np.average(trn_loss))N = len(val_dl)for bx, data in enumerate(val_dl):loss, pts_loss = validate_batch(model, data, criterion)pos = (ex + (bx+1)/N)val_loss.append(loss)val_loss_epochs.append(np.average(val_loss))

绘制模型训练期间训练和验证损失的变化情况(损失值是人群计数的 MAE),如下所示:

epochs = np.arange(n_epochs)+1
plt.plot(epochs, train_loss_epochs, 'bo', label='Training loss')
plt.plot(epochs, val_loss_epochs, 'r', label='Test loss')
plt.title('Training and Test loss over increasing epochs')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid('off')
plt.show()

模型检测
(6) 使用训练后的模型对新图像进行推理。

读取测试图像并对其进行预处理:

from matplotlib import cm as c
import numpy as np
from torchvision import datasets, transforms
from PIL import Image
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]),])test_folder = 'archive/shanghaitech_with_people_density_map/ShanghaiTech/part_A/test_data/'
imgs = glob(f'{test_folder}/images/*.jpg')f = random.choice(imgs)
print(f)
img = transform(Image.open(f).convert('RGB')).to(device)

使用训练好的模型处理图像:

output = model(img[None])
print("Predicted Count : ",int(output.detach().cpu().sum().numpy()))
temp = np.asarray(output.detach().cpu().reshape(output.detach().cpu().shape[2],output.detach().cpu().shape[3]))
plt.imshow(temp, cmap = c.jet)
plt.show()

预测结果

从以上输出结果,可以看出模型能够得到合理准确的热力图,预测人数接近实际值。

相关链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络
PyTorch深度学习实战(12)——数据增强
PyTorch深度学习实战(13)——可视化神经网络中间层输出
PyTorch深度学习实战(14)——类激活图
PyTorch深度学习实战(15)——迁移学习
PyTorch深度学习实战(16)——面部关键点检测
PyTorch深度学习实战(17)——多任务学习
PyTorch深度学习实战(18)——目标检测基础
PyTorch深度学习实战(19)——从零开始实现R-CNN目标检测
PyTorch深度学习实战(20)——从零开始实现Fast R-CNN目标检测
PyTorch深度学习实战(21)——从零开始实现Faster R-CNN目标检测
PyTorch深度学习实战(22)——从零开始实现YOLO目标检测
PyTorch深度学习实战(23)——使用U-Net架构进行图像分割
PyTorch深度学习实战(24)——从零开始实现Mask R-CNN实例分割

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

17、类模板

17、类模板 类模板类模板的声明类模板的使用类模板的静态成员类模板的递归实例化 类模板扩展数值型的模板参数模板型成员变量模板型成员函数模板型成员类型模板型模板参数 典型模板错误嵌套依赖依赖模板参数访问成员函数模板子类模板访问基类模板类模板中的成员虚函数 类模板 …

什么是神经网络的非线性

大家好啊,我是董董灿。 最近在写《计算机视觉入门与调优》(右键,在新窗口中打开链接)的小册,其中一部分说到激活函数的时候,谈到了神经网络的非线性问题。 今天就一起来看看,为什么神经网络需…

Vue router深入学习

Vue router深入学习 一、单页应用程序介绍 1.概念 单页应用程序:SPA【Single Page Application】是指所有的功能都在一个html页面上实现 2.具体示例 单页应用网站: 网易云音乐 https://music.163.com/ 多页应用网站:京东 https://jd.co…

【MYSQL】单表查询

查询语法: select 字段(*表示全字段) from 数据表 【where 条件表达式】 【group by 分组字段【having 分组条件表达式】】 【order by 排序字段【asc | desc】】 例子: 教职工表Teacher(Tno, TName, age, sal, mgr, DNo)&#…

通过异步序列化提高图表性能 Diagramming for WPF

通过异步序列化提高图表性能 2023 年 12 月 6 日 MindFusion.Diagramming for WPF 4.0.0 添加了异步加载和保存文件的功能,从而提高了响应能力。 MindFusion.Diagramming for WPF 提供了一个全面的工具集,用于创建各种图表,包括组织结构图、图…

【概率方法】MCMC 之 Gibbs 采样

上一篇文章讲到,MCMC 中的 HM 算法,它可以解决拒绝采样效率低的问题,但是实际上,当维度高的时候 HM 算法还是在同时处理多个维度,以两个变量 x [ x , y ] \mathbf{x} [x,y] x[x,y] 来说,也就是同时从联合…

机器学习 | Python贝叶斯超参数优化模型答疑

机器学习 | Python贝叶斯超参数优化模型答疑 目录 机器学习 | Python贝叶斯超参数优化模型答疑问题汇总问题1答疑问题2答疑问题3答疑问题汇总 问题1:想问一下贝叶斯优化是什么? 问题2:为什么使用贝叶斯优化? 问题3:如何实现? 问题1答疑 超参数优化在大多数机器学习流水线…

[GPT]Andrej Karpathy微软Build大会GPT演讲(上)--GPT如何训练

前言 OpenAI的创始人之一,大神Andrej Karpthy刚在微软Build 2023开发者大会上做了专题演讲:State of GPT(GPT的现状)。 他详细介绍了如何从GPT基础模型一直训练出ChatGPT这样的助手模型(assistant model)。作者不曾在其他公开视频里看过类似的内容,这或许是OpenAI官方…

Project Euler 865 Triplicate Numbers(线性dp)

题目 能通过每次消除3个一样的数字,最终把数字消成空的数字是合法的, 求串长度不超过n的,没有前导0的数字中,合法的数字的个数 n10000,答案对998244353取模,只需要输出数字 思路来源 乱搞AC 题解 暴力…

MacBook电脑内存容量小根本不够用?如何一键解决?

得益于M1系列芯片的强势表现,很多朋友都换用了MacBook,首次接触到了macOS系统。但出乎意料的是,很多人就开始受罪了……明明这么出色的硬件,为何到处都不顺手呢?尤其是容量,MacBook相比同价位的Windows笔记…

在 Qt Creator 中编写 Doxygen 风格的注释

2023年12月10日,周日上午 如何生成Doxygen 风格的注释 在需要Doxygen 风格注释的函数上方输入 /**,然后按下 Enter 键。Qt Creator 将自动为你生成一个注释模板。 输入,Qt Creator会自动帮你补全Doxygen标签 不得不说,写了Doxyge…

江科大 STM32入门教程 P14 定时中断和定时器外部时钟

1 通用定时器中断的初始化(Time2) 1.1 开启RCC的TimxCLK时钟, 由于Time2是由APB1总线的外设控制的 RccAPB1PeriphClockCmd(RCC_APB1PeriPh_TIM2,ENABLE);//使能APB1总线1.2 选择时基单元时钟 选择时基单元内部时钟 TIM_InteralClockConfig(IIM2);//内…