Skip to main content

Format I/O

Panlabel provides I/O modules for reading and writing various annotation formats. All formats convert through the Intermediate Representation, ensuring consistent behavior.

Supported Formats

Each format has a dedicated module in panlabel::ir with read_* and write_* functions:
FormatModuleRead FunctionWrite Function
IR JSONio_jsonread_ir_jsonwrite_ir_json
COCOio_coco_jsonread_coco_jsonwrite_coco_json
YOLOio_yoloread_yolo_dirwrite_yolo_dir
Pascal VOCio_voc_xmlread_voc_dirwrite_voc_dir
CVATio_cvat_xmlread_cvat_xmlwrite_cvat_xml
Label Studioio_label_studio_jsonread_label_studio_jsonwrite_label_studio_json
TensorFlow Object Detectionio_tfod_csvread_tfod_csvwrite_tfod_csv

Common Patterns

All I/O functions follow consistent patterns:

Reading

pub fn read_FORMAT(path: &Path) -> Result<Dataset, PanlabelError>

Writing

pub fn write_FORMAT(path: &Path, dataset: &Dataset) -> Result<(), PanlabelError>

IR JSON

Panlabel’s native JSON format - lossless representation of the IR.
use panlabel::ir::io_json;
use std::path::Path;

// Read IR JSON
let dataset = io_json::read_ir_json(Path::new("dataset.json"))?;

// Write IR JSON
io_json::write_ir_json(Path::new("output.json"), &dataset)?;

// String utilities for testing
let json_str = io_json::to_json_string(&dataset)?;
let dataset = io_json::from_json_str(&json_str)?;
Format characteristics:
  • Lossless (preserves all IR information)
  • Human-readable JSON
  • Single file
  • Direct serialization of IR types

COCO JSON

Microsoft COCO object detection format.
use panlabel::ir::io_coco_json;
use std::path::Path;

// Read COCO format
let dataset = io_coco_json::read_coco_json(
    Path::new("annotations/instances_train2017.json")
)?;

// Write COCO format
io_coco_json::write_coco_json(
    Path::new("output/annotations.json"),
    &dataset
)?;
Format characteristics:
  • Single JSON file
  • Bounding boxes in [x, y, width, height] format
  • Preserves most metadata and attributes
  • Standard format for COCO dataset
Coordinate conversion:
  • COCO uses [x, y, width, height] where (x, y) is top-left
  • Panlabel IR uses (xmin, ymin, xmax, ymax)
  • Conversion: xmax = x + width, ymax = y + height

YOLO

Ultralytics YOLO directory-based format.
use panlabel::ir::io_yolo;
use std::path::Path;

// Read YOLO dataset
let dataset = io_yolo::read_yolo_dir(Path::new("yolo_dataset/"))?;

// Write YOLO dataset
io_yolo::write_yolo_dir(Path::new("output/"), &dataset)?;
Format characteristics:
  • Directory-based: images/ and labels/ subdirectories
  • Class mapping from data.yaml or classes.txt
  • Normalized coordinates: <class_idx> <cx> <cy> <w> <h>
  • One .txt file per image (empty files for images without annotations)
Directory structure:
yolo_dataset/
├── data.yaml           # Class names and dataset info
├── images/
│   ├── img1.jpg
│   └── img2.jpg
└── labels/
    ├── img1.txt        # One line per annotation
    └── img2.txt
Label file format:
0 0.516667 0.500000 0.200000 0.333333
1 0.716667 0.650000 0.150000 0.200000
Coordinate conversion:
  • YOLO uses normalized center-based: (cx, cy, w, h) in [0, 1]
  • Panlabel IR uses pixel XYXY: (xmin, ymin, xmax, ymax)
  • Conversion:
    • To YOLO: normalize by image dimensions, convert to center format
    • From YOLO: denormalize by image dimensions, convert to XYXY

Pascal VOC XML

Pascal VOC directory-based XML format.
use panlabel::ir::io_voc_xml;
use std::path::Path;

// Read VOC dataset
let dataset = io_voc_xml::read_voc_dir(Path::new("VOCdevkit/VOC2012/"))?;

// Write VOC dataset
io_voc_xml::write_voc_dir(Path::new("output/"), &dataset)?;
Format characteristics:
  • Directory-based: Annotations/ (XML) and JPEGImages/ (images)
  • One XML file per image
  • Preserves attributes: pose, truncated, difficult, occluded
  • Pixel coordinates in <bndbox> elements
Directory structure:
VOC2012/
├── Annotations/
│   ├── image1.xml
│   └── image2.xml
└── JPEGImages/
    ├── image1.jpg
    └── image2.jpg
