txt_json
import cv2
import os
import json
'''
该脚本实现将yolo格式标签转为json格式标签
需要的数据:原始图像 原始yolo格式标签(txt文件)
'''
imgs_path = "D:/.../images/" # 图像路径
txt_dir = "D:/.../labels/" # txt标签目录路径
out_json_dir = 'D:/.../json/' # 保存的json文件路径
# 确保输出目录存在
os.makedirs(out_json_dir, exist_ok=True)
# 定义YOLO标签类别映射
name_label = ['A', 'B', 'C'] # 需要与训练时的类别顺序一致
def convert_yolo_bbox(box, img_shape):
"""将YOLO格式的归一化坐标转换为像素坐标"""
x_center, y_center, w, h = box
img_h, img_w = img_shape[:2]
x1 = int((x_center - w / 2) * img_w)
y1 = int((y_center - h / 2) * img_h)
x2 = int((x_center + w / 2) * img_w)
y2 = int((y_center + h / 2) * img_h)
return [x1, y1, x2, y2]
def parse_yolo_txt(img_path, txt_path):
"""解析YOLO标签文件"""
img = cv2.imread(img_path)
if img is None:
raise FileNotFoundError(f"无法读取图像: {img_path}")
bboxes = []
if not os.path.exists(txt_path):
return img, bboxes # 返回空列表表示没有目标
with open(txt_path, 'r') as f:
for line in f.readlines():
parts = line.strip().split()
if len(parts) != 5:
continue
# 解析YOLO格式
label_idx = int(parts[0])
label = name_label[label_idx]
x_center = float(parts[1])
y_center = float(parts[2])
width = float(parts[3])
height = float(parts[4])
# 转换坐标
bbox = convert_yolo_bbox([x_center, y_center, width, height], img.shape)
bbox.append(label)
bboxes.append(bbox)
return img, bboxes
def create_labelme_json(img_name, img_dir, txt_dir, output_dir):
"""创建Labelme格式的JSON标注文件"""
# 构建完整路径
img_path = os.path.join(img_dir, img_name)
txt_path = os.path.join(txt_dir, img_name.replace('.jpg', '.txt'))
# 解析数据
try:
img, gt_boxes = parse_yolo_txt(img_path, txt_path)
except Exception as e:
print(f"处理 {img_name} 时出错: {str(e)}")
return
# 构建基础字典
label_dict = {
"version": "5.5.0",
"flags": {},
"shapes": [],
"imagePath": img_path,
"imageData": None,
"imageHeight": img.shape[0],
"imageWidth": img.shape[1]
}
# 添加标注形状
for box in gt_boxes:
shape = {
"label": box[-1],
"points": [[box[0], box[1]], [box[2], box[3]]],
"group_id": None,
"description": "",
"shape_type": "rectangle",
"flags": {},
"mask": None
}
label_dict["shapes"].append(shape)
# 写入JSON文件
output_path = os.path.join(output_dir, img_name.replace('.jpg', '.json'))
with open(output_path, 'w') as f:
json.dump(label_dict, f, indent=2)
print(f"已生成: {output_path}")
# 主处理循环
img_files = [f for f in os.listdir(imgs_path) if f.lower().endswith('.jpg')]
for idx, img_name in enumerate(img_files):
if idx % 100 == 0:
print(f"处理进度: {idx}/{len(img_files)}")
create_labelme_json(img_name, imgs_path, txt_dir, out_json_dir)
print("转换完成!")
xml_json
import os
import json
import xml.etree.ElementTree as ET
from typing import List, Dict, Any
import numpy as np
from pathlib import Path
class VOC2LabelMe:
def __init__(self):
self.version = "5.0.1" # LabelMe的版本号
def parse_voc_xml(self, xml_path: str) -> Dict[str, Any]:
"""解析VOC格式的XML文件"""
tree = ET.parse(xml_path)
root = tree.getroot()
# 解析图片信息
size = root.find('size')
img_width = int(size.find('width').text)
img_height = int(size.find('height').text)
# 解析所有目标
objects = []
for obj in root.findall('object'):
label = obj.find('name').text
bndbox = obj.find('bndbox')
xmin = float(bndbox.find('xmin').text)
ymin = float(bndbox.find('ymin').text)
xmax = float(bndbox.find('xmax').text)
ymax = float(bndbox.find('ymax').text)
# 转换为多边形格式(矩形四个顶点)
points = [
[xmin, ymin], # 左上
[xmax, ymin], # 右上
[xmax, ymax], # 右下
[xmin, ymax] # 左下
]
# 创建LabelMe格式的shape
shape = {
"label": label,
"points": points,
"group_id": None,
"shape_type": "polygon",
"flags": {}
}
objects.append(shape)
return {
"width": img_width,
"height": img_height,
"objects": objects
}
def convert_single_file(self, xml_path: str, image_path: str = None) -> Dict[str, Any]:
"""转换单个XML文件到LabelMe JSON格式"""
# 解析XML
parsed_data = self.parse_voc_xml(xml_path)
# 获取文件名
xml_file = Path(xml_path)
image_file_name = xml_file.stem + ".jpg" # 假设图片是jpg格式
# 如果提供了图片路径,使用提供的路径
if image_path:
image_path_str = str(Path(image_path).resolve())
else:
# 假设图片和XML在同一目录
image_path_str = str(xml_file.parent / image_file_name)
# 构建LabelMe JSON结构
labelme_json = {
"version": self.version,
"flags": {},
"shapes": parsed_data["objects"],
"imagePath": image_file_name,
"imageData": None, # 可以设置为None,LabelMe会从imagePath加载图片
"imageHeight": parsed_data["height"],
"imageWidth": parsed_data["width"]
}
return labelme_json
def convert_folder(self, xml_folder: str, image_folder: str = None,
output_folder: str = None, save_image_data: bool = False):
"""转换整个文件夹的XML文件"""
xml_folder = Path(xml_folder)
# 设置输出文件夹
if output_folder:
output_folder = Path(output_folder)
else:
output_folder = xml_folder / "labelme_json"
output_folder.mkdir(parents=True, exist_ok=True)
# 获取所有XML文件
xml_files = list(xml_folder.glob("*.xml"))
print(f"找到 {len(xml_files)} 个XML文件")
for xml_file in xml_files:
try:
# 构建图片路径
if image_folder:
img_path = Path(image_folder) / f"{xml_file.stem}.jpg"
else:
img_path = None
# 转换单个文件
labelme_json = self.convert_single_file(str(xml_file), str(img_path) if img_path else None)
# 保存JSON文件
output_path = output_folder / f"{xml_file.stem}.json"
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(labelme_json, f, indent=2, ensure_ascii=False)
print(f"✓ 已转换: {xml_file.name} -> {output_path.name}")
except Exception as e:
print(f"✗ 转换失败 {xml_file.name}: {str(e)}")
print(f"\n转换完成!JSON文件保存在: {output_folder}")
# 使用示例
if __name__ == "__main__":
converter = VOC2LabelMe()
# 方法1: 转换单个文件
# xml_path = "path/to/your/annotation.xml"
# image_path = "path/to/your/image.jpg" # 可选
# result = converter.convert_single_file(xml_path, image_path)
#
# # 保存JSON
# with open("output.json", 'w', encoding='utf-8') as f:
# json.dump(result, f, indent=2, ensure_ascii=False)
# 方法2: 批量转换整个文件夹
# 假设XML文件和对应的图片文件在同一文件夹
xml_folder = "D:\\hat\\VOC2028\\Annotations" # 请修改为你的XML文件夹路径
image_folder = "D:\\hat\\VOC2028\\JPEGImages" # 如果图片在不同文件夹,可以指定
output_folder = "D:\\hat\\VOC2028\\json" # 可选,默认会在XML文件夹下创建labelme_json文件夹
converter.convert_folder(
xml_folder=xml_folder,
image_folder=None, # 如果图片在不同文件夹,请指定
output_folder=None
)
json_txt
import json
import os
input_folder = 'D:/.../json'
output_folder = 'D:/.../txt'
os.makedirs(output_folder, exist_ok=True)
# 标注类别名
label_to_id_mapping = {
'A': 0,
'B': 1,
'C': 2,
'D': 3
# Add more mappings as needed
}
def convert_annotation(json_file, txt_file, label_to_id_mapping):
# Read the JSON file
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# Extract image dimensions, assuming 'imageWidth' and 'imageHeight' fields are present
image_width = data.get('imageWidth')
image_height = data.get('imageHeight')
# Check if image dimensions are present
if image_width is None or image_height is None:
raise ValueError(f"Missing image dimensions in {json_file}")
# Iterate over all shapes (annotations)
with open(txt_file, 'w', encoding='utf-8') as out_file:
for shape in data.get('shapes', []):
# Extract point coordinates, assuming each shape has a 'points' field
points = shape.get('points', [])
# Check if points are present
if not points:
raise ValueError(f"Missing points in a shape in {json_file}")
x_values = [point[0] for point in points]
y_values = [point[1] for point in points]
x_min = min(x_values)
y_min = min(y_values)
x_max = max(x_values)
y_max = max(y_values)
# Calculate bounding box center, width, and height
bbox_center_x = (x_min + x_max) / 2
bbox_center_y = (y_min + y_max) / 2
bbox_width = x_max - x_min
bbox_height = y_max - y_min
# Convert bounding box coordinates to ratios relative to image dimensions
bbox_center_x_ratio = bbox_center_x / image_width
bbox_center_y_ratio = bbox_center_y / image_height
bbox_width_ratio = bbox_width / image_width
bbox_height_ratio = bbox_height / image_height
# Get the category ID, assuming each shape has a 'label' field
category_id = shape.get('label', "unknown")
if isinstance(category_id, str):
# If the label is a string, map it to a numeric ID using the provided mapping
category_id = label_to_id_mapping.get(category_id, -1) # Default to -1 if label is unknown
# Write the result to the TXT file in YOLO format
out_file.write(
f"{int(category_id)} {bbox_center_x_ratio} {bbox_center_y_ratio} {bbox_width_ratio} {bbox_height_ratio}\n")
# Input and output folder paths
# Iterate over all JSON files in the input folder
for filename in os.listdir(input_folder):
if filename.endswith('.json'):
json_file = os.path.join(input_folder, filename)
txt_file = os.path.join(output_folder, filename.replace('.json', '.txt'))
try:
convert_annotation(json_file, txt_file, label_to_id_mapping)
print(f'{txt_file},Conversion successful!')
except Exception as e:
print(f"An error occurred while processing {json_file}: {e}")
参考链接:https://blog.csdn.net/chao_xy/article/details/147363834