A fast, keyboard-driven desktop tool for building image annotation datasets. Define your labelling schema in a JSON file, point the tool at a folder of images, and start annotating — bounding boxes, points, hierarchical parent-child relationships, per-annotation flags, and tags all supported out of the box.
Annotations are stored in lightweight JSON sidecar files next to your images, so your dataset folder stays self-contained and version-control friendly. Export to COCO JSON or YOLO TXT whenever you are ready to train.
| Capability | Description |
|---|---|
| Schema-driven labelling | Define annotation types, colors, icons, flags, and tags in a plain JSON file — no code required |
| Multiple schemas | Load several schemas at once and annotate the same image folder with different label sets simultaneously |
| Bounding boxes & points | Draw rectangles or place keypoints; parent annotations unlock child annotations automatically |
| Hierarchical annotations | Child types attach to a selected parent, enabling fine-grained part-level labelling |
| Per-annotation flags & tags | Boolean flags and dropdown tags on individual annotations for quality, occlusion, or custom attributes |
| Image flags | Schema-level per-image metadata (e.g. blur, lighting, quality ratings) |
| Mark Empty | One-click or keyboard shortcut to mark images that contain no valid targets |
| ML Export | Export annotated data to COCO JSON or YOLO TXT format for immediate model training |
| Batch operations | Mark untagged images as no-target or delete all annotations of a type across the entire dataset |
| Statistical dashboard | Per-schema progress, annotation geometry analytics, spatial heatmaps, and class-balance metrics |
| Backup & recovery | Periodic automatic backups with crash-recovery dialog on next launch |
| Schema editor | Create and edit schemas from inside the app — no need to hand-edit JSON |
| Dark mode | System-aware dark/light theme toggle |
annotator/
├── annotator/
│ ├── canvas/
│ │ ├── graphics_scene.py # AnnotationGraphicsScene — image + annotation items
│ │ └── graphics_view.py # AnnotationGraphicsView — zoom, pan, rotation
│ ├── commands/
│ │ └── annotation_commands.py # Undo/redo command objects
│ ├── io/
│ │ ├── backup.py # BackupManager — crash-recovery sidecar backups
│ │ ├── export.py # COCO JSON and YOLO TXT exporters
│ │ ├── migration.py # Sidecar format migration helpers
│ │ └── sidecar.py # Read/write per-image annotation JSON files
│ ├── models/
│ │ ├── annotation.py # ImageAnnotations, AnnotationRecord data classes
│ │ ├── image_list.py # DatasetModel — image list with multi-schema state
│ │ └── schema.py # SchemaConfig, AnnotationTypeDef loaders
│ ├── panels/
│ │ ├── annotation_tree.py # Right dock — annotation list and selection
│ │ ├── image_browser.py # Left dock — image list with progress indicators
│ │ ├── schema_editor.py # Schema creation/editing dialog
│ │ ├── schema_panel.py # Left dock — per-schema tool buttons
│ │ ├── statistics_panel.py # Statistics tab — pyqtgraph charts and metrics
│ │ └── tool_options.py # Right dock — flags, tags, shortcuts reference
│ ├── tools/
│ │ ├── base_tool.py # BaseTool interface
│ │ ├── box_tool.py # Bounding box draw tool
│ │ ├── point_tool.py # Point/keypoint place tool
│ │ └── select_tool.py # Selection, move, resize, nudge tool
│ ├── app.py # QApplication entry point
│ ├── main_window.py # MainWindow — toolbar, menus, dock wiring
│ └── theme.py # Dark/light palette helpers
│
├── icons/
│ ├── app_icon.png # Application window icon
│ └── system/ # Toolbar and UI icons (see ATTRIBUTION.md)
│
├── schemas/ # Example schemas (not tracked in production)
│
├── main.py # CLI entry point
├── run.ps1 # PowerShell launcher (Windows)
├── Makefile # Common dev tasks
├── Dockerfile # Container build
├── docker-compose.yml # Container run configuration
├── requirements.txt # pip dependencies
└── ATTRIBUTION.md # Icon credits
flowchart TD
classDef ui fill:#1f77b4,stroke:#0d3a63,stroke-width:1px,color:white
classDef model fill:#2ca02c,stroke:#145214,stroke-width:1px,color:white
classDef io fill:#ff7f0e,stroke:#a64b00,stroke-width:1px,color:white
classDef tool fill:#9467bd,stroke:#4b2e7f,stroke-width:1px,color:white
subgraph UI["UI Layer"]
MW[MainWindow]:::ui
IB[ImageBrowser]:::ui
SP[SchemaPanel]:::ui
AT[AnnotationTree]:::ui
TO[ToolOptions<br/>Flags · Tags]:::ui
STAT[StatisticsPanel]:::ui
end
subgraph Canvas["Canvas"]
GV[GraphicsView<br/>Zoom · Pan · Rotate]:::ui
GS[GraphicsScene<br/>Image + Annotation Items]:::ui
end
subgraph Tools["Tools"]
SEL[SelectTool]:::tool
BOX[BoxTool]:::tool
PT[PointTool]:::tool
end
subgraph Models["Models"]
DM[DatasetModel<br/>Image list · Schema state]:::model
SC[SchemaConfig<br/>Types · Flags · Tags]:::model
IA[ImageAnnotations<br/>Records · Undo stack]:::model
end
subgraph IO["I/O"]
SIDE[Sidecar JSON<br/>per-image files]:::io
BK[BackupManager]:::io
EXP[Exporter<br/>COCO · YOLO]:::io
end
MW --> IB & SP & AT & TO & STAT
MW --> GV --> GS
MW --> SEL & BOX & PT
MW --> DM --> SC & IA
IA <--> SIDE
IA --> BK
DM --> EXP
Screenshots below use the Plastic Bottles Dataset — a publicly available collection of plastic bottle images, used here solely for illustration purposes.
- Python 3.10+
- One of: pip + venv, Docker, or PowerShell (Windows)
python -m venv .venv
# Linux / macOS
source .venv/bin/activate
# Windows (PowerShell)
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txtmake setup # Creates .venv and installs dependencies
make run # Launches the application.\run.ps1The script auto-discovers all *.json files in the schemas/ folder and launches the annotator with them.
docker compose upNote: The Docker container requires an X11 display. On Windows use VcXsrv or WSLg; on macOS use XQuartz.
Create a JSON file in the schemas/ directory. The schema defines your annotation types, their visual appearance, and optional flags/tags. You can also use the built-in Schema Editor (toolbar button) to create and edit schemas from inside the app.
Minimal schema example:
{
"schema_name": "MyDataset",
"annotation_types": [
{
"id": "object",
"label": "Object",
"type": "box",
"color": "#e74c3c"
},
{
"id": "keypoint",
"label": "Keypoint",
"type": "point",
"color": "#3498db",
"parent": "object"
}
],
"image_flags": [
{ "id": "blurry", "label": "Blurry", "default": false }
]
}Launch the app and click Open Dataset (toolbar) or pass the folder path as a command-line argument:
python main.py /path/to/images --schemas schemas/my_schema.jsonMultiple schemas can be passed simultaneously:
python main.py /path/to/images --schemas schemas/schema_a.json schemas/schema_b.json| Action | How |
|---|---|
| Draw bounding box | Select box tool → click and drag |
| Place keypoint | Select point tool → click |
| Select / move / resize | V key or Select tool → click annotation |
| Nudge selected | Arrow keys (+ Shift for ×10) |
| Delete selected | Del |
| Zoom | Mouse wheel |
| Pan | Middle-click drag or Ctrl+Left-click drag |
| Fit to view | F |
| Rotate view | Ctrl+R (right) / Ctrl+Shift+R (left) |
| Switch tool by index | 1 – 9 |
| Cycle schema | Tab |
| Mark image empty | Space or E button |
| Next untagged image | N |
| Navigate images | Ctrl+← / Ctrl+→ |
| Undo / Redo | Ctrl+Z / Ctrl+Y |
| Save | Ctrl+S |
Go to File > Export to export annotations in ML-ready formats:
- COCO JSON — standard COCO instance annotation format (bounding boxes →
bbox, points → keypoints) - YOLO TXT — one
.txtper image with normalizedclass x_center y_center width heightvalues, plus aclasses.txtmapping file
Each image produces a sidecar file named {image_stem}.{schema_name}.json alongside the image:
{
"schema": "MyDataset",
"image": "photo_001.jpg",
"image_flags": { "blurry": false },
"no_target": false,
"annotations": [
{
"uid": "a1b2c3d4",
"type_id": "object",
"box": [120, 45, 380, 210],
"flags": { "occluded": false },
"tags": { "condition": "good" },
"children": [
{
"uid": "e5f6g7h8",
"type_id": "keypoint",
"point": [250, 130],
"flags": {},
"tags": {}
}
]
}
]
}Fields:
box—[x1, y1, x2, y2]in original image pixel coordinatespoint—[x, y]in original image pixel coordinates- Image rotation is display-only and stored in
image_flags["_rotation"]— export coordinates are always in original image space
Switch to the Statistics tab to get a live view of your annotation progress:
| Panel | Metrics |
|---|---|
| Overview | Annotated / total images per schema, annotation counts per type |
| Box Geometry | Area distribution, size category breakdown, aspect ratio histogram |
| Spatial Distribution | 10×10 center-of-mass heatmap, edge-touching rate, inter-annotation overlap |
| Quality Metrics | Shannon diversity, evenness, Gini coefficient, imbalance ratio, class balance chart |
The annotator is fully schema-driven — there is no global config file. All behaviour is controlled per-schema in the JSON files. Schema fields:
| Field | Type | Description |
|---|---|---|
schema_name |
string | Display name (also used as sidecar file suffix) |
annotation_types |
list | List of type definitions (see below) |
image_flags |
list | Per-image boolean flags |
Annotation type fields:
| Field | Type | Description |
|---|---|---|
id |
string | Unique identifier |
label |
string | Display label |
type |
"box" | "point" |
Geometry type |
color |
string | Hex color (e.g. "#e74c3c") |
icon |
string | Optional relative path to a PNG icon |
parent |
string | Optional parent type id (makes this a child annotation) |
annotation_flags |
list | Per-annotation boolean flags |
tag_groups |
list | Per-annotation dropdown tag groups |
A built-in annotation guide wiki that teams can write directly inside the app. Each schema would carry an optional wiki document with labelling guidelines, example images for edge cases, and decision trees for ambiguous objects. Annotators could open the wiki panel without leaving the application, reducing context switching and improving inter-annotator agreement.
Support for pixel-accurate segmentation masks alongside bounding boxes. Rather than fitting a rectangle around an object, the annotator would allow tracing a freehand polygon (or a SAM-assisted auto-mask) that tightly follows object boundaries. This produces richer training data — particularly useful for instance segmentation models (Mask R-CNN, YOLO-seg) where the exact object silhouette matters. Planned output formats: COCO RLE masks and binary PNG mask files.
This project is licensed under the MIT License. See LICENSE for details.
Icons used in the toolbar and UI are sourced from Flaticon. See ATTRIBUTION.md for full credits.
Claudio Bendini (2026)



