当前位置:   article > 正文

RT-DETR 检测铁路工人是否带安全帽_铁路检测大模型

铁路检测大模型

最近关注到了PaddleDetection新出的RT-DETR模型,根据论文给出的数据,能够达到实时检测的效果,在速度和精度上都超越了YOLO系列模型,我也是迫不及待地实践了一下。

整体情况如下:

模型名称mAP(0.50, 11point)average FPS
ppyoloe_vit_base_csppan_cae_36e_coco87.64%13.92
rtdetr_hgnetv2_x_6x_coco96.26%18.32

单从本项目来看,mAP(0.50, 11point) 提升了 8.62 的百分点,average FPS 提升了 4.4 个点,是一个非常喜人的提升。可以看出RT-DETR在精度和速度上都是有着显著的提升,因此大家也可以在自己的项目中尝试使用这个模型,看看是否能够达到更好的效果。

学习本项目过程中,你可能感兴趣的链接地址:

  1. RT-DETR Github链接:https://github.com/PaddlePaddle/PaddleDetection/tree/develop/configs/rtdetr
  2. RT-DETR 原论文链接:https://arxiv.org/abs/2304.08069
  3. PaddleDetection Github链接:https://github.com/PaddlePaddle/PaddleDetection
  4. 数据集链接:https://aistudio.baidu.com/aistudio/datasetdetail/200816

一、模型简介

RT-DETR由BackBone、混合编码器和带有辅助预测头的Transformer编码器组成。整体结构如下所示(图片来自原论文): 

具体来说:

  1. 利用骨干网络最后三个阶段{�3S3​、�4S4​、�5S5​}的输出特征作为编码器的输入;
  2. 设计了一个高效的混合编码器通过解耦尺度内交互(AIFI)和跨尺度融合模块(CCFM)来高效处理多尺度特征转换为一系列图像特征;
  3. 采用IoU感知的查询选择机制来选择固定数量的图像特征以优化解码器查询的初始化;
  4. 带有辅助预测头的解码器迭代地优化对象查询以生成方框和置信度分数。

官方数据如下:

ModelEpochbackboneinput shape�����APval��50���AP50val​Params(M)FLOPs(G)T4 TensorRT
FP16(FPS)
RT-DETR-R506xResNet-5064053.171.342136108
RT-DETR-R1016xResNet-10164054.372.77625974
RT-DETR-L6xHGNetv264053.071.632110114
RT-DETR-X6xHGNetv264054.873.16723474
  1. backbone:飞桨团队采用了经典的 ResNet 和可缩放的 HGNetv2 两种,使用两种 backbone 各训练了两个版本的 RT-DETR ,以 HGNetv2 为 backbone 的 RT-DETR 包括 L 和 X 版本,以 ResNet 为 backbone 的 RT-DETR 则包括 RT-DETR-R50 和 RT-DETR-R101 。 RT-DETR-R50 / 101 方便和现有的 DETR 变体进行对比,而 RT-DETR-L / X 则用来和现有的实时检测器( YOLO 系列模型)进行对比。
  1. Neck:现有的多尺度 Transformer 编码器在多个尺度的特征之间进行注意力运算,同时进行尺度内和尺度间特征交互,计算消耗较大。为了减少计算消耗,一个简单的办法是直接削减编码器层数。但是飞桨团队认为这并不能从根本上解决问题并且势必会对精度造成较大影响,更本质的方法应该是要解耦这种尺度内和尺度间的同时交互,缩短输入编码器的序列长度。为此,飞桨团队设计了一系列编码器变体来验证解耦尺度内和尺度间特征交互的可行性并最终演化为 HybridEncoder ,其包括两部分:Attention-based Intra-scale Feature Interaction (AIFI) 和 CNN-based Cross-scale Feature-fusion Module (CCFM) 。

  1. Decoder & Head:DETR 架构有两个关键组件: Query Selection 和 Decoder 。Query Selection 的作用是从 Encoder 输出的特征序列中选择固定数量的特征作为 object queries ,其经过 Decoder 后由预测头映射为置信度和边界框。现有的 DETR 变体都是利用这些特征的分类分数直接选择 top-K 特征。然而,由于分类分数和 IoU 分数的分布存在不一致,分类得分高的预测框并不一定是和 GT 最接近的框,这导致高分类分数低 IoU 的框会被选中,而低分类分数高 IoU 的框会被丢弃,这将会损害检测器的性能。为解决这一问题,飞桨团队提出了 IoU-aware Query Selection ,通过在训练期间约束检测器对高 IoU 的特征产生高分类分数,对低 IoU 的特征产生低分类分数。从而使得模型根据分类分数选择的 top-K 特征对应的预测框同时具有高分类分数和高 IoU 分数。对于 Decoder ,飞桨团队并没有对其结构进行调整,目的是为了方便使用高精度的 DETR 的大检测模型对轻量级 DETR 检测器进行蒸馏。
  1. 数据增强和训练策略:数据增强采用的是基础的随机颜色抖动、随机翻转、裁剪和 Resize ,并且在验证和推理时图像的输入尺寸统一为 640 ,与 DETR 系列的处理方式有较大的不同,主要是为了满足实时性的要求。训练策略则是和 DETR 系列基本相同,优化器同样采用 AdamW ,默认在 COCO train2017 上训练 6x ,即 72 个 epoch 。

