1. 项目概述:从通用数据工具出发,高效构建你的专属数据集
在计算机视觉和机器学习项目中,数据集的构建与标注往往是决定项目成败的关键一步,也是最耗费人力的环节之一。很多开发者,尤其是刚入行的朋友,常常会陷入一个误区:要么从零开始,手动收集和标注海量图片,效率极低;要么直接使用现成的公开数据集,却发现其类别、格式或标注精细度与自己的项目需求存在差距。有没有一种方法,能够让我们站在巨人的肩膀上,快速、灵活地构建出符合特定需求的定制化数据集呢?答案是肯定的,而“利用通用数据工具从COCO数据集构建你的数据集”正是解决这一痛点的绝佳实践。
这个项目的核心思路,就是将一个庞大而权威的公开数据集——COCO(Common Objects in Context)——作为我们的“原材料仓库”,然后通过一个功能强大且灵活的“加工工具”——Universal Data Tool(UDT)——来对其进行筛选、转换、增强和重新标注,最终“生产”出完全贴合我们项目目标的专属数据集。COCO数据集以其丰富的场景、精细的实例分割标注和庞大的数据量(超过33万张图片,250万个标注实例)而闻名,涵盖了80个常见的物体类别。它为我们提供了一个高质量、多样化的数据基础。而Universal Data Tool则是一个开源的、支持多种数据类型(图像、文本、音频、视频)标注和管理的Web应用,其最大的优势在于高度的可定制化和易用性,允许我们以可视化的方式快速处理数据。
简单来说,这个过程就像是一位厨师要做一道新菜。他不需要从种菜开始,而是直接去一个品类齐全的大型超市(COCO数据集),挑选出需要的食材(特定类别的图片和标注)。然后,他回到自己的现代化厨房(Universal Data Tool),根据新菜的配方,对这些食材进行清洗、切配、调味(筛选、格式转换、补充标注),最终烹饪出独一无二的美味佳肴(你的专属数据集)。这个项目非常适合那些希望快速启动计算机视觉项目(如目标检测、实例分割)的研究者、学生和工程师,特别是当你的目标类别是COCO 80类的一个子集,或者你需要在COCO的基础上增加新的标注属性时,这套工作流将极大地提升你的效率。
2. 核心工作流设计与工具选型解析
2.1 为什么是COCO + UDT?组合优势深度剖析
选择COCO作为数据源,UDT作为处理工具,并非随意之举,而是基于两者特性互补所形成的强大合力。首先看COCO,它不仅仅是一个图片集合,更是一个结构严谨、标注质量极高的标准数据集。其标注文件采用JSON格式,结构清晰,包含了图片信息、类别信息以及详细的标注信息(如边界框bbox、分割多边形segmentation)。这意味着我们可以通过编程方式,精确地提取出任何我们感兴趣的图片和标注。例如,如果你的项目只关心“人”、“汽车”和“狗”这三类,你可以轻松地写一个脚本,从COCO的33万张图片中,过滤出所有包含这三类中任意一类的图片及其标注,瞬间获得一个规模可观、标注准确的子集。这比从零开始收集和标注要快上几个数量级。
然而,直接从COCO提取的子集可能还不完全符合你的要求。你可能需要调整标注格式以适配不同的训练框架(如YOLO、Detectron2),可能需要为某些样本添加新的属性标签(比如“汽车的颜色”),甚至可能发现某些标注在特定场景下不够精确需要微调。这时,Universal Data Tool的优势就凸显出来了。UDT是一个本地运行的Web应用,它允许你通过一个友好的界面导入数据,并以可视化的方式进行交互式标注和编辑。它支持导入COCO的JSON格式,并能将图片和标注关联展示。你可以在界面上直接查看边界框和分割多边形,进行拖动修改、删除误标、增加漏标等操作。更重要的是,UDT支持自定义标注schema(模式),你可以轻松地定义新的标签类型,例如为每个“人”的实例添加一个“是否佩戴眼镜”的布尔属性,或者为“汽车”添加一个“颜色”的下拉选择框。这种灵活性是许多固定功能的标注工具所不具备的。
因此,“COCO提供优质原料,UDT提供精加工能力”的组合,形成了一条高效的数据集生产线。它既利用了现有大规模数据集的规模和质量红利,又通过灵活的编辑工具满足了项目的个性化需求,完美平衡了效率与定制化之间的矛盾。
2.2 工具链准备与环境搭建
在开始具体操作之前,我们需要准备好整个工具链。这里假设你使用的是Linux或macOS系统(Windows用户可通过WSL获得类似体验),并具备基本的Python和命令行操作知识。
1. COCO数据集获取:COCO数据集官方提供了多个版本,对于大多数项目,从2017版本开始是一个好选择,它包含了训练集(118K张)、验证集(5K张)和测试集(开发工具不提供标注)。你可以从COCO官网下载,但更推荐使用官方的Python API工具包pycocotools来辅助操作。首先安装必要的库:
pip install pycocotools numpy matplotlib然后,你需要下载数据集文件。通常你需要train2017.zip(训练图片),val2017.zip(验证图片),以及对应的标注文件annotations_trainval2017.zip。下载后解压到一个清晰的目录结构中,例如:
coco_data/ ├── annotations/ │ ├── instances_train2017.json │ └── instances_val2017.json ├── train2017/ │ └── ... (图片文件) └── val2017/ └── ... (图片文件)2. Universal Data Tool (UDT) 安装与启动:UDT的安装极其简单,因为它是一个基于Electron的桌面应用,也提供了Docker镜像。对于大多数用户,我强烈推荐使用Docker方式,它能避免复杂的本地环境依赖问题。
# 确保你的系统已安装Docker docker pull universaldatatool/universaldatatool # 运行UDT容器,将本地的一个目录(例如 ./my_datasets)挂载到容器的 /datasets 目录,用于存放项目数据 docker run -p 3000:3000 -v $(pwd)/my_datasets:/datasets universaldatatool/universaldatatool执行上述命令后,打开浏览器访问http://localhost:3000,你就能看到UDT的界面了。通过Docker运行,所有项目数据都保存在你本地挂载的my_datasets目录下,安全且便于管理。
注意:首次使用UDT时,界面可能会提示你创建一个“工作区”(Workspace)。你可以将其理解为一个项目文件夹,用于管理多个标注任务。建议为你从COCO构建新数据集的项目单独创建一个工作区。
3. 从COCO中提取目标数据子集
3.1 使用pycocotools进行精准数据过滤
拿到完整的COCO数据集后,第一步就是“取材”。我们需要编写一个Python脚本,利用pycocotools从海量数据中精准地提取出我们需要的部分。假设我们的新项目是一个“城市街道元素检测系统”,只关心四个类别:person(人),car(汽车),traffic light(交通灯),stop sign(停止标志)。
首先,我们来解析COCO标注文件的结构,并制定过滤策略。COCO的标注JSON中,images数组存储图片信息,annotations数组存储每个实例的标注,categories数组存储类别信息。我们的目标是:找到所有包含至少一个目标类别的图片,并将这些图片以及对应的所有标注(包括非目标类别的标注,我们可以选择保留或过滤)提取出来。
下面是一个核心的过滤脚本示例:
from pycocotools.coco import COCO import json import os import shutil # 1. 初始化路径和参数 dataDir = './coco_data' annFile = f'{dataDir}/annotations/instances_train2017.json' # 以训练集为例 target_categories = ['person', 'car', 'traffic light', 'stop sign'] output_dir = './my_street_dataset' images_output_dir = f'{output_dir}/images' os.makedirs(images_output_dir, exist_ok=True) # 2. 加载COCO标注 coco = COCO(annFile) # 3. 获取目标类别的ID catIds = coco.getCatIds(catNms=target_categories) print(f"目标类别ID: {catIds}") # 4. 获取包含目标类别的所有图片ID imgIds = [] for catId in catIds: ids = coco.getImgIds(catIds=[catId]) imgIds.extend(ids) # 去重,因为一张图片可能包含多个目标类别 imgIds = list(set(imgIds)) print(f"找到 {len(imgIds)} 张包含目标类别的图片。") # 5. 构建新的标注数据结构 new_annotations = [] new_images = [] used_image_ids = set() # 6. 遍历筛选出的图片 for img_id in imgIds: img_info = coco.loadImgs(img_id)[0] ann_ids = coco.getAnnIds(imgIds=img_id) anns = coco.loadAnns(ann_ids) # 可选:只保留目标类别的标注。如果注释掉下面这行,则保留图片中的所有原始标注。 anns = [ann for ann in anns if ann['category_id'] in catIds] if not anns: continue # 如果过滤后该图片没有标注了,则跳过(如果上一步选择保留所有标注,则不会出现此情况) # 复制图片文件到新目录 src_image_path = os.path.join(dataDir, 'train2017', img_info['file_name']) dst_image_path = os.path.join(images_output_dir, img_info['file_name']) shutil.copy(src_image_path, dst_image_path) # 重新索引标注ID,避免冲突 for ann in anns: new_id = len(new_annotations) + 1 ann['id'] = new_id new_annotations.append(ann) new_images.append(img_info) used_image_ids.add(img_id) # 7. 构建新的JSON文件 new_coco_format = { "info": coco.dataset['info'], "licenses": coco.dataset['licenses'], "categories": [c for c in coco.dataset['categories'] if c['id'] in catIds], # 只保留目标类别信息 "images": new_images, "annotations": new_annotations } # 8. 保存新的标注文件 output_ann_file = os.path.join(output_dir, 'annotations_train.json') with open(output_ann_file, 'w') as f: json.dump(new_coco_format, f) print(f"数据集构建完成!图片保存在: {images_output_dir}") print(f"新标注文件保存在: {output_ann_file}")关键操作解析与注意事项:
getCatIds和getImgIds:这是pycocotools的核心查询函数,能高效地根据类别名称找到ID,再根据类别ID找到相关的图片ID。- 标注过滤策略:脚本中
anns = [ann for ann in anns if ann['category_id'] in catIds]这行代码决定了你是否保留非目标类别的标注。如果你的模型只需要检测特定类别,建议过滤掉,可以减少标注文件大小和后续处理的干扰。但如果你希望保留上下文信息(例如,模型可能隐式学习到“狗通常不会出现在办公桌上”),可以选择保留。 - 文件复制:使用
shutil.copy复制图片。确保你有足够的磁盘空间,因为即使经过过滤,数据量也可能很大。 - ID重映射:这是一个极易出错但至关重要的步骤。COCO数据集中,标注ID (
annotation[‘id’]) 是全局唯一的。当我们从原始数据中抽取一部分标注时,必须为它们分配新的唯一ID,否则在UDT或后续训练框架中加载时,可能会因为ID冲突导致严重错误。上面的脚本通过new_id = len(new_annotations) + 1简单地实现了顺序重映射。
3.2 数据子集的统计与质量检查
提取出子集后,不要急于导入UDT,先做一次简单的统计分析,做到心中有数。
# 接续上面的脚本,或在新的分析脚本中加载刚生成的标注文件 with open(output_ann_file, 'r') as f: new_data = json.load(f) print(f"新数据集中图片数量: {len(new_data['images'])}") print(f"新数据集中标注实例数量: {len(new_data['annotations'])}") # 统计每个类别的实例数 from collections import Counter category_counter = Counter() for ann in new_data['annotations']: category_counter[ann['category_id']] += 1 for cat in new_data['categories']: cid = cat['id'] cname = cat['name'] count = category_counter.get(cid, 0) print(f"类别 '{cname}' (ID:{cid}): {count} 个实例") # 检查是否有图片没有标注(理论上经过我们的过滤,应该没有) image_ids_in_anns = set([ann['image_id'] for ann in new_data['annotations']]) image_ids_in_images = set([img['id'] for img in new_data['images']]) if len(image_ids_in_images - image_ids_in_anns) > 0: print("警告:存在没有标注的图片!") else: print("所有图片都有对应的标注。")这个检查能帮你确认数据提取是否按预期进行,各类别数据量是否均衡(严重不均衡可能需要后续进行数据增强或重采样)。例如,你可能会发现“人”和“汽车”的实例数远远多于“停止标志”,这对于模型训练是一个需要关注的信号。
4. 使用Universal Data Tool进行数据增强与标注定制
4.1 导入COCO格式数据至UDT
现在,我们拥有了一个符合COCO JSON格式的子集。接下来就是将其导入UDT进行进一步的加工。启动你的UDT Docker容器并打开浏览器。
- 创建新数据集:在UDT主页,点击“Create New Dataset”。给你的数据集起个名字,例如 “Street_Elements_From_COCO”。
- 选择接口类型:UDT支持图像分类、目标检测、实例分割、关键点等多种任务。对于COCO数据,我们通常选择“Image Segmentation”或“Object Detection”。两者都支持边界框,但“Image Segmentation”能更好地处理COCO中的多边形分割标注。这里我们选择“Image Segmentation”。
- 导入数据:
- 在“Add Samples”页面,选择“Import from COCO”。
- 在“Images Directory”中,填写你本地挂载到Docker容器内的图片路径,例如
/datasets/my_street_dataset/images。(记住,/datasets对应你启动Docker时-v参数挂载的本地目录)。 - 在“COCO JSON File”中,选择对应的标注文件,例如
/datasets/my_street_dataset/annotations_train.json。 - 点击“Import”。UDT会解析JSON文件,将图片和标注加载进来。
实操心得:首次导入大量数据(如上万张图片)时,UDT可能会需要一些时间进行索引和生成缩略图。建议可以先导入一个小样本(比如100张)进行流程测试,确认无误后再导入全部数据。另外,确保你的JSON文件路径在Docker容器内是可访问的,这是最常见的导入失败原因。
4.2 利用UDT进行可视化审查与标注修正
导入成功后,你会进入数据集的主界面。这里以网格形式展示所有图片,带有标注的图片会在缩略图上显示标记。
- 快速审查与筛选:利用UDT的过滤和排序功能,可以快速定位问题。例如,你可以点击左侧的标签列表,只显示包含“car”的图片,集中检查该类别的标注质量。
- 交互式修正:点击任意一张图片进入标注视图。你可以:
- 查看现有标注:COCO导入的边界框和多边形会以不同颜色显示。
- 修正标注:直接拖动边界框的顶点或边来调整其位置和大小。对于分割多边形,可以拖动多边形上的点进行微调。这是修正COCO原始标注中可能存在的小偏差(如框体不精确)的最直接方法。
- 删除错误标注:点击某个标注,按Delete键或使用工具栏的删除按钮,移除错误的标注(例如,将阴影误标为物体)。
- 添加漏标:使用工具栏的“框选”或“多边形”工具,手动为图中未标注的目标物体添加新的标注,并选择正确的类别标签。
一个关键的技巧是使用“标签视图”(Label View)。在这个视图中,所有同类别的实例会平铺展示,非常有利于发现系统性标注问题。比如,你可能会发现所有“交通灯”的标注都只框住了灯体,而没有包括支撑杆,如果你的项目需要检测整个结构,就需要批量修正。
4.3 扩展标注Schema:添加自定义属性
这是UDT超越简单标注编辑器的强大之处。假设我们的街道元素检测系统不仅需要知道物体的位置和类别,还需要知道“汽车的颜色”和“交通灯的状态(红、黄、绿)”。COCO原始数据没有这些信息,我们可以在UDT中轻松添加。
- 进入数据集设置:在数据集主界面,点击右上角的“Settings”(齿轮图标)。
- 编辑接口配置:在“Interface”选项卡,你可以看到当前“Image Segmentation”接口的JSON配置。这是一个强大的DSL(领域特定语言),允许你自定义标注表单。
- 为特定类别添加属性:我们需要修改
labels数组。找到对应“car”的标签配置,为其添加一个attributes字段。修改后的配置片段可能如下所示:
{ "type": "image-segmentation", "labels": [ { "id": "1", "name": "person", "color": "#FF0000" }, { "id": "2", "name": "car", "color": "#00FF00", "attributes": [ { "type": "select", "name": "color", "options": ["white", "black", "red", "blue", "silver", "other"] } ] }, { "id": "3", "name": "traffic light", "color": "#0000FF", "attributes": [ { "type": "radio", "name": "state", "options": ["red", "yellow", "green", "unknown"] } ] } ] }- 保存并应用:保存设置后,返回标注界面。当你新建或编辑一个“汽车”的标注时,下方就会出现一个“color”下拉菜单供你选择。编辑“交通灯”时,则会出现“state”单选按钮。你可以组织团队成员,利用这个功能对筛选出的图片进行属性标注,极大地丰富了数据集的信息维度。
5. 数据格式转换与导出
5.1 转换为其他训练框架格式
在UDT中完成审查、修正和属性添加后,我们得到了一个增强版的、符合项目需求的COCO格式数据集。但最终,我们需要将其转换为特定深度学习框架所需的格式。UDT本身支持导出多种格式,包括COCO JSON、VOC XML、YOLO TXT等。
- 在UDT中导出:在数据集主界面,点击“Export”按钮。你可以选择“COCO JSON”以保留所有信息(包括自定义属性,它们会保存在
annotation[‘attributes’]字段中)。也可以选择其他格式,如“YOLO Darknet”。 - 处理自定义属性:需要注意的是,像YOLO这样的简单格式可能不支持自定义属性。如果你添加了属性,通常需要额外处理:
- 方案A:将属性信息写入单独的JSON或CSV文件,与标注文件关联。例如,导出一个
image_id_attributes.csv,包含image_id, annotation_id, attribute_name, attribute_value等列。 - 方案B:将属性编码到类别ID中。例如,原本“汽车”的类别ID是2,你可以定义“白色汽车”为20,“黑色汽车”为21……但这会急剧增加类别数量,仅适用于属性值离散且数量不多的场景。
- 方案C(推荐):在模型训练时,将属性作为额外的监督信号,使用多任务学习框架。这需要你编写自定义的数据加载器,在读取标准格式(如COCO)标注的同时,从另一个文件中加载属性信息。
- 方案A:将属性信息写入单独的JSON或CSV文件,与标注文件关联。例如,导出一个
一个实用的脚本示例:将UDT导出的COCO JSON(含属性)转换为YOLO格式,并将属性保存为额外文件。
import json import os def convert_coco_to_yolo_with_attr(coco_json_path, output_dir, img_dir): with open(coco_json_path, 'r') as f: data = json.load(f) # 创建输出目录 labels_dir = os.path.join(output_dir, 'labels') os.makedirs(labels_dir, exist_ok=True) # 构建类别ID到连续索引的映射(YOLO通常从0开始) cat_id_to_yolo_idx = {} for idx, cat in enumerate(data['categories']): cat_id_to_yolo_idx[cat['id']] = idx # 用于存储属性的列表 attributes_list = [] # 按图片分组标注 from collections import defaultdict img_to_anns = defaultdict(list) for ann in data['annotations']: img_to_anns[ann['image_id']].append(ann) # 处理每张图片 for img_info in data['images']: img_id = img_info['id'] img_w = img_info['width'] img_h = img_info['height'] file_name = img_info['file_name'] base_name = os.path.splitext(file_name)[0] label_path = os.path.join(labels_dir, base_name + '.txt') with open(label_path, 'w') as label_f: for ann in img_to_anns.get(img_id, []): # YOLO格式: class_id center_x center_y width height (归一化) cat_id = ann['category_id'] yolo_class = cat_id_to_yolo_idx[cat_id] # COCO bbox格式: [x_top_left, y_top_left, width, height] bbox = ann['bbox'] x_center = (bbox[0] + bbox[2] / 2) / img_w y_center = (bbox[1] + bbox[3] / 2) / img_h width_n = bbox[2] / img_w height_n = bbox[3] / img_h label_f.write(f"{yolo_class} {x_center:.6f} {y_center:.6f} {width_n:.6f} {height_n:.6f}\n") # 处理并保存属性 if 'attributes' in ann: for attr in ann['attributes']: attributes_list.append({ 'image_id': img_id, 'annotation_id': ann['id'], 'category_id': cat_id, 'attribute_name': attr.get('name'), 'attribute_value': attr.get('value') }) # 保存属性文件 if attributes_list: import csv attr_csv_path = os.path.join(output_dir, 'attributes.csv') with open(attr_csv_path, 'w', newline='') as csvfile: fieldnames = ['image_id', 'annotation_id', 'category_id', 'attribute_name', 'attribute_value'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() writer.writerows(attributes_list) print(f"属性已保存至: {attr_csv_path}") # 保存类别映射文件 classes_path = os.path.join(output_dir, 'classes.txt') with open(classes_path, 'w') as f: for cat in data['categories']: f.write(f"{cat['name']}\n") print(f"类别列表已保存至: {classes_path}") print(f"YOLO格式标签已保存至: {labels_dir}") # 使用示例 convert_coco_to_yolo_with_attr( coco_json_path='./my_street_dataset/annotations_enhanced.json', # UDT导出的增强版标注 output_dir='./yolo_street_dataset', img_dir='./my_street_dataset/images' )5.2 数据集划分与版本管理
最后,别忘了将你的最终数据集划分为训练集、验证集和测试集。可以使用scikit-learn的train_test_split进行随机划分,并确保同一张图片的所有标注(包括自定义属性)被完整地划分到同一个集合中。
import os import shutil from sklearn.model_selection import train_test_split def split_dataset(image_dir, label_dir, output_base_dir, test_ratio=0.15, val_ratio=0.15, seed=42): """ 划分YOLO格式的数据集。 image_dir: 原图片目录 label_dir: 原标签目录(.txt文件) output_base_dir: 输出根目录 """ all_images = [f for f in os.listdir(image_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))] all_images.sort() # 第一次分割:分出测试集 train_val_imgs, test_imgs = train_test_split(all_images, test_size=test_ratio, random_state=seed) # 第二次分割:从训练验证集中分出验证集 train_imgs, val_imgs = train_test_split(train_val_imgs, test_size=val_ratio/(1-test_ratio), random_state=seed) print(f"训练集: {len(train_imgs)} 张, 验证集: {len(val_imgs)} 张, 测试集: {len(test_imgs)} 张") splits = {'train': train_imgs, 'val': val_imgs, 'test': test_imgs} for split_name, img_list in splits.items(): split_image_dir = os.path.join(output_base_dir, 'images', split_name) split_label_dir = os.path.join(output_base_dir, 'labels', split_name) os.makedirs(split_image_dir, exist_ok=True) os.makedirs(split_label_dir, exist_ok=True) for img_file in img_list: # 复制图片 src_img = os.path.join(image_dir, img_file) dst_img = os.path.join(split_image_dir, img_file) shutil.copy(src_img, dst_img) # 复制对应的标签文件 base_name = os.path.splitext(img_file)[0] label_file = base_name + '.txt' src_label = os.path.join(label_dir, label_file) dst_label = os.path.join(split_label_dir, label_file) if os.path.exists(src_label): shutil.copy(src_label, dst_label) # 创建数据集配置文件(例如YOLO用的data.yaml) yaml_content = f""" path: {os.path.abspath(output_base_dir)} # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 test: images/test # 测试集图片相对路径 nc: {len(data['categories'])} # 类别数,这里需要从之前的data变量获取,实际使用时需调整 names: {[cat['name'] for cat in data['categories']]} # 类别名列表 """ yaml_path = os.path.join(output_base_dir, 'data.yaml') with open(yaml_path, 'w') as f: f.write(yaml_content) print(f"数据集划分完成,配置文件保存至: {yaml_path}") # 注意:此函数中的 data['categories'] 需要从你的标注数据中传入。完成划分后,你就得到了一个结构清晰、可直接用于模型训练的数据集目录。建议使用Git或DVC(Data Version Control)对这个最终的数据集目录进行版本管理,记录下每次数据变更的缘由,这对于团队协作和实验复现至关重要。
6. 常见问题与排查技巧实录
在实际操作中,你几乎一定会遇到一些问题。下面是我在多次实践中总结出的典型问题及其解决方案。
问题1:UDT导入COCO JSON时失败,提示“无法解析”或“加载错误”。
- 可能原因A:JSON文件格式错误或路径不对。
- 排查:首先用Python的
json.load()函数验证你的JSON文件是否能被正确解析。检查Docker容器内的路径是否正确,确保挂载的卷(-v参数)包含了图片和JSON文件。
- 排查:首先用Python的
- 可能原因B:图片路径在JSON中与UDT访问路径不匹配。
- 排查:COCO JSON中的
image[‘file_name’]字段是相对路径(如000000001.jpg)。UDT会尝试在指定的“Images Directory”下寻找这个文件。请确保“Images Directory”设置的就是包含所有这些图片文件的文件夹,并且文件名大小写一致(Linux系统区分大小写)。
- 排查:COCO JSON中的
- 可能原因C:标注ID重复或图片ID缺失。
- 排查:回顾我们在提取子集时进行的ID重映射步骤。确保新的JSON中,
annotations数组里的每个id是唯一的,并且每个annotation的image_id都能在images数组中找到对应的id。
- 排查:回顾我们在提取子集时进行的ID重映射步骤。确保新的JSON中,
问题2:在UDT中编辑后,导出的标注文件丢失了部分信息(如分割多边形)。
- 可能原因:导出时选择了错误的格式。
- 解决:UDT的“Image Segmentation”接口在导出时,如果选择“COCO JSON”,会完整保留多边形信息。如果选择“Object Detection (Bounding Box)”,则只会导出边界框。请根据你的需要选择正确的导出格式。如果你同时需要边界框和多边形,导出COCO JSON即可,大部分框架都支持从COCO格式中读取这两种信息。
问题3:使用转换脚本后,YOLO标签文件中的坐标值异常(如大于1或为负数)。
- 可能原因:边界框坐标计算错误或图像尺寸读取有误。
- 排查:检查你的转换脚本中的归一化计算逻辑:
中心点x = (bbox_x + bbox_width/2) / 图像宽度。确保bbox_x和bbox_width是COCO格式的绝对像素值,图像宽度是从images数组里正确读取的。打印出几张图片的转换前后坐标进行对比验证。另外,COCO的bbox格式是[x_min, y_min, width, height],其中x_min, y_min是左上角坐标,这点不要弄错。
- 排查:检查你的转换脚本中的归一化计算逻辑:
问题4:数据类别严重不均衡,某个类别样本极少。
- 解决策略:这更多是一个数据策略问题,而非工具问题。有几种应对方法:
- 数据增强(Data Augmentation):对少数类别的图片进行更激进的数据增强,如旋转、裁剪、颜色抖动、 mosaic 等。可以在UDT阶段就复制这些样本,也可以在训练时通过数据加载器动态增强。
- 重采样(Re-sampling):在训练时,让数据加载器更频繁地采样少数类别的图片。例如,PyTorch的
WeightedRandomSampler。 - 类别权重(Class Weight):在损失函数中为少数类别赋予更高的权重,让模型更关注这些类别的错误。
- 回到源头:考虑是否可以从COCO数据集中,通过更宽松的条件(如包含相关场景的图片)筛选出更多包含少数类别的样本。
问题5:UDT在标注大量图片时,界面变得卡顿。
- 性能优化建议:
- 分批处理:不要一次性导入数万张图片。可以按类别或随机分成多个子数据集(如
dataset_part1,dataset_part2),在UDT中分别处理,最后再合并标注文件。 - 硬件加速:确保UDT运行在有独立显卡的机器上,浏览器硬件加速已开启。
- 关闭预览:在标注界面,可以暂时关闭“显示所有标注”的选项,只加载当前正在编辑的标注。
- 使用标签视图进行批量操作:对于同类别的标注修正,在“标签视图”下操作比在单张图片视图下逐张修改效率高得多。
- 分批处理:不要一次性导入数万张图片。可以按类别或随机分成多个子数据集(如
这套从COCO到UDT,再到最终训练格式的工作流,其威力在于将数据准备的各个环节工具化、流程化。它显著降低了从想法到数据原型的门槛。当你下一次启动一个新的视觉项目时,不妨先问自己:“我需要的数据,能否从COCO这个宝库中,通过UDT这把瑞士军刀,快速地加工出来?” 在大多数情况下,答案会是肯定的。这不仅能节省你数周甚至数月的时间,更能让你将精力集中在模型设计和调优这些更具创造性的工作上。