XML format example:
<annotation>
  <filename>image1.jpg</filename>
  <size>
    <width>640</width>
    <height>480</height>
  </size>
  <object>
    <name>person</name>
    <bndbox>
      <xmin>100</xmin>
      <ymin>150</ymin>
      <xmax>300</xmax>
      <ymax>450</ymax>
    </bndbox>
  </object>
</annotation>

CVAT XML

CVAT for Images XML export format.
use panlabel::ir::io_cvat_xml;
use std::path::Path;

// Read CVAT export
let dataset = io_cvat_xml::read_cvat_xml(
    Path::new("cvat_export/annotations.xml")
)?;

// Write CVAT format
io_cvat_xml::write_cvat_xml(
    Path::new("output/annotations.xml"),
    &dataset
)?;
Format characteristics:
  • Single XML file or directory with annotations.xml
  • Preserves attributes: occluded, z_order, custom attributes
  • Pixel coordinates in xtl, ytl, xbr, ybr attributes
XML format example:
<annotations>
  <meta>
    <task>
      <name>My Task</name>
      <labels>
        <label>
          <name>person</name>
        </label>
      </labels>
    </task>
  </meta>
  <image name="img1.jpg" width="640" height="480">
    <box label="person" xtl="10" ytl="20" xbr="100" ybr="200" occluded="0">
      <attribute name="pose">standing</attribute>
    </box>
  </image>
</annotations>

Label Studio JSON

Label Studio task export format.
use panlabel::ir::io_label_studio_json;
use std::path::Path;

// Read Label Studio export
let dataset = io_label_studio_json::read_label_studio_json(
    Path::new("project-export.json")
)?;

// Write Label Studio format
io_label_studio_json::write_label_studio_json(
    Path::new("output.json"),
    &dataset
)?;
Format characteristics:
  • JSON array of tasks
  • Percentage-based coordinates (0-100)
  • Preserves rotation with ls_rotation_deg attribute
  • Image references stored in data.image
JSON format example:
[
  {
    "data": {
      "image": "/data/upload/img1.jpg"
    },
    "annotations": [
      {
        "result": [
          {
            "value": {
              "x": 10.5,
              "y": 15.2,
              "width": 25.0,
              "height": 30.0,
              "rectanglelabels": ["person"]
            },
            "from_name": "label",
            "to_name": "image",
            "type": "rectanglelabels"
          }
        ]
      }
    ]
  }
]

TensorFlow Object Detection CSV

TensorFlow Object Detection CSV format.
use panlabel::ir::io_tfod_csv;
use std::path::Path;

// Read TFOD CSV
let dataset = io_tfod_csv::read_tfod_csv(Path::new("annotations.csv"))?;

// Write TFOD CSV
io_tfod_csv::write_tfod_csv(Path::new("output.csv"), &dataset)?;
Format characteristics:
  • Single CSV file
  • Pixel coordinates: xmin, ymin, xmax, ymax
  • One row per annotation
  • Images without annotations are not represented
CSV format:
filename,width,height,class,xmin,ymin,xmax,ymax
img1.jpg,640,480,person,100,150,300,450
img1.jpg,640,480,car,400,200,550,350
img2.jpg,1920,1080,dog,800,600,1200,900

Error Handling

All I/O functions return Result<T, PanlabelError> with detailed error context:
use panlabel::PanlabelError;

match io_coco_json::read_coco_json(path) {
    Ok(dataset) => {
        println!("Loaded {} images", dataset.images.len());
    }
    Err(PanlabelError::Io(err)) => {
        eprintln!("I/O error: {}", err);
    }
    Err(PanlabelError::CocoJsonParse { path, source }) => {
        eprintln!("Failed to parse COCO JSON at {:?}: {}", path, source);
    }
    Err(err) => {
        eprintln!("Error: {}", err);
    }
}

Complete Example

Convert between multiple formats:
use panlabel::ir::*;
use panlabel::PanlabelError;
use std::path::Path;

fn convert_formats() -> Result<(), PanlabelError> {
    // Read from COCO
    let dataset = io_coco_json::read_coco_json(
        Path::new("input/coco.json")
    )?;
    
    println!("Loaded COCO dataset:");
    println!("  Images: {}", dataset.images.len());
    println!("  Categories: {}", dataset.categories.len());
    println!("  Annotations: {}", dataset.annotations.len());
    
    // Write to multiple formats
    io_yolo::write_yolo_dir(Path::new("output/yolo/"), &dataset)?;
    io_voc_xml::write_voc_dir(Path::new("output/voc/"), &dataset)?;
    io_json::write_ir_json(Path::new("output/ir.json"), &dataset)?;
    
    println!("Conversion complete!");
    Ok(())
}

Next Steps

Build docs developers (and LLMs) love