项目效果
配置项目运行环境
!pip install paddlepaddle-gpu==1.8.0 -i https://mirror.baidu.com/pypi/simple #环境使用 paddlepaddle-gpu 1.8.0版本
!pip install paddlex==1.3.7 -i https://mirror.baidu.com/pypi/simple #下载paddlex1.3.7版本模型包
解压数据集
!unzip /home/aistudio/data/data21544/PascalVOC2007.zip -d /home/aistudio/work/ #解压数据集到工作目录work
导入依赖并设置工作环境
import matplotlib
matplotlib.use('Agg') #修改渲染器使其只生成图像,不显示图像
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0' #设置程序的运行环境为gpu 此命令必须再导入paddle和paddlex之前执行
import paddlex as pdx
os.chdir('/home/aistudio/work/') #将工作目录切换到work目录
进行数据的读取和准备
base = '/home/aistudio/work/PascalVOC2007/VOC2007_test' #设置数据集目录
imgs = os.listdir(os.path.join(base, 'JPEGImages')) #利用join()函数合成图片所在目录,并获取文件的路径以list的形式返回
print('total:', len(imgs))
with open(os.path.join(base, 'train_list.txt'), 'w') as f: #以读取的方法打开 train_list.txt 若没有则新建此文本
for im in imgs[:-200]:
info = 'JPEGImages/'+im+' '
info += 'Annotations/'+im[:-4]+'.xml\n'
f.write(info) #第一个至倒数第200个为训练集 每一数据为图片路径 第二部分为其对应的xml信息
with open(os.path.join(base, 'val_list.txt'), 'w') as f:
for im in imgs[-200:]:
info = 'JPEGImages/'+im+' '
info += 'Annotations/'+im[:-4]+'.xml\n'
f.write(info) #倒数两百个为测试集
CLASSES = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus',
'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',
'motorbike', 'person', 'pottedplant', 'sheep', 'sofa',
'train', 'tvmonitor'] # 记录图像识别中的物体种类
with open('labels.txt', 'w') as f:
for v in CLASSES:
f.write(v+'\n') #写入到work目录下的labels.txt
进行图像增强和标准化
from paddlex.det import transforms #从 paddlex模块中导入图像预处理
train_transforms = transforms.Compose([
transforms.MixupImage(mixup_epoch=250), #对图像进行mixup操作
transforms.RandomDistort(), #以一定的概率对图像进行随机像素内容变换
transforms.RandomExpand(), #随机扩张
transforms.RandomCrop(), #随机裁剪
transforms.Resize(target_size=512, interp='RANDOM'), #将图片长宽设置为[target_size, target_size],差值方式为随机
transforms.RandomHorizontalFlip(), #以一定的概率对图像进行随机水平翻转
transforms.Normalize(), #图片标准化
])
eval_transforms = transforms.Compose([
transforms.Resize(target_size=512, interp='CUBIC'), #将图片长宽设置为[target_size, target_size],差值方式为‘CUBIC’
transforms.Normalize(), #图片标准化
])
模型设置与训练
train_dataset = pdx.datasets.VOCDetection(
data_dir=base, #设置数据集所在的目录路径
file_list=os.path.join(base, 'train_list.txt'), #数据集图片文件和对应标注文件的文件路径
label_list='labels.txt', #数据集包含的类别信息文件路径
transforms=train_transforms, #数据集中每个样本的预处理/增强算子
shuffle=True) #对数据集中样本打乱顺序
eval_dataset = pdx.datasets.VOCDetection(
data_dir=base, #设置数据集所在的目录路径
file_list=os.path.join(base, 'val_list.txt'), #数据集图片文件和对应标注文件的文件路径
label_list='labels.txt', #数据集包含的类别信息文件路径
transforms=eval_transforms) #数据集中每个样本的预处理/增强算子
num_classes = len(train_dataset.labels) + 1 #数据集包含的类别数
model = pdx.det.YOLOv3( #采用paddlex中的YOLOv3模型
num_classes=num_classes, #设置数据集类别数
backbone='DarkNet53' #主干网络为'DarkNet53'
)
model.train(
num_epochs=50, #训练迭代轮数
train_dataset=train_dataset, #设置训练数据集
train_batch_size=8, #训练数据batch的大小
eval_dataset=eval_dataset, #设置训练测试集
learning_rate=0.00025, #设置优化器的学习率
lr_decay_epochs=[10, 15], #优化器的学习率衰减轮数
save_interval_epochs=10, #模型保存间隔
log_interval_steps=100, #训练日志输出间隔
save_dir='./YOLOv3', #模型保存路径
pretrain_weights='IMAGENET', #自动下载在ImageNet图像数据上预训练的模型权重
use_vdl=True) #使用VisualDL进行可视化
model = pdx.load_model('./YOLOv3/best_model') #载入训练出来的最优模型
model.evaluate(eval_dataset, batch_size=1, epoch_id=None, metric=None, return_details=False) #在测试集上进行模型评估
测试模型在图片上的识别效果
import cv2
import time
import numpy as np
import matplotlib.pyplot as plt #导入图像处理相关的包
%matplotlib inline
#ipython魔法函数,可以直接在python console中生成图像
image_name = './test.jpg' #设置待处理的图像名称
start = time.time() #记录识别开始时间
result = model.predict(image_name, eval_transforms) #对图像进行识别
print('infer time:{:.6f}s'.format(time.time()-start)) #输出图像识别所花时间
print('detected num:', len(result)) #输出识别到的目标数
im = cv2.imread(image_name) #cv2读取图片
font = cv2.FONT_HERSHEY_SIMPLEX #设置添加文字的字体
threshold = 0.0 #设置过滤边界框的阈值,此处为不过滤
for value in result:
xmin, ymin, w, h = np.array(value['bbox']).astype(np.int) #获取识别到边界框的坐标、宽度、高度
cls = value['category'] #获取识别到的物体类别
score = value['score'] #获取识别的置信度
if score < threshold: #如果低于设置的置信阈值就将其过滤
continue
cv2.rectangle(im, (xmin, ymin), (xmin+w, ymin+h), (0, 255, 0), 4) #绘制识别到的边界框,颜色为绿,粗细为4
cv2.putText(im, '{:s} {:.3f}'.format(cls, score),
(xmin, ymin), font, 0.5, (255, 0, 0), thickness=2) #在左上角显示识别种类和置信度
cv2.imwrite('result.jpg', im) #将结果图像保存
plt.figure(figsize=(15,12)) #创建一个画板
plt.imshow(im[:, :, [2,1,0]]) #
plt.show() #显示结果图片
定义基于图像识别的距离检测类
class PersonDistanceDetector(object):
def __init__(self): #定义类的构造函数
self.model = model = pdx.load_model('./YOLOv3/best_model') #导入训练的识别模型
self.boxes = [] #定义list用来存放识别到的边界框
self.threshold = 0.3 #定义边界框置信度过滤阈值
self.process_this_frame = False #设置标志位来记录此图片是否在被处理
def feedCap(self, frame):
self.process_this_frame = not self.process_this_frame #将标志位改为true
if self.process_this_frame:
result = self.model.predict(frame) #对该帧图像进行识别
self.boxes = [v['bbox'] for v in result if v['score'] > self.threshold]#将边界框大于置信值的边框加入对象的boxes list中
circles = []
for i in range(len(self.boxes)):
x, y = int(self.boxes[i][0]), int(self.boxes[i][1]) #获取边界框的坐标
w, h = int(self.boxes[i][2]), int(self.boxes[i][3]) #获取边界框的宽高
cx, cy = x + w // 2, y + h #定义边界框的中点为底部中点
frame = cv2.line(frame, (cx, cy), (cx, cy - h // 2), (0, 255, 0), 2)#画一条从中点到边界框几何中点的红线
frame = cv2.circle(frame, (cx, cy - h // 2), 5, (255, 20, 200), -1) #在边界框的几何中点画一个实心小圆
circles.append([cx, cy - h // 2, h]) #将边界框底部的圆加入圆的list
indexes = []
#依次判断各个圆之间的距离
for i in range(len(circles)):
x1, y1, r1 = circles[i] #获取标记圆的坐标和半径
for j in range(i + 1, len(circles)): # i之前的都已经判断过,直接从i+1开始
x2, y2, r2 = circles[j] #获取待比较圆的坐标和半径
if (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <= (r1 + r2) * (r1 + r2) and abs(y1 - y2) < r1 // 4:
indexes.append(i)
indexes.append(j) #如果两圆相交且圆心 y 方向的距离小于标记圆半径的四分之一,则记录它们编号
cv2.line(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) #在两圆之间画一条红色的线
for i in range(len(circles)):
x, y, r = circles[i] #依次对图像中的每个圆进行处理
if i in indexes:
transparentOverlay1(frame, dst_circle, (x, y - 5), scale=(r) / 100, color=(0, 0, 255), alphaVal=110)
else:
transparentOverlay1(frame, dst_circle, (x, y - 5), scale=(r) / 100, color=(0, 200, 0),alphaVal=110) #对检测距离标记的圆分别采用不同颜色#
rows, cols, _ = frame.shape #获取图片的大小信息
cv2.putText(frame,
"Total Persons : " + str(len(self.boxes)), #显示的内容
(20, rows - 40), #显示的位置
fontFace=cv2.QT_FONT_NORMAL, #显示的字体
fontScale=1, #字体的大小
color=(215, 220, 245)) #字体的颜色
return frame #返回处理完成的图像
M3 = np.array([
[0.8092, -0.2960, 11],
[0.0131, 0.0910, 30],
[0.0001, -0.0052, 1.0]
])
circle_img = cv2.circle(np.zeros((100, 100, 3)), center=(50, 50), radius=40, color=(0, 240, 0), thickness=4) #在黑色背景中心绘制一个半径为40的圆
dst_circle = cv2.warpPerspective(circle_img, M3, (100, 100)) #将圆变换成椭圆效果的圆(即平行四边形化)
def transparentOverlay1(src, overlay, pos=(0, 0), scale=1, color=(0, 200, 100), alphaVal=255):
h, w, _ = overlay.shape # 标记圆圈的信息
rows, cols, _ = src.shape # 识别图像的信息
x, y = pos[0], pos[1] # 原来圆心的位置
x -= w // 2 #将x设置为圆最左边的点
background = src[y:min(y + h, rows), x:min(x + w, cols)] #获取在待画圆范围的原图像
b_h, b_w, _ = background.shape #获取识别圆的信息
if b_h <= 0 or b_w <= 0:
return src #若有一部分不在原图像内则直接返回原图
foreground = overlay[0:b_h, 0:b_w] #设置在图片范围内的识别圆
alpha = foreground[:, :, 1].astype(float) #将图片里的像素信息转化为浮点数形式进行处理
alpha[alpha > 235] = alphaVal #去除图像中的黑色部分
alpha = cv2.merge([alpha, alpha, alpha]) #根据alpha的合成图像
alpha = alpha / 255.0 #统一到0-1处理
foreground = foreground.astype(float)
background = background.astype(float) #变换为浮点数处理
foreground = np.zeros_like(foreground) + color #生成和背景图片一样大小的空数组,并设置指定颜色
foreground = cv2.multiply(alpha, foreground[:, :, :3])
background = cv2.multiply(1.0 - alpha, background)
outImage = cv2.add(foreground, background).astype("uint8") #用待显示的圆替代原图本来位置的图像
src[y:y + b_h, x:x + b_w] = outImage #实现替换
return src
安装imutils
!pip install imutils
视频处理
import cv2 #强大的图像处理包
import imutils #一个依赖 Numpy cv2 mat matplotlib 的包,可以更方便的实现图片的旋转放大等操作
from tqdm import tqdm #python 提供的进度条模块
if __name__ == '__main__':
path = 'p.mp4' #待处理图像的路径
det = PersonDistanceDetector() #实例化化一个行人检测对象
cap = cv2.VideoCapture(path) #cv2.VideoCapture()视频读取函数 默认为电脑‘0’摄像头,也可以是图像的路径
fps = int(cap.get(5)) #获取图像的帧率
frames_num = int(cap.get(7)) #获取视频的总帧数
size = None
for i in tqdm(range(frames_num)): #对视频的每一帧图像进行识别检测,并以进度条显示执行进度
flag, im = cap.read() #按帧读取图像,第一个返回值为布尔型 表示是否正确读取,第二个为读取的图像帧
if not flag:
break #如果未读取到图片就停止循环
result = det.feedCap(im)
result = imutils.resize(result, height=500)
if size is None:
size = (result.shape[1], result.shape[0])
fourcc = cv2.VideoWriter_fourcc(
'm', 'p', '4', 'v') # opencv3.0
videoWriter = cv2.VideoWriter(
'result.mp4', fourcc, fps, (result.shape[1],result.shape[0]))
videoWriter.write(result) #将处理结果result写成视频
cv2.waitKey(1) #等待键盘输入一毫秒若无输入返回-1,用来更新图片输出
cap.release()
videoWriter.release() #释放线程所占有的资源
cv2.destroyAllWindows() #删除所有的窗口
print('done') #输出工作已经完成
项目参考,在原作者的基础上进行了相关算法和代码的优化【PaddleX助力疫情防护】基于PaddleX的行人社交安全距离检测 - 飞桨AI Studio - 人工智能学习与实训社区