# 一项目介绍
在企业,公司和工厂的生产过程中,经常出现公共区域甚至工作区域的抽烟现象,对于需要禁燃禁烟的企业安全生产中,抽烟会在生产中出现安全隐患,引发烟雾报警,火灾等重大安全事故。
然而传统的方法就是通过企业安装摄像头和烟雾报警器进行人为监控,然而这种方式耗费大量的人力,且人工检查的精度和效率都偏低,人工检查存在漏查和注意力不足的情况。为了企业的安全生产和检查效率,我利用百度AI深度学习库paddlepaddle中的PaddleDetection开源检测包,部署在Nvidia Jetson Nano b01边缘计算处理器上,来进行人工智能吸烟行为检测。参考一下项目和文档:
从0到1教你在Jetson Xavier NX玩转PaddlePaddle! - 飞桨AI Studio
飞桨PaddlePaddle-源于产业实践的开源深度学习平台
# 二 硬件配置
准备一个Jetson Nano b01 4GB板子,配置 一张wifi无线网卡,一个USB webcam 720P,一个hdmi转接显示器,一个5VDC供电器,一个64GB的TF储存卡。安装系统为 Ubuntu 18.04LTS系统,jetpack=4.6, cuda=10.2.
# 三 环境配置
首先,在Jetson Nano中安装conda环境,Jetson Nano属于arm架构的板子,所以不能安装anaconda或者miniconda。可以安装archiconda,下载链接放在下面:
https://github.com/Archiconda/build-tools/release Archiconda3-0.2.3-Linux-aarch64.shhttps://github.com/Archiconda/build-tools/releases/download/0.2.3/Archiconda3-0.2.3-Linux-aarch64.sh
下载成功后,在文件所在文件夹,右键打开terminal,使用指令:
bash Archiconda3-0.2.3-Linux-aarch64.sh
安装成功后,创建python3.6的环境(Jetson Nano兼容python3.6),然后进入环境。
conda create -n env_name python==3.6
conda activate env_name
现在进入python 3.6的conda环境中,需要安装paddlepaddle的python编译库。关于paddlepaddle这个人工智能深度学习库可以查看官网,上面有链接。
因为我们是在Jetson Nano 安装paddlepaddle,因此需要找到适配Jetson Nano的编译库,下载网址在这里:
选择Jetpack4.6,cuda10.2的拔本,然后下载。注意cp36指的是python3.6的版本。然后使用pip指令进行安装。
pip install paddlepaddle-xxxxxx.whl
安装好paddlepaddle后,进入训练的准备流程。
# 四 训练数据集
训练数据集阶段,建议安装Jupiter Notebook,指令如下:
pip install Jupyter Notebook
在py36环境中,输入Jupyter Notebook,随后进入Jupyter Notebook. 新建一个python文件,进入接下来的阶段。
数据集使用公开数据集,pp_smoke, 数据集的链接在这里,里面包含了783张抽烟图片和783个xml分割label,大家可以从这里直接下载, 然后解压缩到你指定的路径中去:吸烟、抽烟检测数据集 - 飞桨AI Studio星河社区
!unzip -oq pp_smoke.zip -d your_dir
接下来是训练数据集的过程,使用源码paddledetection,直接git下载PaddleDetection的库,建议安装2.2-2.5版本。
! git clone https://gitee.com/paddlepaddle/PaddleDetection.git -2.5.0/
确定数据集中标签和图片的位置:
import random
import os
#设置随机种子以便复现
random.seed(1)
xml_dir = '/home/dir/Annotations'#标签文件地址
img_dir = '/home/dir/images'#图像文件地址
path_list = list()
for img in os.listdir(img_dir):
img_path = os.path.join(img_dir,img)
xml_path = os.path.join(xml_dir,img.replace('jpg', 'xml'))
path_list.append((img_path, xml_path))
random.shuffle(path_list)
生成训练集和验证集,大概比例是8:2,所以选择ratio=0.8
ratio = 0.8#设置比例为8:2
train_f = open('/home/dir/train.txt','w') #生成训练文件
val_f = open('/home/dir/val.txt' ,'w')#生成验证文件
生成训练集和验证集的text,
for i ,content in enumerate(path_list):
img, xml = content
text = img + ' ' + xml + '\n'
if i < len(path_list) * ratio:
train_f.write(text)
else:
val_f.write(text)
train_f.close()
val_f.close()
生成标签文档,text
label = ['smoke']#抽烟相关的英语单词都可以
with open('/home/dir/label_list.txt', 'w') as f:
for text in label:
f.write(text+'\n')
进入 PaddleDetection, 安装 pycocotools,安装所有需要的依赖包和dependencies。
cd /home/dir/PaddleDetection-2.5.0/
!pip install pycocotools
!pip install -r requirements.txt
将参数进行更改,需要更改PaddleDetection-2.5.0/config/datasets/voc.yml处的路径和分类类别;
configs/yolov3/_base_/optimizer_270e.yml中的epoch,epoch改为200-300之间,路径同样进行更改然后训练!
!python tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_270e_voc.yml --eval --use_vdl=True --vdl_log_dir="./output"
这里有一个建议,找一个ubuntu系统的电脑或者服务器,安装同样的环境,在上面进行训练,训练结果会导入output文件夹内,因为训练时会保存很多的模型文件,因此会降低Jetson的性能。所以使用高性能的电脑进行训练然后导出模型是一个很好的方案。
训练完成后直接进行模型评估:
!python -u tools/eval.py -c configs/yolov3/yolov3_mobilenet_v1_270e_voc.yml -o weights=output/yolov3_mobilenet_v1_270e_voc/best_model.pdparams
然后再用一个图简单测试一下:
!python tools/infer.py -c configs/yolov3/yolov3_mobilenet_v1_270e_voc.yml -o weights=/home/dir/best_model.pdparams
--infer_img=/home/dir/images/smoke_a217.jpg
在output文件夹中会有一个测试图片生成。接下来导出inference model。生成.pdiparams文件:
!python /tools/export_model.py -c configs/yolov3/yolov3_mobilenet_v1_270e_voc.yml --output_dir=./inference_model -o weights=output/yolov3_mobilenet_v1_270e_voc/best_model
如果不想弄这些,直接可以用我已经训练好的模型,GitHub在这里:
GitHub - Brettict/Jetson_Nano_SmokingBehavior_Detection
接下来写个py代码,使用inference 模型在视频和相机上实时推理。利用opencv进行摄像头和视频图片的调用进行推理。
!git clone https://github.com/Brettict/Jetson_Nano_SmokingBehavior_Detection
cd Jetson_Nano_SmokingBehavior_Detection
相关的代码参考如下,修改其中代码,以满足自己的需求。
import cv2
import numpy as np
import paddle.inference as paddle_infer
from paddle.inference import Config
from paddle.inference import create_predictor
def resize(img, target_size):
"""resize to target size"""
if not isinstance(img, np.ndarray):
raise TypeError('image type is not numpy.')
im_shape = img.shape
im_size_min = np.min(im_shape[0:2])
im_size_max = np.max(im_shape[0:2])
im_scale_x = float(target_size) / float(im_shape[1])
im_scale_y = float(target_size) / float(im_shape[0])
img = cv2.resize(img, None, None, fx=im_scale_x, fy=im_scale_y)
return img
def normalize(img, mean, std):
img = img / 255.0
mean = np.array(mean)[np.newaxis, np.newaxis, :]
std = np.array(std)[np.newaxis, np.newaxis, :]
img -= mean
img /= std
return img
def preprocess(img, img_size):
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
img = resize(img, img_size)
img = img[:, :, ::-1].astype('float32') # bgr -> rgb
img = normalize(img, mean, std)
img = img.transpose((2, 0, 1)) # hwc -> chw
return img[np.newaxis, :]
def Config(prog_file,params_file):
# 创建 config
config = paddle_infer.Config()
# 通过 API 设置模型文件夹路径
#config.set_prog_file("./mobilenet_v2/__model__") #路径修改
#config.set_params_file("./mobilenet_v2/__params__") #路径修改
config.set_prog_file(prog_file)
config.set_params_file(params_file)
# 根据 config 创建 predictor
config.enable_use_gpu(1000, 0)
config.switch_ir_optim()
config.enable_memory_optim()
#config.enable_tensorrt_engine(workspace_size=1 << 30, precision_mode=paddle_infer.PrecisionType.Float32,max_batch_size=1, min_subgraph_size=5, use_static=False, use_calib_mode=False)
predictor = paddle_infer.create_predictor(config)
return predictor
def predic(predictor, img):
# copy img data to input tensor
input_names = predictor.get_input_names()
for i, name in enumerate(input_names):
input_tensor = predictor.get_input_handle(name)
input_tensor.reshape(img[i].shape)
input_tensor.copy_from_cpu(img[i].copy())
# do the inference
predictor.run()
results = []
# get out data from output tensor
output_names = predictor.get_output_names()
for i, name in enumerate(output_names):
output_tensor = predictor.get_output_handle(name)
output_data = output_tensor.copy_to_cpu()
results.append(output_data)
return results
def draw_bbox(img, result,label_list, threshold=0.5):
"""draw bbox"""
for res in result:
id, score, bbox = res[0], res[1], res[2:]
if score < threshold:
continue
xmin, ymin, xmax, ymax = bbox
cv2.rectangle(img, (int(xmin),int(ymin)), (int(xmax), int(ymax)), (255,0,255), 2)
print('category id is {}, bbox is {}'.format(id, bbox))
try:
label_id = label_list[int(id)]
cv2.putText(img, label_id, (int(xmin), int(ymin-2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 2)
cv2.putText(img, str(round(score,2)), (int(xmin-35), int(ymin-2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
except KeyError:
pass
if __name__ == '__main__':
print("开始进行预测")
#摄像头读取
cap = cv2.VideoCapture(0)
# 图像尺寸相关参数初始化
ret, img = cap.read()
#模型文件路径
prog_file = './voc/model.pdmodel' #路径修改
params_file = './voc/model.pdiparams' #路径修改
predictor = Config(prog_file,params_file)
img_size = 224
scale_factor = np.array([img_size * 1. / img.shape[0], img_size * 1. / img.shape[1]]).reshape((1, 2)).astype(np.float32)
im_shape = np.array([img_size, img_size]).reshape((1, 2)).astype(np.float32)
while True:
ret, img = cap.read()
pro_data = preprocess(img,img_size)
result = predic(predictor, [im_shape, pro_data, scale_factor])
draw_bbox(img,result[0],label_list)
cv2.imshow("pred", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
打开terminal,输入
python /demo_yuan/infer_video.py
开始进行视频推理,视频是网络下载的抽烟视频,均为剪辑,无不良影响和诱导。
python /demo_yuan/infer_cv.py
开始进行相机实时推理,可以参考 视频 Jetson Nano抽烟动作识别和检测_哔哩哔哩_bilibili