news 2026/6/4 11:30:00

YOLO txt 格式转换为COCO Json 格式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO txt 格式转换为COCO Json 格式

将数据集YOLO txt格式转换为 COCO Json格式

在下面的脚本中:

要注意YOLO训练集的图片目录和标签目标

classes.txt文件就是YOLO数据集中的类别,直接写在txt文件中,一行一个,不用带引号和逗号。

以及生成的训练集json文件和测试集json文件

代码1

import os import cv2 import json from tqdm import tqdm from sklearn.model_selection import train_test_split import argparse parser = argparse.ArgumentParser() parser.add_argument('--root_dir', default='/dataset/dataset_seaship',type=str, help="root path of images and labels, include ./images and ./labels and classes.txt") parser.add_argument('--save_path', type=str,default='instances_val2017.json', help="if not split the dataset, give a path to a json file") arg = parser.parse_args() def yolo2coco(arg): root_path = arg.root_dir print("Loading data from ",root_path) assert os.path.exists(root_path) originLabelsDir = os.path.join(root_path, 'test/labels') originImagesDir = os.path.join(root_path, '/test/images') with open(os.path.join(root_path, 'classes.txt')) as f: classes = list(map(lambda x:x.strip(), f.readlines())) # images dir name indexes = os.listdir(originImagesDir) dataset = {'categories': [], 'annotations': [], 'images': []} for i, cls in enumerate(classes, 0): dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'}) # 标注的id ann_id_cnt = 0 for k, index in enumerate(tqdm(indexes)): # 支持 png jpg 格式的图片。 txtFile = index.replace('images','txt').replace('.jpg','.txt').replace('.png','.txt') # 读取图像的宽和高 im = cv2.imread(os.path.join(originImagesDir, index)) height, width, _ = im.shape # 添加图像的信息 if not os.path.exists(os.path.join(originLabelsDir, txtFile)): # 如没标签,跳过,只保留图片信息。 continue dataset['images'].append({'file_name': index, 'id': int(index[:-4]) if index[:-4].isnumeric() else index[:-4], 'width': width, 'height': height}) with open(os.path.join(originLabelsDir, txtFile), 'r') as fr: labelList = fr.readlines() for label in labelList: label = label.strip().split() x = float(label[1]) y = float(label[2]) w = float(label[3]) h = float(label[4]) # convert x,y,w,h to x1,y1,x2,y2 H, W, _ = im.shape x1 = (x - w / 2) * W y1 = (y - h / 2) * H x2 = (x + w / 2) * W y2 = (y + h / 2) * H # 标签序号从0开始计算, coco2017数据集标号混乱,不管它了。 cls_id = int(label[0]) width = max(0, x2 - x1) height = max(0, y2 - y1) dataset['annotations'].append({ 'area': width * height, 'bbox': [x1, y1, width, height], 'category_id': cls_id, 'id': ann_id_cnt, 'image_id': int(index[:-4]) if index[:-4].isnumeric() else index[:-4], 'iscrowd': 0, # mask, 矩形是从左上角点按顺时针的四个顶点 'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]] }) ann_id_cnt += 1 # 保存结果 with open(arg.save_path, 'w') as f: json.dump(dataset, f) print('Save annotation to {}'.format(arg.save_path)) if __name__ == "__main__": yolo2coco(arg)

补充一下 classes.txt 文件

classes.txt # 举例 car truck bus # 需要注意的是,后面不允许有逗号, 例如 'car,' , # 全部使用小写字母

增强版代码2,解决代码1中对于前缀为0丢失的情况,例如0000001_05249_d_0000009.jpg

