Python实现将LabelMe生成的JSON格式转换成YOLOv8支持的TXT格式

      标注工具 LabelMe 生成的标注文件为JSON格式,而YOLOv8中支持的为TXT文件格式。以下Python代码实现3个功能

     1.将JSON格式转换成TXT格式;

     2.将数据集进行随机拆分,生成YOLOv8支持的目录结构;

     3.生成YOLOv8支持的YAML文件。

     代码test_labelme2yolov8.py如下:

import os
import json
import argparse
import colorama
import random
import shutil

def parse_args():
	parser = argparse.ArgumentParser(description="json(LabelMe) to txt(YOLOv8)")

	parser.add_argument("--dir", required=True, type=str, help="images, json files, and generated txt files, all in the same directory")
	parser.add_argument("--labels", required=True, type=str, help="txt file that hold indexes and labels, one label per line, for example: face 0")
	parser.add_argument("--val_size", default=0.2, type=float, help="the proportion of the validation set to the overall dataset:[0., 0.5]")
	parser.add_argument("--name", required=True, type=str, help="the name of the dataset")

	args = parser.parse_args()
	return args

def get_labels_index(name):
	labels = {} # key,value
	with open(name, "r") as file:
		for line in file:
			# print("line:", line)

			key_value = []
			for v in line.split(" "):
				# print("v:", v)
				key_value.append(v.replace("\n", "")) # remove line breaks(\n) at the end of the line
			if len(key_value) != 2:
				print(colorama.Fore.RED + "Error: each line should have only two values(key value):", len(key_value))
				continue

			labels[key_value[0]] = key_value[1]
		
	with open(name, "r") as file:
		line_num = len(file.readlines())

	if line_num != len(labels):
		print(colorama.Fore.RED + "Error: there may be duplicate lables:", line_num, len(labels))

	return labels

def get_json_files(dir):
	jsons = []
	for x in os.listdir(dir):
		if x.endswith(".json"):
			jsons.append(x)

	return jsons

def parse_json(name):
	with open(name, "r") as file:
		data = json.load(file)

	width = data["imageWidth"]
	height = data["imageHeight"]
	# print(f"width: {width}; height: {height}")

	objects=[]
	for shape in data["shapes"]:
		if shape["shape_type"] != "rectangle":
			print(colorama.Fore.YELLOW + "Warning: only the rectangle type is supported:", shape["shape_type"])
			continue

		object = []
		object.append(shape["label"])
		object.append(shape["points"])
		objects.append(object)

	return width, height, objects

def get_box_width_height(box):
	dist = lambda val: max(val) - min(val)

	x = [pt[0] for pt in box]
	y = [pt[1] for pt in box]

	return min(x), min(y), dist(x), dist(y)

def bounding_box_normalization(width, height, objects, labels):
	boxes = []
	for object in objects:
		box = [] # class x_center y_center width height
		box.append(labels[object[0]])

		# print("point:", object[1])
		x_min, y_min, box_w, box_h = get_box_width_height(object[1])
		box.append(round((float(x_min + box_w / 2.0) / width), 6))
		box.append(round((float(y_min + box_h / 2.0) / height), 6))
		box.append(round(float(box_w / width), 6))
		box.append(round(float(box_h / height), 6))

		boxes.append(box)
	
	return boxes	

def write_to_txt(dir, json, width, height, objects, labels):
	boxes = bounding_box_normalization(width, height, objects, labels)
	# print("boxes:", boxes)

	name = json[:-len(".json")] + ".txt"
	# print("name:", name)
	with open(dir + "/" + name, "w") as file:
		for item in boxes:
			# print("item:", item)
			if len(item) != 5:
				print(colorama.Fore.RED + "Error: the length must be 5:", len(item))
				continue
			string = item[0] + " " + str(item[1]) + " " + str(item[2]) + " " + str(item[3]) + " " + str(item[4]) + "\r"
			file.write(string)

