Metadata-Version: 2.4
Name: pybx
Version: 0.7.0
Summary: A simple python module to generate anchor boxes for object detection tasks.
Author-email: Geevarghese George <4496097+thatgeeman@users.noreply.github.com>
License: MIT
Project-URL: Repository, https://github.com/thatgeeman/pybx
Project-URL: Documentation, https://thatgeeman.github.io/pybx
Keywords: anchor-box,anchor-boxes,bounding-boxes,computer-vision,deep-learning,multi-box,multibox,multibox-detector,object-detection,python,rcnn,rcnn-model,single-shot-detection,single-shot-detector,single-shot-multibox-detector
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore>=1.5.27
Requires-Dist: numpy>=1.21.6
Provides-Extra: viz
Requires-Dist: matplotlib>=3.5; extra == "viz"
Provides-Extra: opencv
Requires-Dist: opencv-python>=4.7.0.72; extra == "opencv"
Provides-Extra: all
Requires-Dist: matplotlib>=3.5; extra == "all"
Requires-Dist: opencv-python>=4.7.0.72; extra == "all"
Provides-Extra: dev
Requires-Dist: black; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Requires-Dist: ipykernel; extra == "dev"
Requires-Dist: matplotlib>=3.5; extra == "dev"
Requires-Dist: nbdev<4,>=3.3.15; extra == "dev"
Requires-Dist: opencv-python>=4.7.0.72; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# PyBx


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

