Annotation

The Story Behind Annot8.xyz: A Web-Based Image Annotation Tool

Wandering the quiet streets of the Asakusa district of Tokyo Photo by Greg Rorem on Unsplash

The Beginning

So, why the heck did I make an annotation tool? Well, here's the story. Before all these AI tools came around, I was given a hefty task for one of our products - create an annotation tool that we could use to annotate images and train AI models, all from the web.

The thing is, I didn't know anything about annotations, COCO formats, or PASCAL VOC at the time. We were using an open-source tool called LabelImg (Ubuntu-based, I think) which had all these functionalities that seemed quite difficult to mimic.

The Challenge

My biggest fear was pretty straightforward - I had to create a tool that could:

  • Dynamically load images
  • Let users create labels
  • Allow drawing annotations
  • Dynamically load annotations on images
  • Let users drag, resize, or delete annotations

It all seemed a bit overwhelming. I thought there must be something available on the internet to do all these things.

The Search

Unfortunately, I couldn't find anything very robust for our use case. But I did stumble upon some interesting tools - Annotorious and OpenSeadragon Annotorious. They had the core functionality for drawing annotations and handling labels, but there was a catch. Annotating hundreds or thousands of images would be pretty tedious the way they did it.

So, I had to integrate and modify their approach to get closer to what we wanted. I managed to make something with similar functionality, but it wasn't anywhere near as comfortable to use as LabelImg.

Fast Forward to Annot8

Here's where things get interesting. I never found a very comprehensive annotation tool that simply draws rectangle annotations and exports them in COCO or PASCAL format. So in my free time, with GPTs and Claude and all these AI tools, I let them figure out the logic for drawing annotations while I focused on the rest.

Let's look at some interesting parts of Annot8's implementation:

interface Annotation {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  label: Label;
}

interface Label {
  id: string;
  name: string;
  color: string;
}

The drawing system uses SVG for precise annotations and implements a smart coordinate system:

const getSVGCoordinates = (e: React.MouseEvent): { x: number; y: number } | null => {
  const svg = svgRef.current;
  if (!svg) return null;

  const CTM = svg.getScreenCTM();
  if (!CTM) return null;

  const point = new DOMPoint(e.clientX, e.clientY);
  const transformed = point.matrixTransform(CTM.inverse());

  return {
    x: transformed.x,
    y: transformed.y
  };
};

One interesting aspect is how annotations are stored locally:

const storageService = {
  async storeImage(image: StoredImage): Promise<void> {
    const db = await openDB();
    await db.put('images', image);
  },
  
  async getAllImages(): Promise<StoredImage[]> {
    const db = await openDB();
    return db.getAll('images');
  },
  
  async removeImage(id: string): Promise<void> {
    const db = await openDB();
    await db.delete('images', id);
    await annotationStorage.removeImageAnnotations(id);
  }
};

The export system supports both PASCAL VOC and COCO formats:

const generatePascalVOCAnnotation = (
  imageId: string, 
  imageName: string, 
  imageWidth: number, 
  imageHeight: number,
  imageAnnotations: Annotation[]
) => {
  return `<?xml version="1.0" encoding="UTF-8"?>
    <annotation>
      <filename>${imageName}</filename>
      <size>
        <width>${imageWidth}</width>
        <height>${imageHeight}</height>
        <depth>3</depth>
      </size>
      ${imageAnnotations.map(ann => `
        <object>
          <name>${ann.label.name}</name>
          <bndbox>
            <xmin>${Math.round(ann.x)}</xmin>
            <ymin>${Math.round(ann.y)}</ymin>
            <xmax>${Math.round(ann.x + ann.width)}</xmax>
            <ymax>${Math.round(ann.y + ann.height)}</ymax>
          </bndbox>
        </object>
      `).join('')}
    </annotation>`;
};

Current Features

Annot8 is built to be simple yet effective:

  • Completely browser-based - no installations needed
  • Local storage for all your data
  • Simple label management system
  • Easy annotation drawing with Shift + Drag
  • Export to COCO and PASCAL VOC formats
  • Keyboard shortcuts for faster workflow

Work in Progress

There are still a few things that need work:

  • Updating label names and colors after creation
  • Safari support (it's being a bit tricky)
  • PASCAL format is not accurate
  • YOLO format support
  • Additional keyboard shortcuts and controls

Looking Forward

I'm pretty happy with how Annot8 turned out. It might not have all the features of LabelImg, but it gets the job done right in your browser. Plus, since everything's stored locally, your data stays private and secure.

If you find any bugs or have ideas for improvements, please let me know. I built this tool because of course I was not happy the way I did it for the first time, and I hope it helps others who might need something similar.

Happy Annotating!

Gracias