当你辛辛苦苦搞了一堆实例分割的标注数据,比如用 LabelMe、CVAT 或者 COCO 格式标了一堆多边形轮廓,结果回头想拿这些数据去训个 YOLOv11 的目标检测模型,发现格式对不上?😭

因为 YOLO 只要边界框(Bounding Box),而你的标注是分割轮廓(Segmentation),这不就尴尬了?

别慌!今天这篇博客就是来救场的!

👉 教你用一段 Python 脚本,一键把实例分割标注转成 YOLOv11 能吃的检测框格式!

全程无痛,代码我都给你写好了,你只需要改个路径,就能跑起来,直接省下几天手动重标的功夫!


🧠 为啥要做这个转换?

先说清楚:实例分割 vs 目标检测,差在哪?

  • 实例分割:不仅要框出物体,还要标出它的精确轮廓(一堆点连成的多边形)。
  • 目标检测:只需要一个矩形框 + 类别,简单粗暴。

所以,如果你已经有分割数据,但只想做检测任务,没必要重新标一遍!

我们只需要从多边形里“挤”出一个最小外接矩形,再转成 YOLO 的归一化格式,完事儿!

✅ 一句话总结:多边形 → 外接矩形 → YOLO 格式


🔧 转换思路(超简单)

咱们的转换逻辑就三步:

  1. :读取 txt 文件里的 所有坐标点。
  2. :找出这些点的 x_min, x_max, y_min, y_max,得到外接矩形。
  3. :把矩形转成 YOLO 要的格式:class_id center_x center_y width height(全部归一化到 0~1)。

是不是特别简单?根本不用深度学习,纯数学计算!


💻 完整代码来了!(直接复制可用)

下面这段代码,我亲测好用,支持从 YOLOv11 格式的实例分割标注 直接生成 YOLOv11 目标检测训练所需的检测框标注文件。

你只需要把路径改一下,就能跑!

import os
import glob
import shutil

def convert_segmentation_to_bbox(seg_txt_path, bbox_txt_path):
    """
    将实例分割标注转换为目标检测标注(外接矩形)
    
    Args:
        seg_txt_path: 输入的分割标注文件路径
        bbox_txt_path: 输出的目标检测标注文件路径
    """
    with open(seg_txt_path, 'r') as f:
        lines = f.readlines()
    
    bbox_lines = []
    
    for line in lines:
        parts = line.strip().split()
        if len(parts) < 2:
            continue
            
        # 第一个值是类别ID
        class_id = parts[0]
        
        # 剩余的是分割点的坐标 (归一化的 x1, y1, x2, y2, ...)
        points = list(map(float, parts[1:]))
        
        if len(points) < 6:  # 至少需要3个点(6个坐标值)才能形成多边形
            continue
        
        # 找到所有x坐标和y坐标
        x_coords = points[0::2]  # 所有x坐标
        y_coords = points[1::2]  # 所有y坐标
        
        # 计算外接矩形
        x_min = min(x_coords)
        x_max = max(x_coords)
        y_min = min(y_coords)
        y_max = max(y_coords)
        
        # 计算中心点坐标和宽高
        x_center = (x_min + x_max) / 2
        y_center = (y_min + y_max) / 2
        width = x_max - x_min
        height = y_max - y_min
        
        # 确保坐标在[0,1]范围内
        x_center = max(0, min(1, x_center))
        y_center = max(0, min(1, y_center))
        width = max(0, min(1, width))
        height = max(0, min(1, height))
        
        # 格式: class_id x_center y_center width height
        bbox_line = f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n"
        bbox_lines.append(bbox_line)
    
    # 写入新的标注文件
    with open(bbox_txt_path, 'w') as f:
        f.writelines(bbox_lines)

