Yolo分割数据集错误数据删除

检查yolo系列的分割数据,如果怀疑数据集有问题,可以使用此代码,dry_run 先设置为True,执行后会打印有问题的数据,观察打印的数据符合预期,然后,再改为False,删除异常数据。代码如下:

csharp 复制代码
import os
import glob

def check_and_clean_segment_labels(label_dir, img_dir=None, num_classes=None, dry_run=False):
    """
    检查 YOLO 分割标签格式,并删除异常标签及其对应的图像。

    Args:
        label_dir (str): 标签文件目录(如 'labels/train')
        img_dir (str or None): 图像文件目录。若为 None,则自动推断(替换 'labels' 为 'images')
        num_classes (int or None): 类别总数,用于检查 class_id 范围
        dry_run (bool): 若为 True,仅打印将要删除的文件,不实际删除
    """
    if img_dir is None:
        # 自动推断 images 路径:将 'labels' 替换为 'images'
        if 'labels' in label_dir:
            img_dir = label_dir.replace('labels', 'images')
        else:
            raise ValueError("无法自动推断 img_dir,请显式传入 img_dir 参数")

    label_paths = glob.glob(os.path.join(label_dir, "*.txt"))
    if not label_paths:
        print(f"⚠️ 警告:在 {label_dir} 中未找到任何 .txt 标签文件!")
        return

    # 支持的图像扩展名
    IMG_EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}

    invalid_files = []

    for path in label_paths:
        with open(path, 'r', encoding='utf-8') as f:
            lines = [line.strip() for line in f.readlines() if line.strip()]

        if not lines:
            invalid_files.append((path, "空文件或无有效行"))
            continue

        valid_file = True
        for i, line in enumerate(lines):
            parts = line.split()
            if len(parts) < 7:
                invalid_files.append(
                    (path, f"第 {i + 1} 行:少于7个数值(当前{len(parts)}个),至少需要1个类别+3个点(7个数)"))
                valid_file = False
                break

            try:
                values = list(map(float, parts))
            except ValueError:
                invalid_files.append((path, f"第 {i + 1} 行:包含非数字字符"))
                valid_file = False
                break

            class_id = int(values[0])
            if num_classes is not None and (class_id < 0 or class_id >= num_classes):
                invalid_files.append((path, f"第 {i + 1} 行:类别ID {class_id} 超出范围 [0, {num_classes - 1}]"))
                valid_file = False
                break

            coords = values[1:]
            if len(coords) % 2 != 0:
                invalid_files.append((path, f"第 {i + 1} 行:坐标数量为奇数({len(coords)}),必须为偶数"))
                valid_file = False
                break

            if not all(0 <= c <= 1 for c in coords):
                invalid_files.append((path, f"第 {i + 1} 行:存在坐标超出 [0,1] 范围"))
                valid_file = False
                break

    # 删除或预览异常文件
    if invalid_files:
        print(f"\n❌ 在 {label_dir} 中发现 {len(invalid_files)} 个异常标签文件:\n")
        for txt_path, reason in invalid_files:
            print(f"  - {txt_path} → {reason}")

            # 查找对应的图像文件
            base_name = os.path.splitext(os.path.basename(txt_path))[0]
            img_path = None
            for ext in IMG_EXTS:
                candidate = os.path.join(img_dir, base_name + ext)
                if os.path.exists(candidate):
                    img_path = candidate
                    break

            if dry_run:
                print(f"    🗑️  [DRY RUN] 将删除: {txt_path}")
                if img_path:
                    print(f"    🗑️  [DRY RUN] 将删除: {img_path}")
                else:
                    print(f"    ⚠️  未找到对应图像文件(检查 {img_dir})")
            else:
                # 实际删除
                try:
                    os.remove(txt_path)
                    print(f"    ✅ 已删除标签: {txt_path}")
                except Exception as e:
                    print(f"    ❌ 删除失败 {txt_path}: {e}")

                if img_path:
                    try:
                        os.remove(img_path)
                        print(f"    ✅ 已删除图像: {img_path}")
                    except Exception as e:
                        print(f"    ❌ 删除失败 {img_path}: {e}")
                else:
                    print(f"    ⚠️  未找到对应图像文件(检查 {img_dir})")
    else:
        print(f"✅ {label_dir} 中所有标签文件格式合法!")


if __name__ == "__main__":
    # ====== 配置你的路径 ======
    train_label_dir = "datasets/Seg/labels/train"
    val_label_dir = "datasets/Seg/labels/val"
    num_classes = 1  # 你的类别数

    # 设置 dry_run=True 先预览,确认无误后再设为 False 执行删除
    dry_run = True # 👈 先设为 True 预览!确认后再改为 False

    print("=== 检查并清理训练集 ===")
    check_and_clean_segment_labels(
        label_dir=train_label_dir,
        num_classes=num_classes,
        dry_run=dry_run
    )

    print("\n=== 检查并清理验证集 ===")
    check_and_clean_segment_labels(
        label_dir=val_label_dir,
        num_classes=num_classes,
        dry_run=dry_run
    )
相关推荐
9i编程3 分钟前
6. 对SKILL进行一次全新尝试,改为框架+细节方式的实践及验证:admin-web联调与bug修复
人工智能·openai·ai编程
别动我齐刘海3 分钟前
从0到1独立搭建机器人软件系统
c++·人工智能·神经网络·opencv·目标检测·机器学习·机器人
夏夜霜4 分钟前
糖球系列③:ESP 圆屏不跑模型,它只是后台的语音客户端
人工智能
小宋10215 分钟前
MCP 是什么:从零编写一个可供 AI 调用的工具服务
人工智能·github
桃西西呀8 分钟前
AI 为什么一本正经地胡说八道?3 个底层原因 + 2 个防坑法
人工智能·llm·ai编程
天空1109 分钟前
Claude Code 怎么换用 Claude Fable 5.1:配置步骤、端点验证方法,以及缓存降价 75% 到底省了多少(2026 年 9 月)
人工智能·ai编程
船厂电气自动化ai大模型11 分钟前
AI大模型与数学第64课:矩阵×向量乘法(神经网络矩阵运算底层)
数据结构·深度学习·线性代数·机器学习·推荐算法
用户7686874404912 分钟前
我为什么不用 OpenAI 的 function calling,自己用正则解析工具调用
人工智能
正在走向自律12 分钟前
从 Python 基础到大模型落地:读《深入浅出 Python 人工智能》,吃透 AI 工程化 CRUD 实战
开发语言·人工智能·python·机器学习·知识脉络
长沙京卓13 分钟前
MagenticLite 设备需求:能不能本地跑,先分清 App 端和模型端
人工智能·ai