Skip to content

Instantly share code, notes, and snippets.

@vdalv
Created July 4, 2018 18:40
Show Gist options
  • Save vdalv/321876a7076caaa771d47216f382cba5 to your computer and use it in GitHub Desktop.
Save vdalv/321876a7076caaa771d47216f382cba5 to your computer and use it in GitHub Desktop.

Convert PascalVOC Annotations to YOLO

This script reads PascalVOC xml files, and converts them to YOLO txt files.

Note: This script was written and tested on Ubuntu. YMMV on other OS's.

Disclaimer: This code is a modified version of Joseph Redmon's voc_label.py

Instructions:

  1. Place the convert_voc_to_yolo.py file into your data folder.
  2. Edit the dirs array (line 8) to contain the folders where your images and xmls are located. Note: this script assumes all of your images are .jpg's (line 13).
  3. Edit the classes array (line 9) to contain all of your classes.
  4. Run the script. Upon running the script, each of the given directories will contain a 'yolo' folder that contains all of the YOLO txt files. A text file containing all of the image paths will be created in the cwd, for each given directory.

convert_voc_to_yolo.py:

import glob
import os
import pickle
import xml.etree.ElementTree as ET
from os import listdir, getcwd
from os.path import join

dirs = ['train', 'val']
classes = ['person', 'car']

def getImagesInDir(dir_path):
    image_list = []
    for filename in glob.glob(dir_path + '/*.jpg'):
        image_list.append(filename)

    return image_list

def convert(size, box):
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[1])/2.0 - 1
    y = (box[2] + box[3])/2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

def convert_annotation(dir_path, output_path, image_path):
    basename = os.path.basename(image_path)
    basename_no_ext = os.path.splitext(basename)[0]

    in_file = open(dir_path + '/' + basename_no_ext + '.xml')
    out_file = open(output_path + basename_no_ext + '.txt', 'w')
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)

    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult)==1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = convert((w,h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

cwd = getcwd()

for dir_path in dirs:
    full_dir_path = cwd + '/' + dir_path
    output_path = full_dir_path +'/yolo/'

    if not os.path.exists(output_path):
        os.makedirs(output_path)

    image_paths = getImagesInDir(full_dir_path)
    list_file = open(full_dir_path + '.txt', 'w')

    for image_path in image_paths:
        list_file.write(image_path + '\n')
        convert_annotation(full_dir_path, output_path, image_path)
    list_file.close()

    print("Finished processing: " + dir_path)
@myounus96
Copy link

myounus96 commented Nov 8, 2019

Thank you for the script.

@myounus96
Copy link

And please add these instructions to readme.
For running the script put images and .xml(annotations) in root of train and val folder.Make sure images and .xml are not in subfolder they should be in root.Like(image below)
image
The 'yolo folder' is generated by the script.And VOCData is my folder name just like train and val in above script I put train and val data in VOCData just for the sake of simplicity.

@ebley
Copy link

ebley commented Jun 25, 2020

Wow - no fuss no muss - worked first crack - I was a little dismayed after I learned I might have to redo all the work. Phew. Works for png's too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment