旷世CrowdHuman数据集分析、使用
零、数据下载
一、划分数据集
这里只讲解验证数据集的解析方式,训练集同理。
- 将下载好的图像文件解压
- 提取出解压后的
Images
文件夹复制到自己创建的test
文件夹中,有4370张图片(训练数据集分为01、02和03三个压缩包,分别提取出其中的images
文件夹,合并成一个。三个压缩包每个5000张图片,合成之后的Images
文件夹中15000张图片)
- 按照上述操作之后形成如下图所示的目录结构
二、分析数据标注文件
第一次使用旷世提供的CrowdHuman dataset对应的annotation_train.odgt
标注文件时,容易懵逼,无从下手。让我们逐项分析这些数据:
上图是在vscode中打开的annotation_val.odgt
文件,每一行代表一张图片的标注信息。下面分析一下每张图片的标注信息。
-
"ID": "273271,c9db000d5146c15"
- 表示该图像的唯一标识符,与对应的图片文件同名。
-
"gtboxes"
这是一个列表,包含了该图像中所有检测到的人体对象的信息。列表中每个对象都是一个字典,描述了一个人体实例,包含以下键值对:"fbox": [72, 202, 163, 503]
- 这是该人体实例的全身边界框坐标,格式为 [x, y, w, h]。
"tag": "person"
- 表明这是一个人体实例。
"hbox": [171, 208, 62, 83]
- 这是该人体实例的头部边界框坐标。
"extra": {"box_id": 0, "occ": 0}
box_id
是该实例的唯一标识符,occ
代表是否被遮挡,0表示未遮挡。
"vbox": [72, 202, 163, 398]
- 这是该人体实例的可见部分边界框坐标。
"head_attr": {"ignore": 0, "occ": 0, "unsure": 0}
- 这描述了与头部有关的属性,
ignore
、occ
和unsure
分别表示是否忽略、是否遮挡和是否不确定。全为0表示正常情况。
- 这描述了与头部有关的属性,
三、解析annotation.odgt文件
import json
import os
import cv2
from pathlib import Path
def image_shape(ID: str, images_dir: Path) ->tuple:
jpg_path = images_dir / ('%s.jpg' % ID)
assert jpg_path.is_file(), f"{jpg_path} not found"
img = cv2.imread(jpg_path.as_posix())
return img.shape
def Normalized(size_list: list, original_shape: tuple) -> str:
x, y, w, h = size_list
ori_w, ori_h = original_shape[:2]
cx = (x + w / 2) / w
cy = (y + h / 2) / h
w /= ori_w
h /= ori_h
return f"{cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n"
def process(annotation_filename: str, output_dir: Path, images_dir: Path, classes_list: list):
assert output_dir is not None
output_dir.mkdir(exist_ok=True)
with open(annotation_filename, 'r') as fanno:
for raw_anno in fanno.readlines():
anno = json.loads(raw_anno)
ID = anno['ID'] # e.g. '273271,c9db000d5146c15'
print('Processing ID: %s' % ID)
original_shape = image_shape(ID, images_dir) # 获取原始图像size,方便后续归一化 (600, 800, 3)
txt_path = output_dir / ('%s.txt' % ID) # 标注文件生成地址
with open(txt_path.as_posix(), 'w') as ftxt:
for obj in anno['gtboxes']:
if obj['tag'] != 'person':
continue
for i, cls in enumerate(classes_list):
if cls in obj.keys():
assert len(obj[cls]) == 4, f"{cls} error '{ID}.txt' "
line = f"{i} " + Normalized(obj[cls], original_shape)
ftxt.write(line)
if __name__ == "__main__":
process(r"F:\crowdhuman\annotation_val.odgt", Path("./test/labels"), Path("./test/Images"), ['fbox', 'hbox'])
process(r"F:\crowdhuman\annotation_train.odgt", Path("./train/labels"), Path("./train/Images"), ['fbox', 'hbox'])
image_shape
函数根据给定的图像 ID 和图像目录路径,读取对应的图像文件,并返回图像的形状(高度、宽度和通道数)。
Normalized
函数将边界框坐标从像素值转换为归一化的相对值,以适应 YOLO 格式的要求。它接受两个参数:一个包含边界框坐标[x, y, w, h]
的列表和原始图像的形状。计算出边界框中心点的相对坐标(cx, cy)
,以及相对于图像宽度和高度的归一化边界框宽度和高度。最后,将这些值格式化为一个字符串,并返回。process
函数是主要的处理函数。它接受四个参数:annotation_filename
: CrowdHuman 数据集的标注文件路径。output_dir
: 用于存储生成的 YOLO 格式标注文件的输出目录路径。images_dir
: 包含图像文件的目录路径。classes_list
: 一个列表,包含需要处理的边界框类型,如['fbox', 'hbox']
。
该函数首先创建输出目录(如果不存在)。然后,它逐行读取标注文件中的 JSON 数据。对于每个 JSON 对象,它提取图像 ID,获取原始图像的形状,并构造输出的 YOLO 格式标注文件路径。
接下来,它遍历每个目标对象的边界框信息。如果该对象是'mask'
类型,则跳过。否则,它断言该对象是'person'
类型。然后,对于每个指定的边界框类型(classes_list
中的元素),如果该对象包含该类型的边界框坐标,就将其转换为 YOLO 格式的标注行,并写入输出文件。
- 最后,在主程序中,调用
process
函数处理验证集和训练集的标注文件,并将生成的 YOLO 格式标注文件存储在./test/labels
和./train/labels
目录中。处理时,只考虑了'fbox'
和'hbox'
两种边界框类型。
四、结语
后续可以根据自己的需求对代码部分进行修改。有错误的地方欢迎指正。