news 2026/8/5 12:14:03

目标检测数据集预处理:VOC/COCO转YOLO格式与科学划分训练验证集

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
目标检测数据集预处理:VOC/COCO转YOLO格式与科学划分训练验证集

1. 项目概述:从混乱到秩序,数据集的标准化之路

做目标检测的朋友,十有八九都卡在第一步:数据准备。你兴冲冲地下载了一个数据集,或者自己辛苦标注了几百张图片,结果发现,标注文件格式五花八门,有的是VOC的XML,有的是COCO的JSON,还有的是YOLO的txt,甚至可能是某个标注工具自创的格式。更头疼的是,所有图片和标签都混在一个文件夹里,训练集、验证集、测试集?不存在的。这个“目标检测---数据集格式转化及训练集和验证集划分”的项目,就是解决这个“万事开头难”的核心痛点。它不是一个炫技的算法,而是确保你后续所有模型训练工作能顺利、高效、科学进行的地基工程。简单说,就是把你的原始标注数据,转换成你选定框架(比如YOLOv5/v8/v11,或者Detectron2、MMDetection)能“吃”得下去的格式,并且按照合理的比例,把数据分成用于“学习”的训练集和用于“考试”的验证集。这个过程看似基础,却直接决定了模型最终性能的上限和评估的可信度。无论你是刚入门的新手,还是被数据格式折磨过的老手,系统性地掌握这套数据预处理流程,都能让你的项目赢在起跑线上。

2. 核心思路与方案设计:为何要“转化”与“划分”

在动手写代码之前,我们必须先想清楚两个根本问题:为什么要转换格式?为什么要划分数据集?这直接决定了我们方案设计的每一个细节。

2.1 数据集格式转化的核心逻辑

不同的目标检测框架对输入数据的格式有不同要求,这背后是框架设计哲学和效率权衡的体现。

  1. VOC格式:以XML文件存储,结构清晰,可读性强,包含了图片尺寸、对象类别、边界框坐标(通常是xmin, ymin, xmax, ymax的绝对像素值)等丰富信息。它是一种“自描述”性很强的格式,但解析速度相对较慢,因为需要读取和解析XML树。
  2. COCO格式:采用单个庞大的JSON文件来管理整个数据集的信息,包括图片列表、标注列表、类别列表等。它的优势在于“集中管理”,特别适合大规模数据集,且官方提供了完善的评估工具。但动辄几百MB的JSON文件,在读取和索引时对内存有一定要求。
  3. YOLO格式:追求极致的简洁和效率。每个图片对应一个同名的.txt文件,每行表示一个目标,格式为:<class_id> <x_center> <y_center> <width> <height>。这里的坐标是归一化后的值(即相对于图片宽高的比例),class_id是整数索引。这种格式读取速度快,占用空间小,非常适合YOLO系列这种强调训练速度的框架。

格式转化的本质,是在不丢失信息的前提下,进行数据结构的“翻译”。例如,从VOC到YOLO,你需要:

  • 将类别名称(如“person”、“car”)映射为整数class_id(如0, 1)。
  • 将绝对坐标(xmin, ymin, xmax, ymax)转换为归一化的中心点坐标和宽高:x_center = (xmin + xmax) / 2.0 / image_widthy_center = (ymin + ymax) / 2.0 / image_heightwidth = (xmax - xmin) / image_widthheight = (ymax - ymin) / image_height这个过程必须保证精度,并且要处理好边界情况(如坐标越界)。

2.2 训练集/验证集划分的科学性与实操性

