Skip to main content

Manipulate bounding boxes as objects

Project description

bbox-objected

Mutable bounding boxes for computer vision. Zero required dependencies.

Contents

At A Glance

AbsBBox  <->  RelBBox
   |            |
   |            +-- as_abs(img_w, img_h)
   +-- as_rel(img_w, img_h)

Codecs: coco | pascal_voc | easyocr | winocr | mss
Adapters: numpy crop | opencv draw
Metrics: IoU | distance | angle | NMS | sort_clockwise

Install

Use uv:

uv add bbox-objected

Optional dependencies

numpy and opencv are optional. Install them manually or use extras:

  • uv add "bbox-objected[opencv]" (opencv + numpy)
  • uv add "bbox-objected[headless]" (opencv-headless + numpy)
  • uv add "bbox-objected[contrib]" (opencv-contrib + numpy)
  • uv add "bbox-objected[contrib-headless]" (opencv-contrib-headless + numpy)

Note: different opencv variants are mutually incompatible.

Quickstart

AbsBBox stores absolute integer coordinates:

from bbox_objected import AbsBBox

# Create a bbox with absolute coordinates.
bbox = AbsBBox(35, 45, 135, 125, text="abs_sample")

assert repr(bbox) == "<AbsBBox(x1=35, y1=45, x2=135, y2=125) - abs_sample>"
assert bbox.as_tuple() == (35, 45, 135, 125)

RelBBox stores relative coordinates in [0.0, 1.0]:

from bbox_objected import RelBBox

# Create a bbox with relative coordinates.
bbox = RelBBox(0.1, 0.5, 0.2, 0.6, text="rel_sample")

assert repr(bbox) == "<RelBBox(x1=0.1, y1=0.5, x2=0.2, y2=0.6) - rel_sample>"
assert bbox.tl == (0.1, 0.5)

Conversion

Convert between absolute and relative forms when image size is known:

from bbox_objected import RelBBox

# Convert between coordinate systems.
bbox = RelBBox(0.1, 0.2, 0.5, 0.6, text="sample")

assert repr(bbox) == "<RelBBox(x1=0.1, y1=0.2, x2=0.5, y2=0.6) - sample>"
assert repr(bbox.as_abs(1920, 1080)) == "<AbsBBox(x1=192, y1=216, x2=960, y2=648) - sample>"
assert bbox.as_abs(1920, 1080).as_rel(1920, 1080) is not bbox

Geometry And Attributes

Each bbox exposes geometry helpers and derived attributes.

Need mutation? Jump to Editing.

from bbox_objected import AbsBBox

# Access coordinate and geometry helpers.
bbox = AbsBBox(40, 40, 60, 60)

assert (bbox.x1, bbox.y1, bbox.x2, bbox.y2) == (40, 40, 60, 60)
assert (bbox.w, bbox.h) == (20, 20)
assert (bbox.tl, bbox.tr, bbox.br, bbox.bl) == ((40, 40), (60, 40), (60, 60), (40, 60))
assert (bbox.center, bbox.area) == ((50.0, 50.0), 400)
assert (bbox.xc, bbox.yc) == (50.0, 50.0)

Editing

Mutate existing boxes in-place for long-lived objects:

Need format conversions? See Codecs.

from bbox_objected import AbsBBox

# Edit coords in-place to avoid new objects.
bbox = AbsBBox(100, 200, 300, 400)
assert repr(bbox) == "<AbsBBox(x1=100, y1=200, x2=300, y2=400)>"

bbox.zero_basis()
assert repr(bbox) == "<AbsBBox(x1=0, y1=0, x2=200, y2=200)>"

bbox.move(25, 45)
assert repr(bbox) == "<AbsBBox(x1=25, y1=45, x2=225, y2=245)>"

other_bbox = AbsBBox(200, 300, 400, 500)

# Expands current box to cover both.
bbox.update_from(other_bbox)
assert repr(bbox) == "<AbsBBox(x1=25, y1=45, x2=400, y2=500)>"

# Replaces current coords with another box.
bbox.replace_from(other_bbox)
assert repr(bbox) == "<AbsBBox(x1=200, y1=300, x2=400, y2=500)>"

Codecs

Use codecs to translate between bbox formats:

Want to crop or draw on images? See Adapters.

from bbox_objected import AbsBBox
from bbox_objected.codecs import coco, easyocr, mss, pascal_voc, winocr