以上段落有部分摘自于官方发布的超越YOLOv8,飞桨推出精度最高的实时检测器RT-DETR!

二、数据集简介

该数据集用于正确检测工人、他们的反光背心和安全帽。该数据集有3222张图片,其中包含三个标签:工人、反光背心和安全帽。AI Studio链接

用途举例:

  1. 可以判断是否有工人正在铁路上作业;
  2. 可以判断工人是否正确佩戴反光背心和安全帽规范作业。

部分数据集图片如下:

三、数据预处理

Step01: 解压数据集

In [ ]

!unzip /home/aistudio/data/data200816/dataset.zip -d /home/aistudio/work/

Step02: 将 txt 格式标注文件转换成 xml 格式标注文件,代码如下所示。

In [ ]

  1. %cd /home/aistudio/work/dataset
  2. !mv imgs JPEGImages
  3. !mkdir Annotations

In [ ]

  1. from xml.dom.minidom import Document
  2. import os
  3. import cv2
  4. def makexml(picPath, txtPath, xmlPath):
  5. # 标签映射
  6. dic = {'0': "vest", '1': "helmet", '2': "worker"}
  7. files = os.listdir(txtPath)
  8. for i, name in enumerate(files):
  9. if name == ".ipynb_checkpoints":
  10. continue
  11. xmlBuilder = Document()
  12. annotation = xmlBuilder.createElement("annotation") # 创建annotation标签
  13. xmlBuilder.appendChild(annotation)
  14. txtFile = open(txtPath + name)
  15. txtList = txtFile.readlines()
  16. img = cv2.imread(picPath + name[0:-4] + ".jpg") # .jpg/.png
  17. Pheight, Pwidth, Pdepth = img.shape
  18. folder = xmlBuilder.createElement("folder") # folder标签
  19. foldercontent = xmlBuilder.createTextNode("datasetRGB")
  20. folder.appendChild(foldercontent)
  21. annotation.appendChild(folder)
  22. filename = xmlBuilder.createElement("filename") # filename标签
  23. filenamecontent = xmlBuilder.createTextNode(name[0:-4] + ".jpg")
  24. filename.appendChild(filenamecontent)
  25. annotation.appendChild(filename)
  26. size = xmlBuilder.createElement("size") # size标签
  27. width = xmlBuilder.createElement("width") # size子标签width
  28. widthcontent = xmlBuilder.createTextNode(str(Pwidth))
  29. width.appendChild(widthcontent)
  30. size.appendChild(width)
  31. height = xmlBuilder.createElement("height") # size子标签height
  32. heightcontent = xmlBuilder.createTextNode(str(Pheight))
  33. height.appendChild(heightcontent)
  34. size.appendChild(height)
  35. depth = xmlBuilder.createElement("depth") # size子标签depth
  36. depthcontent = xmlBuilder.createTextNode(str(Pdepth))
  37. depth.appendChild(depthcontent)
  38. size.appendChild(depth)
  39. annotation.appendChild(size)
  40. for j in txtList:
  41. oneline = j.strip().split(" ")
  42. object = xmlBuilder.createElement("object") # object 标签
  43. picname = xmlBuilder.createElement("name") # name标签
  44. namecontent = xmlBuilder.createTextNode(dic[oneline[0]])
  45. picname.appendChild(namecontent)
  46. object.appendChild(picname)
  47. pose = xmlBuilder.createElement("pose") # pose标签
  48. posecontent = xmlBuilder.createTextNode("Unspecified")
  49. pose.appendChild(posecontent)
  50. object.appendChild(pose)
  51. truncated = xmlBuilder.createElement("truncated") # truncated标签
  52. truncatedContent = xmlBuilder.createTextNode("0")
  53. truncated.appendChild(truncatedContent)
  54. object.appendChild(truncated)
  55. difficult = xmlBuilder.createElement("difficult") # difficult标签
  56. difficultcontent = xmlBuilder.createTextNode("0")
  57. difficult.appendChild(difficultcontent)
  58. object.appendChild(difficult)
  59. bndbox = xmlBuilder.createElement("bndbox") # bndbox标签
  60. xmin = xmlBuilder.createElement("xmin") # xmin标签
  61. mathData = int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)
  62. xminContent = xmlBuilder.createTextNode(str(mathData))
  63. xmin.appendChild(xminContent)
  64. bndbox.appendChild(xmin)
  65. ymin = xmlBuilder.createElement("ymin") # ymin标签
  66. mathData = int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)
  67. yminContent = xmlBuilder.createTextNode(str(mathData))
  68. ymin.appendChild(yminContent)
  69. bndbox.appendChild(ymin)
  70. xmax = xmlBuilder.createElement("xmax") # xmax标签
  71. mathData = int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)
  72. xmaxContent = xmlBuilder.createTextNode(str(mathData))
  73. xmax.appendChild(xmaxContent)
  74. bndbox.appendChild(xmax)
  75. ymax = xmlBuilder.createElement("ymax") # ymax标签
  76. mathData = int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)
  77. ymaxContent = xmlBuilder.createTextNode(str(mathData))
  78. ymax.appendChild(ymaxContent)
  79. bndbox.appendChild(ymax)
  80. object.appendChild(bndbox) # bndbox标签结束
  81. annotation.appendChild(object)
  82. f = open(xmlPath + name[0:-4] + ".xml", 'w')
  83. # print(name)
  84. xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
  85. f.close()
  86. if __name__ == "__main__":
  87. picPath = "/home/aistudio/work/dataset/JPEGImages/" # 图片所在文件夹路径
  88. txtPath = "/home/aistudio/work/dataset/txt/" # txt所在文件夹路径
  89. xmlPath = "/home/aistudio/work/dataset/Annotations/" # xml文件保存路径
  90. makexml(picPath, txtPath, xmlPath)