def copy_image_and_convert_annotation(seg_file, output_folder):
    """
    复制图片文件并转换对应的标注文件
    
    Args:
        seg_file: 分割标注文件路径
        output_folder: 输出文件夹路径
    """
    # 获取文件名(不含扩展名)
    base_name = os.path.splitext(os.path.basename(seg_file))[0]
    
    # 查找对应的图片文件
    img_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.JPG', '.JPEG', '.PNG', '.BMP']
    img_path = None
    
    for ext in img_extensions:
        potential_img_path = os.path.join(os.path.dirname(seg_file), base_name + ext)
        if os.path.exists(potential_img_path):
            img_path = potential_img_path
            break
    
    if img_path is None:
        print(f"警告: 未找到 {base_name} 对应的图片文件")
        return False
    
    try:
        # 复制图片文件到输出路径
        img_output_path = os.path.join(output_folder, os.path.basename(img_path))
        shutil.copy2(img_path, img_output_path)
        
        # 转换标注文件到输出路径
        bbox_output_path = os.path.join(output_folder, base_name + ".txt")
        convert_segmentation_to_bbox(seg_file, bbox_output_path)
        
        print(f"处理完成: {os.path.basename(seg_file)} -> 图片和标注已保存到输出路径")
        return True
        
    except Exception as e:
        print(f"错误: 处理 {base_name} 时发生异常: {str(e)}")
        return False

def batch_convert_with_output(input_folder, output_folder):
    """
    批量转换文件夹中的所有分割标注文件,并复制图片到指定输出路径
    
    Args:
        input_folder: 输入文件夹路径(包含图片和标注文件)
        output_folder: 输出文件夹路径
    """
    # 创建输出文件夹(如果不存在)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
        print(f"创建输出文件夹: {output_folder}")
    
    # 查找所有的分割标注文件
    seg_files = glob.glob(os.path.join(input_folder, "*.txt"))
    
    converted_count = 0
    total_files = len(seg_files)
    
    print(f"找到 {total_files} 个标注文件,开始处理...")
    
    for i, seg_file in enumerate(seg_files):
        print(f"处理进度: {i+1}/{total_files}", end='\r')
        
        if copy_image_and_convert_annotation(seg_file, output_folder):
            converted_count += 1
    
    print(f"\n\n转换完成!共成功处理了 {converted_count} 个文件。")
    print(f"输出路径: {output_folder}")
    print(f"包含: {converted_count} 个图片文件和 {converted_count} 个标注文件")

if __name__ == "__main__":
    # 输入文件夹路径(包含所有实例分割标注文件和图片的目录)
    input_folder = 'path/to/your/dataset'
    
    # 输出文件夹路径
    output_folder = 'path/to/your/output'
    
    if not os.path.exists(input_folder):
        print("输入文件夹不存在,请检查路径!")
    else:
        batch_convert_with_output(input_folder, output_folder)

🛠️ 使用方法(两步搞定)

  1. 修改两个路径

    • input_folder:你所有的实例分割标注和对应的图片文件路径
    • output_folder:转换后的检测框标注和对应图片的保存路径
  2. 运行脚本

    python labels_segmentation_to_detection.py

运行完,你的 output_folder里就会生成一堆 .txt 文件,同时还有对应的图片,格式长这样:

0 0.456250 0.321875 0.234375 0.187500
1 0.789062 0.654375 0.123438 0.109375

完美适配 YOLOv11 训练!


🎁 总结

已有分割数据 + 一段脚本 = 直接上 YOLO 检测任务

再也不用手动重标了,效率拉满!

如果你也在做 YOLO 相关项目,或者经常处理标注数据,赶紧把这段代码收藏起来,以后遇到类似需求,直接复制粘贴,分分钟搞定!


💬 互动时间

  • 你在标注转换时还遇到过哪些坑?
  • 有没有人需要 OBB 转换的支持?评论区告诉我,我下次安排!
  • 觉得有用的话,点赞 + 收藏 + 关注,三连支持一下!你的支持是我更新的最大动力!
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