annofmt
Immutable geometry types and utilities for computer-vision annotations. Zero dependencies, fully typed, Python 3.12+.
Install
uv add annofmt
# or
pip install annofmt
# or straight from the repo
uv add git+https://github.com/peachfuzz11/annofmt.git
Quickstart
from annofmt.annotation import Annotation
from annofmt.geometry.bbox import BBox
from annofmt.geometry.rbbox import RBBox
from annofmt.geometry.segm import Segm
from annofmt.tag import Tag
bbox = BBox(x=30, y=50, w=40, h=20) # center + extent
bbox.x_min # derived corner values
# Build from whichever coordinate layout you already have
BBox.from_xywh(30, 50, 40, 20) # center x, center y, w, h (native, YOLO-style)
BBox.from_x1y1x2y2(10, 40, 50, 60) # corners: left, top, right, bottom edge (Pascal VOC)
BBox.from_x1x2y1y2(10, 50, 40, 60) # x range then y range: left, right, top, bottom edge
BBox.from_x1y1wh(10, 40, 40, 20) # top-left corner (x1, y1) + w, h (COCO)
# ...and read any layout back out as a plain tuple
bbox.to_xywh() # (center x, center y, w, h)
bbox.to_x1y1x2y2() # (left, top, right, bottom edge)
bbox.to_x1x2y1y2() # (left, right, top, bottom edge)
bbox.to_x1y1wh() # (x1, y1, w, h)
# Normalized coordinates in [0, 1] relative to image dimensions
bbox_normalized = bbox.normalize(Height, Width)
# Denormalize back to pixel coordinates
bbox_denormalized = bbox_normalized.denormalize(Height, Width)
rotated = RBBox.from_degrees(x=30, y=50, w=40, h=20, degrees=25) # same xywh, plus `a`
rotated.corners()
rotated.to_bbox() # lossy enclosing axis-aligned box
segm = Segm.from_polygon([(0, 0), (9, 0), (9, 9), (5, 5), (0, 9)])
segm.indices # integer pixel-index rings
segm.to_rle(height=10, width=10) # COCO-style column-major run-length counts
Segm.from_rle(runs, 10, 10) # ...and back to rings
annotation = Annotation(geometry=segm).add_tag(Tag("person", score=0.98))
annotation.bbox # works for every geometry type
All types are immutable: operations return new instances and never mutate the receiver.
Geometry types
BBox is the base class; storage is always center-plus-extent (x, y,
w, h).
| Type | Extra storage | Description |
|---|---|---|
BBox |
— | axis-aligned box; corners are derived properties |
RBBox |
a |
adds rotation in radians; corners become the enclosing box |
Segm |
indices |
polygon rings over integer pixel indices; x/y/w/h hold the enclosing box |
Conversion matrix
| From \ To | BBox |
RBBox |
Segm |
|---|---|---|---|
BBox |
— | unavailable (rotation cannot be invented) | exact rectangle ring (Segm.from_bbox) |
RBBox |
enclosing box (to_bbox, lossy) |
— | corner ring (Segm.from_rbbox, rounded to lattice) |
Segm |
enclosing box (to_bbox, lossy) |
minimum-area rect (as_rbbox, lossy) |
— |
IoU is defined only between two geometries of the same type and raises
TypeError otherwise. Convert explicitly first when a cross-type comparison
is really wanted (e.g. compare every geometry's to_bbox()).
Coordinate layouts
BBox stores center-plus-extent, but you rarely have data in that form.
Each from_* classmethod builds a box from a named coordinate layout and
each to_* method returns that layout as a plain tuple. The method name
spells out the argument order — x1/y1 are the top-left corner (left and
top edge), x2/y2 the bottom-right corner (right and bottom edge):
| Constructor | Accessor | Arguments |
|---|---|---|
BBox.from_xywh |
bbox.to_xywh |
center x, center y, width, height (native, YOLO-style) |
BBox.from_x1y1x2y2 |
bbox.to_x1y1x2y2 |
x1, y1, x2, y2 — left, top, right, bottom edge (Pascal VOC) |
BBox.from_x1x2y1y2 |
bbox.to_x1x2y1y2 |
x1, x2, y1, y2 — left, right, top, bottom edge |
BBox.from_x1y1wh |
bbox.to_x1y1wh |
x1, y1, width, height — top-left corner + size (COCO) |
from_x1y1x2y2 / from_x1x2y1y2 accept the two edges in either order. The
classmethods are inherited by RBBox (angle defaults to 0). Every
constructor and accessor takes / preserves meta.
Conventions
- Angles: stored in radians, canonically wrapped to
[-pi, pi). Build withfrom_degrees, read via.angle_deg/.angle_rad. - Indices:
Segmcoordinates are integer pixel indices; fractional input is rejected. Vertices sit on the integer lattice. - Rasterization: pixels are unit squares centered at
(col + 0.5, row + 0.5); rings combine under an even-odd rule, so nested rings act as holes regardless of winding order. - RLE: COCO-style column-major counts starting with zeros;
to_rle(height, width)/from_rle(runs, height, width)round-trip. - Immutability: every operation yields a new instance.
Metadata
Release files for annofmt 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| annofmt-0.2.0.tar.gz | 32.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| annofmt-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.4 kB
Release files / annofmt-0.2.0.tar.gz
| Download URL | annofmt-0.2.0.tar.gz |
|---|---|
| Size | 32.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a4e971647b390119c7f5a88fdde990a47d4a38c167e9382e17ee8f5a5aeaaec9
|
|
BLAKE2b-256 checksum How to use checksums |
0976b47ff13e8c95e1eebff4a7883166583999f1bc508ffd9e5d28b20f69e016
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / annofmt-0.2.0-py3-none-any.whl
| Download URL | annofmt-0.2.0-py3-none-any.whl |
|---|---|
| Size | 7.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
040e3a060ad8c89547605fc35739dddb235a4eb50c6c1f62528aa7b604e78404
|
|
BLAKE2b-256 checksum How to use checksums |
128b43a8e01654afe3b088c37b05ab7b9a5e3f0c97c1f2f742ef15215d35b6b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|