In [4]

!rm -rf /home/aistudio/work/dataset/txt

Step03: 可视化转换结果,判断转换是否正确,代码如下所示。

  • 红色框(0,0,255):代表worker;
  • 绿色框(0,255,0):代表helmet;
  • 蓝色框(255,0,0):代表vest。

In [ ]

  1. import xml.etree.ElementTree as ET
  2. import os
  3. import cv2
  4. src_XML_dir = '/home/aistudio/work/dataset/Annotations' # xml源路径
  5. src_IMG_dir = '/home/aistudio/work/dataset/JPEGImages' # IMG原路径
  6. IMG_format = '.jpg' # IMG格式
  7. out_dir = '/home/aistudio/work/output' # 输出路径
  8. if not os.path.exists(out_dir):
  9. os.makedirs(out_dir)
  10. xml_file = os.listdir(src_XML_dir) # 只返回文件名称,带后缀
  11. for each_XML in xml_file: # 遍历所有xml文件
  12. # 读入IMG
  13. if each_XML == ".ipynb_checkpoints":
  14. continue
  15. xml_FirstName = os.path.splitext(each_XML)[0]
  16. img_save_file = os.path.join(out_dir, xml_FirstName+IMG_format)
  17. img_src_path = os.path.join(src_IMG_dir, xml_FirstName+IMG_format)
  18. img = cv2.imread(img_src_path)
  19. # 解析XML
  20. each_XML_fullPath = src_XML_dir + '/' + each_XML # 每个xml文件的完整路径
  21. tree = ET.parse(each_XML_fullPath) # ET.parse()内要为完整相对路径
  22. root = tree.getroot() # 类型为element
  23. # 画框
  24. for obj in root.findall('object'):
  25. if obj.find('bndbox'):
  26. if obj.find('name').text == 'worker':
  27. bndbox = obj.find('bndbox')
  28. xmin = int(bndbox.find('xmin').text)
  29. xmax = int(bndbox.find('xmax').text)
  30. ymin = int(bndbox.find('ymin').text)
  31. ymax = int(bndbox.find('ymax').text)
  32. cv2.rectangle(img=img,
  33. pt1=(xmin,ymin),
  34. pt2=(xmax,ymax),
  35. color=(0,0,255),
  36. thickness=2)
  37. if obj.find('name').text == 'helmet':
  38. bndbox = obj.find('bndbox')
  39. xmin = int(bndbox.find('xmin').text)
  40. xmax = int(bndbox.find('xmax').text)
  41. ymin = int(bndbox.find('ymin').text)
  42. ymax = int(bndbox.find('ymax').text)
  43. cv2.rectangle(img=img,
  44. pt1=(xmin,ymin),
  45. pt2=(xmax,ymax),
  46. color=(0,255,0),
  47. thickness=2)
  48. if obj.find('name').text == 'vest':
  49. bndbox = obj.find('bndbox')
  50. xmin = int(bndbox.find('xmin').text)
  51. xmax = int(bndbox.find('xmax').text)
  52. ymin = int(bndbox.find('ymin').text)
  53. ymax = int(bndbox.find('ymax').text)
  54. cv2.rectangle(img=img,
  55. pt1=(xmin,ymin),
  56. pt2=(xmax,ymax),
  57. color=(255,0,0),
  58. thickness=2)
  59. cv2.imwrite(filename=img_save_file, img=img)
  60. # print('保存结果{}'.format(xml_FirstName))

