无人机目标检测实战AU-AIR数据集JSON转YOLO格式保姆级教程附完整代码在无人机航拍场景中小目标检测一直是计算机视觉领域的难点。AU-AIR作为首个多模态无人机检测数据集其标注文件采用JSON格式而主流YOLO系列模型训练需要TXT格式标注。本文将手把手教你完成格式转换全流程并解决实际工程中的典型问题。1. 环境准备与数据理解首先需要确认你的开发环境已安装Python 3.7版本。推荐使用Anaconda创建独立环境conda create -n yolo_converter python3.8 conda activate yolo_converter安装必要的依赖库pip install tqdm numpy opencv-pythonAU-AIR数据集结构解析annotations.json包含所有标注信息的JSON文件JPEGImages/存储原始无人机拍摄图像标注信息关键字段image_name对应图片文件名image_width/image_height图像尺寸bbox边界框信息数组class目标类别编号注意不同版本数据集字段可能略有差异建议先用JSON查看器检查实际结构2. 核心转换算法解析YOLO格式要求每个标注文件对应一张图片内容为class x_center y_center width height其中坐标和尺寸都是相对于图像宽高的归一化值0-1之间。转换函数的关键数学原理def convert(size, box): # 计算归一化系数 dw 1. / size[0] # 宽度归一化因子 dh 1. / size[1] # 高度归一化因子 # 计算中心点坐标 x box[0] box[2] / 2.0 y box[1] box[3] / 2.0 # 原始宽高 w box[2] h box[3] # 归一化处理保留6位小数 x round(x * dw, 6) w round(w * dw, 6) y round(y * dh, 6) h round(h * dh, 6) return (x, y, w, h)参数说明size元组形式 (image_width, image_height)box列表形式 [x_min, y_min, width, height]3. 完整转换代码实现以下是增强版的转换脚本增加了错误处理和日志记录import os import json from tqdm import tqdm import argparse import logging # 配置日志系统 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, filenameconversion.log ) logger logging.getLogger(__name__) def validate_json_structure(data): 验证JSON结构是否符合预期 required_keys {annotations, categories} if not all(key in data for key in required_keys): raise ValueError(Invalid JSON structure: missing required fields) def convert(size, box): 坐标转换函数 try: dw 1. / size[0] dh 1. / size[1] x box[0] box[2] / 2.0 y box[1] box[3] / 2.0 w box[2] h box[3] return ( round(x * dw, 6), round(y * dh, 6), round(w * dw, 6), round(h * dh, 6) ) except ZeroDivisionError: logger.error(fInvalid size: {size}) return (0, 0, 0, 0) def main(): parser argparse.ArgumentParser() parser.add_argument(--json_path, requiredTrue, helpPath to input JSON file) parser.add_argument(--save_path, defaultlabels, helpOutput directory for labels) parser.add_argument(--image_dir, defaultJPEGImages, helpDirectory containing images) args parser.parse_args() try: with open(args.json_path, r) as f: data json.load(f) validate_json_structure(data) os.makedirs(args.save_path, exist_okTrue) # 创建训练集清单文件 list_file open(trainval.txt, w) for img in tqdm(data[annotations], descProcessing annotations): try: filename img[image_name] img_width img[image_width] img_height img[image_height] # 生成对应的TXT文件名 base_name os.path.splitext(filename)[0] txt_path os.path.join(args.save_path, f{base_name}.txt) with open(txt_path, w) as f_txt: for bbox in img[bbox]: box [ bbox[left], bbox[top], bbox[width], bbox[height] ] class_id bbox[class] x, y, w, h convert((img_width, img_height), box) f_txt.write(f{class_id} {x} {y} {w} {h}\n) # 写入训练清单 list_file.write(f{args.image_dir}/{filename}\n) except KeyError as e: logger.warning(fMissing key in annotation: {e}) except Exception as e: logger.error(fError processing {filename}: {str(e)}) list_file.close() logger.info(Conversion completed successfully) except Exception as e: logger.critical(fFatal error: {str(e)}) raise if __name__ __main__: main()改进功能增加了JSON结构验证添加了完善的错误处理支持自定义图像目录生成详细的转换日志4. 常见问题与解决方案4.1 字段不匹配错误典型报错KeyError: image_width解决方法使用JSON查看器检查实际字段名修改代码对应字段# 原代码 img_width img[image_width] # 修改为实际字段名 img_width img[width]4.2 坐标越界问题当出现坐标超出图像边界时建议添加边界检查def convert(size, box): # 添加边界检查 box[0] max(0, min(box[0], size[0] - 1)) # x_min box[1] max(0, min(box[1], size[1] - 1)) # y_min box[2] max(1, min(box[2], size[0] - box[0])) # width box[3] max(1, min(box[3], size[1] - box[1])) # height # 原有转换逻辑...4.3 多类别处理如果数据集包含多个类别建议建立类别映射表class_mapping { person: 0, car: 1, bicycle: 2, # 其他类别... } # 使用时替换 class_id class_mapping[bbox[class_name]]4.4 性能优化技巧处理大型数据集时可以使用多进程加速from multiprocessing import Pool def process_annotation(img): # 处理单个标注的逻辑 pass if __name__ __main__: with Pool(processes4) as pool: results list(tqdm( pool.imap(process_annotation, data[annotations]), totallen(data[annotations]) ))5. 转换结果验证转换完成后建议使用以下脚本可视化检查结果import cv2 import os def visualize_annotation(image_path, label_path): image cv2.imread(image_path) h, w image.shape[:2] with open(label_path, r) as f: for line in f.readlines(): class_id, x, y, w_, h_ map(float, line.strip().split()) # 转换回绝对坐标 x_center int(x * w) y_center int(y * h) box_w int(w_ * w) box_h int(h_ * h) x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(Annotation, image) cv2.waitKey(0) # 示例使用 image_file JPEGImages/000001.jpg label_file labels/000001.txt visualize_annotation(image_file, label_file)验证要点边界框是否准确包围目标小目标是否被正确标注多目标场景是否所有实例都被处理6. 进阶应用YOLO训练准备完成格式转换后建议按以下结构组织数据auair/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── train.txt └── val.txt创建数据集配置文件auair.yaml# AU-AIR数据集配置 path: ../auair train: train.txt val: val.txt # 类别数量 nc: 5 # 类别名称 names: [person, car, bicycle, dog, umbrella]在YOLOv5中开始训练python train.py --img 640 --batch 16 --epochs 50 --data auair.yaml --weights yolov5s.pt针对无人机小目标检测推荐训练参数调整增大输入分辨率--img 1280使用更密集的锚点--anchor-multiple 1.5增加小目标检测层
无人机目标检测实战:AU-AIR数据集JSON转YOLO格式保姆级教程(附完整代码)
无人机目标检测实战AU-AIR数据集JSON转YOLO格式保姆级教程附完整代码在无人机航拍场景中小目标检测一直是计算机视觉领域的难点。AU-AIR作为首个多模态无人机检测数据集其标注文件采用JSON格式而主流YOLO系列模型训练需要TXT格式标注。本文将手把手教你完成格式转换全流程并解决实际工程中的典型问题。1. 环境准备与数据理解首先需要确认你的开发环境已安装Python 3.7版本。推荐使用Anaconda创建独立环境conda create -n yolo_converter python3.8 conda activate yolo_converter安装必要的依赖库pip install tqdm numpy opencv-pythonAU-AIR数据集结构解析annotations.json包含所有标注信息的JSON文件JPEGImages/存储原始无人机拍摄图像标注信息关键字段image_name对应图片文件名image_width/image_height图像尺寸bbox边界框信息数组class目标类别编号注意不同版本数据集字段可能略有差异建议先用JSON查看器检查实际结构2. 核心转换算法解析YOLO格式要求每个标注文件对应一张图片内容为class x_center y_center width height其中坐标和尺寸都是相对于图像宽高的归一化值0-1之间。转换函数的关键数学原理def convert(size, box): # 计算归一化系数 dw 1. / size[0] # 宽度归一化因子 dh 1. / size[1] # 高度归一化因子 # 计算中心点坐标 x box[0] box[2] / 2.0 y box[1] box[3] / 2.0 # 原始宽高 w box[2] h box[3] # 归一化处理保留6位小数 x round(x * dw, 6) w round(w * dw, 6) y round(y * dh, 6) h round(h * dh, 6) return (x, y, w, h)参数说明size元组形式 (image_width, image_height)box列表形式 [x_min, y_min, width, height]3. 完整转换代码实现以下是增强版的转换脚本增加了错误处理和日志记录import os import json from tqdm import tqdm import argparse import logging # 配置日志系统 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, filenameconversion.log ) logger logging.getLogger(__name__) def validate_json_structure(data): 验证JSON结构是否符合预期 required_keys {annotations, categories} if not all(key in data for key in required_keys): raise ValueError(Invalid JSON structure: missing required fields) def convert(size, box): 坐标转换函数 try: dw 1. / size[0] dh 1. / size[1] x box[0] box[2] / 2.0 y box[1] box[3] / 2.0 w box[2] h box[3] return ( round(x * dw, 6), round(y * dh, 6), round(w * dw, 6), round(h * dh, 6) ) except ZeroDivisionError: logger.error(fInvalid size: {size}) return (0, 0, 0, 0) def main(): parser argparse.ArgumentParser() parser.add_argument(--json_path, requiredTrue, helpPath to input JSON file) parser.add_argument(--save_path, defaultlabels, helpOutput directory for labels) parser.add_argument(--image_dir, defaultJPEGImages, helpDirectory containing images) args parser.parse_args() try: with open(args.json_path, r) as f: data json.load(f) validate_json_structure(data) os.makedirs(args.save_path, exist_okTrue) # 创建训练集清单文件 list_file open(trainval.txt, w) for img in tqdm(data[annotations], descProcessing annotations): try: filename img[image_name] img_width img[image_width] img_height img[image_height] # 生成对应的TXT文件名 base_name os.path.splitext(filename)[0] txt_path os.path.join(args.save_path, f{base_name}.txt) with open(txt_path, w) as f_txt: for bbox in img[bbox]: box [ bbox[left], bbox[top], bbox[width], bbox[height] ] class_id bbox[class] x, y, w, h convert((img_width, img_height), box) f_txt.write(f{class_id} {x} {y} {w} {h}\n) # 写入训练清单 list_file.write(f{args.image_dir}/{filename}\n) except KeyError as e: logger.warning(fMissing key in annotation: {e}) except Exception as e: logger.error(fError processing {filename}: {str(e)}) list_file.close() logger.info(Conversion completed successfully) except Exception as e: logger.critical(fFatal error: {str(e)}) raise if __name__ __main__: main()改进功能增加了JSON结构验证添加了完善的错误处理支持自定义图像目录生成详细的转换日志4. 常见问题与解决方案4.1 字段不匹配错误典型报错KeyError: image_width解决方法使用JSON查看器检查实际字段名修改代码对应字段# 原代码 img_width img[image_width] # 修改为实际字段名 img_width img[width]4.2 坐标越界问题当出现坐标超出图像边界时建议添加边界检查def convert(size, box): # 添加边界检查 box[0] max(0, min(box[0], size[0] - 1)) # x_min box[1] max(0, min(box[1], size[1] - 1)) # y_min box[2] max(1, min(box[2], size[0] - box[0])) # width box[3] max(1, min(box[3], size[1] - box[1])) # height # 原有转换逻辑...4.3 多类别处理如果数据集包含多个类别建议建立类别映射表class_mapping { person: 0, car: 1, bicycle: 2, # 其他类别... } # 使用时替换 class_id class_mapping[bbox[class_name]]4.4 性能优化技巧处理大型数据集时可以使用多进程加速from multiprocessing import Pool def process_annotation(img): # 处理单个标注的逻辑 pass if __name__ __main__: with Pool(processes4) as pool: results list(tqdm( pool.imap(process_annotation, data[annotations]), totallen(data[annotations]) ))5. 转换结果验证转换完成后建议使用以下脚本可视化检查结果import cv2 import os def visualize_annotation(image_path, label_path): image cv2.imread(image_path) h, w image.shape[:2] with open(label_path, r) as f: for line in f.readlines(): class_id, x, y, w_, h_ map(float, line.strip().split()) # 转换回绝对坐标 x_center int(x * w) y_center int(y * h) box_w int(w_ * w) box_h int(h_ * h) x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(Annotation, image) cv2.waitKey(0) # 示例使用 image_file JPEGImages/000001.jpg label_file labels/000001.txt visualize_annotation(image_file, label_file)验证要点边界框是否准确包围目标小目标是否被正确标注多目标场景是否所有实例都被处理6. 进阶应用YOLO训练准备完成格式转换后建议按以下结构组织数据auair/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── train.txt └── val.txt创建数据集配置文件auair.yaml# AU-AIR数据集配置 path: ../auair train: train.txt val: val.txt # 类别数量 nc: 5 # 类别名称 names: [person, car, bicycle, dog, umbrella]在YOLOv5中开始训练python train.py --img 640 --batch 16 --epochs 50 --data auair.yaml --weights yolov5s.pt针对无人机小目标检测推荐训练参数调整增大输入分辨率--img 1280使用更密集的锚点--anchor-multiple 1.5增加小目标检测层