[![PyPI version](https://badge.fury.io/py/pybx.svg)](https://badge.fury.io/py/pybx)
[![Open In Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/thatgeeman/pybx/blob/master/examples/pybx_walkthrough_0.4.ipynb)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/thatgeeman/pybx)

A simple python package to generate anchor boxes for multi-box and single shot object detection models.

Calculated anchor boxes are in `pascal_voc` format by default.

### Installation

``` shell
pip install pybx
```

The core package only installs the dependencies needed for bounding-box and anchor operations. Install optional features when needed:

``` shell
pip install "pybx[viz]"     # Matplotlib visualization
pip install "pybx[opencv]"  # image loading and resizing
pip install "pybx[all]"     # both optional features
```

If an optional feature is used without its dependency, PyBx raises an error containing the appropriate installation command. Loading an image from disk and displaying it requires `pybx[all]`.

### Usage

To calculate the anchor boxes for a single feature size and
aspect ratio, given the image size:

``` python
from pybx import anchor, ops

image_sz = (256, 256)
feature_sz = (10, 10)
asp_ratio = 1 / 2.0

coords, labels = anchor.bx(image_sz, feature_sz, asp_ratio)
```

100 anchor boxes of `asp_ratio` 0.5 is generated along with [unique labels](../data/README.md):

``` python
len(coords), len(labels)
```

    (100, 100)

The anchor box labels are especially useful, since they are pretty descriptive:

``` python
coords[-1], labels[-1]
```

    ([234, 225, 252, 256], 'a_10x10_0.5_99')

To calculate anchor boxes for **multiple** feature sizes and
aspect ratios, we use `anchor.bxs` instead:

``` python
feature_szs = [(10, 10), (8, 8)]
asp_ratios = [1.0, 1 / 2.0, 2.0]

coords, labels = anchor.bxs(image_sz, feature_szs, asp_ratios)
```

All anchor boxes are returned as `ndarrays` of shape `(N,4)` where N
is the number of boxes.

The box labels are even more important now, since they help you uniquely identify
to which feature map size or aspect ratios they belong to.

``` python
coords[101], labels[101]
```

    (array([29,  0, 47, 30]), 'a_10x10_0.5_1')

``` python
coords[-1], labels[-1]
```

    (array([217, 228, 256, 251]), 'a_8x8_2.0_63')

#### [`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx) methods

Box coordinates (with/without labels) in any format
(usually `ndarray`, `list`, `json`, `dict`)
can be instantialized as a [`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx), exposing many useful
methods and attributes of [`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx).
For example to calculate the area of each box iteratively:

``` python
from pybx.basics import *

# passing anchor boxes and labels from anchor.bxs()
print(coords.shape)

boxes = mbx(coords, labels)
type(boxes)
```

    (492, 4)

    pybx.basics.MultiBx

``` python
len(boxes)
```

    492

``` python
areas = [b.area for b in boxes]
```

Each annotation in the [`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx) object `boxes` is also a [`BaseBx`](https://thatgeeman.github.io/pybx/basics.html#basebx)
with its own set of methods and properties.

``` python
boxes[-1]
```

    BaseBx(coords=[[217, 228, 256, 251]], label=['a_8x8_2.0_63'])

``` python
boxes[-1].coords, boxes[-1].label
```

    ([[217, 228, 256, 251]], ['a_8x8_2.0_63'])

[`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx) objects can also be “added” which stacks
them vertically to create a new [`MultiBx`](https://thatgeeman.github.io/pybx/basics.html#multibx) object:

``` python
boxes_true = mbx(coords_json)  # annotation as json records
len(boxes_true)
```

    2

``` python
boxes_anchor = mbx(coords_numpy)  # annotation as ndarray
len(boxes_anchor)
```

    492

``` python
boxes_true.coords
```

    [{'x_min': 130, 'y_min': 63, 'x_max': 225, 'y_max': 180, 'label': 'clock'},
     {'x_min': 13, 'y_min': 158, 'x_max': 90, 'y_max': 213, 'label': 'frame'}]

``` python
boxes_anchor.coords
```

    array([[  0,   0,  25,  25],
           [ 25,   0,  51,  25],
           [ 51,   0,  76,  25],
           ...,
           [153, 228, 198, 251],
           [185, 228, 230, 251],
           [217, 228, 256, 251]])

``` python
boxes = boxes_true + boxes_anchor
```

``` python
len(boxes)
```

    494

# Use ground truth boxes for model training

``` python
from pybx.anchor import get_gt_thresh_iou, get_gt_max_iou
from pybx.vis import VisBx
```

``` python
image_sz
```

    (256, 256)

``` python
boxes_true
```

    MultiBx(coords: 2, labels: 2)

Calculate candidate anchor boxes for many aspect ratios and scales.

``` python
feature_szs = [(10, 10), (3, 3), (2, 2)]
asp_ratios = [0.3, 1 / 2.0, 2.0]

anchors, labels = anchor.bxs(image_sz, feature_szs, asp_ratios)
```

Wrap using pybx methods. This step is not necessary but convenient.

``` python
boxes_anchor = get_bx(anchors, labels)
len(boxes_anchor)
```

    341

The following function returns a [`MatchResult`](https://thatgeeman.github.io/pybx/anchor.html#matchresult) containing the matched boxes, anchor indices, IoUs, masks, and box IDs. Every mapping is keyed by stable box IDs rather than class labels, so multiple objects can share the same class. Supply application IDs (including UUIDs) when identity must survive reordering; otherwise deterministic zero-based input positions are used.

``` python
match_result = get_gt_max_iou(
    true_annots=boxes_true,
    anchor_boxes=boxes_anchor,  # if plain numpy, pass anchor_boxes and anchor_labels
    box_ids=["clock-1", "frame-1"],  # optional; defaults to 0..N-1
    update_labels=False,  # whether to replace ground truth labels with true labels
    positive_boxes=1,  # can request extra boxes
)
gt_anchors = match_result.matched_boxes
```

``` python
gt_anchors
```

    {'clock-1': BaseBx(coords=[[156, 0, 227, 180]], label=['a_2x2_0.3_1']),
     'frame-1': BaseBx(coords=[[12, 152, 72, 256]], label=['a_3x3_0.5_6'])}

``` python
all_gt_anchors = gt_anchors["clock-1"] + gt_anchors["frame-1"]
all_gt_anchors
```

    /work1/u31l94/pybx/pybx/basics.py:599: BxViolation: Change of object type imminent if trying to add <class 'pybx.basics.BaseBx'>+<class 'pybx.basics.BaseBx'>. Use <class 'pybx.basics.BaseBx'>+<class 'pybx.basics.BaseBx'> instead or basics.stack_bxs().
      warnings.warn(

    MultiBx(coords: 2, labels: 2)

``` python
v = VisBx(pth="../data/", img_fn="image.jpg", image_sz=image_sz)
v.show(all_gt_anchors, color={"a_2x2_0.3_1": "red", "a_3x3_0.5_6": "red"})
```

![](index_files/figure-commonmark/cell-27-output-1.png)

More exploratory stuff in the [walkthrough notebook](https://github.com/thatgeeman/pybx/blob/master/examples/pybx_walkthrough_0.4.ipynb) or [![Open In Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/thatgeeman/pybx/blob/master/examples/pybx_walkthrough_0.4.ipynb)
