Skip to content

Types

types

Classes

BBox

Bases: NamedTuple

Bounding box representation in XYXY format.

This class represents a rectangular bounding box using the XYXY coordinate format (top-left and bottom-right corners). It provides conversions to other common formats and utility methods for area calculation.

The internal representation uses XYXY format (x_min, y_min, x_max, y_max) which is the most common format for object detection tasks.

Attributes:

Name Type Description
x_min float

Minimum X coordinate of the bounding box.

y_min float

Minimum Y coordinate of the bounding box.

x_max float

Maximum X coordinate of the bounding box.

y_max float

Maximum Y coordinate of the bounding box.

Example
from boxlab.dataset.types import BBox

# Create from XYXY coordinates
bbox = BBox(x_min=10, y_min=20, x_max=100, y_max=150)

# Access coordinates
print(f"Top-left: ({bbox.x_min}, {bbox.y_min})")
print(f"Bottom-right: ({bbox.x_max}, {bbox.y_max})")

# Get area
print(f"Area: {bbox.area}")  # 11700
Example
# Convert to different formats
bbox = BBox(10, 20, 100, 150)

xyxy = bbox.xyxy  # (10, 20, 100, 150)
xywh = bbox.xywh  # (10, 20, 90, 130)
cxcywh = bbox.cxcywh  # (55.0, 85.0, 90, 130)
Example
# Create from other formats
bbox1 = BBox.from_xywh(x=10, y=20, w=90, h=130)
bbox2 = BBox.from_cxcywh(cx=55, cy=85, w=90, h=130)

print(bbox1 == bbox2)  # True
Attributes
x_min instance-attribute
x_min: float

Minimum X coordinate of the bounding box.

y_min instance-attribute
y_min: float

Minimum Y coordinate of the bounding box.

x_max instance-attribute
x_max: float

Maximum X coordinate of the bounding box.

y_max instance-attribute
y_max: float

Maximum Y coordinate of the bounding box.

xyxy property
xyxy: tuple[float, float, float, float]

Return bounding box in XYXY format.

Returns:

Type Description
tuple[float, float, float, float]

Tuple of (x_min, y_min, x_max, y_max).

xywh property
xywh: tuple[float, float, float, float]

Convert to XYWH format (COCO format).

Returns:

Type Description
tuple[float, float, float, float]

Tuple of (x, y, width, height) where (x, y) is the top-left corner.

Example
bbox = BBox(10, 20, 100, 150)
x, y, w, h = bbox.xywh
# x=10, y=20, w=90, h=130
cxcywh property
cxcywh: tuple[float, float, float, float]

Convert to center format (YOLO format).

Returns:

Type Description
tuple[float, float, float, float]

Tuple of (center_x, center_y, width, height).

Example
bbox = BBox(10, 20, 100, 150)
cx, cy, w, h = bbox.cxcywh
# cx=55.0, cy=85.0, w=90, h=130
area property
area: float

Calculate bounding box area.

Returns:

Type Description
float

Area in square pixels (width * height).

Example
bbox = BBox(0, 0, 10, 20)
print(bbox.area)  # 200.0
Functions
from_xywh classmethod
from_xywh(x: float, y: float, w: float, h: float) -> BBox

Create BBox from XYWH format (COCO format).

Parameters:

Name Type Description Default
x float

Left X coordinate.

required
y float

Top Y coordinate.

required
w float

Width of the box.

required
h float

Height of the box.

required

Returns:

Type Description
BBox

BBox instance.

Example
# COCO format: [x, y, width, height]
bbox = BBox.from_xywh(x=10, y=20, w=90, h=130)
print(bbox.xyxy)  # (10, 20, 100, 150)
Source code in boxlab/dataset/types.py
@classmethod
def from_xywh(cls, x: float, y: float, w: float, h: float) -> BBox:
    """Create BBox from XYWH format (COCO format).

    Args:
        x: Left X coordinate.
        y: Top Y coordinate.
        w: Width of the box.
        h: Height of the box.

    Returns:
        BBox instance.

    Example:
        ```python
        # COCO format: [x, y, width, height]
        bbox = BBox.from_xywh(x=10, y=20, w=90, h=130)
        print(bbox.xyxy)  # (10, 20, 100, 150)
        ```
    """
    return cls(x, y, x + w, y + h)
from_cxcywh classmethod
from_cxcywh(cx: float, cy: float, w: float, h: float) -> BBox

Create BBox from center format (YOLO format).

Parameters:

