准备工作
用自己的数据集训练 YOLO 模型可能会出现数据集标签格式不一样的问题,比如你的数据集如果使用 .json (可以是所有图片写在单个 json )存储那么就是 coco 格式;又比如VOC 数据集使用 .txt (每张 image 对应一个)存储标签,那它就是 yolo 格式。
以下是 coco 格式 RPC 数据集的例子:
以下是 yolo 格式 VOC 数据集的例子:
以下是 COCO JSON 和 YOLO 的目录结构,实际可能会有一点区别,比如缺少 annotations 这一级目录:
COCO_JSON/
├── 2017train/
│ └── *.jpg # 训练集图片
├── 2017val/
│ └── *.jpg # 验证集图片
├── 2017test/
│ └── *.jpg # 测试集图片
└── annotations/
├── instances_train.json # 训练集标注
├── instances_val.json # 验证集标注
└── instances_test.json # 测试集标注
YOLO/
├── images/
│ ├── train/
│ │ └── *.jpg # 训练集图片
│ ├── val/
│ │ └── *.jpg # 验证集图片
│ └── test/
│ └── *.jpg # 测试集图片
└── labels/
├── train/
│ └── *.txt # 训练集标签
├── val/
│ └── *.txt # 验证集标签
└── test/
└── *.txt # 测试集标签
copy from:xiaohangguo/JSON_YOLO_intertwined: COCO JSON 格式 和YOLO 相互转化的代码,包含YOLO2JSON 和JSON2YOLO,在很多数据集上测试有效。
有很多博主分享了自己的 json2yolo 的代码(比如以上这个github项目),但其实官方自己有 json2yolo 的工具,而且使用起来十分方便,我将使用 RPC 数据集为例演示一下如何使用。
RPC数据集:
Project Page:RPC-Dataset Project Page
kaggle:Retail Product Checkout Dataset
官方工具:ultralytics/JSON2YOLO: Convert JSON annotations into YOLO format.
(使用 git clone 或download zip)
使用
首先,RPC 数据集是缺少 annotations 目录的,可以自己补上:
然后我们查看一下已经下载好的官方工具代码
打开 general_json2yolo.py:
官方为我们提供了多种格式的 json2yolo 如 infolks_json、vott_json等,这里我们使用 coco_json :
def convert_coco_json(json_dir="../coco/annotations/", use_segments=False, cls91to80=False):
"""Converts COCO JSON format to YOLO label format, with options for segments and class mapping."""
save_dir = make_dirs() # output directory
coco80 = coco91_to_coco80_class()
# Import json
for json_file in sorted(Path(json_dir).resolve().glob("*.json")):
fn = Path(save_dir) / "labels" / json_file.stem.replace("instances_", "") # folder name
fn.mkdir()
with open(json_file) as f:
data = json.load(f)
# Create image dict
images = {"{:g}".format(x["id"]): x for x in data["images"]}
# Create image-annotations dict
imgToAnns = defaultdict(list)
for ann in data["annotations"]:
imgToAnns[ann["image_id"]].append(ann)
# Write labels file
for img_id, anns in tqdm(imgToAnns.items(), desc=f"Annotations {json_file}"):
img = images[f"{img_id:g}"]
h, w, f = img["height"], img["width"], img["file_name"]
bboxes = []
segments = []
for ann in anns:
if ann["iscrowd"]:
continue
# The COCO box format is [top left x, top left y, width, height]
box = np.array(ann["bbox"], dtype=np.float64)
box[:2] += box[2:] / 2 # xy top-left corner to center
box[[0, 2]] /= w # normalize x
box[[1, 3]] /= h # normalize y
if box[2] <= 0 or box[3] <= 0: # if w <= 0 and h <= 0
continue
cls = coco80[ann["category_id"] - 1] if cls91to80 else ann["category_id"] - 1 # class
box = [cls] + box.tolist()
if box not in bboxes:
bboxes.append(box)
# Segments
if use_segments:
if len(ann["segmentation"]) > 1:
s = merge_multi_segment(ann["segmentation"])
s = (np.concatenate(s, axis=0) / np.array([w, h])).reshape(-1).tolist()
else:
s = [j for i in ann["segmentation"] for j in i] # all segments concatenated
s = (np.array(s).reshape(-1, 2) / np.array([w, h])).reshape(-1).tolist()
s = [cls] + s
if s not in segments:
segments.append(s)
# Write
with open((fn / f).with_suffix(".txt"), "a") as file:
for i in range(len(bboxes)):
line = (*(segments[i] if use_segments else bboxes[i]),) # cls, box or segments
file.write(("%g " * len(line)).rstrip() % line + "\n")
函数定义中的3个参数分别为:
json_dir :.json 文件所在的目录
use_segments:是否使用分割信息,如果只做目标检测则没有分割信息
cls91to80:是否使用 COCO91 to COCO80,将91种类别映射到常见的80种类别
主函数:
if __name__ == "__main__":
source = "COCO"
if source == "COCO":
convert_coco_json(
"/root/dataset/retail_product_checkout/annotations", # directory with *.json
use_segments=False,
cls91to80=False,
)
elif source == "infolks": # Infolks https://infolks.info/
convert_infolks_json(name="out", files="../data/sm4/json/*.json", img_path="../data/sm4/images/")
elif source == "vott": # VoTT https://github.com/microsoft/VoTT
convert_vott_json(
name="data",
files="../../Downloads/athena_day/20190715/*.json",
img_path="../../Downloads/athena_day/20190715/",
) # images folder
elif source == "ath": # ath format
convert_ath_json(json_dir="../../Downloads/athena/") # images folder
我们只关注 source=coco 的情况,我们需要修改 .json 文件目录,第二个参数如果不用分割信息就 false,而最后一个参数是false,因为我使用的RPC数据集一共有200个类别,并且也不是COCO91,所以不需要将它映射到80个类别。
修改完后,接下来就可以直接执行这个文件,它将在你数据集的目录下生成一个 new_dir 的文件夹,转换后的 .txt 标签已经放在 labels 文件夹下:
到此使用官方工具进行 josn2yolo 的转换已经完成了。
最后,如需正常使用数据集,记得按 yolo 格式目录结构整理一下自己的目录。