HBB2OBB: Horizontal to Oriented Bounding Box Conversion and Evaluation Tool
HBB2OBB converts horizontal (axis-aligned) bounding boxes (HBBs) into oriented (rotated) bounding boxes (OBBs) by using your existing HBB annotations as prompts for segmentation models from the SAM (Segment Anything Model) family. It targets object detection tasks where objects appear at arbitrary orientations, such as aerial imagery, satellite data, or traffic monitoring, producing OBBs that tightly encapsulate non-upright objects. Beyond conversion, it ships evaluation, hyperparameter optimization, and annotation format-conversion tools, with both a command-line interface and a Python API.
Why HBB2OBB
- 🎯 Accurate OBBs from HBBs: prompts SAM-family segmentation models with your existing horizontal boxes to fit tight oriented boxes around non-upright objects, with no re-annotation required.
- 🧩 Model ensemble: combines masks from multiple SAM variants through majority voting for more robust, accurate results (see Usage).
- 🛡️ Spatially constrained & safe: region-specific masking and contour refinement keep segmentation inside the object, and a fallback keeps the original HBB when no valid mask is found.
- 🔎 Confidence-scored output: every OBB gets a quality score in
[0, 1]that flags silent fallbacks and low-confidence conversions, so you know which boxes to trust (see Confidence scores). - 📐 Flexible scaling: positive or negative scale factors (optionally different for the short and long sides) recover cropped object parts or tighten overly conservative annotations.
- 📊 Evaluate & optimize: built-in IoU evaluation against ground truth plus a hyperparameter optimizer over SAM inference resolution × scale factors.
- 🔄 Format conversions: utilities to move between YOLO TXT and COCO / Pascal VOC / LabelMe JSON annotations.
- ⚙️ CLI + Python API:
hbb2obbandhbb2obb-evalcommands plus an importable API, with transparent visualizations of every step.
📋 Full Feature Overview
- HBB to OBB conversion: converts YOLO-format horizontal bounding boxes to oriented bounding boxes.
- Segmentation-based: uses state-of-the-art SAM models for accurate object boundary detection.
- Multiple model support: SAM, SAM2, SAM2.1, SAM3, Mobile SAM, and FastSAM families (details).
- Model ensemble: combine multiple models via majority voting for enhanced accuracy.
- Confidence scoring: a per-OBB quality score flags fallbacks and low-confidence conversions for triage (details).
- Evaluation tools: assess OBB accuracy against ground truth using IoU metrics.
- Hyperparameter optimization: search SAM inference resolutions and HBB scale factors for the best settings on your data.
- Visualization tools: render HBBs, segmentation masks, derived contours, and resulting OBBs.
- Format conversion utilities: convert between LabelMe / COCO / Pascal VOC annotations and YOLO TXT.
🚀 Planned Enhancements
- Improved morphological operations: more advanced operations for better mask refinement.
- Integration with other libraries: integrate with popular object detection libraries to alleviate the need for HBB annotations.
- Support for other segmentation models: extend compatibility beyond the SAM/FastSAM families.
🔗 Related Projects
HBB2OBB integrates with and complements several specialized tools:
-
Geo-trax 🛰️: georeferenced vehicle trajectory extraction pipeline for high-altitude drone imagery, built on YOLO detection and multi-object tracking. Its vehicle detector produces the horizontal bounding box annotations HBB2OBB consumes, so it can supply the HBB inputs for vehicle use cases (car, bus, truck, motorcycle).
-
Stabilo 🌀: Python library for video and trajectory stabilization using robust homography transformations. Supports various feature detectors, RANSAC algorithms, and user-defined masks.
-
Stabilo-Optimize 🎯: benchmarking and hyperparameter optimization framework for Stabilo. Evaluates stabilization performance through ground truth-free assessment using random perturbations.
Install
Create and activate a Python virtual environment (Python 3.9–3.13), then install from PyPI:
python3.11 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install hbb2obb
Also works with uv (uv pip install hbb2obb) and conda.
[!NOTE] SAM model weights are downloaded automatically by Ultralytics on first use into a
models/directory relative to your current working directory: run commands from a consistent location so weights are reused. The one exception is SAM 3, which must be downloaded manually (see below).
Alternatives: conda or uv
conda create -n hbb2obb python=3.11 -y
conda activate hbb2obb
uv (fastest; then uv pip install hbb2obb):
uv venv --python 3.11
source .venv/bin/activate # On Windows: .venv\Scripts\activate
Install from source (development / editable)
git clone https://github.com/rfonod/hbb2obb.git
cd hbb2obb && pip install -e '.[dev]'
The -e flag makes your local changes take effect without reinstalling; the [dev] extra adds pytest and ruff. For a plain install, use pip install . instead.
SAM 3 model weights (manual download required)
Unlike other SAM models, SAM 3 weights (sam3.pt) are not auto-downloaded by Ultralytics. To use SAM 3:
- Request access on the SAM 3 model page on Hugging Face.
- Once approved, download
sam3.pt. - Place
sam3.ptin themodels/directory (relative to where you run the conversion).
See the Ultralytics SAM 3 documentation for more.
Quick Start
A small sample dataset ships in data/. From the repository root:
# Convert the sample HBBs to OBBs (default single model) and save visualizations
hbb2obb data/images --save_img
# Higher accuracy with a model ensemble
hbb2obb data/images --sam_models sam_b sam_l sam2_b sam2.1_b
# Evaluate the converted OBBs against ground truth
hbb2obb-eval data/labels_obb_gt data/labels_obb -mp data/classes.yaml
Converted OBB annotations are written to data/labels_obb/. Run hbb2obb --help or hbb2obb-eval --help for all options.
Usage
Converting HBB to OBB
# Default single model (sam_b), HBBs read from <img_source>/../labels_hbb
hbb2obb /path/to/images --hbb_dir /path/to/hbb/annotations
# Model ensemble (majority voting across models)
hbb2obb /path/to/images --sam_models sam_b sam_l sam2_b sam2.1_b
# Scale HBBs: expand to recover cropped parts, or shrink conservative boxes
hbb2obb /path/to/images --scale_factors 0.1 # expand uniformly
hbb2obb /path/to/images --scale_factors -0.02 # shrink uniformly
hbb2obb /path/to/images --scale_factors 0.1 0.05 # short side / long side
# Save visualization images of the conversion
hbb2obb /path/to/images --save_img
Evaluating OBB Predictions
hbb2obb-eval /path/to/ground_truth /path/to/predictions
More CLI arguments
Run hbb2obb --help / hbb2obb-eval --help for the full list. Key conversion arguments:
--hbb_dir/-hd: directory of HBB annotations in YOLO TXT format (default:<img_source>/../labels_hbb).--obb_dir/-od: directory to save OBB annotations (default:<img_source>/../labels_obb).--sam_models/-sm: SAM model(s) to use (e.g.sam_b,sam_l,sam2_b,sam2.1_b,sam3,mobile_sam,FastSAM-s).--imgsz: SAM inference resolution (default: 1280).--scale_factors/-sf: factor(s) to scale HBBs (single value, or two values for short/long sides).--opening_kernel_percentage/-okp: morphological opening kernel size as a percentage of the mask's smaller dimension.--save_confidence: append a per-OBB confidence score as a 10th column in the output TXT files.--save_img,--viz_dir,--show_confidence, and--hide_hbb/--hide_obb/--hide_masks/--hide_segments/--hide_class_labels: visualization controls.--model_kwargs/-k: extra Ultralytics inference kwargs askey1=value1,key2=value2.
Key evaluation arguments:
--excluded_classes/-e: class IDs to exclude from evaluation.--iou_threshold/-t: IoU threshold for a match (default: 0.1).--class_agnostic/-ca: ignore class-label matching (useful for re-classified GT).--exclude_edge_cases/-exc,--edge_tolerance/-et,--img_width/-iw,--img_height/-ih: edge-case handling.--map_path/-mp: path to a label map YAML mapping class IDs to names.
Python API
Converting HBB to OBB: hbb2obb() processes a single image and returns the OBB annotations as a NumPy array:
from pathlib import Path
from hbb2obb.converter import hbb2obb, save_obb_annotations
img_path = Path("/path/to/images/img1.jpg")
# Single SAM model
obb_annotations = hbb2obb(
img_path=img_path,
hbb_dir="/path/to/hbb/annotations",
sam_models="sam_b",
imgsz=1280,
scale_factors=0.05,
opening_kernel_percentage=0.15,
save_img=True,
viz_dir="/path/to/save/visualizations",
)
# Model ensemble with per-side scale factors
obb_annotations = hbb2obb(
img_path=img_path,
hbb_dir="/path/to/hbb/annotations",
sam_models=["sam_b", "sam_l", "sam2_b", "sam2.1_b"],
scale_factors=[0.1, 0.05], # short side / long side
)
# Writes <obb_dir>/img1.txt, deriving the filename from img_path
save_obb_annotations(obb_annotations, "/path/to/save/obb/annotations", img_path)
# Also return per-OBB confidence scores, and write them as a 10th column
obb_annotations, confidences = hbb2obb(img_path=img_path, sam_models="sam_b", return_confidence=True)
save_obb_annotations(obb_annotations, "/path/to/save/obb/annotations", img_path, confidences=confidences)
Evaluating OBB predictions:
from pathlib import Path
from hbb2obb.evaluator import evaluate_obb, print_results
results = evaluate_obb(
gt_dir=Path("/path/to/ground_truth_annotations"),
pred_dir=Path("/path/to/predictions"),
iou_threshold=0.1,
class_agnostic=True, # optional: match regardless of class label
exclude_edge_cases=True, # optional: drop boxes at the image edge
img_width=3840,
img_height=2160,
)
print_results(results, "/path/to/label_map.yaml")
End-to-end workflow with LabelMe JSON annotations
Starting from LabelMe JSON annotations for HBB and OBB ground truth:
# 1. Convert HBB and OBB ground-truth JSON to YOLO TXT
python scripts/json2yolo.py project/json_hbb -td project/labels_hbb
python scripts/json2yolo.py project/json_obb_gt -td project/labels_obb_gt
# 2. Optimize hyperparameters to find the best settings
python scripts/optimize_hbb2obb.py project/images project/labels_obb_gt -sm sam_b sam_l sam2_b -n multi_sam
# 3. Inspect the best parameters, then convert with them
cat project/benchmark_results/multi_sam/summary.txt
hbb2obb project/images --hbb_dir project/labels_hbb --obb_dir project/labels_obb \
--sam_models sam_b sam_l --imgsz 1280 --scale_factors 0.05 \
--opening_kernel_percentage 0.15 --save_img --viz_dir project/visualizations
# 4. Evaluate against ground truth
hbb2obb-eval project/labels_obb_gt project/labels_obb -mp project/label_map.yaml
# 5. Convert OBBs back to JSON for review in LabelMe
python scripts/yolo2json.py project/labels_obb project/label_map.yaml -jd project/json_obb
labelme project/images --output project/json_obb --nodata
Utility Scripts
Format Conversion
# LabelMe JSON -> YOLO TXT (HBB and OBB); -mp label map optional
python scripts/json2yolo.py /path/to/json_dir -mp /path/to/label_map.yaml
# Official COCO instance JSON -> YOLO TXT (HBB only)
python scripts/json2yolo.py /path/to/instances.json --input_format coco -td /path/to/labels_yolo
# Pascal VOC XML -> YOLO TXT (HBB)
python scripts/voc2yolo.py /path/to/voc_xml_dir -td /path/to/labels_yolo
# YOLO TXT -> LabelMe-compatible JSON (HBB and OBB); label map required
python scripts/yolo2json.py /path/to/yolo /path/to/label_map.yaml
json2yolo.py defaults to LabelMe input (each JSON has imageHeight, imageWidth, shapes); use --input_format coco for official COCO instance files (images, annotations, categories, HBB only).
Hyperparameter Optimization
# Search inference resolutions x scale factors for one or more SAM models
python scripts/optimize_hbb2obb.py /path/to/images /path/to/ground_truth -sm sam_b sam_l sam2_b sam2.1_b -n multi_sam
# Visualize the results
python scripts/plot_optimization_results.py /path/to/optimization_results
Data Format
HBB annotations (input): YOLO TXT, one file per image; coordinates relative (0–1) or absolute px:
class_id x_center y_center width height
You bring your own HBB annotations. If you don't have any and your objects are vehicles (car, bus, truck, motorcycle), you can generate them from drone imagery with Geo-trax and feed its detections straight into HBB2OBB.
OBB annotations (output): YOLO TXT, one file per image; four corners in absolute px:
class_id x1 y1 x2 y2 x3 y3 x4 y4
With --save_confidence, a 10th column holds the per-OBB confidence score:
class_id x1 y1 x2 y2 x3 y3 x4 y4 confidence
hbb2obb-eval ignores the trailing confidence column, so evaluation works on either variant.
Label map (optional): YAML mapping class IDs to names:
0: Car
1: Bus
2: Truck
How It Works
HBB2OBB fits each OBB by prompting SAM with your HBBs, refining the resulting mask, and wrapping it in a minimum-area rotated box.
Conversion pipeline
- Load HBB annotations from YOLO TXT.
- Scale bounding boxes: positive factors expand HBBs (recover cropped parts), negative factors shrink them (tighten conservative boxes); short and long sides can be scaled differently.
- Segmentation: run SAM model(s) with the HBBs as prompts.
- Mask aggregation: with an ensemble, combine masks by majority voting; clip to the scaled HBB region; apply morphological opening.
- Contour extraction: extract the largest refined mask contour per object.
- OBB computation: fit a minimum-area oriented bounding box.
- Fallback: if no valid mask is found inside an HBB, keep the original HBB as the OBB (confidence
0.0). - Confidence: score each OBB in
[0, 1](see Confidence Scores). - Visualization (optional): overlay HBBs, masks, contours, and OBBs (colored by confidence).
Key characteristics:
- Label preservation: OBBs inherit the class label of their source HBB (no re-classification).
- Corrective effects: the transformation can recover cropped parts (positive scaling) and produce tighter boxes through precise segmentation.
Confidence Scores
Each OBB carries a heuristic quality score in [0, 1] that helps triage a converted dataset: high scores are trustworthy SAM fits, low scores warrant a look, and 0.0 marks a fallback where the original HBB was kept. It is a heuristic, not a calibrated probability. The detail crop below shows the score that --show_confidence prints next to each converted box.
The score is the product of two factors:
- Rectangularity: the fitted contour area divided by the area of its minimum-area rotated rectangle, i.e. how tightly the OBB wraps the segmented shape (
1.0for a perfectly rectangular object). - Ensemble consensus: the fraction of the per-model mask union that survived the majority vote, i.e. how strongly the SAM models agree. This is
1.0for a single model and equals the mask IoU for two models.
Enable it with --save_confidence (writes a 10th column to each output file). In the visualization, OBBs are always tinted on a green→red gradient by score, and --show_confidence prints the numeric value next to each box. When using the Python API, pass return_confidence=True to hbb2obb() to get the scores back alongside the OBBs.
Best Practices
- For optimal results, combine multiple SAM models, e.g.
--sam_models sam_b sam_l sam2_b sam2.1_b sam3. - Experiment with scale factors and inference resolutions based on your dataset.
- Run the hyperparameter optimizer to find the best settings for your data.
- Use class-agnostic evaluation when comparing against manually annotated ground truth with different class labels.
- Visualize the conversion to understand how the model interprets your HBBs.
Limitations
- Results depend on the quality of the input HBBs and the SAM models used; poor annotations or weak segmentation lead to inaccurate OBBs.
- Highly occluded or complex objects, where the HBB gives insufficient context, may not convert well.
Citation
If you use HBB2OBB in your research or software, please cite the archived release:
@software{fonod2026hbb2obb,
author = {Fonod, Robert},
title = {HBB2OBB: Horizontal to Oriented Bounding Box Conversion and Evaluation Tool},
year = {2026},
license = {MIT},
doi = {10.5281/zenodo.15151143},
url = {https://github.com/rfonod/hbb2obb}
}
Each GitHub release is automatically archived to Zenodo via the Zenodo–GitHub integration; see CITATION.cff for the latest version and DOI.
Contributing
Contributions are welcome! If you encounter issues or have suggestions, please open a GitHub Issue or submit a pull request.
License
This project is distributed under the MIT License. See the LICENSE file for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hbb2obb-1.3.0.tar.gz.
File metadata
- Download URL: hbb2obb-1.3.0.tar.gz
- Upload date:
- Size: 38.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c60811aa5bb7cc6f387efba6d88b7f836bd4d5ebd54b224ea33b80f4400ac98
|
|
| MD5 |
cf2ee491699771a14e30a5bf22dd71d9
|
|
| BLAKE2b-256 |
a9f4f1ceb5d8ba546c62951ab52547cd854ddd3fef1ac700e55ea79ae975235b
|
Provenance
The following attestation bundles were made for hbb2obb-1.3.0.tar.gz:
Publisher:
publish.yml on rfonod/hbb2obb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hbb2obb-1.3.0.tar.gz -
Subject digest:
8c60811aa5bb7cc6f387efba6d88b7f836bd4d5ebd54b224ea33b80f4400ac98 - Sigstore transparency entry: 2107578826
- Sigstore integration time:
-
Permalink:
rfonod/hbb2obb@955542715f8b9b762e416d02ccb574aadca3fda1 -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/rfonod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@955542715f8b9b762e416d02ccb574aadca3fda1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file hbb2obb-1.3.0-py3-none-any.whl.
File metadata
- Download URL: hbb2obb-1.3.0-py3-none-any.whl
- Upload date:
- Size: 25.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9ee793064cd5afe91f71d009d3943bc02948340490f7fe7797aeb49c769692c
|
|
| MD5 |
b204f3ead5500a9ca27c224f1afff3b9
|
|
| BLAKE2b-256 |
f9f5e083e3124bd50849ce52fea8ac79432b31fd5dbef51bd8cfcd02bc6dc3f8
|
Provenance
The following attestation bundles were made for hbb2obb-1.3.0-py3-none-any.whl:
Publisher:
publish.yml on rfonod/hbb2obb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hbb2obb-1.3.0-py3-none-any.whl -
Subject digest:
b9ee793064cd5afe91f71d009d3943bc02948340490f7fe7797aeb49c769692c - Sigstore transparency entry: 2107578997
- Sigstore integration time:
-
Permalink:
rfonod/hbb2obb@955542715f8b9b762e416d02ccb574aadca3fda1 -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/rfonod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@955542715f8b9b762e416d02ccb574aadca3fda1 -
Trigger Event:
release
-
Statement type: