Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Local Label

Offline image annotation for object detection and segmentation. Point it at a folder, draw boxes and polygons, export COCO / YOLO / Pascal VOC.

Your images never leave your machine. There is no upload, no account, no database, and no network traffic beyond 127.0.0.1.

pip install -r requirements.txt
python app.py /path/to/photos

Your browser opens on http://127.0.0.1:8000. That is the whole setup.


Why this exists

Most annotation tools want you to upload a dataset to a server or stand up a database before you can draw a single box. That is overkill when you are one person labelling a folder of images on your own laptop, and a non-starter when the images are private, sensitive, or simply too large to upload.

Local Label reads the files straight off disk, writes annotations next to them, and gets out of the way.

  • Nothing is copied or uploaded. Images are streamed from their original location and never duplicated.
  • Annotations are plain JSON, one small file per image. Readable, diffable, and trivial to parse yourself.
  • Move the folder and the labels come with it. Everything lives in a .labeller/ directory beside your images.
  • Exports are real training files, not a JSON blob you have to convert.

Features

  • Bounding boxes and polygons
  • Zoom, pan, and crosshair guides for precise edges
  • Keyboard-driven, so the whole workflow works without the mouse
  • Multi-select, bulk relabel, copy and paste shapes between images
  • Repeat the previous image's shapes with one key, for sequential footage
  • Autosave, undo and redo, and resume exactly where you left off
  • Brightness and contrast sliders for dark or flat images (view only, your files are never modified)
  • Light and dark themes, plus a neutral canvas surround tuned for long sessions
  • Dataset summary: class balance, shape sizes, unlabelled-class warnings
  • Export to COCO, YOLO (Ultralytics-ready), and Pascal VOC

Requirements

Python 3.9 or newer, and a modern browser. Four dependencies, no database, no build step, no Node.

Install

git clone https://github.com/diptiman-mohanta/Local-Label.git
cd Local-Label
pip install -r requirements.txt

A virtual environment is recommended but not required:

python -m venv .venv

# Windows
.venv\Scripts\activate

# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt

Usage

python app.py                          # start, then pick a folder in the UI
python app.py /path/to/photos          # open that folder immediately
python app.py /path/to/photos --recursive
python app.py /path/to/photos --port 8080 --no-browser

Options

Flag Default What it does
folder none Folder of images to open. Optional; you can pick one in the UI.
--recursive off Include images in subfolders.
--port 8000 Port to serve on.
--host 127.0.0.1 Bind address. Read the security note before changing this.
--no-browser off Do not open a browser window on start.
--version none Print the version and exit.

Supported image types: .jpg .jpeg .png .bmp .webp .tif .tiff

Keyboard shortcuts

Press ? in the app to see this list at any time.

Key Action
A / D Previous / next image
Tab Jump to next unannotated image
V B P Select, Box, Polygon
1 to 9 Pick label (also relabels the selection)
Enter Close the polygon you are drawing
Right-click or Backspace Undo the last polygon point
Esc Cancel the draft, or deselect
Shift+click Add to selection
Ctrl+A Select all shapes
Arrow keys Nudge selection (Shift for larger steps)
Del Delete selection
Ctrl+C / Ctrl+V Copy and paste shapes across images
R Repeat the previous image's shapes
H Hide all shapes
Ctrl+Z / Ctrl+Y Undo / redo
Ctrl+S Save now (it also autosaves)
F Fit image to view
Scroll, middle-drag, Space-drag Zoom, pan, pan
Alt+click Remove a polygon vertex

Where your data lives

photos/
  cat1.jpg
  cat2.jpg
  .labeller/
    classes.txt                  one label per line; order sets the class id
    annotations/
      cat1.jpg.json              one sidecar per image
    exports/
      coco/annotations.json
      yolo/images/ labels/ data.yaml
      voc/cat1.xml

An image with no shapes has no sidecar. Delete .labeller/ and you are back to a plain folder of photos.

Annotation format

Coordinates are original image pixels, so they stay correct at any zoom level or window size.

{
  "version": 1,
  "image": "cat1.jpg",
  "width": 1920,
  "height": 1080,
  "shapes": [
    { "id": "a1b2c3d4", "type": "bbox", "label": "cat", "points": [[120, 80], [640, 700]] },
    { "id": "e5f6a7b8", "type": "polygon", "label": "dog", "points": [[10, 10], [90, 20], [50, 95]] }
  ],
  "updated_at": "2026-09-03T00:42:05"
}

A bbox is two corner points, top-left and bottom-right. A polygon is three or more points in order.

Exports

Exports are written into .labeller/exports/<format>/ and never touch your images. Re-running an export replaces the previous one, including removing files for images whose annotations you have since cleared.

COCO — a single annotations.json. Polygons carry a segmentation array alongside a derived bbox and shoelace area.

YOLO — a ready-to-train Ultralytics dataset:

yolo/
  images/     hard links to your originals, so no extra disk is used
  labels/     one .txt per image
  data.yaml
yolo detect train data=".labeller/exports/yolo/data.yaml" model=yolo11n.pt

Both folders are required: Ultralytics locates a label by swapping the /images/ segment of an image path for /labels/. Boxes use cls xc yc w h; polygons use YOLO-seg (cls x1 y1 x2 y2 ...), normalised to 0-1. Only annotated images are included, and val points at the same set, so split it yourself before publishing a result.

Pascal VOC — one .xml per image. Polygons get a <bndbox> plus a <polygon> element holding the real vertices.

Notes

EXIF orientation is handled. Camera JPEGs store portrait shots as landscape pixels plus a rotation tag. Local Label reads that tag so annotations and exports use the dimensions you actually see, which is the same convention Ultralytics uses. Tools that ignore it place boxes wrongly on rotated photos.

Renaming a label migrates existing shapes. If you rename a class, shapes using it are updated. Any label that matches no class is reported rather than silently exported as class 0.

The image folder must be writable, since .labeller/ lives inside it. A read-only or network location is rejected with a clear message.

Security. --host defaults to loopback for a reason: the folder-open endpoint can read any directory the process can see. That is correct for a personal tool on 127.0.0.1 and unsafe on a routable address. Binding elsewhere prints a warning.

Roadmap

Local Label is deliberately small. Planned next:

  • Import existing COCO / YOLO / VOC datasets
  • Model-assisted pre-labelling
  • One-click segmentation (SAM)
  • Per-shape attributes: occluded, truncated, difficult
  • Train / val / test splitting on export

Ideas and disagreement both welcome. Open an issue.

Contributing

Bug reports, feature requests, and pull requests are all welcome. Start with CONTRIBUTING.md.

Found a bug, or something confusing? Open an issue and include your OS, your Python version, and what you expected to happen.

License

MIT © Diptiman Mohanta

About

A simple, local-first image annotation tool for creating computer vision datasets.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages