Metadata-Version: 2.4
Name: nsharper
Version: 0.2.0
Summary: A lightweight computer-vision framework for building, training and benchmarking compact object detectors
License-Expression: MIT
Project-URL: Documentation, https://github.com/nsharper/nsharper/blob/main/docs.md
Keywords: computer-vision,object-detection,deep-learning,benchmark
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: torch>=2.1
Requires-Dist: torchvision>=0.16
Requires-Dist: numpy>=1.24
Requires-Dist: pillow>=9.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# nsharper

A lightweight Python framework for building and evaluating computer-vision
models with a deliberately simple API.

```text
Dataset → Model → Summary → Train → Test → Benchmark → Save
```

> Keep the model definition simple. Keep the workflow obvious.

Full documentation lives in [`docs.md`](docs.md).

## Install

```bash
pip install nsharper
```

## A complete project

```python
import nsharper as ns

train = ns.Dataset("dataset/train")
val = ns.Dataset("dataset/val")

model = ns.Model([
    ns.Conv(224, 224, 3, 16, 3, 2),
    ns.Conv(112, 112, 16, 32, 3, 2),
    ns.Conv(56, 56, 32, 64, 3, 2),
    ns.Detect(28, 28, 64, 10)
])

model.summary()

model.fit(train, epochs=50, batch_size=32, lr=0.001, val=val)

results = model.test(val)
results.show()

model.save("supercharger.nsh")
```

## What each command answers

| Command       | Question it answers                            |
| ------------- | ---------------------------------------------- |
| `summary()`   | What is my model, and what does it cost?       |
| `test()`      | How well does it do on *my* dataset?           |
| `benchmark()` | How well does it do on COCO?                   |

`summary()` reports parameters, FLOPs, model size, memory and measured
latency/FPS on every backend the machine offers, plus a Mermaid diagram of
the graph. It needs no trained weights, so architectures can be compared
before spending time on training.

## Layers

| Layer | Signature | Effect on shape |
| ----- | --------- | --------------- |
| `Conv` | `(h, w, in, out, kernel, stride)` | grid ÷ stride, channels → `out` |
| `DepthwiseConv` | `(h, w, channels, kernel, stride)` | grid ÷ stride, channels unchanged |
| `PointwiseConv` | `(h, w, in, out)` | grid unchanged, channels → `out` |
| `SeparableConv` | `(h, w, in, out, kernel, stride)` | grid ÷ stride, channels → `out` |
| `TransposedConv` | `(h, w, in, out, kernel, stride)` | grid × stride, channels → `out` |
| `DeformableConv` | `(h, w, in, out, kernel, stride)` | grid ÷ stride, channels → `out` |
| `BatchNorm` | `(h, w, channels)` | unchanged |
| `GroupNorm` | `(h, w, channels, groups)` | unchanged |
| `Activation` | `(h, w, channels, kind)` | unchanged |
| `MaxPool` / `AvgPool` | `(h, w, channels, kernel, stride)` | grid ÷ stride, channels unchanged |
| `Upsample` | `(h, w, channels, scale, mode)` | grid × scale, channels unchanged |
| `Dropout` | `(h, w, channels, p)` | unchanged |
| `Residual` | `(layers)` | unchanged — the block must return it |
| `Detect` | `(h, w, channels, classes)` | grid unchanged, produces detections |

Convolution layers carry batch norm and a SiLU activation by default;
`norm=False` and `activation=None` hand those back to explicit layers, and
`groups=` / `dilation=` cover grouped and dilated convolution. Every layer
and argument is documented in [`docs.md`](docs.md) sections 10–21.

```python
model = ns.Model([
    ns.Conv(224, 224, 3, 16, 3, 2),
    ns.SeparableConv(112, 112, 16, 32, 3, 2),
    ns.Residual([
        ns.Conv(56, 56, 32, 32, 3, 1),
        ns.Conv(56, 56, 32, 32, 3, 1, activation=None)
    ]),
    ns.MaxPool(56, 56, 32, 2, 2),
    ns.PointwiseConv(28, 28, 32, 64),
    ns.Detect(28, 28, 64, 10)
])
```

`DeformableConv` has gradients on CPU and CUDA only; training a model
containing it on MPS is refused before the first epoch.

## Converting a COCO dataset

Public detection data usually ships in COCO's single-JSON format. The
`nsharper` command converts one split of it into the layout below:

```bash
nsharper convert coco/
```

That is the whole command. It handles both common layouts — one
`annotations/` directory for the whole release, or a directory per split
with its own `images/` folder — finds the images each set of annotations
describes, and writes `dataset/train` and `dataset/val`. Boxes
become normalized `[x1, y1, x2, y2]`, categories become class names, crowd
regions and empty images are skipped, and images are symlinked rather than
copied (`--copy` if the dataset must stand on its own). `--classes person,car`
keeps a subset. The same conversion is available as `nsharper.cli.convert()`.

## Dataset layout

```text
dataset/
├── images/001.jpg
└── labels/001.yaml
```

```yaml
cat:
  - [0.12, 0.20, 0.43, 0.61]
dog:
  - [0.55, 0.31, 0.88, 0.72]
```

Boxes are normalized `[x1, y1, x2, y2]`, all coordinates between `0.0` and `1.0`.

## Devices and the data path

`"auto"` (default) walks CUDA → MPS → CPU and takes the first available
backend. `"cpu"`, `"cuda"`, `"mps"` and `"gpu"` select one explicitly.

```python
model.fit(train, epochs=50, device="mps")
ns.device()   # 'mps'
```

A small detector is rarely compute-bound, so `fit()`, `test()` and
`benchmark()` also manage how images reach the device. A dataset that fits
in device memory is decoded once and kept there (`cache="auto"`), pixels
travel as `uint8` and become floats on the device, and on CUDA the loader
runs worker processes with pinned memory so the next batch is copied while
the current one trains. `cache=False` streams from disk instead;
`workers=n` sets the loader processes by hand.

```text
Data: 1.7 GB cached on cuda
```

## Architecture errors surface immediately

```python
ns.Model([
    ns.Conv(224, 224, 3, 16, 3, 2),
    ns.Conv(112, 112, 64, 32, 3, 2),
])
```

```text
error: channel mismatch

layer: Conv
expected: 64
received: 16
```

## COCO benchmark

COCO is not bundled. Point `benchmark()` at a local copy — by argument, via
`NSHARPER_COCO`, or by placing it in `~/.nsharper/coco`:

```python
ns.benchmark("supercharger.nsh")
ns.benchmark(model, data="~/datasets/coco")
```

Both the nsharper layout (`images/` + `labels/`) and the official COCO
layout (`val2017/` + `annotations/instances_val2017.json`) are read.

## Try it

`main.py` runs the whole workflow on a synthetic dataset in about a minute:

```bash
python main.py
```

The examples break the same workflow into single steps:

```bash
python examples/make_shapes.py dataset   # generate a synthetic dataset
python examples/train.py                 # train, test and save a detector
python examples/predict.py dataset/val/images/0000.jpg
python examples/benchmark.py             # benchmark saved models
python examples/architectures.py         # compare two architectures' cost
```

## Development

```bash
pip install -e ".[dev]"
pytest
```