部分可视化结果如下:

四、代码实现

4.1 检测数据分析

该数据集总共包含 3 个标签,各类标签的数量分别为:

  • worker: 7973
  • vest: 7883
  • helmet: 6515

In [ ]

  1. import os
  2. from unicodedata import name
  3. import xml.etree.ElementTree as ET
  4. import glob
  5. def count_num(indir):
  6. # 提取xml文件列表
  7. os.chdir(indir)
  8. annotations = os.listdir('.')
  9. annotations = glob.glob(str(annotations) + '*.xml')
  10. dict = {} # 新建字典,用于存放各类标签名及其对应的数目
  11. for i, file in enumerate(annotations): # 遍历xml文件
  12. # actual parsing
  13. in_file = open(file, encoding = 'utf-8')
  14. tree = ET.parse(in_file)
  15. root = tree.getroot()
  16. # 遍历文件的所有标签
  17. for obj in root.iter('object'):
  18. name = obj.find('name').text
  19. if(name in dict.keys()): dict[name] += 1 # 如果标签不是第一次出现,则+1
  20. else: dict[name] = 1 # 如果标签是第一次出现,则将该标签名对应的value初始化为1
  21. # 打印结果
  22. print("各类标签的数量分别为:")
  23. for key in dict.keys():
  24. print(key + ': ' + str(dict[key]))
  25. indir='/home/aistudio/work/dataset/Annotations/' # xml文件所在的目录
  26. count_num(indir) # 调用函数统计各类标签数目