Name Type Description Default
cx float

Center X coordinate.

required
cy float

Center Y coordinate.

required
w float

Width of the box.

required
h float

Height of the box.

required

Returns:

Type Description
BBox

BBox instance.

Example
# YOLO format: [center_x, center_y, width, height]
bbox = BBox.from_cxcywh(cx=55, cy=85, w=90, h=130)
print(bbox.xyxy)  # (10.0, 20.0, 100.0, 150.0)
Source code in boxlab/dataset/types.py
@classmethod
def from_cxcywh(cls, cx: float, cy: float, w: float, h: float) -> BBox:
    """Create BBox from center format (YOLO format).

    Args:
        cx: Center X coordinate.
        cy: Center Y coordinate.
        w: Width of the box.
        h: Height of the box.

    Returns:
        BBox instance.

    Example:
        ```python
        # YOLO format: [center_x, center_y, width, height]
        bbox = BBox.from_cxcywh(cx=55, cy=85, w=90, h=130)
        print(bbox.xyxy)  # (10.0, 20.0, 100.0, 150.0)
        ```
    """
    return cls(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2)

Annotation

Bases: NamedTuple

Object annotation with bounding box and category information.

Represents a single object annotation in an image, including its spatial location (bounding box) and semantic information (category). Follows COCO annotation conventions.

Attributes:

Name Type Description
bbox BBox

Bounding box of the annotated object.

category_id int

Integer category identifier.

category_name str

Human-readable category name.

image_id str

ID of the image this annotation belongs to.

annotation_id str | None

Optional unique identifier for this annotation.

area float | None

Optional pre-computed area. If None, calculated from bbox.

iscrowd int

Crowd annotation flag (0=single object, 1=crowd of objects).

Example
from boxlab.dataset.types import Annotation, BBox

# Create an annotation
annotation = Annotation(
    bbox=BBox(x_min=10, y_min=20, x_max=100, y_max=150),
    category_id=1,
    category_name="person",
    image_id="img_001",
    annotation_id="ann_001",
    area=11700.0,
    iscrowd=0,
)

print(f"Object: {annotation.category_name}")
print(f"Location: {annotation.bbox.xyxy}")
print(f"Area: {annotation.get_area()}")
Example
# Create annotation without pre-computed area
annotation = Annotation(
    bbox=BBox(0, 0, 100, 100),
    category_id=2,
    category_name="car",
    image_id="img_002",
)

# Area is computed from bbox automatically
print(annotation.get_area())  # 10000.0
Attributes
bbox instance-attribute
bbox: BBox

Bounding box of the annotation.

category_id instance-attribute
category_id: int

Category ID of the annotation.

category_name instance-attribute
category_name: str

Category name of the annotation.

image_id instance-attribute
image_id: str

ID of the image the annotation belongs to.

annotation_id class-attribute instance-attribute
annotation_id: str | None = None

Unique ID of the annotation.

area class-attribute instance-attribute
area: float | None = None

Area of the annotation, if available.

iscrowd class-attribute instance-attribute
iscrowd: int = 0

Crowd annotation flag (0 or 1).

Functions
get_area
get_area() -> float

Get annotation area.

Returns pre-computed area if available, otherwise calculates from bbox.

Returns:

Type Description
float

Area in square pixels.

Example
# With pre-computed area
ann1 = Annotation(
    bbox=BBox(0, 0, 10, 10),
    category_id=1,
    category_name="obj",
    image_id="1",
    area=100.0,
)
print(ann1.get_area())  # 100.0 (uses pre-computed)

# Without pre-computed area
ann2 = Annotation(
    bbox=BBox(0, 0, 10, 10),
    category_id=1,
    category_name="obj",
    image_id="1",
)
print(ann2.get_area())  # 100.0 (computed from bbox)
Source code in boxlab/dataset/types.py
def get_area(self) -> float:
    """Get annotation area.

    Returns pre-computed area if available, otherwise calculates from bbox.

    Returns:
        Area in square pixels.

    Example:
        ```python
        # With pre-computed area
        ann1 = Annotation(
            bbox=BBox(0, 0, 10, 10),
            category_id=1,
            category_name="obj",
            image_id="1",
            area=100.0,
        )
        print(ann1.get_area())  # 100.0 (uses pre-computed)

        # Without pre-computed area
        ann2 = Annotation(
            bbox=BBox(0, 0, 10, 10),
            category_id=1,
            category_name="obj",
            image_id="1",
        )
        print(ann2.get_area())  # 100.0 (computed from bbox)
        ```
    """
    return self.area if self.area is not None else self.bbox.area

