sezer-muhammed's picture
feat: add fold metadata and paginated results
3de92d7
|
Raw
History Blame Contribute Delete
4.25 kB
# MRI Inference API
Base URL (local): `http://localhost:7860`
Base URL (HF Space): `https://<your-space>.hf.space`
---
## Endpoints
### `GET /models`
Returns the list of available model checkpoint names.
**Response**
```json
{
"models": ["small3dresnet_centiloid_v1"]
}
```
---
### `POST /inference`
Runs centiloid regression on an uploaded MRI scan and stores the result.
Re-submitting the same `filename` + `model_name` pair **overwrites** the existing row.
**Request**`multipart/form-data`
| Field | Type | Required | Description |
|---|---|---|---|
| `file` | file | yes | `.nii`, `.nii.gz`, or `.tar` / `.tar.gz` containing a `.nii` |
| `model_name` | string | yes | Must match a name returned by `GET /models` |
| `label` | string | no | Ground-truth centiloid label (for tracking) |
| `fold` | integer | no | Optional fold id, `0`-`10`, for train/val/test analysis splits |
**curl**
```bash
curl -X POST http://localhost:7860/inference \
-F "file=@subject_001.nii.gz" \
-F "model_name=small3dresnet_centiloid_v1" \
-F "label=38.5"
```
**Python (requests)**
```python
import requests
with open("subject_001.nii.gz", "rb") as f:
r = requests.post(
"http://localhost:7860/inference",
files={"file": f},
data={"model_name": "small3dresnet_centiloid_v1", "label": "38.5"},
)
print(r.json())
```
**Response `200`**
```json
{
"id": 1,
"filename": "subject_001.nii.gz",
"model_name": "small3dresnet_centiloid_v1",
"centiloid": 42.317,
"raw_output": 0.648231,
"label": "38.5",
"fold": null
}
```
| Field | Description |
|---|---|
| `centiloid` | Predicted centiloid value (inverse-transformed: `sinh(raw_output) × 50`) |
| `raw_output` | Raw model output in asinh-transformed space |
| `label` | Ground-truth label as provided, or `null` |
| `fold` | Optional fold id, or `null` |
**Errors**
| Status | Reason |
|---|---|
| `400` | Empty file |
| `404` | `model_name` not found in `checkpoints/` |
| `422` | Preprocessing or inference failed (bad NIfTI, no valid voxels, etc.) |
---
### `GET /results`
Returns paginated past inference results, most recent first.
Query params:
| Param | Type | Default | Description |
|---|---:|---:|---|
| `limit` | integer | `250` | Page size, max `1000` |
| `offset` | integer | `0` | Row offset |
| `fold` | integer | none | Optional fold filter, `0`-`10` |
**curl**
```bash
curl "http://localhost:7860/results?limit=250&offset=0"
```
**Response `200`**
```json
{
"count": 2,
"limit": 250,
"offset": 0,
"has_more": false,
"results": [
{
"id": 2,
"filename": "subject_002.nii.gz",
"model_name": "small3dresnet_centiloid_v1",
"centiloid": 87.14,
"raw_output": 1.053812,
"label": null,
"fold": null,
"created_at": "2026-05-24T10:31:00.123456"
},
{
"id": 1,
"filename": "subject_001.nii.gz",
"model_name": "small3dresnet_centiloid_v1",
"centiloid": 42.317,
"raw_output": 0.648231,
"label": "38.5",
"fold": 2,
"created_at": "2026-05-24T10:28:44.987654"
}
]
}
```
---
### `GET /results/done`
Returns only completed `(filename, model_name)` pairs for resume scripts.
```bash
curl http://localhost:7860/results/done
```
---
### `GET /results/folds`
Returns the fold ids currently present in the DB.
```json
{ "folds": [0, 1, 2] }
```
---
### `PATCH /results/fold`
Updates fold values in bulk. Use either `id` or `filename + model_name`. Set `fold` to `null` to clear it.
```bash
curl -X PATCH http://localhost:7860/results/fold \
-H "Content-Type: application/json" \
-d '{"updates":[{"filename":"subject_001.nii.gz","model_name":"small3dresnet_centiloid_v1","fold":2}]}'
```
```json
{ "updated": 1, "missing": [] }
```
---
## Adding a New Model
1. Place the `.ckpt` file in the `checkpoints/` directory.
2. The file stem becomes the `model_name` — e.g. `checkpoints/small3dresnet_v2.ckpt``"small3dresnet_v2"`.
3. No restart required; `GET /models` picks it up dynamically.
> **Note:** Checkpoints must be PyTorch Lightning `.ckpt` files saved from `CentiloidRegressorModule`. The API extracts `hyper_parameters.model_config` and `hyper_parameters.train_config` automatically.