划分数据集不是为了“分文件夹”,而是为了模拟模型在真实世界中的应用场景,并进行可靠的性能评估。

  1. 根本目的

    • 训练集:用于模型学习,调整数百万甚至数十亿的权重参数。模型从这里学习从图片像素到目标位置和类别的映射规律。
    • 验证集:用于“模拟考试”。在训练过程中,定期在验证集上评估模型表现(如mAP),此结果用于调整超参数(如学习率)、进行早停(Early Stopping)或选择最佳模型。验证集的表现是指导训练过程的“指挥棒”。
    • 测试集:用于“最终大考”。在模型训练和调参全部完成后,用测试集做一次最终、客观的性能评估,这个分数才代表模型的泛化能力。测试集在训练和调参阶段绝对不能使用。很多公开数据集会提供官方的测试集标注,而个人项目中,常从全部数据中划分出一部分作为测试集。
  2. 划分比例:没有黄金标准,但常见经验是7:2:18:1:1(训练:验证:测试)。核心原则是:

    • 训练集要足够大,确保模型能学到充分的特征,特别是对于复杂场景或小样本类别。
    • 验证集要足够有代表性,其数据分布应尽可能接近训练集和真实数据,否则“模拟考试”会失灵。通常采用分层抽样,确保每个类别在训练集和验证集中的比例大致相同,避免某个类别只在训练集出现而从未被“考”到。
  3. 划分方法:绝对不能简单随机打乱。必须保证同一张图片的所有标注(可能包含多个目标)必须同时被划分到同一个集合中。更高级的划分还需要考虑场景的独立性,例如,从连续视频帧中抽取的图片,如果随机划分会导致训练集和验证集画面高度相似,造成“数据泄露”,使验证分数虚高。此时应按视频片段或场景ID进行划分。

注意:很多人容易混淆验证集和测试集。一个简单的记忆方法是:验证集是“开发集”,你在项目开发过程中会反复使用它来调整模型;测试集是“评估集”,只在一切尘埃落定后使用一次,给出最终报告分数。在学术论文中,通常只提及训练集和测试集,其“测试集”实际承担了验证和最终评估的双重角色,但这在工程实践中容易导致过拟合测试集的风险。

3. 实战流程:从VOC到YOLO格式的完整转换与划分

下面,我将以最常见的VOC格式数据集转换为YOLO格式,并进行规范划分为例,展示一个完整的、可复现的实操流程。假设我们有一个名为MyDataset的原始数据集,结构如下:

MyDataset/ ├── JPEGImages/ # 存放所有图片(.jpg) │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... └── Annotations/ # 存放所有VOC格式标注文件(.xml) ├── 000001.xml ├── 000002.xml └── ...

3.1 步骤一:解析VOC XML并提取关键信息

我们需要编写一个脚本来读取每个XML文件,提取出图片尺寸和每个目标的类别、边界框信息。

import os import xml.etree.ElementTree as ET import random from pathlib import Path def parse_voc_annotation(xml_path): """解析单个VOC XML文件,返回图片信息和目标列表""" tree = ET.parse(xml_path) root = tree.getroot() # 获取图片尺寸 size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) objects = [] for obj in root.findall('object'): # 获取类别名 class_name = obj.find('name').text # 获取边界框 bndbox = obj.find('bndbox') xmin = int(float(bndbox.find('xmin').text)) ymin = int(float(bndbox.find('ymin').text)) xmax = int(float(bndbox.find('xmax').text)) ymax = int(float(bndbox.find('ymax').text)) # 确保坐标在图片范围内(处理可能的标注错误) xmin = max(0, min(xmin, width - 1)) ymin = max(0, min(ymin, height - 1)) xmax = max(0, min(xmax, width - 1)) ymax = max(0, min(ymax, height - 1)) if xmax <= xmin or ymax <= ymin: print(f"Warning: Invalid bbox in {xml_path}, skipped.") continue objects.append({ 'class_name': class_name, 'bbox': [xmin, ymin, xmax, ymax] }) return {'width': width, 'height': height, 'objects': objects}

3.2 步骤二:构建类别映射字典

我们需要为数据集中所有出现的类别建立一个从名称到ID的映射。这个映射字典必须保存下来,在训练和推理时都要用到。

def build_class_index(annotations_dir): """遍历所有XML,收集所有类别并构建索引字典""" class_set = set() for xml_file in Path(annotations_dir).glob('*.xml'): tree = ET.parse(xml_file) root = tree.getroot() for obj in root.findall('object'): class_name = obj.find('name').text class_set.add(class_name) # 按类别名称字母排序,确保每次生成的ID一致 class_list = sorted(list(class_set)) class_to_id = {name: idx for idx, name in enumerate(class_list)} id_to_class = {idx: name for name, idx in class_to_id.items()} # 保存类别映射文件(至关重要!) with open('class_names.txt', 'w') as f: for name in class_list: f.write(name + '\n') print(f"Found {len(class_list)} classes: {class_list}") return class_to_id, id_to_class

实操心得class_names.txt文件是连接数据和模型的桥梁。YOLO训练时需要的data.yaml文件里的names列表,必须和这个文件顺序完全一致。一个常见的坑是:转换脚本和训练脚本使用了不同的类别列表顺序,导致模型学到的“0”号类别实际是“人”,但你以为的是“车”,结果全乱套了。务必保证源头唯一。

3.3 步骤三:坐标转换与YOLO格式写入

这是格式转换的核心计算步骤。

def convert_bbox_to_yolo(size, box): """将VOC的绝对坐标(xmin, ymin, xmax, ymax)转换为YOLO归一化坐标(x_center, y_center, width, height)""" dw = 1. / size[0] # 1/width dh = 1. / size[1] # 1/height x = (box[0] + box[2]) / 2.0 # x_center (absolute) y = (box[1] + box[3]) / 2.0 # y_center (absolute) w = box[2] - box[0] # width (absolute) h = box[3] - box[1] # height (absolute) x = x * dw # x_center (normalized) y = y * dh # y_center (normalized) w = w * dw # width (normalized) h = h * dh # height (normalized) # 确保坐标在[0,1]范围内 x = max(0.0, min(1.0, x)) y = max(0.0, min(1.0, y)) w = max(0.0, min(1.0, w)) h = max(0.0, min(1.0, h)) return (x, y, w, h) def convert_voc_to_yolo_single(xml_path, class_to_id, output_label_dir): """转换单个XML文件为YOLO格式的txt文件""" annotation = parse_voc_annotation(xml_path) if not annotation['objects']: return # 跳过无目标图片 # 准备写入内容 txt_lines = [] for obj in annotation['objects']: class_id = class_to_id[obj['class_name']] yolo_bbox = convert_bbox_to_yolo( (annotation['width'], annotation['height']), obj['bbox'] ) line = f"{class_id} {yolo_bbox[0]:.6f} {yolo_bbox[1]:.6f} {yolo_bbox[2]:.6f} {yolo_bbox[3]:.6f}" txt_lines.append(line) # 写入txt文件,文件名与图片名相同 txt_filename = Path(xml_path).stem + '.txt' txt_path = Path(output_label_dir) / txt_filename with open(txt_path, 'w') as f: f.write('\n'.join(txt_lines))

3.4 步骤四:科学划分训练集与验证集

这是保证评估有效性的关键。我们采用分层随机划分,并确保划分结果可复现。

def split_dataset(image_dir, label_dir, val_ratio=0.2, seed=42): """ 划分数据集,创建对应的图片和标签符号链接或文件列表。 参数: image_dir: 原始图片目录 label_dir: 转换后的YOLO标签目录 val_ratio: 验证集比例 seed: 随机种子,确保每次划分结果一致 """ random.seed(seed) # 获取所有图片文件名(不含后缀) all_image_stems = [p.stem for p in Path(image_dir).glob('*.jpg')] # 确保对应的标签文件存在 all_image_stems = [stem for stem in all_image_stems if (Path(label_dir) / f"{stem}.txt").exists()] if not all_image_stems: raise ValueError("No valid image-label pairs found!") # 随机打乱 random.shuffle(all_image_stems) # 计算划分点 split_idx = int(len(all_image_stems) * (1 - val_ratio)) train_stems = all_image_stems[:split_idx] val_stems = all_image_stems[split_idx:] print(f"Total samples: {len(all_image_stems)}") print(f"Training set: {len(train_stems)}") print(f"Validation set: {len(val_stems)}") # 创建YOLO标准目录结构 datasets_dir = Path('datasets') datasets_dir.mkdir(exist_ok=True) for split, stems in [('train', train_stems), ('val', val_stems)]: split_dir = datasets_dir / split images_split_dir = split_dir / 'images' labels_split_dir = split_dir / 'labels' images_split_dir.mkdir(parents=True, exist_ok=True) labels_split_dir.mkdir(parents=True, exist_ok=True) # 方式1:创建符号链接(节省空间,推荐在Linux/macOS下使用) # for stem in stems: # src_image = Path(image_dir) / f"{stem}.jpg" # dst_image = images_split_dir / f"{stem}.jpg" # dst_image.symlink_to(src_image.resolve()) # # src_label = Path(label_dir) / f"{stem}.txt" # dst_label = labels_split_dir / f"{stem}.txt" # dst_label.symlink_to(src_label.resolve()) # 方式2:创建文件列表(txt文件,每行是图片的绝对或相对路径) list_file = datasets_dir / f"{split}.txt" with open(list_file, 'w') as f: for stem in stems: # 这里写入相对路径,相对于项目根目录 img_path = f"datasets/{split}/images/{stem}.jpg" f.write(img_path + '\n') print(f"Created {split} list: {list_file}") return train_stems, val_stems

