LabelImg标注完别急着训练!手把手教你用Python把YOLO的txt坐标转成OpenCV能画的真实像素值
LabelImg标注可视化检查用Python将YOLO坐标快速转换为OpenCV可绘制格式刚用LabelImg标注完数据集的新手常会遇到这样的困惑生成的YOLO格式txt文件里那些0到1之间的数字到底对应图片上哪个位置直接拿这些数据训练模型前如何快速验证标注框是否准确本文将手把手教你用Python脚本实现从归一化坐标到真实像素值的转换并用OpenCV直观展示标注效果。1. 理解YOLO标注格式与OpenCV绘制的关键差异当你用LabelImg以YOLO格式保存标注时生成的txt文件包含5个关键数据class_id x_center y_center width height例如0 0.435546875 0.49166666666666664 0.1484375 0.24166666666666667这些坐标值都是归一化后的结果x_center和y_center表示边界框中心点相对于整张图片的坐标比例width和height表示边界框的宽高相对于图片尺寸的比例而OpenCV的cv2.rectangle()需要的是像素坐标系下的左上角(x_min, y_min)右下角(x_max, y_max)转换关系如下# 假设图片宽度为w高度为h x_min w * (x_center - width/2) y_min h * (y_center - height/2) x_max w * (x_center width/2) y_max h * (y_center height/2)2. 完整Python实现从读取到可视化的全流程下面是一个可直接运行的脚本包含注释和错误处理import cv2 import os def yolo_to_pixel(img_path, txt_path): 将YOLO格式标注转换为像素坐标 :param img_path: 图片路径 :param txt_path: 标注文件路径 :return: 类别ID, 像素坐标(x_min, y_min, x_max, y_max) # 读取图片获取尺寸 img cv2.imread(img_path) if img is None: raise FileNotFoundError(f图片未找到: {img_path}) h, w img.shape[:2] # 读取标注文件 if not os.path.exists(txt_path): raise FileNotFoundError(f标注文件未找到: {txt_path}) with open(txt_path, r) as f: lines f.readlines() # 存储所有转换后的标注 annotations [] for line in lines: parts line.strip().split() if len(parts) ! 5: continue class_id, x_center, y_center, box_w, box_h parts # 转换为float类型 x_center, y_center float(x_center), float(y_center) box_w, box_h float(box_w), float(box_h) # 计算像素坐标 x_min int((x_center - box_w/2) * w) y_min int((y_center - box_h/2) * h) x_max int((x_center box_w/2) * w) y_max int((y_center box_h/2) * h) annotations.append({ class_id: int(class_id), coordinates: (x_min, y_min, x_max, y_max) }) return img, annotations def visualize_annotations(img, annotations): 可视化标注框 :param img: 原始图片 :param annotations: 标注信息列表 # 深拷贝图片避免修改原图 img_draw img.copy() # 定义不同类别的颜色 colors { 0: (0, 255, 0), # 绿色 1: (0, 0, 255), # 红色 2: (255, 0, 0) # 蓝色 } for ann in annotations: class_id ann[class_id] x_min, y_min, x_max, y_max ann[coordinates] # 绘制矩形框 color colors.get(class_id, (255, 255, 255)) # 默认白色 cv2.rectangle(img_draw, (x_min, y_min), (x_max, y_max), color, 2) # 添加类别标签 label fClass {class_id} cv2.putText(img_draw, label, (x_min, y_min-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # 显示结果 cv2.imshow(Annotation Preview, img_draw) cv2.waitKey(0) cv2.destroyAllWindows() if __name__ __main__: # 示例用法 img_path example.jpg txt_path example.txt try: img, annotations yolo_to_pixel(img_path, txt_path) visualize_annotations(img, annotations) except Exception as e: print(f发生错误: {str(e)})3. 实际应用中的常见问题与解决方案3.1 坐标越界处理转换后的坐标可能会超出图片范围小于0或大于宽高需要做边界检查# 在计算x_min, x_max, y_min, y_max后添加 x_min max(0, x_min) y_min max(0, y_min) x_max min(w, x_max) y_max min(h, y_max)3.2 多对象标注处理一个txt文件可能包含多个对象的标注每行一个对象。上述代码已经处理了这种情况但可视化时可以添加更多信息# 在visualize_annotations函数中添加计数显示 total_objects len(annotations) cv2.putText(img_draw, fTotal: {total_objects}, (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)3.3 批量处理与保存结果如果需要批量处理整个数据集并保存可视化结果def batch_visualize(img_dir, txt_dir, output_dir): 批量处理并保存可视化结果 if not os.path.exists(output_dir): os.makedirs(output_dir) img_files [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] for img_file in img_files: base_name os.path.splitext(img_file)[0] txt_file base_name .txt img_path os.path.join(img_dir, img_file) txt_path os.path.join(txt_dir, txt_file) try: img, annotations yolo_to_pixel(img_path, txt_path) visualize_annotations(img, annotations) # 保存结果 output_path os.path.join(output_dir, fvis_{img_file}) cv2.imwrite(output_path, img) print(f已处理: {img_file}) except Exception as e: print(f处理{img_file}时出错: {str(e)})4. 进阶技巧交互式标注验证工具对于需要精细检查的场景可以创建一个交互式工具def interactive_validation(img_path, txt_path): 交互式标注验证工具 img, annotations yolo_to_pixel(img_path, txt_path) img_draw img.copy() current_index 0 total len(annotations) def redraw(): nonlocal img_draw img_draw img.copy() ann annotations[current_index] x_min, y_min, x_max, y_max ann[coordinates] # 绘制当前选框 cv2.rectangle(img_draw, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) # 显示信息 info f{current_index1}/{total} | Class: {ann[class_id]} cv2.putText(img_draw, info, (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imshow(Interactive Validation, img_draw) redraw() while True: key cv2.waitKey(0) 0xFF if key ord(n): # 下一个 current_index min(current_index 1, total - 1) redraw() elif key ord(p): # 上一个 current_index max(current_index - 1, 0) redraw() elif key 27: # ESC退出 break cv2.destroyAllWindows()使用说明按n键查看下一个标注框按p键查看上一个标注框按ESC退出这个工具特别适合检查密集标注的场景可以逐个验证每个边界框的准确性。