使用PPLCNet模型对车辆朝向进行识别
1. 配置PaddlePaddle,PaddleClas环境
安装:请先参考文档 环境准备 配置 PaddleClas 运行环境。
2. 准备数据
首先从VeRi数据集官网中申请并下载数据。
2.1 标注数据格式
参考:基于PP-Vehicle的交通监控分析系统
VeRi数据集标注了车辆的10种属性,其中10种车辆颜色,9种车型属性,具体如下:
# 车辆颜色
- "yellow"
- "orange"
- "green"
- "gray"
- "red"
- "blue"
- "white"
- "golden"
- "brown"
- "black"
# 车型
- "sedan"
- "suv"
- "van"
- "hatchback"
- "mpv"
- "pickup"
- "bus"
- "truck"
- "estate"
在标注文件中使用长度为19的序列来表示上述属性。
举例:
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
前10位中,位序号0的值为1,表示车辆颜色为"yellow"。
后9位中,位序号11的值为1,表示车型为"suv"。
现在定义一种新的属性车辆朝向
,包含forward、sideward、backward
三个属性,因此要在上述19位标志的后面增加3位分别对应上面的3个方向。
新的属性定义变成了[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1].(括号内为新增属性位,最后一位为1,表示朝向为backward
)
2.2 标注数据
(1)创建list_direction.txt
,添加内容:
1 forward
2 sideward
3 backward
(2)修改train_label.xml
、test_label.xml
,添加directionID
字段,如下:
其中directionID
为1
表示车头朝前,2
表示车辆水平,3
表示车头朝后
(3)转换label
,生成train_list.txt
可以在python终端中执行下面的命令,也可以将其写入一个文件,然后使用python convert.py的方式运行该文件。
import os
from xml.dom.minidom import parse
vehicleids = []
def convert_annotation(input_fp, output_fp, subdir):
in_file = open(input_fp)
list_file = open(output_fp, 'w')
tree = parse(in_file)
root = tree.documentElement
for item in root.getElementsByTagName("Item"):
label = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']
if item.hasAttribute("imageName"):
name = item.getAttribute("imageName")
if item.hasAttribute("vehicleID"):
vehicleid = item.getAttribute("vehicleID")
if vehicleid not in vehicleids :
vehicleids.append(vehicleid)
vid = vehicleids.index(vehicleid)
if item.hasAttribute("colorID"):
colorid = int (item.getAttribute("colorID"))
label[colorid-1] = '1'
if item.hasAttribute("typeID"):
typeid = int (item.getAttribute("typeID"))
label[typeid+9] = '1'
if item.hasAttribute("directionID"):
typeid = int (item.getAttribute("directionID"))
label[typeid+18] = '1'
label = ','.join(label)
list_file.write(os.path.join(subdir, name) + "\t" + label + "\n")
list_file.close()
convert_annotation('train_label.xml', 'train_list.txt', 'image_train') #imagename vehiclenum colorid typeid
convert_annotation('test_label.xml', 'test_list.txt', 'image_test')
执行上述命令后,VeRi目录中具有以下数据:
VeRi
├── image_train
│ ├── 0001_c001_00016450_0.jpg
│ ├── 0001_c001_00016460_0.jpg
│ ├── 0001_c001_00016470_0.jpg
...
├── image_test
│ ├── 0002_c002_00030600_0.jpg
│ ├── 0002_c002_00030605_1.jpg
│ ├── 0002_c002_00030615_1.jpg
...
...
├── train_list.txt
├── test_list.txt
├── train_label.xml
├── test_label.xml
其中train/
和test/
分别为训练集和验证集。train_list.txt
和test_list.txt
分别为训练集和验证集的转换后用于训练的标签文件。
3. 模型训练
官网: PULC 车辆属性识别模型
标注好的数据,放在PaddleClas
的dataset
目录下,数据集目录名为VeRi
。
3.1 修改配置文件
ppcls/configs/PULC/vehicle_attribute/PPLCNet_x1_0.yaml
(1)训练属性种类
修改Arch
下的class_num
为22,对应上面最终属性总位数。
Arch:
name: "PPLCNet_x1_0"
pretrained: True
use_ssld: True
class_num: 22 #属性种类数量
(2)训练数据路径
修改DataLoader
中Train
、Eval
下的 image_root
和cls_label_path
。分别表示训练数据跟路径和标注文件路径。
需要注意的是标注文件train_list.txt
中的图片路径是相对于image_root
目录的相对路径。
DataLoader:
Train:
dataset:
name: MultiLabelDataset
image_root: "dataset/VeRi/" # the root path of training images
cls_label_path: "dataset/VeRi/train_list.txt" # the location of the training list file
label_ratio: True
transform_ops:
...
Eval:
dataset:
name: MultiLabelDataset
image_root: "dataset/VeRi/" # the root path of evaluation images
cls_label_path: "dataset/VeRi/val_list.txt" # the location of the evaluation list file
label_ratio: True
transform_ops:
...
3.2 训练、评估
上述准备工作完成后,可以执行以下命令进行训练:
python tools/train.py -c ./ppcls/configs/PULC/vehicle_attribute/PPLCNet_x1_0.yaml
训练完成后可以执行以下命令进行性能评估:
python tools/eval.py -c ./ppcls/configs/PULC/vehicle_attribute/PPLCNet_x1_0.yaml \
-o Global.pretrained_model=./output/PPLCNet_x1_0/best_model
3.3 模型导出
python3 tools/export_model.py \
-c ./ppcls/configs/PULC/vehicle_attribute/PPLCNet_x1_0.yaml \
-o Global.pretrained_model=output/PPLCNet_x1_0/best_model \
-o Global.save_inference_dir=deploy/models/PPLCNet_x1_0_vehicle_attribute_model
4 模型预测
修改后处理文件ppcls\data\postprocess\attr_rec.py
class VehicleAttribute(object):
def __init__(self, color_threshold=0.5, type_threshold=0.5, direction_threshold=0.5):
self.color_threshold = color_threshold
self.type_threshold = type_threshold
self.direction_threshold = direction_threshold
self.color_list = [
"yellow", "orange", "green", "gray", "red", "blue", "white",
"golden", "brown", "black"
]
self.type_list = [
"sedan", "suv", "van", "hatchback", "mpv", "pickup", "bus",
"truck", "estate"
]
self.direction_list = [
"forward", "sideward", "backward"]
def __call__(self, x, file_names=None):
......
for idx, res in enumerate(x):
res = res.tolist()
label_res = []
color_idx = np.argmax(res[:10])
type_idx = np.argmax(res[10:19])
direction_idx = np.argmax(res[19:])
print(color_idx, type_idx)
if res[color_idx] >= self.color_threshold:
color_info = f"Color: ({self.color_list[color_idx]}, prob: {res[color_idx]})"
else:
color_info = "Color unknown"
if res[type_idx + 10] >= self.type_threshold:
type_info = f"Type: ({self.type_list[type_idx]}, prob: {res[type_idx + 10]})"
else:
type_info = "Type unknown"
if res[direction_idx + 19] >= self.direction_threshold:
direction_info = f"Direction: ({self.direction_list[direction_idx]}, prob: {res[direction_idx + 19]})"
else:
direction_info = "Direction unknown"
label_res = f"{color_info}, {type_info}, {direction_info}"
threshold_list = [self.color_threshold
] * 10 + [self.type_threshold] * 9 + [self.direction_threshold] * 3
pred_res = (np.array(res) > np.array(threshold_list)
).astype(np.int8).tolist()
batch_res.append({
"attr": label_res,
"pred": pred_res,
"file_name": file_names[idx]
})
return batch_res
执行以下命令进行预测:
python tools/infer.py -c ./ppcls/configs/PULC/vehicle_attribute/PPLCNet_x1_0.yaml -o Global.pretrained_model=output/PPLCNet_x1_0/best_model
输出结果如下:
[{'attr':
'Color: (yellow, prob: 0.9991264939308167),
Type: (hatchback, prob: 0.99992835521698),
Direction: (backward, prob: 0.9999992847442627)',
'pred': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], 'file_name': './deploy/images/PULC/vehicle_attribute/0002_c002_00030670_0.jpg'}]