ImageInfo

Bases: NamedTuple

Image metadata container.

Stores essential information about an image in the dataset, including dimensions and file location.

Attributes:

Name Type Description
image_id str

Unique identifier for the image.

file_name str

Filename of the image (e.g., "image001.jpg").

width int

Image width in pixels.

height int

Image height in pixels.

path Path | None

Optional filesystem path to the image file.

Example
from pathlib import Path
from boxlab.dataset.types import ImageInfo

# Create image metadata
img_info = ImageInfo(
    image_id="img_001",
    file_name="photo.jpg",
    width=1920,
    height=1080,
    path=Path("/data/images/photo.jpg"),
)

print(f"Image: {img_info.file_name}")
print(f"Resolution: {img_info.width}x{img_info.height}")
print(
    f"Exists: {img_info.path.exists() if img_info.path else 'N/A'}"
)
Example
# Create without path (for export scenarios)
img_info = ImageInfo(
    image_id="img_002",
    file_name="image002.jpg",
    width=640,
    height=480,
)
Attributes
image_id instance-attribute
image_id: str

Unique identifier for the image.

file_name instance-attribute
file_name: str

Filename of the image.

width instance-attribute
width: int

Width of the image.

height instance-attribute
height: int

Height of the image.

path class-attribute instance-attribute
path: Path | None = None

Optional filesystem path to the image.

DatasetStatistics

Bases: TypedDict

Dataset statistics container.

TypedDict containing comprehensive statistical information about a dataset, including counts, distributions, and aggregate metrics.

This structure is returned by Dataset.get_statistics() and related methods.

Attributes:

Name Type Description
num_images int

Total number of images in the dataset.

num_annotations int

Total number of annotations across all images.

num_categories int

Number of unique object categories.

category_distribution dict[str, int]

Mapping of category names to their annotation counts.

avg_annotations_per_image float

Mean number of annotations per image.

std_annotations_per_image float

Standard deviation of annotations per image.

min_annotations_per_image int

Minimum annotations found in any image.

max_annotations_per_image int

Maximum annotations found in any image.

avg_bbox_area float

Mean bounding box area in square pixels.

median_bbox_area float

Median bounding box area in square pixels.

Example
from boxlab.dataset import Dataset

dataset = Dataset(name="my_dataset")
# ... populate dataset ...

# Get statistics
stats = dataset.get_statistics()

print(f"Images: {stats['num_images']}")
print(f"Annotations: {stats['num_annotations']}")
print(f"Categories: {stats['num_categories']}")
print(
    f"Avg objects per image: {stats['avg_annotations_per_image']:.2f}"
)

# Category distribution
for category, count in stats[
    "category_distribution"
].items():
    print(f"  {category}: {count}")
Example
# Analyze bounding box sizes
stats = dataset.get_statistics()

print(f"Average bbox area: {stats['avg_bbox_area']:.2f}")
print(f"Median bbox area: {stats['median_bbox_area']:.2f}")

# Check for annotation imbalance
min_anns = stats["min_annotations_per_image"]
max_anns = stats["max_annotations_per_image"]
if max_anns > min_anns * 10:
    print(
        "Warning: Large annotation count variance detected"
    )
Attributes
num_images instance-attribute
num_images: int

Number of images in the dataset.

num_annotations instance-attribute
num_annotations: int

Number of annotations in the dataset.

num_categories instance-attribute
num_categories: int

Number of unique categories in the dataset.

category_distribution instance-attribute
category_distribution: dict[str, int]

Distribution of annotations per category.

avg_annotations_per_image instance-attribute
avg_annotations_per_image: float

Average number of annotations per image.

std_annotations_per_image instance-attribute
std_annotations_per_image: float

Standard deviation of annotations per image.

min_annotations_per_image instance-attribute
min_annotations_per_image: int

Minimum number of annotations in any image.

max_annotations_per_image instance-attribute
max_annotations_per_image: int

Maximum number of annotations in any image.

avg_bbox_area instance-attribute
avg_bbox_area: float

Average bounding box area.

median_bbox_area instance-attribute
median_bbox_area: float

Median bounding box area.

SplitRatio

Bases: NamedTuple

Dataset split ratios for train/val/test division.

Defines the proportions for splitting a dataset into training, validation, and test sets. All ratios must sum to 1.0.

Attributes:

Name Type Description
train float

Training set ratio (default: 0.8).

val float

Validation set ratio (default: 0.1).

test float

Test set ratio (default: 0.1).

Raises:

Type Description
ValidationError

If ratios don't sum to 1.0 (within tolerance of 1e-6).

Example
from boxlab.dataset.types import SplitRatio

# 70% train, 20% val, 10% test
split_ratio = SplitRatio(train=0.7, val=0.2, test=0.1)
split_ratio.validate()  # OK

# Use with dataset
dataset.split(split_ratio, seed=42)
Example
# 80% train, 20% val, no test set
split_ratio = SplitRatio(train=0.8, val=0.2, test=0.0)
split_ratio.validate()  # OK
Example
# Invalid split (doesn't sum to 1.0)
try:
    split_ratio = SplitRatio(train=0.7, val=0.2, test=0.2)
    split_ratio.validate()
except ValidationError as e:
    print(f"Error: {e}")
    # Output: Split ratios must sum to 1.0, got 1.1
Example
# Default split (80/10/10)
split_ratio = SplitRatio()
print(f"Train: {split_ratio.train}")  # 0.8
print(f"Val: {split_ratio.val}")  # 0.1
print(f"Test: {split_ratio.test}")  # 0.1
Attributes
train class-attribute instance-attribute
train: float = 0.8

Training set ratio.

val class-attribute instance-attribute
val: float = 0.1

Validation set ratio.

test class-attribute instance-attribute
test: float = 0.1

Test set ratio.

Functions
validate
validate() -> None

Validate that split ratios sum to 1.0.

Checks that train + val + test equals 1.0 within a tolerance of 1e-6.

Raises:

Type Description
ValidationError

If ratios don't sum to 1.0.

Example
split_ratio = SplitRatio(train=0.7, val=0.2, test=0.1)
split_ratio.validate()  # OK

bad_split = SplitRatio(train=0.5, val=0.3, test=0.1)
bad_split.validate()  # Raises ValidationError
Source code in boxlab/dataset/types.py
def validate(self) -> None:
    """Validate that split ratios sum to 1.0.

    Checks that train + val + test equals 1.0 within a tolerance of 1e-6.

    Raises:
        ValidationError: If ratios don't sum to 1.0.

    Example:
        ```python
        split_ratio = SplitRatio(train=0.7, val=0.2, test=0.1)
        split_ratio.validate()  # OK

        bad_split = SplitRatio(train=0.5, val=0.3, test=0.1)
        bad_split.validate()  # Raises ValidationError
        ```
    """
    total = self.train + self.val + self.test
    if not abs(total - 1.0) < 1e-6:
        raise ValidationError(f"Split ratios must sum to 1.0, got {total}")

options: show_root_heading: true show_source: true heading_level: 2 members_order: source show_signature_annotations: true separate_signature: true

Overview

The types module defines core data structures used throughout BoxLab. These types provide type-safe, immutable representations of bounding boxes, annotations, images, and dataset metadata.

Bounding Box Formats

BoxLab supports three common bounding box coordinate formats:

XYXY Format (Internal)

Top-left and bottom-right corners:

(x_min, y_min, x_max, y_max)
  • Most common in detection frameworks
  • Used internally by BoxLab
  • Direct representation of box corners

XYWH Format (COCO)

Top-left corner and dimensions:

(x, y, width, height)
  • Used by COCO dataset
  • Convenient for drawing operations
  • Common in visualization tools

CXCYWH Format (YOLO)

Center point and dimensions:

(center_x, center_y, width, height)
  • Used by YOLO formats
  • Natural for certain augmentations
  • Often used with normalized coordinates

Format Conversions

BBox Conversions

from boxlab.dataset.types import BBox

# Create in XYXY (internal format)
bbox = BBox(x_min=10, y_min=20, x_max=100, y_max=150)

# Convert to other formats
xyxy = bbox.xyxy  # (10, 20, 100, 150)
xywh = bbox.xywh  # (10, 20, 90, 130)
cxcywh = bbox.cxcywh  # (55.0, 85.0, 90, 130)

# Create from other formats
bbox1 = BBox.from_xywh(x=10, y=20, w=90, h=130)
bbox2 = BBox.from_cxcywh(cx=55, cy=85, w=90, h=130)

# All represent the same box
assert bbox == bbox1 == bbox2

Usage Patterns

Creating Annotations

from boxlab.dataset.types import BBox, Annotation

# Complete annotation
annotation = Annotation(
    bbox=BBox(10, 20, 100, 150),
    category_id=1,
    category_name="person",
    image_id="img_001",
    annotation_id="ann_001",
    area=11700.0,
    iscrowd=0
)