3.5 步骤五:生成YOLO配置文件(data.yaml)

YOLO系列模型通过一个YAML文件来定位数据和定义类别。

def create_data_yaml(datasets_base_path, class_list, train_txt='train.txt', val_txt='val.txt'): """ 生成YOLO训练所需的data.yaml配置文件。 """ yaml_content = f""" # 数据集路径(相对于此yaml文件,或绝对路径) path: {datasets_base_path} # 数据集根目录 train: {train_txt} # 训练集列表文件路径 val: {val_txt} # 验证集列表文件路径 # 类别数量 nc: {len(class_list)} # 类别名称列表 (必须与class_names.txt顺序一致!) names: {class_list} """ with open('data.yaml', 'w') as f: f.write(yaml_content.strip()) print("Created data.yaml configuration file.")

3.6 主流程整合

将以上所有步骤串联起来,形成一个完整的脚本。

def main(): # 1. 定义路径 voc_images_dir = 'MyDataset/JPEGImages' voc_annotations_dir = 'MyDataset/Annotations' yolo_labels_dir = 'labels' # 临时存放转换后的YOLO标签 Path(yolo_labels_dir).mkdir(exist_ok=True) # 2. 构建类别字典 class_to_id, id_to_class = build_class_index(voc_annotations_dir) class_list = list(class_to_id.keys()) # 3. 批量转换所有VOC标注到YOLO格式 print("Converting VOC annotations to YOLO format...") xml_files = list(Path(voc_annotations_dir).glob('*.xml')) for i, xml_file in enumerate(xml_files): convert_voc_to_yolo_single(xml_file, class_to_id, yolo_labels_dir) if (i+1) % 100 == 0: print(f"Processed {i+1}/{len(xml_files)} files.") print("Conversion completed.") # 4. 划分数据集 (8:2) train_list, val_list = split_dataset(voc_images_dir, yolo_labels_dir, val_ratio=0.2) # 5. 生成data.yaml create_data_yaml('./datasets', class_list) print("\nAll done! Dataset is ready for YOLO training.") print(f"Class names saved to: class_names.txt") print(f"Dataset structure prepared under: datasets/") print(f"Training config file: data.yaml") if __name__ == '__main__': main()

运行这个脚本后,你将得到一个标准的YOLO数据集结构:

project/ ├── class_names.txt ├── data.yaml ├── datasets/ │ ├── train/ │ │ ├── images/ # (符号链接或实际图片) │ │ └── labels/ # (符号链接或实际标签) │ ├── val/ │ │ ├── images/ │ │ └── labels/ │ ├── train.txt # 训练集图片路径列表 │ └── val.txt # 验证集图片路径列表 └── labels/ # 所有转换后的YOLO格式标签

现在,你可以直接使用data.yaml文件,用YOLOv5/v8/v11等框架启动训练了。

4. 进阶问题与深度优化策略

完成了基础流程,我们来看看在实际项目中会遇到哪些更复杂的情况,以及如何优化。

4.1 处理复杂和特殊的标注情况

  1. 目标遮挡与截断:VOC数据集中,difficulttruncated标签如何处理?对于difficult=1的目标,通常在训练时忽略,在评估时不计入。在转换时,可以将其单独记录或赋予一个特殊的标志,在生成YOLO标签时选择性地跳过。对于小目标或截断目标,除非完全不可辨认,否则建议保留,这对模型的鲁棒性是个考验。

  2. 分割标注(Segmentation):VOC格式可能包含<segmented><polygon>信息。如果我们的任务只是目标检测(矩形框),可以忽略这些。但如果未来想做实例分割,这些多边形数据就极为宝贵。一个良好的实践是,在解析XML时,将多边形坐标也提取并保存下来,即使当前不用,也为后续任务留好数据接口。

  3. 空标签图片:有些图片经过标注后可能没有目标。在目标检测中,这些“负样本”图片同样重要,它们告诉模型“这里什么都没有”。YOLO格式中,空标签图片对应的.txt文件应该是0字节的空文件。在划分数据集时,这些图片也需要被考虑进去,并按比例分配到训练集和验证集中。

