Recipe — Python object detection
For LYNX SDK 1.0. Complete, end-to-end. Install:
install/python.md. API:api/python.md. Conventions:api/conventions.md.
Goal: load a model, run detection on an image, get boxes + labels + scores. Model: lynx-basic (keyless, 80 COCO classes — see models/catalog.md).
The whole thing
lynx.open and model.predict are synchronous and raise on failure; the first open downloads the model, so do it once and reuse the Model. predict takes an image path directly (or a (H, W, 3) uint8 RGB numpy array). A detection's class name is resolved through model.classes — there's no Detection.class_name.
1import lynx2from lynx.errors import LynxError, ModelNotFound3 4def detect(image_path, min_confidence=0.4):5 """Load lynx-basic (keyless), run detection, print each label + score + box."""6 # Open once. First call downloads + verifies + caches the model.7 with lynx.open("lynx-basic") as model:8 # conf is a bare float threshold; pass a path or an (H, W, 3) uint8 RGB array.9 frame = model.predict(image_path, conf=min_confidence)10 11 if len(frame.detections) == 0:12 print(f"No objects found in {image_path!r}.")13 return14 15 print(f"{len(frame.detections)} detection(s) in {image_path!r}:")16 for d in frame.detections:17 name = model.classes(d.class_id).name # e.g. "PERSON"18 x1, y1, x2, y2 = d.box # [x1, y1, x2, y2] input pixels19 print(f" {name:16} {d.confidence:.2f} "20 f"box=({x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f})")21 22 23if __name__ == "__main__":24 import sys25 image_path = sys.argv[1] if len(sys.argv) > 1 else "photo.jpg"26 try:27 detect(image_path)28 except ModelNotFound as e:29 print("model not found (bad slug/version):", e.message)30 except LynxError as e:31 print(f"lynx error [{e.code}]: {e.message}")Working with the vectorized columns
frame.detections also exposes parallel numpy columns — handy for filtering or plotting without a Python loop:
1dets = frame.detections2boxes = dets.boxes # (N, 4) float32, each [x1, y1, x2, y2]3scores = dets.confidence # (N,) float324ids = dets.class_id # (N,) int5 6# boolean-index to a sub-collection view, then iterate it:7people = dets[ids == model.classes["PERSON"]]8print(f"{len(people)} person(s)")Passing pixels instead of a path
If you already have the image decoded (e.g. from Pillow or a camera), hand predict the array directly — it must be C-contiguous (H, W, 3) uint8 RGB:
1import numpy as np2from PIL import Image # pip install pillow3 4rgb = np.asarray(Image.open("photo.jpg").convert("RGB"))5frame = model.predict(rgb, conf=0.4)Notes
boxis[x1, y1, x2, y2]in the input image's pixels (top-left origin).conf=accepts a barefloat(raw threshold), aConfMode(e.g.lynx.ConfMode.MAX_PRECISIONfor the model's calibrated point), aConf, orNonefor the model's calibrated default.- Detection only needs the box head, which every model has. For pose/segmentation/depth on the same model, open with those tasks (or realize them per-frame) and read
frame.detections.run([lynx.Task.POSE])→d.keypoints/d.mask, andframe.depth_map/d.depthfor depth — seeapi/python.md. - Reuse one loaded
Modelacross calls; don't reopen per image. Run the firstopenoff the UI thread (it downloads).