detector¶
The detection behind hbb2obb-detect: horizontal boxes for images that have none. See the guide for a worked example.
from pathlib import Path
import cv2
from hbb2obb.detector import detect_hbb, save_hbb_annotations
img_path = Path("data/images/04433.jpg")
boxes = detect_hbb(cv2.imread(str(img_path)), model="geotrax") # (N, 6): class, xc, yc, w, h, conf
save_hbb_annotations(boxes, Path("data/labels_hbb"), img_path, scores=boxes[:, 5])
Detecting¶
detect_hbb
¶
detect_hbb(img: ndarray, model: str = 'geotrax', imgsz: int = None, conf: float = None, iou: float = None, classes: Sequence[int] = None, class_map: Dict[int, int] = None, max_det: int = None, device: str = None, model_kwargs: Dict[str, Any] = None, models_dir: Optional[Path] = None) -> ndarray
Detect horizontal bounding boxes in one image.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img
|
ndarray
|
Image as read by |
required |
model
|
str
|
Detector name, path or Hugging Face reference, as |
'geotrax'
|
imgsz
|
int
|
Inference resolution (default: the detector's own, 640 if it is not registered) |
None
|
conf
|
float
|
Confidence threshold (default: the detector's own) |
None
|
iou
|
float
|
NMS IoU threshold (default: the detector's own) |
None
|
classes
|
Sequence[int]
|
Class IDs to keep, in the detector's own numbering (default: the detector's own) |
None
|
class_map
|
Dict[int, int]
|
Remap the detector's class IDs onto yours; boxes of an unlisted class are dropped, so it selects and renumbers in one step |
None
|
max_det
|
int
|
Maximum detections per image |
None
|
device
|
str
|
Inference device, e.g. 'cpu', '0', 'mps' (default: Ultralytics picks) |
None
|
model_kwargs
|
Dict[str, Any]
|
Any other Ultralytics predictor arguments, passed through unchecked; a key
that is also given by name above raises |
None
|
models_dir
|
Optional[Path]
|
Weights directory (default: |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
An (N, 6) array of |
Source code in hbb2obb/detector.py
save_hbb_annotations
¶
save_hbb_annotations(hbb_annotations: ndarray, hbb_dir: Path, img_path: Path, scores: Union[Sequence[float], ndarray] = None, precision: int = 2, normalize: bool = False, img_shape: Tuple[int, int] = None) -> Path
Save HBB annotations to a text file, the format hbb2obb reads back.
Each line is class x_center y_center width height, in absolute pixels unless
normalize is set. When scores is provided, the confidence is appended as a 6th
field. Trailing zeros are trimmed, so a whole pixel is written as such rather than padded
to the full precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hbb_annotations
|
ndarray
|
(N, 5) or (N, 6) array; a 6th column is ignored in favour of |
required |
hbb_dir
|
Path
|
Directory to write into (default: |
required |
img_path
|
Path
|
Image the annotations belong to, which names the output file |
required |
scores
|
Union[Sequence[float], ndarray]
|
Per-box confidence, written as the trailing field |
None
|
precision
|
int
|
Decimal places for the coordinates |
2
|
normalize
|
bool
|
Write coordinates relative to the frame size instead of in pixels |
False
|
img_shape
|
Tuple[int, int]
|
(width, height) of the frame, required when |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
The path written. |
Source code in hbb2obb/detector.py
load_detector
¶
Load a detector by name or path, reusing a cached instance when one is already loaded.
Source code in hbb2obb/detector.py
resolve_weights
¶
Resolve a --model value to a weights file on disk, downloading it if it is not there yet.
Accepted, in this order: a name in SUPPORTED_DETECTORS; a path to an existing file; a
Hugging Face file, as a link or as <user>/<repo>/<path>.pt; any other http(s) link to a
checkpoint; and an Ultralytics model name such as yolo11s.pt. Downloads land in the models
directory beside the SAM checkpoints (models_dir, else $HBB2OBB_MODELS_DIR, else
models) under the file's own name, and Ultralytics names are left for Ultralytics itself to
fetch on first use. A path-like value that exists nowhere is an error rather than a guess.
Source code in hbb2obb/detector.py
Classes¶
parse_class_map
¶
Parse a --class_map string such as '2=0,5=1,7=2' into {2: 0, 5: 1, 7: 2}.
Source code in hbb2obb/detector.py
apply_class_map
¶
Renumber the class column of detection rows, dropping the classes the map does not name.
A detector trained on COCO calls a car 2 and a bus 5; {2: 0, 5: 1} turns those into the
0 and 1 of a four-class vehicle label map and discards everything else.
Source code in hbb2obb/detector.py
Merging with hand-drawn boxes¶
merge_detections
¶
merge_detections(manual: ndarray, detected: ndarray, iou_threshold: float = 0.5) -> MergeReport
Attach detector confidences to hand-drawn boxes without touching their geometry.
The hand-drawn set is authoritative: it stays as it is, in its own order, and each box only takes the confidence of the detection that overlaps it most. A box the detector missed gets 1.0, because a box somebody drew by hand is not less certain than one a model proposed, it is certain by construction. Detections that back no hand-drawn box are reported rather than added, since whether they are real objects the annotator missed is a judgement call.
Matching is greedy on IoU and class-agnostic, so a vehicle the detector called a truck and
the annotator called a car still matches; the disagreement is reported in conflicts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manual
|
ndarray
|
(M, 5) hand-drawn boxes as |
required |
detected
|
ndarray
|
(N, 6) detections as |
required |
iou_threshold
|
float
|
Minimum IoU for a detection to be considered the same object |
0.5
|
Returns:
| Type | Description |
|---|---|
MergeReport
|
A |
Source code in hbb2obb/detector.py
MergeReport
dataclass
¶
What merge_detections found, so that a caller can report it or review the leftovers.