import os import cv2 import json from tqdm import tqdm from sklearn.model_selection import train_test_split import argparse parser = argparse.ArgumentParser() parser.add_argument('--root_dir', default='dataset/VisDrone/',type=str, help="root path of images and labels, include ./images and ./labels and classes.txt") parser.add_argument('--save_path', type=str,default='dataset/VisDrone/val.json', help="if not split the dataset, give a path to a json file") arg = parser.parse_args() def yolo2coco(arg): root_path = arg.root_dir print("Loading data from ", root_path) assert os.path.exists(root_path) originLabelsDir = os.path.join(root_path, 'VisDrone2019-DET-val/labels') originImagesDir = os.path.join(root_path, 'VisDrone2019-DET-val/images') with open(os.path.join(root_path, 'classes.txt')) as f: classes = list(map(lambda x: x.strip(), f.readlines())) indexes = os.listdir(originImagesDir) dataset = {'categories': [], 'annotations': [], 'images': []} for i, cls in enumerate(classes, 0): dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'}) ann_id_cnt = 0 for k, index in enumerate(tqdm(indexes)): # 支持多种图片格式 txtFile = index.rsplit('.', 1)[0] + '.txt' # 更安全的方式获取对应的txt文件 # 读取图像 im = cv2.imread(os.path.join(originImagesDir, index)) if im is None: print(f"Warning: Cannot read image {index}, skipping...") continue height, width, _ = im.shape # 关键修改:使用完整文件名(不含扩展名)作为字符串ID image_id = os.path.splitext(index)[0] # 例如: '0000001_05249_d_0000009' if not os.path.exists(os.path.join(originLabelsDir, txtFile)): # 没有标签文件,只保留图片信息 dataset['images'].append({ 'file_name': index, 'id': image_id, # 字符串ID,保留原始格式 'width': width, 'height': height }) continue dataset['images'].append({ 'file_name': index, 'id': image_id, # 字符串ID 'width': width, 'height': height }) with open(os.path.join(originLabelsDir, txtFile), 'r') as fr: labelList = fr.readlines() for label in labelList: label = label.strip().split() if len(label) != 5: print(f"Warning: Invalid label format in {txtFile}: {label}") continue x = float(label[1]) y = float(label[2]) w = float(label[3]) h = float(label[4]) # 转换为COCO格式的bbox (x1, y1, width, height) H, W = height, width # 使用已读取的高度和宽度 x1 = (x - w / 2) * W y1 = (y - h / 2) * H x2 = (x + w / 2) * W y2 = (y + h / 2) * H cls_id = int(label[0]) bbox_width = max(0, x2 - x1) bbox_height = max(0, y2 - y1) dataset['annotations'].append({ 'area': bbox_width * bbox_height, 'bbox': [x1, y1, bbox_width, bbox_height], 'category_id': cls_id, 'id': ann_id_cnt, 'image_id': image_id, # 使用相同的字符串ID 'iscrowd': 0, 'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]] }) ann_id_cnt += 1 # 保存结果 with open(arg.save_path, 'w') as f: json.dump(dataset, f) print(f'Save annotation to {arg.save_path}') print(f'Total images: {len(dataset["images"])}') print(f'Total annotations: {len(dataset["annotations"])}') if __name__ == "__main__": yolo2coco(arg)

代码 3

