yolo处理数据的实用代码
·
1、json转txt
import os
import json
from glob import glob
def convert_labelme_to_yolo(json_dir, output_dir):
"""
将labelme格式的JSON标注文件转换为YOLO格式
:param json_dir: 包含JSON文件的目录
:param output_dir: 输出YOLO格式文件的目录
"""
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
# 收集所有类别
classes = set()
json_files = glob(os.path.join(json_dir, '*.json'))
# 第一次遍历:收集所有类别
for json_file in json_files:
with open(json_file, 'r') as f:
data = json.load(f)
for shape in data.get('shapes', []):
if shape['shape_type'] == 'rectangle':
classes.add(shape['label'])
# 创建类别映射(类别名到ID)
class_to_id = {cls: idx for idx, cls in enumerate(sorted(classes))}
# 保存类别文件
with open(os.path.join(output_dir, 'classes.txt'), 'w') as f:
for cls in sorted(classes):
f.write(f"{cls}\n")
# 第二次遍历:转换标注
for json_file in json_files:
with open(json_file, 'r') as f:
data = json.load(f)
# 获取图像尺寸
img_width = data.get('imageWidth', 0)
img_height = data.get('imageHeight', 0)
if img_width == 0 or img_height == 0:
print(f"警告: {json_file} 中缺少图像尺寸信息,跳过")
continue
# 从imagePath中提取纯文件名(不含路径和扩展名)
image_path = data.get('imagePath', '')
# 处理路径分隔符(兼容Windows和Linux)
image_path = image_path.replace('\\', os.sep).replace('/', os.sep)
# 获取文件名部分(不含路径)
base_name = os.path.basename(image_path)
# 去除扩展名
base_name = os.path.splitext(base_name)[0]
# 如果没有获取到文件名,使用JSON文件名
if not base_name:
base_name = os.path.splitext(os.path.basename(json_file))[0]
# 检查文件名冲突
output_path = os.path.join(output_dir, f"{base_name}.txt")
counter = 1
while os.path.exists(output_path):
# 如果文件已存在,添加后缀
output_path = os.path.join(output_dir, f"{base_name}_{counter}.txt")
counter += 1
# 转换标注
yolo_annotations = []
for shape in data.get('shapes', []):
if shape['shape_type'] == 'rectangle':
# 获取类别ID
class_id = class_to_id.get(shape['label'])
if class_id is None:
continue
# 获取矩形坐标(两个对角点)
points = shape['points']
if len(points) != 2:
continue
x1, y1 = points[0]
x2, y2 = points[1]
# 确保x1 < x2, y1 < y2
xmin = min(x1, x2)
ymin = min(y1, y2)
xmax = max(x1, x2)
ymax = max(y1, y2)
# 计算YOLO格式参数
width = xmax - xmin
height = ymax - ymin
center_x = (xmin + width / 2) / img_width
center_y = (ymin + height / 2) / img_height
norm_width = width / img_width
norm_height = height / img_height
# 添加到标注列表
yolo_annotations.append(f"{class_id} {center_x:.6f} {center_y:.6f} {norm_width:.6f} {norm_height:.6f}")
# 写入YOLO格式文件
with open(output_path, 'w') as f:
f.write('\n'.join(yolo_annotations))
# 使用示例
if __name__ == "__main__":
json_directory = "labels" # 输入JSON文件目录
output_directory = "txt" # 输出YOLO文件目录
convert_labelme_to_yolo(json_directory, output_directory)
print(f"转换完成!结果保存在: {output_directory}")
2、xml转txt
# xml转为txt
import os
import xml.etree.ElementTree as ET
from pathlib import Path
# 标签到数字的映射
label_to_number = {
'label1': 0,
'label2': 1
'label3': 2
}
def xml_to_txt(xml_file, txt_file, image_width, image_height, label_to_number):
"""
将 XML 文件转换为 TXT 文件,并将标签映射为数字,坐标值归一化到 [0, 1]。
"""
if image_width == 0 or image_height == 0:
raise ValueError(f"Image width or height is zero in file: {xml_file}")
tree = ET.parse(xml_file)
root = tree.getroot()
with open(txt_file, 'w') as f:
for obj in root.findall('object'):
name = obj.find('name').text
if name in label_to_number:
number = label_to_number[name]
bbox = obj.find('bbox')
if bbox is None:
bbox = obj.find('bndbox')
xmin = int(bbox.find('xmin').text)
ymin = int(bbox.find('ymin').text)
xmax = int(bbox.find('xmax').text)
ymax = int(bbox.find('ymax').text)
# 归一化坐标值
x_center = (xmin + xmax) / 2.0 / image_width
y_center = (ymin + ymax) / 2.0 / image_height
box_width = (xmax - xmin) / image_width
box_height = (ymax - ymin) / image_height
# 确保坐标值在 [0, 1] 范围内
x_center = max(0, min(x_center, 1))
y_center = max(0, min(y_center, 1))
box_width = max(0, min(box_width, 1))
box_height = max(0, min(box_height, 1))
f.write(f"{number} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}\n")
# 指定文件夹路径
xml_folder = 'xml' # 替换为你的 XML 文件夹路径
txt_folder = 'txt' # 替换为你的 TXT 文件夹路径
# 确保 TXT 文件夹存在
Path(txt_folder).mkdir(parents=True, exist_ok=True)
# 遍历 XML 文件夹
for xml_file in os.listdir(xml_folder):
if xml_file.endswith('.xml'):
xml_path = os.path.join(xml_folder, xml_file)
txt_path = os.path.join(txt_folder, xml_file.replace('.xml', '.txt'))
# 从 XML 文件中获取图片的宽度和高度
tree = ET.parse(xml_path)
root = tree.getroot()
size = root.find('size')
if size is None:
print(f"Size tag not found in file: {xml_file}")
continue
width = size.find('width')
height = size.find('height')
if width is None or height is None:
print(f"Width or height tag not found in file: {xml_file}")
continue
image_width = int(width.text)
image_height = int(height.text)
if image_width == 0 or image_height == 0:
print(f"Image width or height is zero in file: {xml_file}")
continue
# 转换并归一化坐标值
xml_to_txt(xml_path, txt_path, image_width, image_height, label_to_number)
print("All XML files have been converted to TXT files.")
3、xml类别统计
# 统计xml标注文件中的各个标签数量
import os
import xml.etree.ElementTree as ET
from collections import defaultdict
def count_xml_categories(folder_path):
"""
统计指定文件夹下所有 XML 文件中的类别数量及每个类别的标签数量。
参数:
folder_path (str): 包含 XML 文件的文件夹路径
返回:
dict: 类别及其对应的标签数量
"""
category_count = defaultdict(int)
total_files = 0
# 遍历文件夹中的 XML 文件
for filename in os.listdir(folder_path):
if filename.endswith(".xml"):
total_files += 1
file_path = os.path.join(folder_path, filename)
try:
tree = ET.parse(file_path)
root = tree.getroot()
# 查找所有类别标签(假设类别在 <name> 标签中,且属于 <object> 子节点)
for obj in root.findall("object"):
name = obj.find("name")
if name is not None:
category = name.text.strip()
category_count[category] += 1
except ET.ParseError as e:
print(f"解析文件 {filename} 时出错: {e}")
# 输出结果
print(f"共处理 {total_files} 个 XML 文件")
print(f"总类别数: {len(category_count)}")
print("各分类及标签数量:")
for category, count in sorted(category_count.items()):
print(f"{category}: {count}")
return category_count
# 示例调用
if __name__ == "__main__":
folder_path = '' # 替换为你的 XML 文件夹路径
result = count_xml_categories(folder_path)
4、txt类别统计
# 统计指定文件夹下所有 TXT 文件中的类别数量及每个类别的标签数量。
import os
from collections import Counter
def count_labels_in_txt_files(txt_folder):
"""
统计指定文件夹下所有 TXT 文件中的类别数量及每个类别的标签数量。
:param txt_folder: 包含 TXT 标注文件的文件夹路径
:return: 总的类别数量和每个类别的标签数量
"""
label_counts = Counter()
# 遍历文件夹中的所有 TXT 文件
for root, dirs, files in os.walk(txt_folder):
for file in files:
if file.endswith('.txt'): # 确保是 TXT 文件
file_path = os.path.join(root, file)
try:
with open(file_path, 'r') as f:
for line in f:
parts = line.strip().split()
if parts: # 确保行不为空
label = parts[0]
label_counts[label] += 1
except Exception as e:
print(f"Error processing file {file_path}: {e}")
return label_counts
# 指定文件夹路径train
txt_folder = 's' # 替换为你的 TXT 文件夹路径
# 调用函数并打印结果
label_counts = count_labels_in_txt_files(txt_folder)
print(f"Total unique labels: {len(label_counts)}")
for label, count in label_counts.items():
print(f"Label {label}: {count}")
5、类别与图片匹配对应
5.1xml格式与图片互相匹配
import os
def find_unmatched_files(image_folder, txt_folder):
"""
找出没有对应标注文件的图片和没有对应图片的标注文件。
:param image_folder: 包含图片的文件夹路径
:param txt_folder: 包含 TXT 标注文件的文件夹路径
:return: 没有对应标注文件的图片列表和没有对应图片的标注文件列表
"""
# 获取所有图片文件名(不包含扩展名)
image_files = {os.path.splitext(f)[0] for f in os.listdir(image_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg'))}
# 获取所有 TXT 文件名(不包含扩展名)
txt_files = {os.path.splitext(f)[0] for f in os.listdir(txt_folder) if f.lower().endswith('.txt')}
# 找出没有对应标注文件的图片
unmatched_images = image_files - txt_files
# 找出没有对应图片的标注文件
unmatched_txts = txt_files - image_files
return unmatched_images, unmatched_txts
# 指定文件夹路径
image_folder = '' # 替换为你的图片文件夹路径
txt_folder = '' # 替换为你的 TXT 文件夹路径
# 调用函数并打印结果
unmatched_images, unmatched_txts = find_unmatched_files(image_folder, txt_folder)
print("Unmatched images (no corresponding TXT file):")
for image in unmatched_images:
print(f"Image: {image}.jpg")
print("\nUnmatched TXT files (no corresponding image):")
for txt in unmatched_txts:
print(f"TXT: {txt}.txt")
5.2 txt与图片相互匹配
import os
import shutil
from pathlib import Path
def pair_copy(img_root: str, lbl_root: str,
new_img_dir: str, new_lbl_dir: str,
img_exts=('.jpg', '.png', '.bmp', '.jpeg')):
"""
把成对的图片 + YOLO 标签拷贝到新目录
"""
img_root, lbl_root = Path(img_root), Path(lbl_root)
new_img_dir, new_lbl_dir = Path(new_img_dir), Path(new_lbl_dir)
new_img_dir.mkdir(parents=True, exist_ok=True)
new_lbl_dir.mkdir(parents=True, exist_ok=True)
# 建立标签文件名集合(无后缀)
txts = {p.stem for p in lbl_root.rglob('*.txt')}
paired_cnt = 0
# 遍历所有图片
for img_path in img_root.rglob('*'):
if img_path.suffix.lower() not in img_exts:
continue
stem = img_path.stem
txt_path = lbl_root / f"{stem}.txt"
if txt_path.exists():
shutil.copy2(img_path, new_img_dir / img_path.name)
shutil.copy2(txt_path, new_lbl_dir / txt_path.name)
paired_cnt += 1
print(f"✅ 共拷贝 {paired_cnt} 对文件到 {new_img_dir} 和 {new_lbl_dir}")
if __name__ == '__main__':
pair_copy(
img_root=r'', # 原始图片文件夹
lbl_root=r'', # 原始 YOLO 标注文件夹
new_img_dir=r'',
new_lbl_dir=r''
)
6、数据集划分
# 将图片和对应的 TXT 标注文件按照指定比例划分为训练集和验证集
import os
import random
import shutil
from pathlib import Path
def split_images_and_txt(image_folder, txt_folder, train_folder, val_folder, train_ratio=0.9):
"""
将图片和对应的 TXT 标注文件按照指定比例划分为训练集和验证集。
:param image_folder: 包含图片的文件夹路径
:param txt_folder: 包含 TXT 标注文件的文件夹路径
:param train_folder: 训练集文件夹路径
:param val_folder: 验证集文件夹路径
:param train_ratio: 训练集比例(默认为 0.9)
"""
# 确保输出文件夹存在
Path(train_folder).mkdir(parents=True, exist_ok=True)
Path(val_folder).mkdir(parents=True, exist_ok=True)
Path(os.path.join(train_folder, 'images')).mkdir(parents=True, exist_ok=True)
Path(os.path.join(train_folder, 'annotations')).mkdir(parents=True, exist_ok=True)
Path(os.path.join(val_folder, 'images')).mkdir(parents=True, exist_ok=True)
Path(os.path.join(val_folder, 'annotations')).mkdir(parents=True, exist_ok=True)
# 获取所有图片文件名(不包含扩展名)
image_files = {os.path.splitext(f)[0] for f in os.listdir(image_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif'))}
# 获取所有 TXT 文件名(不包含扩展名)
txt_files = {os.path.splitext(f)[0] for f in os.listdir(txt_folder) if f.lower().endswith('.txt')}
# 找出有对应 TXT 文件的图片
matched_files = list(image_files & txt_files)
# 随机打乱文件顺序
random.shuffle(matched_files)
# 计算训练集和验证集的大小
train_size = int(len(matched_files) * train_ratio)
train_files = matched_files[:train_size]
val_files = matched_files[train_size:]
# 移动训练集文件
for file_name in train_files:
# 查找图片扩展名
image_path = next((os.path.join(image_folder, f"{file_name}{ext}") for ext in ['.jpg', '.jpeg', '.png', '.bmp', '.gif'] if os.path.exists(os.path.join(image_folder, f"{file_name}{ext}"))), None)
txt_path = os.path.join(txt_folder, f"{file_name}.txt")
if image_path:
shutil.move(image_path, os.path.join(train_folder, 'images', f"{file_name}{os.path.splitext(image_path)[1]}"))
shutil.move(txt_path, os.path.join(train_folder, 'annotations', f"{file_name}.txt"))
print(f"Copied {file_name} to train set")
# 移动验证集文件
for file_name in val_files:
# 查找图片扩展名
image_path = next((os.path.join(image_folder, f"{file_name}{ext}") for ext in ['.jpg', '.jpeg', '.png', '.bmp', '.gif'] if os.path.exists(os.path.join(image_folder, f"{file_name}{ext}"))), None)
txt_path = os.path.join(txt_folder, f"{file_name}.txt")
if image_path:
shutil.move(image_path, os.path.join(val_folder, 'images', f"{file_name}{os.path.splitext(image_path)[1]}"))
shutil.move(txt_path, os.path.join(val_folder, 'annotations', f"{file_name}.txt"))
print(f"Copied {file_name} to val set")
print(f"Total files in train set: {len(train_files)}")
print(f"Total files in val set: {len(val_files)}")
# 指定文件夹路径
image_folder = '' # 替换为你的图片文件夹路径
txt_folder = '' # 替换为你的 TXT 文件夹路径
train_folder = '' # 替换为你的训练集文件夹路径
val_folder = '' # 替换为你的验证集文件夹路径
# 调用函数
split_images_and_txt(image_folder, txt_folder, train_folder, val_folder, train_ratio=0.9)
更多推荐


所有评论(0)