图像尺寸分析: 通过图像尺寸分析,我们可以看到该数据集图片的尺寸,均为 [1920, 1080]。

In [ ]

  1. import os
  2. from unicodedata import name
  3. import xml.etree.ElementTree as ET
  4. import glob
  5. def Image_size(indir):
  6. # 提取xml文件列表
  7. os.chdir(indir)
  8. annotations = os.listdir('.')
  9. annotations = glob.glob(str(annotations) + '*.xml')
  10. width_heights = []
  11. for i, file in enumerate(annotations): # 遍历xml文件
  12. # actual parsing
  13. in_file = open(file, encoding = 'utf-8')
  14. tree = ET.parse(in_file)
  15. root = tree.getroot()
  16. width = int(root.find('size').find('width').text)
  17. height = int(root.find('size').find('height').text)
  18. if [width, height] not in width_heights: width_heights.append([width, height])
  19. print("数据集中,有{}种不同的尺寸,分别是:".format(len(width_heights)))
  20. for item in width_heights:
  21. print(item)
  22. indir='/home/aistudio/work/dataset/Annotations/' # xml文件所在的目录
  23. Image_size(indir)

4.2 安装PaddleDetection

In [ ]

  1. # 克隆PaddleDetection仓库
  2. # !git clone -b develop https://github.com/PaddlePaddle/PaddleDetection.git
  3. # 安装其他依赖
  4. %cd /home/aistudio/PaddleDetection/
  5. !pip install -r requirements.txt --user
  6. # 编译安装paddledet
  7. !python setup.py install

4.3 数据集划分

首先安装PaddleX。

In [ ]

!pip install paddlex

然后,我们通过 paddlex 中的 split_dataset 命令按照 0.9:0.1 的比例划分训练集和验证集。

In [ ]

!paddlex --split_dataset --format VOC --dataset_dir /home/aistudio/work/dataset --val_value 0.1

4.4 模型训练

In [8]

  1. # 替换配置文件
  2. !rm -rf /home/aistudio/PaddleDetection/configs/rtdetr/_base_
  3. !rm /home/aistudio/PaddleDetection/configs/rtdetr/rtdetr_hgnetv2_x_6x_coco.yml
  4. !rm /home/aistudio/PaddleDetection/configs/runtime.yml
  5. !rm /home/aistudio/PaddleDetection/configs/datasets/voc.yml
  6. !cp -r /home/aistudio/config/_base_ /home/aistudio/PaddleDetection/configs/rtdetr/
  7. !cp /home/aistudio/config/rtdetr_hgnetv2_x_6x_coco.yml /home/aistudio/PaddleDetection/configs/rtdetr/
  8. !cp /home/aistudio/config/runtime.yml /home/aistudio/PaddleDetection/configs/
  9. !cp /home/aistudio/config/voc.yml /home/aistudio/PaddleDetection/configs/datasets/

In [ ]

!python tools/train.py -c configs/rtdetr/rtdetr_hgnetv2_x_6x_coco.yml --eval --use_vdl True --vdl_log_dir vdl_log_dir/scalar

损失函数如图所示:

4.5 模型评估

通过如下命令在单个GPU上评估我们的验证集。

In [ ]

!python tools/eval.py -c configs/rtdetr/rtdetr_hgnetv2_x_6x_coco.yml -o weights=output/rtdetr_hgnetv2_x_6x_coco/best_model.pdparams

指标如下:

  • mAP(0.50, 11point) = 96.26%
  • Total sample number: 311, average FPS: 18.319225946781593

相比[AI达人特训营第三期]PPYOLOE遇上ViT助力铁路工人安全作业使用的以 ViT-base 为 backbone 的 PPYOLOE 模型,mAP(0.50, 11point) 提升了 8.62 的百分点,average FPS 提升了 4.4 个点。

模型名称mAP(0.50, 11point)average FPS
ppyoloe_vit_base_csppan_cae_36e_coco87.64%13.92
rtdetr_hgnetv2_x_6x_coco96.26%18.32