# Minimal annotation (area computed automatically)
annotation = Annotation(
    bbox=BBox(10, 20, 100, 150),
    category_id=1,
    category_name="person",
    image_id="img_001"
)

Image Metadata

from pathlib import Path
from boxlab.dataset.types import ImageInfo

# With file path
img_info = ImageInfo(
    image_id="001",
    file_name="photo.jpg",
    width=1920,
    height=1080,
    path=Path("/data/images/photo.jpg")
)

# Without path (for annotations-only export)
img_info = ImageInfo(
    image_id="001",
    file_name="photo.jpg",
    width=1920,
    height=1080
)

Dataset Splitting

from boxlab.dataset.types import SplitRatio

# Standard split
split = SplitRatio(train=0.7, val=0.2, test=0.1)
split.validate()

# No test set
split = SplitRatio(train=0.8, val=0.2, test=0.0)
split.validate()

# Use with dataset
splits = dataset.split(split, seed=42)

Analyzing Statistics

from boxlab.dataset import Dataset

dataset = Dataset(name="my_dataset")
# ... populate dataset ...

stats = dataset.get_statistics()

# Access statistics
print(f"Total images: {stats['num_images']}")
print(f"Total annotations: {stats['num_annotations']}")

# Category analysis
dist = stats['category_distribution']
most_common = max(dist.items(), key=lambda x: x[1])
print(f"Most common: {most_common[0]} ({most_common[1]} instances)")

# Annotation density
avg = stats['avg_annotations_per_image']
std = stats['std_annotations_per_image']
print(f"Objects per image: {avg:.2f} ± {std:.2f}")

# Box size analysis
print(f"Average box area: {stats['avg_bbox_area']:.2f}")
print(f"Median box area: {stats['median_bbox_area']:.2f}")

Coordinate Systems

Pixel Coordinates

All bounding box coordinates in BoxLab are in absolute pixel coordinates:

# For a 1920x1080 image
bbox = BBox(x_min=100, y_min=200, x_max=300, y_max=400)

# Width and height in pixels
width = bbox.x_max - bbox.x_min  # 200 pixels
height = bbox.y_max - bbox.y_min  # 200 pixels

Normalized Coordinates

For normalized coordinates (e.g., YOLO format), convert manually:

# Image dimensions
img_width, img_height = 1920, 1080

# Absolute bbox
bbox = BBox(100, 200, 300, 400)

# Normalize
cx, cy, w, h = bbox.cxcywh
cx_norm = cx / img_width  # 0.104
cy_norm = cy / img_height  # 0.278
w_norm = w / img_width  # 0.104
h_norm = h / img_height  # 0.185

Coordinate Origin

BoxLab uses the top-left corner as origin (0, 0):

(0,0) ──────→ x
  y

Immutability

All types are immutable (NamedTuple or TypedDict):

bbox = BBox(10, 20, 100, 150)

# This raises an error
# bbox.x_min = 15  # AttributeError

# Create a new bbox instead
new_bbox = BBox(15, 20, 100, 150)

Type Safety

Use type hints for better IDE support:

from boxlab.dataset.types import BBox, Annotation, ImageInfo


def process_bbox(bbox: BBox) -> float:
    return bbox.area


def create_annotation(
    bbox: BBox,
    category_id: int,
    category_name: str,
    image_id: str
) -> Annotation:
    return Annotation(
        bbox=bbox,
        category_id=category_id,
        category_name=category_name,
        image_id=image_id
    )

Validation

SplitRatio Validation

from boxlab.dataset.types import SplitRatio
from boxlab.exceptions import ValidationError

# Valid split
split = SplitRatio(0.7, 0.2, 0.1)
split.validate()  # OK

# Invalid split
try:
    split = SplitRatio(0.5, 0.3, 0.1)  # Sums to 0.9
    split.validate()
except ValidationError as e:
    print(f"Error: {e}")

BBox Validation

BBox doesn't validate coordinates by default. Add custom validation if needed:

def validate_bbox(bbox: BBox, img_width: int, img_height: int) -> bool:
    """Check if bbox is within image bounds."""
    return (
        0 <= bbox.x_min < bbox.x_max <= img_width and
        0 <= bbox.y_min < bbox.y_max <= img_height
    )


bbox = BBox(10, 20, 100, 150)
is_valid = validate_bbox(bbox, 1920, 1080)  # True

See Also