4.2 数据划分的高级策略

简单的随机划分在大多数情况下可行,但在特定场景下需要更精细的策略:

  1. 按类别分层划分:对于类别极度不均衡的数据集(比如“猫”有1000张,“老虎”只有10张),随机划分可能导致验证集中根本没有“老虎”。这时需要使用分层抽样。我们可以用scikit-learn库的StratifiedShuffleSplit,但目标检测的“样本”是图片,而一张图片可能有多个类别。一个实用的近似方法是:根据图片中是否存在稀有类别来作为分层依据。先给每张图片打上它包含的所有类别的标签,然后以这些标签组合进行分层。

  2. 按场景或序列划分:对于来自监控视频或自动驾驶序列的数据,相邻帧高度相似。必须按场景ID或视频片段进行划分,确保同一个视频的所有帧要么全在训练集,要么全在验证集。否则,模型会在验证集上看到与训练集几乎一样的画面,导致性能评估严重失真(数据泄露)。这需要元数据(如文件名包含序列号)的支持。

  3. K折交叉验证:当数据量特别少时,为了更可靠地评估模型,可以采用K折交叉验证。将数据分成K份,轮流将其中一份作为验证集,其余作为训练集,训练K个模型,最后取平均性能。这能更充分地利用数据并减少划分的随机性带来的评估波动。生成K折划分的文件列表是这一步的关键产出。

4.3 格式转换的扩展:支持COCO等其他格式

掌握了VOC到YOLO的转换,其他格式的转换原理相通。例如,COCO到YOLO的转换,核心是解析那个大的instances_train2017.json文件。

import json def convert_coco_to_yolo(coco_json_path, output_label_dir, class_filter=None): """ 将COCO格式的JSON标注转换为YOLO格式的单个txt文件。 class_filter: 可选,一个类别名称列表,只转换这些类别。 """ with open(coco_json_path, 'r') as f: data = json.load(f) # 构建类别ID到连续ID的映射(COCO的category_id可能不连续) categories = {cat['id']: cat['name'] for cat in data['categories']} if class_filter: # 只保留感兴趣的类别 filtered_cat_ids = [cid for cid, name in categories.items() if name in class_filter] cat_id_to_yolo_id = {cid: idx for idx, cid in enumerate(filtered_cat_ids)} else: cat_id_to_yolo_id = {cid: idx for idx, cid in enumerate(categories.keys())} # 构建图片ID到图片信息的映射 images = {img['id']: img for img in data['images']} # 按图片ID分组标注 from collections import defaultdict anns_by_image = defaultdict(list) for ann in data['annotations']: image_id = ann['image_id'] anns_by_image[image_id].append(ann) # 为每张图片生成YOLO标签文件 for img_id, img_info in images.items(): img_width = img_info['width'] img_height = img_info['height'] file_name = img_info['file_name'].replace('.jpg', '.txt') label_path = Path(output_label_dir) / file_name annotations = anns_by_image.get(img_id, []) yolo_lines = [] for ann in annotations: cat_id = ann['category_id'] if class_filter and cat_id not in filtered_cat_ids: continue # COCO的bbox格式是 [x_top_left, y_top_left, width, height] bbox = ann['bbox'] x_tl, y_tl, w, h = bbox # 转换为YOLO格式的中心点归一化坐标 x_center = (x_tl + w / 2) / img_width y_center = (y_tl + h / 2) / img_height w_norm = w / img_width h_norm = h / img_height yolo_id = cat_id_to_yolo_id[cat_id] line = f"{yolo_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}" yolo_lines.append(line) # 写入文件,如果没有目标,写入空文件 with open(label_path, 'w') as f: f.write('\n'.join(yolo_lines))

这个脚本展示了处理另一种主流格式的思路:理解其数据结构,提取关键信息,进行坐标转换和映射,最后按需输出。

4.4 自动化与工程化实践