4.6 模型推理

我们可以通过以下命令在单张GPU上推理文件中的所有图片。

In [ ]

!python tools/infer.py -c configs/rtdetr/rtdetr_hgnetv2_x_6x_coco.yml -o weights=output/rtdetr_hgnetv2_x_6x_coco/best_model.pdparams --infer_dir=/home/aistudio/work/dataset/JPEGImages --output_dir infer_output/

部分可视化结果如下:

4.7 模型导出

Step01. 导出模型

In [ ]

!python tools/export_model.py -c configs/rtdetr/rtdetr_hgnetv2_x_6x_coco.yml -o weights=output/rtdetr_hgnetv2_x_6x_coco/best_model.pdparams

Step02: 转换模型至ONNX

首先安装 Paddle2ONNX 和 ONNX。

In [ ]

  1. !pip install onnx==1.13.0
  2. !pip install paddle2onnx==1.0.5

然后转换模型。

In [ ]

  1. !paddle2onnx --model_dir=./output_inference/rtdetr_hgnetv2_x_6x_coco/ \
  2. --model_filename model.pdmodel \
  3. --params_filename model.pdiparams \
  4. --opset_version 16 \
  5. --save_file rtdetr_hgnetv2_x_6x_coco.onnx

五、ONNXRUNTIME部署示例

首先安装 ONNXRUNTIME。

In [ ]

!pip install onnxruntime

接下来是我用 ONNXRUNTIME 写的一个 demo,案例比较简单,大家可以自行了解。在这我就不多赘述了。

In [ ]

  1. import onnxruntime as rt
  2. import cv2
  3. import numpy as np

In [ ]

  1. sess = rt.InferenceSession("/home/aistudio/PaddleDetection/rtdetr_hgnetv2_x_6x_coco.onnx")
  2. img = cv2.imread("/home/aistudio/work/dataset/JPEGImages/1174.jpg")
  3. org_img = img
  4. im_shape = np.array([[float(img.shape[0]), float(img.shape[1])]]).astype('float32')
  5. img = cv2.resize(img, (640,640))
  6. scale_factor = np.array([[float(640/img.shape[0]), float(640/img.shape[1])]]).astype('float32')
  7. img = img.astype(np.float32) / 255.0
  8. input_img = np.transpose(img, [2, 0, 1])
  9. image = input_img[np.newaxis, :, :, :]
  10. output_dict = ["reshape2_83.tmp_0","tile_3.tmp_0"]
  11. inputs_dict = {
  12. 'im_shape': im_shape,
  13. 'image': image,
  14. 'scale_factor': scale_factor
  15. }
  16. result = sess.run(output_dict, inputs_dict)
  17. for item in result[0]:
  18. if item[1] > 0.5:
  19. if item[0] == 0:
  20. cv2.rectangle(org_img, (int(item[2]), int(item[3])), (int(item[4]), int(item[5])), (255,0,0), 2)
  21. cv2.putText(org_img, "helmet", (int(item[2]), int(item[3])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
  22. elif item[0] == 1:
  23. cv2.rectangle(org_img, (int(item[2]), int(item[3])), (int(item[4]), int(item[5])), (0,255,0), 2)
  24. cv2.putText(org_img, "vest", (int(item[2]), int(item[3])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
  25. elif item[0] == 2:
  26. cv2.rectangle(org_img, (int(item[2]), int(item[3])), (int(item[4]), int(item[5])), (0,0,255), 2)
  27. cv2.putText(org_img, "worker", (int(item[2]), int(item[3])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
  28. cv2.imwrite("/home/aistudio/work/result.png", org_img)

可视化结果如下:

六、总结与提高

本项目是使用飞桨团队最新推出的 RT-DETR 模型来实现的。根据论文描述,模型可以达到实时检测的效果,个人认为该模型是 ViT 系列模型所取得的一个重大突破,揭开了 ViT 实时化的序幕。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/669511
推荐阅读
相关标签
  

闽ICP备14008679号