def json_to_txt(dir, jsons, labels):
	for json in jsons:
		name = dir + "/" + json
		# print("name:", name)

		width, height, objects = parse_json(name)
		# print(f"width: {width}; height: {height}; objects: {objects}")

		write_to_txt(dir, json, width, height, objects, labels)


def is_in_range(value, a, b):
	return a <= value <= b

def get_random_sequence(length, val_size):
	numbers = list(range(0, length))
	val_sequence = random.sample(numbers, int(length*val_size))
	# print("val_sequence:", val_sequence)

	train_sequence = [x for x in numbers if x not in val_sequence]
	# print("train_sequence:", train_sequence)

	return train_sequence, val_sequence

def get_files_number(dir):
	count = 0
	for file in os.listdir(dir):
		if os.path.isfile(os.path.join(dir, file)):
			count += 1

	return count

def split_train_val(dir, jsons, name, val_size):
	if is_in_range(val_size, 0., 0.5) is False:
		print(colorama.Fore.RED + "Error: the interval for val_size should be:[0., 0.5]:", val_size)
		raise

	dst_dir_images_train = "datasets/" + name + "/images/train"
	dst_dir_images_val = "datasets/" + name + "/images/val"
	dst_dir_labels_train = "datasets/" + name + "/labels/train"
	dst_dir_labels_val = "datasets/" + name + "/labels/val"

	try:
		os.makedirs(dst_dir_images_train) #, exist_ok=True
		os.makedirs(dst_dir_images_val)
		os.makedirs(dst_dir_labels_train)
		os.makedirs(dst_dir_labels_val)
	except OSError as e:
		print(colorama.Fore.RED + "Error: cannot create directory:", e.strerror)
		raise

	# supported image formats
	img_formats = (".bmp", ".jpeg", ".jpg", ".png", ".webp")

	# print("jsons:", jsons)
	train_sequence, val_sequence = get_random_sequence(len(jsons), val_size)

	for index in train_sequence:
		for format in img_formats:
			file = dir + "/" + jsons[index][:-len(".json")] + format
			# print("file:", file)
			if os.path.isfile(file):
				shutil.copy(file, dst_dir_images_train)
				break

		file = dir + "/" + jsons[index][:-len(".json")] + ".txt"
		if os.path.isfile(file):
			shutil.copy(file, dst_dir_labels_train)

	for index in val_sequence:
		for format in img_formats:
			file = dir + "/" + jsons[index][:-len(".json")] + format
			if os.path.isfile(file):
				shutil.copy(file, dst_dir_images_val)
				break

		file = dir + "/" + jsons[index][:-len(".json")] + ".txt"
		if os.path.isfile(file):
			shutil.copy(file, dst_dir_labels_val)

	num_images_train = get_files_number(dst_dir_images_train)
	num_images_val = get_files_number(dst_dir_images_val)
	num_labels_train = get_files_number(dst_dir_labels_train)
	num_labels_val = get_files_number(dst_dir_labels_val)

	if  num_images_train + num_images_val != len(jsons) or num_labels_train + num_labels_val != len(jsons):
		print(colorama.Fore.RED + "Error: the number of files is inconsistent:", num_images_train, num_images_val, num_labels_train, num_labels_val, len(jsons))
		raise


def generate_yaml_file(labels, name):
	path = os.path.join("datasets", name, name+".yaml")
	# print("path:", path)
	with open(path, "w") as file:
		file.write("path: ../datasets/%s # dataset root dir\n" % name)
		file.write("train: images/train # train images (relative to 'path')\n")
		file.write("val: images/val  # val images (relative to 'path')\n")
		file.write("test: # test images (optional)\n\n")

		file.write("# Classes\n")
		file.write("names:\n")
		for key, value in labels.items():
			# print(f"key: {key}; value: {value}")
			file.write("  %d: %s\n" % (int(value), key))


