一、项目介绍

笔者的学校经常出现公共区域的抽烟现象,在贴有禁止吸烟的厕所里也有同学吸烟的情况,导致下课去一次洗手间,回来就是一身的烟味! 吸烟可以去合适的场所避免对大家的影响,类似的现象在生活的很多场景中都存在,没有好的方法可以缓解这个现象,为了大学生的美好未来,为了大家的身心健康,基于飞桨的吸烟检测它来了,我在PaddleDetection上训练了这次项目的吸烟检测,为了更好的体现实际,并部署在了Jetson Xavier NX上。(出自本小白项目,大佬路过可留下宝贵意见)。

这次项目所参考的文档及大佬项目

从0到1教你在Jetson Xavier NX玩转PaddlePaddle

在Jetson Nano上基于python部署Paddle Inference

飞桨官网教程

二、硬件准备

准备好一块新鲜出炉的Jetson Xavier NX,并配好基础的开发环境,当然Jetson系列的部署是基本一致的(出自某PPDE-高学长的说法),如果没有可以用Jetson nano代替。不过越贵的板子跑起来越轻松,我便白嫖的实验室小伙伴的硬件(毕竟没有这经济实力),你的Jetson Xavier NX就是我的

三、环境搭建

安装PaddlePaddle环境

去官网链接下载最新即可 编译库下载连接

下载后传到Jetson Xavier NX上并运行如下指令

pip3 install paddlepaddle_gpu-2.1.1-cp36-cp36m-linux_aarch64.whl

完成编译库的安装

测试Paddle Inference

