Converting VOC Annotations to YOLOv5 Format: A Step-by-Step Guide

Overview

This guide explains how to transform dataset annotations from the Pascal VOC XML format into the YOLOv5-compatible text format. The process involves renaming files, generating a file list, converting bounding boxes, and adjusting YOLOv5 configuration files for custom classes.

Step 1: Rename Image and Annotation Files Sequentially

To avoid naming conflicts, rename all images and their corresponding XML annotation files with sequential numbers.

import os

items = os.listdir('all_images')
count = 0

for item in items:
    old_img_path = os.path.join('all_images', item)
    new_img_path = os.path.join('all_images', f'{count}.jpg')
    os.rename(old_img_path, new_img_path)

    old_xml_path = os.path.join('all_annotations', item[:-3] + 'xml')
    new_xml_path = os.path.join('all_annotations', f'{count}.xml')
    os.rename(old_xml_path, new_xml_path)

    count += 1

Step 2: Create a Training File List

Write the base names (without extension) of all images into a text file for later reference.

import os

items = os.listdir('all_images')
with open('train_list.txt', 'w') as f:
    for item in items:
        f.write(item[:-4] + '\n')

Step 3: Convert XML Annotations to YOLO Format

Parse each XML file, extract bounding boxes, normalize them relative to image dimensions, and write them in YOLO format (class_id x_center y_center width height).

import xml.etree.ElementTree as ET

# Define your custom class list
class_names = ['brand', 'cucumber', 'eggplant', 'tomatoes']

def normalize_bbox(size, box):
    """Convert absolute bbox coordinates to relative YOLO format."""
    dw = 1.0 / size[0]
    dh = 1.0 / size[1]
    x_center = (box[0] + box[1]) / 2.0
    y_center = (box[2] + box[3]) / 2.0
    width = box[1] - box[0]
    height = box[3] - box[2]
    return (x_center * dw, y_center * dh, width * dw, height * dh)

def convert_xml_to_yolo(image_id):
    in_path = f'all_annotations/{image_id}.xml'
    out_path = f'labels/{image_id}.txt'
    
    tree = ET.parse(in_path)
    root = tree.getroot()
    size_elem = root.find('size')
    img_width = int(size_elem.find('width').text)
    img_height = int(size_elem.find('height').text)

    with open(out_path, 'w') as out_file:
        for obj in root.iter('object'):
            cls = obj.find('name').text
            if cls not in class_names:
                continue
            cls_id = class_names.index(cls)
            bbox_elem = obj.find('bndbox')
            bbox = (
                float(bbox_elem.find('xmin').text),
                float(bbox_elem.find('xmax').text),
                float(bbox_elem.find('ymin').text),
                float(bbox_elem.find('ymax').text)
            )
            normalized = normalize_bbox((img_width, img_height), bbox)
            out_file.write(f"{cls_id} {normalized[0]:.6f} {normalized[1]:.6f} {normalized[2]:.6f} {normalized[3]:.6f}\n")

# Read image list and process each
with open('train_list.txt', 'r') as f:
    image_ids = f.read().strip().split()
    for img_id in image_ids:
        convert_xml_to_yolo(img_id)

Step 4: Configuer YOLOv5 for Custom Dataset

Adjust three key configuration files in the YOLOv5 repository.

4.1. Dataset Configuration (coco.yaml)

Set paths to your training and validation image folders, define the number of classes (nc), and list class names.

train: ./dataset/coco/images/train
val: ./dataset/coco/images/val
nc: 2
names: ['no_mask', 'mask']

4.2. Model Architecture (yolov5s.yaml)

Update the nc field to match your custom class count. For example, for 2 classes:

nc: 2
depth_multiple: 0.33
width_multiple: 0.50
anchors:
  - [10,13, 16,30, 33,23]
  - [30,61, 62,45, 59,119]
  - [116,90, 156,198, 373,326]
...

4.3. Training Script Arguments (train.py)

Modify the default arguments in parse_opt() to point to your custom files:

parser.add_argument('--weights', type=str, default='yolov5s.pt')
parser.add_argument('--cfg', type=str, default='yolov5s.yaml')
parser.add_argument('--data', type=str, default='coco.yaml')
parser.add_argument('--epochs', type=int, default=300)
parser.add_argument('--batch-size', type=int, default=64)
parser.add_argument('--imgsz', '--img', '--img-size', type=int, default=640)

After these conversions and configuration adjustments, you can start training YOLOv5 with your custom dataset by running:

python train.py --data coco.yaml --cfg yolov5s.yaml --weights ''

Tags: YOLOv5 VOC annotation conversion python XML

Posted on Tue, 13 Oct 2026 16:20:12 +0000 by Redlightpacket