if __name__ == "__main__":
	colorama.init()
	args = parse_args()

	# 1. parse JSON file and write it to a TXT file
	labels = get_labels_index(args.labels)
	# print("labels:", labels)
	jsons = get_json_files(args.dir)
	# print("jsons:", jsons)
	json_to_txt(args.dir, jsons, labels)

	# 2. split the dataset
	split_train_val(args.dir, jsons, args.name, args.val_size)

	# 3. generate a YAML file
	generate_yaml_file(labels, args.name)

	print(colorama.Fore.GREEN + "====== execution completed ======")

      代码有些多,主要函数说明如下:

     1.函数parse_args:解析输入参数;

     2.函数get_labels_index:解析labels文件,数据集中的所有类别及对应的索引,格式labels.txt如下所示:生成YOLOv8的YAML文件时也需要此文件

face 0
hand 1
eye 2
mouth 3
horse 4
tree 5
bridge 6
house 7

     3.函数get_json_files:获取指定目录下的所有json文件;

     4.函数parse_json:解析json文件,将txt文件中需要的数据提取出来;

     5.函数bounding_box_normalization:将bounding box值归一化到(0,1)区间;

     6.函数write_to_txt:将最终结果写入txt文件;

     7.函数split_train_val:将数据集随机拆分为训练集和验证集,并按YOLOv8支持的目录结构存放,根目录为datasets,接着是指定的数据集名,例如为fake,与YOLOv8中数据集coco8目录结构完全一致

     8.函数generate_yaml_file:生成YOLOv8支持的yaml文件,存放在datasets/数据集名下,例如为fake.yaml

      接收4个参数:参数dir为存放数据集的目录;参数labels指定labels文件;参数val_size指定验证集所占的比例;参数name指定新生成的YOLOv8数据集的名字

      这里从网上随机下载了10幅图像,使用LabelMe进行了标注,执行结果如下图所示:

     生成的fake.yaml文件如下图所示:

path: ../datasets/fake # dataset root dir
train: images/train # train images (relative to 'path')
val: images/val  # val images (relative to 'path')
test: # test images (optional)

# Classes
names:
  0: face
  1: hand
  2: eye
  3: mouth
  4: horse
  5: tree
  6: bridge
  7: house

      将生成的fake数据集进行训练,测试代码test_yolov8_detect.py如下:

import argparse
import colorama
from ultralytics import YOLO

def parse_args():
	parser = argparse.ArgumentParser(description="YOLOv8 object detect")
	parser.add_argument("--yaml", required=True, type=str, help="yaml file")
	parser.add_argument("--epochs", required=True, type=int, help="number of training")

	args = parser.parse_args()
	return args

def train(yaml, epochs):
	model = YOLO("yolov8n.pt") # load a pretrained model
	results = model.train(data=yaml, epochs=epochs, imgsz=640) # train the model

	metrics = model.val() # It'll automatically evaluate the data you trained, no arguments needed, dataset and settings remembered

	model.export(format="onnx", dynamic=True) # export the model

if __name__ == "__main__":
	colorama.init()
	args = parse_args()

	train(args.yaml, args.epochs)

	print(colorama.Fore.GREEN + "====== execution completed ======")

      执行结果如下图所示:目前此测试代码接收2个参数:参数yaml指定yaml文件;参数epochs指定训练次数;由以下结果可知,生成的新数据集无需做任何改动即可进行训练

      GitHub:https://github.com/fengbingchun/NN_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/637724.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GetWay

SpringCloud - Spring Cloud 之 Gateway网关&#xff0c;Route路由&#xff0c;Predicate 谓词/断言&#xff0c;Filter 过滤器&#xff08;十三&#xff09;_spring.cloud.gateway.routes-CSDN博客 官网&#xff1a;Spring Cloud Gateway 工作原理&#xff1a;Spring Cloud G…

Spring Boot:SpringBoot 如何优雅地定制JSON响应数据返回