# COCO: x, y, w, h
assert coco.from_abs_bbox(AbsBBox(10, 20, 30, 40)) == (10, 20, 20, 20)

# Pascal VOC: x1, y1, x2, y2
assert pascal_voc.from_abs_bbox(AbsBBox(10, 20, 30, 40)) == (10, 20, 30, 40)

# EasyOCR: tl, tr, br, bl
assert easyocr.from_abs_bbox(AbsBBox(10, 20, 30, 40))[0] == (10, 20)

# WinOCR: mapping
assert winocr.from_abs_bbox(AbsBBox(10, 20, 30, 40))["width"] == 20

# MSS: mapping
assert mss.from_abs_bbox(AbsBBox(10, 20, 30, 40))["height"] == 20

Adapters

Crop using the numpy adapter (requires numpy):

import numpy as np

from bbox_objected import AbsBBox
from bbox_objected.adapters import crop_from_image

# Crop using numpy-backed images.
bbox = AbsBBox(100, 200, 300, 400)
img = np.empty((512, 512, 3), dtype=np.uint8)

cropped = crop_from_image(bbox, img)
assert cropped.shape == (200, 200, 3)

Draw using the OpenCV adapter (requires opencv-python with GUI support):

import cv2
import numpy as np

from bbox_objected import AbsBBox
from bbox_objected.adapters import show_on_image

# Requires a GUI-capable OpenCV build.
bbox = AbsBBox(100, 200, 300, 400)
img = np.empty((512, 512, 3), dtype=np.uint8)

show_on_image(bbox, img, text="sample")
cv2.waitKey(1)
cv2.destroyAllWindows()

Metrics

Compute distance, overlap, and angular relations on absolute boxes:

import pytest

from bbox_objected import get_cos_between, get_distance, get_IoU, sort_clockwise
from bbox_objected.codecs import coco, pascal_voc

bbox_1 = coco.to_abs_bbox((100, 200, 300, 400))
bbox_2 = pascal_voc.to_abs_bbox((100, 400, 200, 800))

assert get_distance(bbox_1, bbox_2) == pytest.approx(223.60679774997897)
assert get_IoU(bbox_1, bbox_2) == pytest.approx(0.14285714285714285)
assert get_cos_between(bbox_1, bbox_2, 450, 350) == pytest.approx(0.9005516363645784)

sorted_boxes = sort_clockwise([bbox_1, bbox_2], 450, 350)
assert len(sorted_boxes) == 2

Non-max suppression works with tuples and optional scores:

from bbox_objected import non_max_suppression

# Picks indices of boxes with higher scores and low overlap.
boxes = [(10, 10, 20, 20), (11, 11, 19, 19), (50, 50, 70, 70)]
picks = non_max_suppression(boxes, thr=0.5, scores=[0.4, 0.9, 0.8])

assert picks == [1, 2]

EasyOCR quad conversion example:

from bbox_objected.codecs import easyocr

bbox = easyocr.to_abs_bbox(((10, 20), (30, 20), (30, 40), (10, 40)))
assert bbox.as_tuple() == (10, 20, 30, 40)

Docs As Tests

README examples are executed as tests via pytest-markdown-docs.

  • Run locally: uv run pytest
  • The pytest config enables --markdown-docs by default.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bbox_objected-1.0.0.tar.gz (8.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bbox_objected-1.0.0-py3-none-any.whl (15.7 kB view details)

Uploaded Python 3

File details

Details for the file bbox_objected-1.0.0.tar.gz.

File metadata

  • Download URL: bbox_objected-1.0.0.tar.gz
  • Upload date:
  • Size: 8.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for bbox_objected-1.0.0.tar.gz
Algorithm Hash digest
SHA256 782ae55bb7b6cffb2fccb7b41f141bf2a3f12b6de46ee1c7ed842d9f33a5ffaf
MD5 7c405b9cfcd8b3eb17a126c7ed28f075
BLAKE2b-256 04eaf7f25c65fdfbc766f064a9c9f09fd581f4f160d9bb62672b0d5ff5b7f27d

See more details on using hashes here.

File details

Details for the file bbox_objected-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: bbox_objected-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 15.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for bbox_objected-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1c9eb8a5ba2fbceef5799bae2174593de03128be0c85e5819cd3138137a5dfff
MD5 ac61b35ac8974b61172044a0d91addc0
BLAKE2b-256 7034eadef6509ac42881a099abdc89139c9ca0db53cc6fd102d5f35653fc4051

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page