项目参考AAAI Association for the Advancement of Artificial Intelligence
研究背景与意义
随着城市化进程的加快和交通流量的增加,车辆行人检测系统在交通安全和智能交通管理中扮演着重要的角色。车辆行人检测系统可以通过识别和跟踪道路上的车辆和行人,提供实时的交通信息和预警,帮助驾驶员和交通管理部门做出更明智的决策,减少交通事故的发生,提高交通效率。
传统的车辆行人检测系统主要基于传感器技术,如雷达、摄像头和红外传感器等。然而,这些传统方法存在一些局限性,如受到光照、天气和目标遮挡等因素的影响,检测准确率较低,容易出现误报和漏报的情况。此外,传统方法需要手动设计特征和分类器,需要大量的人力和时间成本。
近年来,深度学习技术的快速发展为车辆行人检测系统带来了新的机遇。深度学习是一种基于人工神经网络的机器学习方法,通过多层次的神经网络模型来学习和提取数据的特征,从而实现高效准确的目标检测和分类。深度学习网络可以自动学习和提取图像的特征,不需要手动设计特征,大大减少了人力和时间成本。此外,深度学习网络具有较强的泛化能力,可以适应不同场景和复杂环境下的目标检测任务。
基于深度学习网络的车辆行人检测系统具有以下几个方面的意义:
提高检测准确率:深度学习网络可以学习和提取图像的高级特征,能够更准确地识别和跟踪道路上的车辆和行人。相比传统方法,基于深度学习网络的车辆行人检测系统具有更高的检测准确率和更低的误报率。
实时性和稳定性:深度学习网络可以通过GPU并行计算来加速图像处理和目标检测过程,实现实时的车辆行人检测。此外,深度学习网络具有较强的鲁棒性,可以适应不同光照、天气和目标遮挡等复杂环境下的检测任务。
降低成本:基于深度学习网络的车辆行人检测系统不需要手动设计特征和分类器,减少了人力和时间成本。此外,深度学习网络可以通过迁移学习和模型压缩等技术来减少模型的存储和计算资源需求,降低了系统的硬件成本。
智能交通管理:基于深度学习网络的车辆行人检测系统可以实时提供交通信息和预警,帮助交通管理部门做出更明智的决策,优化交通流量和减少交通拥堵。此外,车辆行人检测系统还可以与其他智能交通设备和系统集成,实现智能交通管理和服务。
综上所述,基于深度学习网络的车辆行人检测系统具有重要的研究意义和实际应用价值。通过提高检测准确率、实现实时性和稳定性、降低成本和优化交通管理,基于深度学习网络的车辆行人检测系统将为交通安全和智能交通管理带来新的突破和进展。
基于深度学习网络的车辆行人检测系统_哔哩哔哩_bilibili
首先,我们需要收集所需的图片。这可以通过不同的方式来实现,例如使用现有的公开数据集TrafficDatasets。
labelImg是一个图形化的图像注释工具,支持VOC和YOLO格式。以下是使用labelImg将图片标注为VOC格式的步骤:
(1)下载并安装labelImg。
(2)打开labelImg并选择“Open Dir”来选择你的图片目录。
(3)为你的目标对象设置标签名称。
(4)在图片上绘制矩形框,选择对应的标签。
(5)保存标注信息,这将在图片目录下生成一个与图片同名的XML文件。
(6)重复此过程,直到所有的图片都标注完毕。
由于YOLO使用的是txt格式的标注,我们需要将VOC格式转换为YOLO格式。可以使用各种转换工具或脚本来实现。
下面是一个简单的方法是使用Python脚本,该脚本读取XML文件,然后将其转换为YOLO所需的txt格式。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
classes = [] # 初始化为空列表
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[1]) / 2.0
y = (box[2] + box[3]) / 2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(image_id):
in_file = open('./label_xml\%s.xml' % (image_id), encoding='UTF-8')
out_file = open('./label_txt\%s.txt' % (image_id), 'w') # 生成txt格式文件
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes:
classes.append(cls) # 如果类别不存在,添加到classes列表中
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
xml_path = os.path.join(CURRENT_DIR, './label_xml/')
# xml list
img_xmls = os.listdir(xml_path)
for img_xml in img_xmls:
label_name = img_xml.split('.')[0]
print(label_name)
convert_annotation(label_name)
print("Classes:") # 打印最终的classes列表
print(classes) # 打印最终的classes列表
我们需要将数据集整理为以下结构:
-----data
|-----train
| |-----images
| |-----labels
|
|-----valid
| |-----images
| |-----labels
|
|-----test
|-----images
|-----labels
确保以下几点:
所有的训练图片都位于data/train/images目录下,相应的标注文件位于data/train/labels目录下。
所有的验证图片都位于data/valid/images目录下,相应的标注文件位于data/valid/labels目录下。
所有的测试图片都位于data/test/images目录下,相应的标注文件位于data/test/labels目录下。
这样的结构使得数据的管理和模型的训练、验证和测试变得非常方便。
Epoch gpu_mem box obj cls labels img_size
1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s]
all 3395 17314 0.994 0.957 0.0957 0.0843
Epoch gpu_mem box obj cls labels img_size
2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s]
all 3395 17314 0.996 0.956 0.0957 0.0845
Epoch gpu_mem box obj cls labels img_size
3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
import cv2
import numpy as np
from pathlib import Path
from utils.general import non_max_suppression, scale_coords
from models.common import DetectMultiBackend
from utils.augmentations import letterbox
class VehicleAndPedestrianDetection:
def __init__(self):
......
该程序文件是一个基于YOLOv5的车辆和行人检测系统。程序的主要功能包括加载模型、图像预处理、检测车辆和行人、实时检测等。
import torch.nn as nn
class Conv(nn.Module):
# Standard convolution
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super().__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
def forward(self, x):
return self.act(self.bn(self.conv(x)))
def forward_fuse(self, x):
return self.act(self.conv(x))
class DWConv(Conv):
# Depth-wise convolution class
def __init__(self, c1, c2, k=1, s=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
class Bottleneck(nn.Module):
# Standard bottleneck
def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c_, c2, 3, 1, g=g)
self.add = shortcut and c1 == c2
def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
class BottleneckCSP(nn.Module):
# CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
self.cv4 = Conv(2 * c_, c2, 1, 1)
self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
self.act = nn.SiLU()
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
def forward(self, x):
y1 = self.cv3(self.m(self.cv1(x)))
y2 = self.cv2(x)
return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
class C3(nn.Module):
# CSP Bottleneck with 3 convolutions
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, 1, 1)
self.cv2 = Conv(c1, c_, 1, 1)
self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))
# self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
def forward(self, x):
return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
class C3TR(C3):
# C3 module with TransformerBlock()
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e)
self.m = TransformerBlock(c_, c_, 4, n)
class C3SPP(C3):
# C3 module with SPP()
def __init__(self, c1, c2, k=(5, 9, 13), n=1, shortcut=True, g=1, e=0.5):
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e)
self.m = SPP(c_, c_, k)
class C3Ghost(C3):
# C3 module with GhostBottleneck()
def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e) # hidden channels
self.m = nn.Sequential(*(GhostBottleneck(c_, c_) for _ in range(n))))
这个程序文件是YOLOv5的一个模块,包含了一些常用的函数和类。文件中定义了一些卷积和池化等操作的类,以及一些常用的函数。其中包括了一些常用的卷积操作,如标准卷积(Conv)、深度卷积(DWConv)、CSP卷积(BottleneckCSP)等;还包括了一些常用的池化操作,如空间金字塔池化(SPP)等。此外,还定义了一些辅助函数,如自动填充函数(autopad)、颜色转换函数(colorstr)等。整个文件的目的是提供一些常用的函数和类,方便在YOLOv5模型中使用。
class CrossConv(nn.Module):
# Cross Convolution Downsample
def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
# ch_in, ch_out, kernel, stride, groups, expansion, shortcut
super().__init__()
c_ = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, c_, (1, k), (1, s))
self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g)
self.add = shortcut and c1 == c2
def forward(self, x):
return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
class Sum(nn.Module):
# Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
def __init__(self, n, weight=False): # n: number of inputs
super().__init__()
self.weight = weight # apply weights boolean
self.iter = range(n - 1) # iter object
if weight:
self.w = nn.Parameter(-torch.arange(1.0, n) / 2, requires_grad=True) # layer weights
def forward(self, x):
y = x[0] # no weight
if self.weight:
w = torch.sigmoid(self.w) * 2
for i in self.iter:
y = y + x[i + 1] * w[i]
else:
for i in self.iter:
y = y + x[i + 1]
return y
class MixConv2d(nn.Module):
# Mixed Depth-wise Conv https://arxiv.org/abs/1907.09595
def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): # ch_in, ch_out, kernel, stride, ch_strategy
super().__init__()
n = len(k) # number of convolutions
if equal_ch: # equal c_ per group
i = torch.linspace(0, n - 1E-6, c2).floor() # c2 indices
c_ = [(i == g).sum() for g in range(n)] # intermediate channels
else: # equal weight.numel() per group
b = [c2] + [0] * n
a = np.eye(n + 1, n, k=-1)
a -= np.roll(a, 1, axis=1)
a *= np.array(k) ** 2
a[0] = 1
c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b
self.m = nn.ModuleList(
[nn.Conv2d(c1, int(c_), k, s, k // 2, groups=math.gcd(c1, int(c_)), bias=False) for k, c_ in zip(k, c_)])
self.bn = nn.BatchNorm2d(c2)
self.act = nn.SiLU()
def forward(self, x):
return self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
class Ensemble(nn.ModuleList):
# Ensemble of models
def __init__(self):
super().__init__()
def forward(self, x, augment=False, profile=False, visualize=False):
y = []
for module in self:
y.append(module(x, augment, profile, visualize)[0])
# y = torch.stack(y).max(0)[0] # max ensemble
# y = torch.stack(y).mean(0) # mean ensemble
y = torch.cat(y, 1) # nms ensemble
return y, None # inference, train output
def attempt_load(weights, map_location=None, inplace=True, fuse=True):
from models.yolo import Detect, Model
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
model = Ensemble()
for w in weights if isinstance(weights, list) else [weights]:
ckpt = torch.load(attempt_download(w), map_location=map_location) # load
if fuse:
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
else:
model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().eval()) # without layer fuse
# Compatibility updates
for m in model.modules():
if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU, Detect, Model]:
m.inplace = inplace # pytorch 1.7.0 compatibility
if type(m) is Detect:
if not isinstance(m.anchor_grid, list): # new Detect Layer compatibility
delattr(m, 'anchor_grid')
setattr(m, 'anchor_grid', [torch.zeros(1)] * m.nl)
elif type(m) is Conv:
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
if len(model) == 1:
return model[-1] # return model
else:
print(f'Ensemble created with {weights}\n')
for k in ['names']:
setattr(model, k, getattr(model[-1], k))
model.stride = model[torch.argmax(torch.tensor([m.stride.max() for m in model])).int()].stride # max stride
return model # return ensemble
这个程序文件是YOLOv5的实验模块。文件中定义了几个类和函数:
CrossConv
类:交叉卷积下采样。通过两个卷积层实现,可以选择是否添加shortcut连接。
Sum
类:多个层的加权和。可以选择是否应用权重。
MixConv2d
类:混合深度卷积。通过多个卷积层实现,可以选择是否等通道数。
Ensemble
类:模型集合。可以将多个模型组合成一个集合,用于进行推理。
attempt_load
函数:加载模型权重。可以加载单个模型或模型集合的权重,并进行一些兼容性更新。
总体来说,这个程序文件定义了一些实验性的模块和函数,用于YOLOv5模型的实现和训练。
class YOLOv5:
def __init__(self, weights):
self.weights = weights
self.model = self.load_model()
def load_model(self):
# Load the YOLOv5 model
model = ...
return model
def preprocess_image(self, image):
# Preprocess the input image
processed_image = ...
return processed_image
def postprocess_output(self, output):
# Postprocess the model output
processed_output = ...
return processed_output
def predict(self, image):
# Run inference on the input image
processed_image = self.preprocess_image(image)
output = self.model(processed_image)
processed_output = self.postprocess_output(output)
return processed_output
这是一个使用TensorFlow和Keras实现的YOLOv5模型的程序文件。它包含了YOLOv5模型的各个组件的定义,如卷积层、批归一化层、激活函数等。该文件还定义了YOLOv5模型的前向传播过程,并提供了导出模型的方法。
该文件中的主要组件包括:
TFConv
:标准卷积层TFBN
:TensorFlow的批归一化层包装器TFPad
:填充层TFFocus
:将宽高信息聚合到通道维度的层TFBottleneck
:标准的瓶颈块TFConv2d
:替代PyTorch的nn.Conv2D
的层TFBottleneckCSP
:CSP瓶颈块TFC3
:带有3个卷积层的CSP瓶颈块TFSPP
:YOLOv3-SPP中使用的空间金字塔池化层TFSPPF
:空间金字塔池化-Fast层TFDetect
:检测层该文件还定义了一些辅助函数和变量,用于模型的前向传播和推理过程。
你可以使用该文件中的模型定义来构建和训练YOLOv5模型,或者使用导出方法将模型导出为SavedModel、PB、TFLite或TFJS格式。
class Detect(nn.Module):
stride = None # strides computed during build
onnx_dynamic = False # ONNX export parameter
def __init__(self, nc=80, anchors=(), ch=(), inplace=True): # detection layer
super().__init__()
self.nc = nc # number of classes
self.no = nc + 5 # number of outputs per anchor
self.nl = len(anchors) # number of detection layers
self.na = len(anchors[0]) // 2 # number of anchors
self.grid = [torch.zeros(1)] * self.nl # init grid
self.anchor_grid = [torch.zeros(1)] * self.nl # init anchor grid
self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2)) # shape(nl,na,2)
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
self.inplace = inplace # use in-place ops (e.g. slice assignment)
def forward(self, x):
z = [] # inference output
for i in range(self.nl):
x[i] = self.m[i](x[i]) # conv
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
if not self.training: # inference
if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
y = x[i].sigmoid()
if self.inplace:
y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
y = torch.cat((xy, wh, y[..., 4:]), -1)
z.append(y.view(bs, -1, self.no))
return x if self.training else (torch.cat(z, 1), x)
def _make_grid(self, nx=20, ny=20, i=0):
d = self.anchors[i].device
if check_version(torch.__version__, '1.10.0'): # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility
yv, xv = torch.meshgrid([torch.arange(ny, device=d), torch.arange(nx, device=d)], indexing='ij')
else:
yv, xv = torch.meshgrid([torch.arange(ny, device=d), torch.arange(nx, device=d)])
grid = torch.stack((xv, yv), 2).expand((1, self.na, ny, nx, 2)).float()
anchor_grid = (self.anchors[i].clone() * self.stride[i]) \
.view((1, self.na, 1, 1, 2)).expand((1, self.na, ny, nx, 2)).float()
return grid, anchor_grid
class Model(nn.Module):
def __init__(self, cfg='yolov5s.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes
super().__init__()
if isinstance(cfg, dict):
self.yaml = cfg # model dict
else: # is *.yaml
import yaml # for torch hub
self.yaml_file = Path(cfg).name
with open(cfg, encoding='ascii', errors='ignore') as f:
self.yaml = yaml.safe_load(f) # model dict
# Define model
ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels
if nc and nc != self.yaml['nc']:
LOGGER.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}")
self.yaml['nc'] = nc # override yaml value
if anchors:
LOGGER.info(f'Overriding model.yaml anchors with anchors={anchors}')
self.yaml['anchors'] = round(anchors) # override yaml value
self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist
self.names = [str(i) for i in range(self.yaml['nc'])] # default names
self.inplace = self.yaml.get('inplace', True)
# Build strides, anchors
m = self.model[-1] # Detect()
if isinstance(m, Detect):
s = 256 # 2x min stride
m.inplace = self.inplace
m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward
m.anchors /= m.stride.view(-1, 1, 1)
check_anchor_order(m)
self.stride = m.stride
self._initialize_biases() # only run once
# Init weights, biases
initialize_weights(self)
self.info()
LOGGER.info('')
def forward(self, x, augment=False, profile=False, visualize=False):
if augment:
return self._forward_augment(x) # augmented inference, None
return self._forward_once(x, profile, visualize) # single-scale inference, train
def _forward_augment(self, x):
img_size = x.shape[-2:] # height, width
s = [1, 0.83, 0.67] # scales
f = [None, 3, None] # flips (2-ud, 3-lr)
y = [] # outputs
for si, fi in zip(s, f):
xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max()))
yi = self._forward_once(xi)[0] # forward
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
yi = self._descale_pred(yi, fi, si, img_size)
y.append(yi)
y = self._clip_augmented(y) # clip augmented tails
return torch.cat(y, 1), None # augmented inference, train
def _forward_once(self, x, profile=False, visualize=False):
y, dt = [], [] # outputs
for m in self.model:
if m.f != -1: # if not from previous layer
x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
if profile:
self._profile_one_layer(m, x, dt)
x = m(x) # run
y.append(x if m.i in self.save else None) # save output
if visualize:
feature_visualization(x, m.type, m.i, save_dir=visualize)
return x
def _descale_pred
这个程序文件是YOLOv5的具体实现,包含了YOLOv5的相关模块和类。程序文件中的主要内容包括:
其中,Detect类继承自nn.Module类,用于定义YOLOv5的检测层。Model类也继承自nn.Module类,用于构建整个YOLOv5模型。在Model类中,定义了一些方法,包括前向传播方法、数据增强方法等。
总的来说,这个程序文件是YOLOv5模型的具体实现,包含了模型的定义、前向传播方法和一些辅助方法。
import requests
import json
def get_weather(city):
url = f"http://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q={city}"
response = requests.get(url)
data = json.loads(response.text)
temperature = data['current']['temp_c']
condition = data['current']['condition']['text']
return f"The current temperature in {city} is {temperature}°C with {condition}."
city = input("Enter city name: ")
print(get_weather(city))
封装为类后的代码如下:
import requests
import json
class WeatherAPI:
def __init__(self, api_key):
self.api_key = api_key
def get_weather(self, city):
url = f"http://api.weatherapi.com/v1/current.json?key={self.api_key}&q={city}"
response = requests.get(url)
data = json.loads(response.text)
temperature = data['current']['temp_c']
condition = data['current']['condition']['text']
return f"The current temperature in {city} is {temperature}°C with {condition}."
api_key = "YOUR_API_KEY"
weather_api = WeatherAPI(api_key)
city = input("Enter city name: ")
print(weather_api.get_weather(city))
在封装的类中,我们将原来的函数 get_weather
移动到类中,并添加了一个构造函数 __init__
,用于初始化 API 密钥。在类的方法中,我们使用 self.api_key
来访问 API 密钥,并使用 self
来引用类的实例。这样,我们就可以通过创建一个 WeatherAPI
的实例,并调用 get_weather
方法来获取天气信息。
这个程序文件是一个Python模块的初始化文件,位于models文件夹中。它的主要作用是将models文件夹下的其他模块导入并提供给其他程序使用。
该文件的代码是空的,没有任何具体的功能实现。它可能是一个占位文件,用于标识models文件夹作为一个模块的起点。
在其他程序中,可以通过导入models模块来访问models文件夹下的其他模块。例如,可以使用类似以下的代码导入一个名为model1的模块:
from models import model1
然后可以使用model1模块中定义的函数、类或变量来完成特定的任务。
根据以上分析,该程序的整体功能是实现基于深度学习网络的车辆行人检测系统。它使用YOLOv5模型进行目标检测,并提供了一个用户界面(ui.py)来与系统进行交互。
下面是每个文件的功能整理:
文件路径 | 功能 |
---|---|
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\ui.py | 提供用户界面,与车辆行人检测系统进行交互 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\models\common.py | 定义了一些常用的卷积和池化等操作的类和函数 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\models\experimental.py | 定义了一些实验性的模块和函数,用于YOLOv5模型的实现和训练 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\models\tf.py | 使用TensorFlow和Keras实现的YOLOv5模型的定义和导出方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\models\yolo.py | YOLOv5模型的具体实现,包含模型的定义、前向传播方法和辅助方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\models_init_.py | 模块的初始化文件,用于导入其他模块 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\activations.py | 定义了一些激活函数 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\augmentations.py | 定义了一些数据增强方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\autoanchor.py | 定义了自动锚框生成的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\autobatch.py | 定义了自动批次大小调整的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\callbacks.py | 定义了一些回调函数,用于模型训练过程中的回调操作 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\datasets.py | 定义了数据集的加载和处理方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\downloads.py | 定义了下载数据集和模型权重的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\general.py | 定义了一些通用的辅助函数和方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loss.py | 定义了一些损失函数 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\metrics.py | 定义了一些评估指标 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\plots.py | 定义了一些绘图函数,用于可视化训练和评估结果 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\torch_utils.py | 定义了一些与PyTorch相关的辅助函数和方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools_init_.py | 工具模块的初始化文件,用于导入其他工具模块 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\aws\resume.py | 定义了AWS训练恢复的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\aws_init_.py | AWS工具模块的初始化文件,用于导入其他AWS工具模块 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\flask_rest_api\example_request.py | 定义了一个示例请求的方法,用于Flask REST API的测试 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\flask_rest_api\restapi.py | 定义了Flask REST API的实现,用于提供模型的RESTful接口 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loggers_init_.py | 日志记录器模块的初始化文件,用于导入其他日志记录器模块 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loggers\wandb\log_dataset.py | 定义了使用WandB记录数据集的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loggers\wandb\sweep.py | 定义了使用WandB进行超参数搜索的方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loggers\wandb\wandb_utils.py | 定义了与WandB相关的辅助函数和方法 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\tools\loggers\wandb_init_.py | WandB日志记录器模块的初始化文件,用于导入其他WandB日志记录器模块 |
E:\视觉项目\shop\基于深度学习网络的车辆行人检测系统\code\utils\activations.py | 定义了一些激活函数 |
早期的神经感知网络通过模拟生物视觉神经元间的连接模式建立,LeCun等人在改进神经感知网络的基础上提出了卷积神经网络,设计的LeNet-5网络在识别手写字符上的准确度非常高,证明了卷积神经网络的有效性。随着研究的不断深入,该领域取得了许多令人瞩目的研究成果,引起了当时高度关注,但由于当时计算资源有限等原因,关于卷进神经网络研究热度逐渐褪去,直到AlexNet 的出现,卷积神经网络重新受到研究者们的重视,如今基于卷积神经网络的深度学习技术快速发展,被广泛应用于目标分类和检测、自热语言处理和推荐系统等领域,下面对卷积神经网络中的基本单元进行了说明。
输入层完成对输入图像的预处理和数据增强等。预处理主要包括将输入图像标准化,使其维度大小符合网络输入格式,此外还包括归一化和均值化等处理手段,将数据分布统一到同一范围,降低因数据差异或者图像中的噪声给训练带来的不利影响。当训练数据不够时,可以通过数据增强增加数据量,数据增强通过翻转、裁剪、旋转、添加马赛克和高斯噪声等手段从有限的数据集中产生新的数据,除了增加数据量外,数据增强还可以提高模型的泛化能力。
卷积层是整个卷积神经网络的关键部分,主要用来完成特征提取,浅层卷积层提取低级的基本特征,随着网络深度增加卷积层提取到的特征也变得越来越复杂,其中语义信息也更丰富。卷积运算通过卷积核和图像或者特征图上的相应位置的数据相乘后求和,作为下一层对应位置的特征,该过程通过滑动窗口实现,从图像或特征图的左上角移动到右下角。如图2.1所示,卷积层输入单元大小为5x5,经过3x3的卷积核卷积运算后得到的输出特征图,图展示了卷积运算的基本操作流程,在实际的卷积神经网络中卷积运算还要考虑卷积步长和 padding 等参数。
在卷积神经网络的运行过程中会产生大量的特征图,使得计算量进一步增加,实时性也会受到影响。池化层对特征图降采样,减少特征图中的参数,经过池化层后,网络中重要信息被保留,而冗余的特征被丢弃,在一定程度上还具有防止过拟合的作用。最大池化只保留局部位置中权值最大的特征点,其余特征点被去除。平均池化和最大池化类似,不同的是最后保留的特征点为局部位置内所有特征点的平均值。其他的池化方法还有随机池化、中值池化等。图展示了在池化步长为2在的情况下,特征图在最大池化和平均池化后得到不同输出结果,在该图中经过池化层后特征参数量减少了四分之三。
全连接层通常出现在网络的后半部分,将之前的多维特征图转换成长度固定的特征向量,方便输入到后续的分类器中分类,全连接层的每个结点和前一层的每个结点相连接,因此全连接层占据了网络的大部分参数。有些检测模型为了减少网络参数量,设计了不含全连接层的结构,如GoogLeNet"采用全局平均池化取代全连接层,这样不仅减少了参数量,而且使网络结构更加灵活,不用限制图像的输入分辨率,但全连接层凭借着实现简单且易于理解的特点,依然被广泛采用。
特征提取的过程可以看成是在输入图像上进行线性组合计算的过程,由于计算机视觉中的检测任务十分复杂,仅仅用线性模型很难解决,因此引入了具有非线性计算功能的激活函数,提高模型的表征能力。下面对常用的激活函数进行了详细介绍。
ReLU函数借鉴了脑神经元信号传播的过程,其数学表达式如所示:
在基于深度学习的目标检测算法中,网络在最后的预测阶段会产生大量的检测框,由于对图像中的每个目标最多输出一个检测结果,需要通过后处理去除多余的检测框,最终保留下来的检测框将作为预测结果输出,非极大值抑制算法NMS是最常用的后处理算法,已经成为目标检测算法中不可或缺的步骤。
同一个图像中经过网络前向传播后可能会产生多个类别的检测框,分别将这些检测框划分到各自的类别集合中,每个集合中的检测框按置信度从大到小排序,对于每个集合每次将当前置信度最大的检测框M移入输出集合中,遍历集合中所有剩余的检测框b,若bi与M的交并比IOU大于预设阈值N,则将b,从集合中删除,重复该过程,直到集合为空。非极大值抑制NMS 算法中当前置信度最大的检测框M对于其它检测框置信度抑制方式的数学表达式如所示:
在目标检测中通常用后处理NMS去除多余的检测框,但在存在密集物体的情况NMS直接抑制其他检测框的方式会产生漏检。如图中的两匹马,红色框置信度最高,将会保留下来,如果 NMS的IOU阈值设为0.5,当红色框和绿色框的IOU大于0.5时,含有真实标签的绿色框将会被丢弃。但绿色框和红色框是在检测不同的两匹马,绿色框不应被丢弃,这就是NMS造成的漏检问题,简单地提高IOU阈值可以减少漏检问题,但重复检测的概率将会提高,仅仅靠改变IOU阈值无法平衡召回率和准确率。
对于该系统目标检测环节所需要完成相关任务,既要对机动车辆和非机动车辆等较大目标进行检测,也要对行人和红绿灯等小物体进行检测。同时为了达到实时检测的效果,需要采用结构相对简单的网络模型,因此采用YOLOv5系列的目标检测算法。
鉴于微计算机所配置的硬件、运算能力有限,难以为车辆行人检测提供较大算力的情况,而且从节约成本各方面考虑,本文采用轻量级的YOLOv5s 网络算法结构(图)。
YOLOv5s网络结构由四个部分组成: Input、Backbone、Neck和Head,其中 Backbone和Neck部分是重点部分。在 Backbone部分中,主要使用Focus+CSP结构。Focus是一种对featuremap 的切片操作把宽度w和高度h的信息整合到c维度,具体来说就是将相距为2的四个位置进行堆叠到一个通道上去,因此长h和宽w都缩小两倍,通道c数增加4倍,Focus模块设计用于降低FLOPS和提高速度,而不是提高mAP。而CSP结构通过将基础层的特征图划分为两个部分,然后通过CSP结构将它们合并,可以在能够实现更丰富的梯度组合的同时减少计算量。Neck使用了PANet结构,该结构在FPN的基础上加了一个自底向上方向的增强,使得顶层feature map也可以享受到底层带来的丰富的位置信息,从而提升了对车辆这类大物体的检测效果。
为了保证系统实时检测工作对帧率的要求,可以通过网络参数depth_multiple和 width_multiple的修改配置不同复杂度的模型。其中,参数depth_multiple控制网络的深度(Bottle-neckCSP数),参数width_multiple控制网络的宽度(卷积核数量)。在此项目中,需要通过识别的类别较少,但对精度要求较高,且由于微计算机Jetson的算力有限,因此设置depth_mul-tiple为0.46, width_multiple为0.35。
YOLOv5的损失函数由分类损失(classification loss)、定位损失(localization loss)和置信度损失(confidence loss)三部分构成,总的损失函数为三类损失之和。YOLOv5采用了BE-CLogits损失函数计算 objectness score的损失,而class prob-ability score采用了交叉嫡损失函数(BCEclsloss), bounding box采用了GIOU Loss计算损失,GIOU直接把IoU作为回归的损失,可表示为:
为了更全面地评估本文所做的研究和工作,通过将后处理 MergeSoft-NMS算法和用K-means++提取的先验框应用于之前的LightweightYOLOv5作为本文算法。由于本文算法是对YOLOv5进行改进提出的,在对比算法中加入了YOLOv3,YOLOv3-tiny是基于YOLOv3设计的精简版本,也将它加入了对比算法中。本文还选取了具有代表性的检测算法Faster R-CNN和 SSD。为了确保对比的公平性,所有实验运行平台的软硬件配置相同,对比结果如表4.9。
如表4.9所示,本文算法在检测精度上高于其他算法,mAP相比于YOLOv3、YOLOv3-tiny、SlimYOLOv3、SSD500和FasterR-CNN算法,在精度上分别高出了约5.18%、104%、6.25%、8.62%和 14.3%。在检测时间上,除了YOLOv3-tiny算法,本文算法和其他算法相比优势非常明显,可见在模型压缩的作用下,参数和运行时计算量的减少使得检测速度提升很大,SlimYOLOv3是对YOLOv3进行卷积通道剪枝得到的检测模型,并没有做其他方面的改进,本文算法不仅在卷积通道上剪枝还对残差模块上进行了剪枝,所以和SlimYOLOv3相比,本文算法在检测速度的提升上更明显。SSD500和 Faster-RCNN作为常用的检测算法,无论在检测速度还是精度上都不及本文算法。综上所述,本文算法更适合应用于无人驾驶系统完成车辆和行人检测任务。
下图[完整源码&数据集&环境部署视频教程&自定义UI界面]