一、前言 目前微服务项目中RESTful API已经是前后端对接数据格式的标配模式了&#xff0c;RESTful API是一种基于REST&#xff08;Representational State Transfer&#xff0c;表述性状态转移&#xff09;原则的应用程序编程接口&#xff08;Application Programming Interfac…

光伏电站在线监测智能诊断系统:开启无人值守新纪元

光伏电站在线监测智能诊断系统&#xff1a;开启无人值守新纪元 大家都知道光伏电站是通过汲取着太阳的光芒&#xff0c;为人类提供源源不断的电能源。然而&#xff0c;随着光伏电站规模的扩大和复杂性的增加&#xff0c;如何有效提高发电效率、减少人工维护成本&#xff0c;实…

前端菜鸡,对于35+程序员失业这个事有点麻了

“经常看到30岁程序员失业的新闻&#xff0c;说实话&#xff0c;有点麻。目前程序员供求关系并未失衡&#xff0c;哪怕是最基础的前端或者后台、甚至事务型的岗位也是足够的。 事实上&#xff0c;现在一个开出的岗位要找到一位尽职尽责能顺利完成工作的程序员并不是一件那么容…

YOLOv8原理详解

Yolov8是2023年1月份开源的。与yolov5一样&#xff0c;支持目标检测、分类、分割任务。 Yolov8主要改进之处有以下几个方面&#xff1a; Backbone&#xff1a;依旧采用的CSP的思想&#xff0c;不过将Yolov5中的C3模块替换为C2F模块&#xff0c;进一步降低了参数量&#xff0c…

mysql实战——mysql5.7升级到mysql8.0

1、上传mysql8.0压缩包到/usr/local目录下 tar -zxvf mysql-8.0.25-linux-glibc2.12-x86_64.tar.xz mv mysql-8.0.25-linux-glibc2.12-x86_64 mysql8 #更改文件夹所属 chown -R mysql.mysql /usr/local/mysql8/ 2、更改配置文件my.cnf vi /etc/my.cnf # 最后几个for8.0的参数要…

Java输入与输出详解

Java输入和输出 前言一、Java打印Hello World二、输出到控制台基本语法代码示例格式化字符串 三、从键盘输入读入一个字符正确写法 使用 Scanner 读取字符串/整数/浮点数使用 Scanner 循环读取 N 个数字 前言 推荐一个网站给想要了解或者学习人工智能知识的读者&#xff0c;这…

tomcat jdbc连接池的默认配置

MySQL 5.0 以后针对超长时间数据库连接做了一个处理&#xff0c;即一个数据库连接在无任何操作情况下过了 8 个小时后(MySQL 服务器默认的超时时间是 8 小时)&#xff0c;MySQL 会自动把这个连接关闭。在数据库连接池中的 connections 如果空闲超过 8 小时&#xff0c;MySQL 将…

详解 UML 中的关系概念

关联&#xff08;Association&#xff09; 表示两个类之间的一种语义性联系。例如: 学生与班级之间的关联关系。 有向关联&#xff08;Directed Association&#xff09; 关联关系有方向性,表示一个类能访问另一个类,但不一定反过来。例如: 教师能查看学生的成绩,但学生不能查…

PMapper:助你在AWS中实现IAM权限快速安全评估

关于PMapper PMapper是一款功能强大的脚本工具&#xff0c;该工具本质上是一个基于Python开发的脚本/代码库&#xff0c;可以帮助广大研究人员识别一个AWS账号或AWS组织中存在安全风险的IAM配置&#xff0c;并对IAM权限执行快速评估。 PMapper可以将目标AWS帐户中的不同IAM用户…

C++入门:从C语言到C++的过渡(1)

目录 1.什么是C 2.C的标准库 3.命名空间 3.1为什么要存在命名空间 3.2命名空间的定义 3.3命名空间的使用 3.3.1域作用限定符 3.3.2using关键字引入某个成员 3.3.3using关键字引入命名空间名称 3.4命名空间的嵌套 3.5命名空间的合并 4.C中的输入与输出 1.什么是C C&am…