当处理多个项目或频繁迭代数据集时,手动运行脚本效率低下。我们需要将其工程化:

  1. 配置文件驱动:将数据集路径、类别过滤列表、划分比例、随机种子等参数写入一个config.yaml文件。主脚本读取配置,实现一键处理。
  2. 日志与错误处理:增加详细的日志记录,记录转换了多少文件,跳过了多少无效标注,遇到了哪些异常(如XML解析错误、图片不存在等),并保存到日志文件中,便于排查。
  3. 数据校验与可视化:转换完成后,自动抽样检查。例如,随机选择几张图片,用OpenCV读取,并根据生成的YOLO标签将边界框画回图片上,保存为预览图。这是验证转换是否正确最直观的方法。可以对比原始VOC标注可视化结果和转换后的YOLO标注可视化结果。
  4. 集成到数据管道:使用MakefileDVC(Data Version Control)来管理数据预处理流程。定义好数据依赖关系,当原始数据更新时,可以自动触发格式转换和划分流程,确保数据版本与模型版本的一致性。

5. 避坑指南与常见问题排查

在实际操作中,你几乎一定会遇到下面这些问题。这里是我踩过坑后的经验总结。

5.1 转换过程中的典型错误

  1. 坐标越界或归一化值大于1

    • 现象:训练时YOLO报错“label out of bounds”。
    • 原因:原始标注框的坐标可能超出了图片范围(标注错误),或者在转换计算时,由于浮点数精度问题导致结果略大于1.0。
    • 解决:在convert_bbox_to_yolo函数中加入边界裁剪:x = max(0.0, min(1.0, x))。同时,在解析VOC XML时,就应检查xmax, ymax是否小于等于图片宽高。
  2. 类别ID不匹配

    • 现象:模型训练似乎正常,但预测时类别全乱,或者评估时mAP极低。
    • 原因class_names.txtdata.yaml中的names列表、以及训练时模型输出的类别顺序三者不一致。这是最常见、最隐蔽的坑。
    • 解决:确立单一事实来源。通常以class_names.txt为准。在生成data.yaml时,直接从class_names.txt读取列表。在模型推理时,加载的类别标签文件也必须用同一个。
  3. 空标签文件处理不当

    • 现象:训练时警告“No labels found in xxx.txt”,或者某些图片被跳过。
    • 原因:对于没有目标的图片,YOLO期望一个空的标签文件(0字节)。如果你的脚本在遇到空目标时没有生成对应的.txt文件,或者生成了一个有内容的文件(如注释),就会出错。
    • 解决:确保转换脚本为无目标的图片生成一个空的.txt文件(直接创建并关闭即可)。

5.2 数据集划分的陷阱

  1. 验证集效果远好于/远差于训练集

    • 可能原因1(效果太好):数据泄露。训练集和验证集的数据分布差异太大,或者存在重复、高度相似的样本。检查划分逻辑,确保按场景或序列划分。
    • 可能原因2(效果太差):验证集包含大量训练集中未出现过的类别、背景或场景。检查类别分布,确保分层抽样。也可能是验证集中包含大量特别困难(小、模糊、遮挡)的样本,而训练集中没有。
    • 诊断方法:分别统计训练集和验证集的类别分布直方图目标尺寸分布图每张图片目标数量分布。如果发现明显差异,就需要重新划分。
  2. 划分结果不可复现

    • 现象:每次运行划分脚本,得到不同的训练/验证集,导致实验结果无法对比。
    • 原因:没有设置随机种子(random.seed)。
    • 解决:在调用任何随机函数(如random.shuffle,train_test_split)前,固定一个随机种子。这是可重复实验的基础。

5.3 训练前的最终检查清单

