Dataset Browser¶
hotcoco ships a lightweight visual browser for COCO datasets — no FiftyOne, no heavy dependencies. Two entry points:
coco.browse()— launches inline in Jupyter; opens a local server otherwisecoco explore— CLI subcommand for standalone use
Both require the [browse] optional extra:
pip install hotcoco[browse]
Quick start¶
from hotcoco import COCO
coco = COCO("instances_val2017.json", image_dir="/data/coco/val2017/")
coco.browse()
That's it. A local server starts and a browser tab opens showing a scrollable grid of annotated thumbnails with a lightbox for full-resolution detail.
From the command line:
coco explore \
--gt instances_val2017.json \
--images /data/coco/val2017/
UI overview¶
┌──────────────────────────────────────────────────────────────────┐
│ hotcoco · dataset browser │
├──────────────┬───────────────────────────────────────────────────┤
│ FILTERS │ Thumbnail grid │
│ │ │
│ Category │ [img] [img] [img] [img] │
│ [dropdown] │ [img] [img] [img] [img] │
│ │ [img] [img] [img] [img] │
│ Min Score │ ↓ infinite scroll │
│ [slider] │ │
│ │ │
│ [Shuffle ⇄] │ │
│ 24 of 5000 │ │
├──────────────┴───────────────────────────────────────────────────┤
│ │
│ Lightbox (click any thumbnail): │
│ ┌─────────────────────────────┬──────────────┐ │
│ │ Full-res image + canvas │ Annotations │ │
│ │ overlay (bbox, segm, kpts) │ list sidebar │ │
│ │ │ │ │
│ │ [☑ Boxes] [☑ Segm] [☑ KP] │ ● person GT │ │
│ │ [☑ GT] [☑ DT] │ ● car 0.92│ │
│ └─────────────────────────────┴──────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Sidebar controls:
| Control | What it does |
|---|---|
| Category dropdown | Filter to images containing the selected categories (multi-select) |
| Min Score slider | Filter detections below a confidence threshold (when DT loaded) |
| Shuffle | Randomize the display order |
| N of M images | Live image count for the current filter |
Thumbnail grid:
- Annotated thumbnails with bounding boxes rendered server-side
- Infinite scroll — new batches load automatically as you scroll down
- Click any thumbnail to open the lightbox
Lightbox:
- Full-resolution image with a canvas overlay for annotations
- Hover any annotation to highlight it — the sidebar and canvas stay in sync
- Toggle layers (Boxes / Segments / Keypoints) and sources (GT / DT) instantly
- Scroll to zoom, drag to pan, double-click to reset
- Arrow keys navigate between images; Escape closes the lightbox
Viewing detections¶
Pass detection results to browse() and hotcoco overlays your model's predictions
alongside ground truth:
from hotcoco import COCO
coco = COCO("instances_val2017.json", image_dir="/data/coco/val2017/")
# Pass a path — auto-loaded via load_res()
coco.browse(dt="bbox_results.json")
# Or pass a COCO object you already have
results = coco.load_res("bbox_results.json")
coco.browse(dt=results)
From the command line:
coco explore \
--gt instances_val2017.json \
--images /data/coco/val2017/ \
--dt bbox_results.json
What you get with dt loaded:
| Feature | Description |
|---|---|
| GT labels | Ground truth annotations with solid bounding boxes |
| DT labels | Detection predictions with dashed bounding boxes |
| Score display | Confidence score shown on each detection label |
| Sources toggle | Show/hide ground truth and detections independently |
| Min confidence slider | Filter detections below a score threshold (0–1) |
GT and DT use the same per-category color palette so you can compare spatially. When no DT is loaded the browser behaves exactly as before (no slider, no source toggles).
Tip
Detections often lack segmentation masks. When segm is selected but a detection
has no mask, the browser falls back to its bounding box automatically.
image_dir¶
The browser needs to know where your images live. Pass it at construction:
coco = COCO("annotations.json", image_dir="/data/images/")
coco.browse()
Or set it after the fact:
coco = COCO("annotations.json")
coco.image_dir = "/data/images/"
coco.browse()
Or pass it directly to browse() (overrides image_dir on the object):
coco.browse(image_dir="/different/path/")
image_dir is propagated automatically through filter, split, sample,
and load_res, so a filtered subset keeps the same path:
people = coco.filter(cat_ids=[1])
people.browse() # image_dir carried over from coco
Annotation rendering¶
| Annotation type | How it's rendered |
|---|---|
| Bounding box | Canvas overlay — solid stroke for GT, dashed for DT |
| Oriented bounding box (OBB) | Rotated rectangle polygon — same solid/dashed convention; hover hit-testing follows the rotated shape |
| Segmentation | Canvas polygon fill + stroke |
| Keypoints | Dots + skeleton lines on canvas |
All annotations are rendered client-side on an HTML Canvas overlay, so they stay crisp at any zoom level. Colors are assigned per category deterministically — the same category always gets the same color across all images.
If an image file is missing from image_dir, a gray placeholder is shown instead
of raising an error.
Responsive layout¶
The browser adapts to different viewport sizes, making it suitable for Jupyter IFrames, side-by-side notebook layouts, and standalone browser windows:
- Wide (800px+): Sidebar on the left, gallery grid on the right
- Medium (480–800px): Toolbar mode — controls collapse to a compact horizontal bar
- Narrow (<480px): Minimal toolbar, smaller thumbnails, full-screen lightbox
The lightbox switches between side-by-side (image + info panel) and stacked (image above, annotations below) layout depending on available width.
CLI options¶
coco explore \
--gt <annotations.json> \
--images <images_dir/> \
[--dt <results.json>] \
[--batch-size 12] \
[--port 7860]
Advanced: create_app¶
For full control over launching, use create_app directly:
from hotcoco import COCO
from hotcoco.server import create_app, run_server
coco = COCO("annotations.json", image_dir="/data/images/")
app = create_app(coco, batch_size=24)
run_server(app, port=7861, open_browser=True)
create_app returns a FastAPI app. You can mount it inside a larger application
or run it with any ASGI server.
Eval dashboard¶
When you pass detection results, the browse server adds an interactive
Dashboard page at /dashboard. Navigate to it via the Gallery ↔ Dashboard
pills in the sidebar.
ev = COCOeval(coco, coco.load_res("results.json"), "bbox")
ev.evaluate(); ev.accumulate(); ev.summarize()
coco.browse(eval=ev, image_dir="images/")
# click "Dashboard" in the sidebar
The dashboard shows:
- KPI tiles — AP, AP50, AP75, AR100 at a glance
- PR curves — IoU-sweep precision-recall across 10 thresholds
- Per-category AP — ranked leaderboard with click-through to gallery
- Confusion matrix — interactive heatmap; click a cell to browse those misclassifications
- TIDE error breakdown — classification, localization, duplicate, background, missed
- Calibration — reliability diagram with ECE/MCE
- Per-image F1 — histogram colored by error profile (perfect, FP-heavy, FN-heavy, mixed)
- Label errors — suspected annotation mistakes; click a row to view the image
All charts use the same dark theme as the gallery. The layout is fully responsive — from narrow Jupyter panes (~400px) to wide monitors (1600px+).