X光安检图像识别挑战赛-yolov8实现
赛题链接:https://challenge.xfyun.cn/topic/info?type=Xray-2024
赛事概要
赛题背景
X光安检机是目前我国使用最广泛的安检技术手段,广泛应用于城市轨交、铁路、机场、重点场馆、物流寄递等场景。使用人工智能技术,辅助一线安检员进行X光安检判图,可以有效降低因为人员疲劳或注意力不集中带来的漏报等问题。但在实际场景中,因物品的多样性、成像角度、遮挡等问题,为算法的开发带来了一定的挑战。
赛事任务
本赛事的任务是:基于科大讯飞提供的真实X光安检图像集构建检测模型,对X光安检图像中的指定类别的物品进行检测,识别出物体的位置和类别。
评审规则
数据说明
此次比赛提供带标注的训练数据,即待检测物品在包裹中的X光图像及其标注文件。标注文件中的类别为8类,包括:刀(knife)、扳手(wrench)、玻璃瓶(glassbottle)、饮料瓶(drinkbottle)、充电宝(powerbank)、雨伞(umbrella)、金属杯(metalcup)、打火机(lighter)。待识别物品的X光成像示意图如下图所示。
比赛提供的X光图像及其矩形框标注的文件按照数据来源存放在不同的文件夹中,图像文件采用jpg格式,标注文件采用xml格式,各字段含义参照voc数据集。voc各字段含义对应表为:
├── filename 文件名
├── size 图像尺寸
├── width 图像宽度
├── height 图像高度
└── depth 图像深度,一般为3表示是彩色图像
└── object 图像中的目标,可能有多个
├── name 该目标的标签名称
└── bndbox 该目标的标注框
├── xmin 该目标的左上角宽度方向坐标
├── ymin 该目标的左上角高度方向坐标
├── xmax 该目标的右下角宽度方向坐标
└── ymax 该目标的右下角高度方向坐标
评估指标
评测方式采用计算mAP(IoU = 0.5)的方式。
首先计算每个类的AP:
(1)根据预测框和标注框的IoU是否达到阈值0.5判断该预测框是真阳性还是假阳性;
(2)根据每个预测框的置信度进行从高到低排序;
(3)在不同置信度阈值下计算精确率和召回率,得到若干组PR值;
(4)绘制PR曲线并计算AP值。
然后计算mAP:把所有类的AP值求平均得到mAP。
提交要求
选手需要提交json格式文件,详情见示例。其中,坐标值必须为大于0的正数且不能超过图像的宽高。提交文件需按序排列,首先按图片顺序排列,然后按类别顺序排列,置信度顺序随意。
(a) 图片顺序,请按照图片编号顺序。
(b) 类别顺序,请参照下列顺序: {‘knife’: 1, wrench: 2, ‘glassbottle’: 3, drinkbottle: 4, powerbank: 5, ‘umbrella’: 6, ‘metalcup’: 7, ‘lighter’: 8}
数据转换处理
voc数据集格式转yolo数据集格式
网上有较多示例,这里不再赘述
类别顺序问题
比赛要求是从1开始(1-8)
yolo是从0开始(0-7)
解决方案一
- voc数据集格式转yolo数据集格式时将类别序号修改为yolo格式
- 提交结果时将类别序号修改为要求格式
解决方案二
添加一个背景类别设置类别序号为0
数据特点分析
图片宽高分布
统计图片宽高,根据硬件、精度、速度要求设置imgsz大小;后期根据结果反馈调节
图片标签类别分布
yolov8训练生成的labels.jpg、labels_correlogram.jpg有相关信息;针对类别不平衡一般使用欠采样或过采样的方式解决;后期根据结果反馈调节
数据预处理
统计图片大小得知图片极大多数平均长宽在(512, 640)区间,极少数大于1280;由此暂定imgsz可设置为640、800、960、1120、1280、1440、1600等;考虑比赛追求高精度、硬件设备条件选取imgsz;后期根据结果反馈调节
原图片长宽比不一问题
原因:训练图片长宽比不一时yolov8会暴力调整图片为指定大小,造成图片形变量较大影响推理精度
思路解析
- 将图片按一定规则处理为统一的长宽比,保证训练、推理时形变量较小且较为统一
- 训练时暴力调整后形变量较大原图片也可当作扩增图片使用
- 一般方案为将原图变为按最长边的正方形,不足部分填充指定颜色(yolov8一般为(114, 114, 114))
调整图片难点
图片形变后标签信息已经改变
# img_w, img_h # 原始旧图片宽高
# img_h_new, img_w_new # 新图片宽高
with open(labels_txt_path, 'r') as label_txt:
for line in label_txt.readlines():
yolo_label = line.strip().split(' ')
category_id = int(yolo_label[0].strip())
x_center = float(yolo_label[1].strip())
y_center = float(yolo_label[2].strip())
box_width = float(yolo_label[3].strip())
box_height = float(yolo_label[4].strip())
# 通过img_w, img_h, x_center, y_center, box_width, box_height计算x_min, y_min, x_max, y_max
# ...
# 将box信息转换到yolo格式
box_w = x_max - x_min
box_h = y_max - y_min
xcenter = x_min + box_w / 2 + start_x
ycenter = y_min + box_h / 2 + start_y
# 绝对坐标转相对坐标,保存6位小数
xcenter_01 = round(xcenter / img_w_new, 6)
ycenter_01 = round(ycenter / img_h_new, 6)
w = round(box_w / img_w_new, 6)
h = round(box_h / img_h_new, 6)
推理评估
在
ultralytics/cfg/default.yaml
设置source、save_txt、save_conf等参数后。运行ultralytics/models/yolo/detect/predict.py
文件即可 相关参数解释:
source: ../../../my_00_data/data_21/data/test/images # test图片位置 save_txt: True # (bool) save results as .txt file # 是否保存推理文件 save_conf: True # (bool) save results with confidence scores # 推理结果里是否保存置信度分数
提交结果准备
yolo格式转提交的json格式
核心代码
json_data_01 = [] # 用于存储json文件
for file_path_temp in img_list:
json_data_02 = [] # 用于存储每一张图片的json信息
#
for k in range(classes_num): # classes_num 类别数量 这里为8
json_data_02.append([]) #
json_data_01.append(json_data_02)
image = cv2.imread(file_path_temp)
h, w, c = image.shape
labels_txt_path = file_path_temp.replace('images', 'labels').replace('jpg', 'txt')
if os.path.exists(labels_txt_path):
with open(labels_txt_path, 'r') as label_txt:
for line in label_txt.readlines():
json_data_03 = []
yolo_label = line.strip().split(' ')
category_id = int(yolo_label[0].strip())
x_center = float(yolo_label[1].strip())
y_center = float(yolo_label[2].strip())
box_width = float(yolo_label[3].strip())
box_height = float(yolo_label[4].strip())
# 通过w, h, x_center, y_center, box_width, box_height计算x_min, y_min, x_max, y_max
# ...
# [12.966, 92.649, 159.565, 217.636, 0.901]
json_data_03.append(round(x_min, 2))
json_data_03.append(round(y_min, 2))
json_data_03.append(round(x_max, 2))
json_data_03.append(round(y_max, 2))
json_data_03.append(float(yolo_label[5].strip()))
# 下标从0开始
json_data_02[category_id].append(json_data_03)
else:
pass # 文件不存在
print(json_data_01)
# 保存json文件
# ...
数据集
X光安检图像识别挑战赛(2024)数据集
https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=12077215&loadingShow=1&noTitleBar=1&msource=merchant_share
X光安检图像识别挑战赛(2023)数据集
https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=12077213&loadingShow=1&noTitleBar=1&msource=merchant_share
X光安检图像识别挑战赛(2022)数据集
https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=12077212&loadingShow=1&noTitleBar=1&msource=merchant_share
X光安检图像识别挑战赛(2021)数据集
https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=12077214&loadingShow=1&noTitleBar=1&msource=merchant_share
X光安检图像识别挑战赛(2020)数据集
https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=12077216&loadingShow=1&noTitleBar=1&msource=merchant_share