在启动那个漫长的训练命令前,花10分钟做以下检查,能避免未来数天甚至数周的徒劳:

  • [ ]文件完整性:检查datasets/train/imagesdatasets/train/labels下的文件数量是否匹配(每个jpg是否都有对应的txt?)。
  • [ ]标签格式验证:随机打开几个YOLO格式的.txt文件,检查格式是否为<id> <x> <y> <w> <h>,数值是否在[0,1]之间。
  • [ ]可视化验证:写一个简单的可视化脚本,随机加载几张图片和对应的YOLO标签,将框画上去。肉眼确认框的位置、大小、类别是否正确。
    import cv2 import random def visualize_yolo_label(img_path, label_path, class_names): img = cv2.imread(img_path) h, w, _ = img.shape with open(label_path, 'r') as f: lines = f.readlines() for line in lines: cls_id, xc, yc, bw, bh = map(float, line.strip().split()) # 转换回像素坐标用于绘图 x1 = int((xc - bw/2) * w) y1 = int((yc - bh/2) * h) x2 = int((xc + bw/2) * w) y2 = int((yc + bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0,255,0), 2) cv2.putText(img, class_names[int(cls_id)], (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1) cv2.imshow('Check', img) cv2.waitKey(0) cv2.destroyAllWindows()
  • [ ]data.yaml检查:打开data.yaml,确认path路径是否正确(相对路径还是绝对路径?),trainval指向的列表文件是否存在,nc(类别数)是否正确,names列表是否与class_names.txt完全一致。
  • [ ]数据集统计:运行一个统计脚本,输出训练集和验证集的图片数量、总目标数、每个类别的目标数、平均目标尺寸等信息。对数据有一个量化的认识。

数据处理是深度学习项目中最“脏”最累的活,但也是决定项目成败的基石。把格式转化和数据集划分这套流程打磨成稳定、可复用的工具,以后无论拿到什么格式的数据,你都能快速将其驯服,为模型训练准备好高质量的“粮草”。这套方法论的价值,会随着你接触的项目越多,体会越深。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/5 12:13:47

企业安卓设备安全怎么管?

目录 1. 核心硬件与接口管控&#xff0c;封堵物理泄密通道 2. 网络与通信全维度限制&#xff0c;斩断数据传输风险 3. 应用与系统行为精细化管理&#xff0c;规范设备使用 4. 系统增强与安全审计&#xff0c;提升整体防护能力 总结&#xff1a; 在移动办公时代&#xff0c…

作者头像 李华
网站建设 2026/8/5 12:13:28

3个关键步骤获取RDP Wrapper历史版本:新手也能快速上手的完整指南

3个关键步骤获取RDP Wrapper历史版本&#xff1a;新手也能快速上手的完整指南 【免费下载链接】rdpwrap RDP Wrapper Library 项目地址: https://gitcode.com/gh_mirrors/rd/rdpwrap 你是否曾经因为Windows远程桌面连接限制而感到困扰&#xff1f;想要免费解锁多用户同时…

作者头像 李华
网站建设 2026/8/5 12:12:58

网易云音乐无损下载神器:3分钟打造永久音乐收藏库

网易云音乐无损下载神器&#xff1a;3分钟打造永久音乐收藏库 【免费下载链接】netease-cloud-music-dl Netease cloud music song downloader, with full ID3 metadata, eg: front cover image, artist name, album name, song title and so on. 项目地址: https://gitcode.…

作者头像 李华
网站建设 2026/8/5 12:12:29

微信聊天记录备份终极指南:三步实现数据安全迁移

微信聊天记录备份终极指南&#xff1a;三步实现数据安全迁移 【免费下载链接】PyWxDump 删库 项目地址: https://gitcode.com/GitHub_Trending/py/PyWxDump 还在为微信聊天记录无法完整备份而烦恼吗&#xff1f;PyWxDump作为一款专业的微信数据管理工具&#xff0c;通过…

作者头像 李华
网站建设 2026/8/5 12:11:24

从23.8万秒伤到错失限时:坦克DPS的价值陷阱与决策框架

上周在测试服打红玉圣殿&#xff0c;我开着死亡骑士坦&#xff0c;全程打完伤害统计跳出来23.8万秒伤。数据看着挺漂亮&#xff0c;但最后结算时&#xff0c;因为一个“出生坦克不算进度”的机制问题&#xff0c;硬生生把2限时给卡没了。这事儿让我琢磨了好一阵子&#xff0c;表…

作者头像 李华
网站建设 2026/8/5 12:11:19

嵌入式软硬件系统知识分享系列--前言

# 关于“嵌入式系统从0到1”系列文章&#xff1a;我为什么想写一个完整的知识体系&#xff1f;## 写在开头从事嵌入式开发这些年&#xff0c;我踩过无数坑&#xff0c;也翻过无数遍 datasheet。回头看自己走过的路&#xff0c;最大的感触不是技术有多难&#xff0c;而是学习资料…

作者头像 李华