import os import json import cv2 import random import time from PIL import Image # 修改为要生成的annotations文件夹位置 coco_format_save_path = './data/coco/annotations' #要生成的标准coco格式标签所在文件夹 # 类别文件:原来COCO Json格式中的类别名称,将其写到classes.txt文件中,每行一个 yolo_format_classes_path = './data/coco/classes.txt' #类别文件,一行一个类 # 注意区分训练集和测试集文件位置 yolo_format_annotation_path = './YOLOdataset/train/labels' #yolo格式标签所在文件夹 img_pathDir = './YOLOdataset/train/images' # 图片所在文件夹 if not os.path.exists(coco_format_save_path): os.makedirs(coco_format_save_path) #打开并读取类别文件 with open(yolo_format_classes_path,'r') as fr: lines1=fr.readlines() # print(lines1) #存储类别的列表 categories=[] for j,label in enumerate(lines1): label=label.strip() #将类别信息添加到categories中 categories.append({'id':j+1,'name':label,'supercategory':'None'}) # print(categories) # 写入.json文件的大字典 write_json_context=dict() write_json_context['info']= {'description': '', 'url': '', 'version': '', 'year': 2024, 'contributor': 'kak', 'date_created': '2024-06-28'} write_json_context['licenses']=[{'id':1,'name':None,'url':None}] write_json_context['categories']=categories write_json_context['images']=[] write_json_context['annotations']=[] #接下来的代码主要添加'images'和'annotations'的key值 #遍历该文件夹下的所有文件,并将所有文件名添加到列表中 imageFileList=os.listdir(img_pathDir) for i,imageFile in enumerate(imageFileList): #获取图片的绝对路径 imagePath = os.path.join(img_pathDir,imageFile) image = Image.open(imagePath) #读取图片,然后获取图片的宽和高 W, H = image.size #使用一个字典存储该图片信息 img_context={} #返回path最后的文件名。如果path以/或\结尾,那么就会返回空值 #img_name=os.path.basename(imagePath) img_context['file_name']=imageFile img_context['height']=H img_context['width']=W img_context['date_captured']='2022-07-8' #该图片的id img_context['id']=i img_context['license']=1 img_context['color_url']='' img_context['flickr_url']='' #将该图片信息添加到'image'列表中 write_json_context['images'].append(img_context) txtFile = imageFile.replace('.jpg','.txt') #获取该图片获取的txt文件 #txtFile=imageFile[:5]+'.txt' with open(os.path.join(yolo_format_annotation_path,txtFile),'r') as fr: #读取txt文件的每一行数据,lines2是一个列表,包含了一个图片的所有标注信息 lines=fr.readlines() for j,line in enumerate(lines): #将每一个bounding box信息存储在该字典中 bbox_dict = {} # line = line.strip().split() # print(line.strip().split(' ')) #获取每一个标注框的详细信息 class_id,x,y,w,h=line.strip().split(' ') #将字符串类型转为可计算的int和float类型 class_id,x, y, w, h = int(class_id), float(x), float(y), float(w), float(h) #坐标转换 xmin=(x-w/2)*W ymin=(y-h/2)*H xmax=(x+w/2)*W ymax=(y+h/2)*H w=w*W h=h*H #bounding box的坐标信息 bbox_dict['id']=i*10000+j bbox_dict['image_id']=i #注意目标类别要加一 bbox_dict['category_id']=class_id+1 bbox_dict['iscrowd']=0 height,width=abs(ymax-ymin),abs(xmax-xmin) bbox_dict['area']=height*width bbox_dict['bbox']=[xmin,ymin,w,h] bbox_dict['segmentation']=[[xmin,ymin,xmax,ymin,xmax,ymax,xmin,ymax]] #将每一个由字典存储的bounding box信息添加到'annotations'列表中 write_json_context['annotations'].append(bbox_dict) # 要注意区分 生成的训练集json文件和测试json文件 name = os.path.join(coco_format_save_path, "instances_train2017" + '.json') if not os.path.exists(name): with open(name, 'w') as fw: #将字典信息写入.json文件中 json.dump(write_json_context,fw,indent=2)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/4 11:29:04

终极指南:SMUDebugTool专业硬件调试工具完整教程

终极指南:SMUDebugTool专业硬件调试工具完整教程 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gitcode…

作者头像 李华
网站建设 2026/6/4 11:25:18

Elsevier投稿追踪插件:告别手动刷新的智能审稿监控方案

Elsevier投稿追踪插件:告别手动刷新的智能审稿监控方案 【免费下载链接】Elsevier-Tracker 项目地址: https://gitcode.com/gh_mirrors/el/Elsevier-Tracker 你是否曾经为等待Elsevier期刊审稿结果而焦虑不安?每天重复登录投稿系统查看状态&…

作者头像 李华
网站建设 2026/6/4 11:24:54

信奥赛C++提高组csp-s之搜索进阶(记忆化搜索核心思想)

信奥赛C提高组csp-s之搜索进阶(记忆化搜索核心思想) 记忆化搜索原理详解 一、什么是记忆化搜索 记忆化搜索(Memoization Search)是一种通过记录已经遍历过的状态信息,从而避免对同一状态重复遍历的搜索算法。可以把它…

作者头像 李华
网站建设 2026/6/4 11:24:15

3个现代Anki卡片模板让你的学习效率翻倍:告别枯燥记忆

3个现代Anki卡片模板让你的学习效率翻倍:告别枯燥记忆 【免费下载链接】anki-prettify Collection of customizable Anki flashcard templates with modern and clean themes. 项目地址: https://gitcode.com/gh_mirrors/an/anki-prettify 你是否厌倦了Anki默…

作者头像 李华