BatBot智慧能源管理平台,更加有效地管理能源

随着能源消耗的不断增加&#xff0c;能源管理已成为全球面临的重要问题。BatBot智慧能源管理作为一种的能源管理技术&#xff0c;促进企业在用能效率及管理有着巨大的提升。 BatBot智慧能源管理是一种基于人工智能技术的能源管理系统&#xff0c;通过智能分析和优化能源使用&…

7. 3 层神经网络的实现和输出层的激活函数

目录 1. 3 层神经网络的实现 1.1 输入层数据到 1 层神经元 1.2 1 层神经元到 2 层神经元 1.3 2 层神经元到输出层神经元 2. 输出层的激活函数 2.1 恒等函数 2.2 softmax 函数 2.2.1 softmax 函数表达式 2.2.2 softmax 代码实现 2.2.3 softmax 的改进 2.2.3 softmax 函…

Mixiy(米思齐)安装

Mixiy(米思齐)安装 官网地址&#xff1a;爱上米思齐 打开官网&#xff0c;选择下图的软件进行下载 复制提取码&#xff0c;点击链接跳转到网盘进行下载&#xff0c;选择(RC4完整版) 下载完成后&#xff0c;解压到合适的位置&#xff0c;进入文件夹&#xff0c;双击Mixly.exe即…

Spark SQL【Java API】

前言 之前对 Spark SQL 的影响一直停留在 DSL 语法上面&#xff0c;感觉可以用 SQL 表达的&#xff0c;没有必要用 Java/Scala 去写&#xff0c;但是面试一段时间后&#xff0c;发现不少公司还是在用 SparkSQL 的&#xff0c;京东也在使用 Spark On Hive 而不是我以为的 Hive O…

C++数据结构——哈希表

前言&#xff1a;本篇文章将继续进行C数据结构的讲解——哈希表。 目录 一.哈希表概念 二.哈希函数 1.除留取余法 三.哈希冲突 1.闭散列 线性探测 &#xff08;1&#xff09;插入 &#xff08;2&#xff09;删除 2. 开散列 开散列概念 四.闭散列哈希表 1.基本框架 …

一.ffmpeg 将内存中的H264跟PCM 数据流合成多媒体文件

在有一些嵌入式平台中&#xff0c;H264数据流一般来自芯片内部的硬编码器&#xff0c; AAC音频数据则是通过采集PCM进行软编码&#xff0c;但是如何对它实时进行封装多媒体文件 &#xff0c;参考ffmpeg example&#xff0c;花了一些时间终于实现了该功能。 流程图如下&#xf…

es问题汇总--待完善

1. 查询某个索引库中数据总量 方式一&#xff1a; CountRequest 鄙人喜欢这种方式 public long getTotalNum(String indexName) throws IOException {CountRequest countRequest new CountRequest(indexName);// 如果需要&#xff0c;你可以在这里添加查询条件// countReques…

内脏油脂是什么?如何减掉?

真想减的人&#xff0c;减胖是很容易的&#xff0c;但想要形体美又健康&#xff0c;还是得从减内脏油脂开始&#xff0c;那么&#xff0c;问题来了&#xff0c;什么是内脏油脂&#xff1f; 油脂它分部于身体的各个角落&#xff0c;四肢、腹部、腰、臀部、脸、脖子...等&#xf…

暴雨信息液冷计算解决方案亮相CCIG 2024

5月24日&#xff0c;2024中国图象图形大会&#xff08;CCIG&#xff09;在陕西西安正式开幕。作为涵盖图像图形各专业领域的综合性的全国性学术会议&#xff0c;CCIG面向开放创新、交叉融合的发展趋势&#xff0c;为图像图形相关领域的专家学者和产业界的同仁&#xff0c;搭建了…