此处可以参考开头提到参考项目中的第二个连接里的测试流程。(我也是这样测试的

测试成功大致环境搭建已经完成,可以开始做自己的吸烟检测任务

四、吸烟检测

数据集采用大佬Niki_173的吸烟检测数据集,感谢大佬的数据集,此处小白引用。

首先拉取PaddleDetection

放入data文件夹中便于之后启动环境,避免内存大时启动不方便

#从gitee上克隆PaddleDetection
! git clone https://gitee.com/paddlepaddle/PaddleDetection.git
! mv PaddleDetection data
Cloning into 'PaddleDetection'...
remote: Enumerating objects: 2854, done.[K
remote: Counting objects: 100% (2854/2854), done.[K
remote: Compressing objects: 100% (1193/1193), done.[K
remote: Total 17429 (delta 1861), reused 2529 (delta 1654), pack-reused 14575[K
Receiving objects: 100% (17429/17429), 141.74 MiB | 6.43 MiB/s, done.
Resolving deltas: 100% (12604/12604), done.
Checking connectivity... done.
#解压数据集
import os 
os.mkdir('data/voc')
! unzip -oq /home/aistudio/data/data94796/pp_smoke.zip -d data
! mv data/images data/voc
! mv data/Annotations data/voc

Paddlex的分解VOC数据集,十分方便,并且将训练集,验证集,测试集划分好,生成对应的txt文件

运行下面的代码前,先把解压的数据集目录下的data/voc/images 修改为data/voc/JPEGImages,不然会报错
#paddlex 一键分解voc数据真香
!pip install paddlex==2.0rc
#JPEGImages
!paddlex --split_dataset --format VOC --dataset_dir data/voc --val_value 0.2 --test_value 0.1
#进行环境配置,不然无法训练
!pip install --upgrade -r data/PaddleDetection/requirements.txt -i https://mirror.baidu.com/pypi/simple

对应的yml文件修改大致如下,可以根据自己的想法修改,可以训练出更好的模型。写好的yml文件已经保存在work的config目录下,直接调用训练即可

yolov3_mobilenet_v1_ssld_270e_voc.yml:
_BASE_: [
  '../datasets/voc.yml',
  '../runtime.yml',
  '_base_/optimizer_270e.yml',
  '_base_/yolov3_mobilenet_v1.yml',
  '_base_/yolov3_reader.yml',
]

snapshot_epoch: 5
pretrain_weights:  https://paddledet.bj.bcebos.com/models/pretrained/MobileNetV1_ssld_pretrained.pdparams
weights: output/yolov3_mobilenet_v1_ssld_270e_voc/model_final

# set collate_batch to false because ground-truth info is needed
# on voc dataset and should not collate data in batch when batch size
# is larger than 1.
EvalReader:
  collate_batch: false

LearningRate:
  base_lr: 0.000125
  schedulers:
  - !PiecewiseDecay
    gamma: 0.1
    milestones:
    - 216
    - 243
  - !LinearWarmup
    start_factor: 0.
    steps: 1000

voc.yml:
metric: VOC
map_type: 11point
num_classes: 1

TrainDataset:
  !VOCDataSet
    dataset_dir: data/voc
    anno_path: train_list.txt
    label_list: labels.txt
    data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult']

EvalDataset:
  !VOCDataSet
    dataset_dir: data/voc
    anno_path: val_list.txt
    label_list: labels.txt
    data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult']

TestDataset:
  !ImageFolder
    anno_path: data/voc/test_list.txt

runtime.yml:
use_gpu: true
log_iter: 10
save_dir: output
snapshot_epoch: 5
print_flops: false

optimizer_270e.yml:
epoch: 800

LearningRate:
  base_lr: 0.000125
  schedulers:
  - !PiecewiseDecay
    gamma: 0.1
    milestones:
    - 216
    - 243
  - !LinearWarmup
    start_factor: 0.
    steps: 4000

OptimizerBuilder:
  optimizer:
    momentum: 0.9
    type: Momentum
  regularizer:
    factor: 0.0005
    type: L2
yolov3_reader.yml:

worker_num: 0
TrainReader:
  inputs_def:
    num_max_boxes: 50
  sample_transforms:
    - Decode: {}
    - Mixup: {alpha: 1.5, beta: 1.5}
    - RandomDistort: {}
    - RandomExpand: {fill_value: [123.675, 116.28, 103.53]}
    - RandomCrop: {}
    - RandomFlip: {}
  batch_transforms:
    - BatchRandomResize: {target_size: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608], random_size: True, random_interp: True, keep_ratio: False}
    - NormalizeBox: {}
    - PadBox: {num_max_boxes: 50}
    - BboxXYXY2XYWH: {}
    - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
    - Permute: {}
    - Gt2YoloTarget: {anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]], anchors: [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]], downsample_ratios: [32, 16, 8]}
  batch_size: 64
  shuffle: true
  drop_last: true
  mixup_epoch: 250
  use_shared_memory: true

EvalReader:
  inputs_def:
    num_max_boxes: 50
  sample_transforms:
    - Decode: {}
    - Resize: {target_size: [608, 608], keep_ratio: False, interp: 2}
    - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
    - Permute: {}
  batch_size: 64

TestReader:
  inputs_def:
    image_shape: [3, 608, 608]
  sample_transforms:
    - Decode: {}
    - Resize: {target_size: [608, 608], keep_ratio: False, interp: 2}
    - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
    - Permute: {}
  batch_size: 1

如果不打算自己修改,直接运行下面的命令就可以进行模型训练

# 进行模型训练
#因为笔的设置会产较多epoch会产生比较多的模型文件,文件太多时过于占内存,运行一下 remove.py即可(就是简单的删除文件而已)
!python data/PaddleDetection/tools/train.py -c work/config/yolov3/yolov3_mobilenet_v1_270e_voc.yml --eval -o use_gpu=true
#模型训练好以后随机选择一张图片
#选择一张图片进行预测效果测试
#运行命令进行模型预测
!python data/PaddleDetection/tools/infer.py -c work/config/yolov3/yolov3_mobilenet_v1_270e_voc.yml --infer_img=data/voc/JPEGImages/smoke_a325.jpg -o weights=output/yolov3_mobilenet_v1_270e_voc/best_model.pdparams

看看预测效果,放上星爷的帅照

#训练完成之后,既可导出
!python data/PaddleDetection/tools/export_model.py -c work/config/yolov3/yolov3_mobilenet_v1_270e_voc.yml --output_dir=./inference_model \
ference_model \
    -o weights=output/yolov3_mobilenet_v1_270e_voc/best_model

四、模型部署

创建一个自己的工作文件夹(如 demo),我把模型文件传上Jetson Xavier NX存放在demo/voc下
推荐大家使用 vs code 远程登陆操作,更改代码,传输或删除文件,都很方便!

文件传送好的格式大致如下(只看demo文件目录即可)

再编写python预测,放在demo的目录下即可
这个程序的编写可以参考文档给的示例,我的程序就是简单修改例程。链接在这yolov3模型示例
python预测中的文件读取路径,改成自己部署时的路径
infer.py

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

此项目导出的模型文件已经放在inference_model下,可以直接下载本地后部署

部署效果


不抽烟,只能拿只激光笔骗一骗模型

简单的吸烟检测部署就完成了,小白项目,如有错误请多包涵

个人简介

AIstudio主页:me jun(小白求关注~)

吴世君 东北大学秦皇岛分校 测控技术与仪器 本科在读

感兴趣方向:计算机视觉

请点击此处查看本环境基本用法.

Please click here for more detailed instructions.

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