Visão computacional (ONNX)¶
APIs de classificação, detecção e segmentação rodando em ONNX Runtime,
via ort-vision-sdk. O extra
[vision] traz o motor de inferência; o módulo
tempest_fastapi_sdk.vision adiciona a camada que falta pra uma API: os
schemas Pydantic de resposta e os mappers que convertem o
resultado do modelo neles.
Submódulo, não top-level
Como cache/queue/tasks, a visão é pesada (ONNX Runtime) e fica
no submódulo: from tempest_fastapi_sdk.vision import Detector.
Acessar Detector/Classifier/Segmenter sem o extra instalado
levanta um ImportError claro apontando pra [vision]. Os schemas e
mappers não dependem do extra — importam sempre.
Detecção¶
Detector carrega um modelo ONNX (YOLO por padrão) e roda async_predict
(async via asyncio.to_thread). Cada chamada devolve list[...] de
tamanho 1 — pegue o [0] e mapeie pros schemas:
# src/api/routers/vision.py
from fastapi import APIRouter, UploadFile
from tempest_fastapi_sdk.vision import DetectionSchema, Detector, to_detection_schemas
router = APIRouter(prefix="/api/vision", tags=["vision"])
# Carregue o modelo uma vez (no startup / singleton), não por request.
detector = Detector("models/yolov8n.onnx", labels="coco")
@router.post("/detect")
async def detect(file: UploadFile) -> list[DetectionSchema]:
"""Detecta objetos na imagem enviada."""
results = (await detector.async_predict(await file.read()))[0]
return to_detection_schemas(results)
Cada DetectionSchema traz class_id, class_name, confidence e
box (x1/y1/x2/y2 em pixels). Sem deteções → [].
Carregue o modelo uma vez
Instanciar Detector lê e otimiza o arquivo ONNX — caro. Faça no
startup (ou um singleton em app.state / dependency) e reuse entre
requests. async_predict já joga a inferência numa thread, então não
bloqueia o event loop.
Classificação¶
Classifier devolve o top-1 + a lista ranqueada. to_classification_schema
retorna um ClassificationSchema (não uma lista):
from tempest_fastapi_sdk.vision import (
ClassificationSchema,
Classifier,
to_classification_schema,
)
classifier = Classifier("models/resnet.onnx", labels="imagenet")
@router.post("/classify")
async def classify(file: UploadFile) -> ClassificationSchema:
"""Classifica a imagem (top-1 + probabilidades top-k)."""
results = (await classifier.async_predict(await file.read(), top_k=5))[0]
return to_classification_schema(results)
ClassificationSchema: class_id/class_name/confidence (top-1) +
probabilities: list[ClassProbabilitySchema] (ranqueado).
Segmentação¶
Segmenter é como o detector mas com máscaras. to_segmentation_schemas
devolve box + label por instância — a máscara em pixels é omitida do
JSON (raramente é o que uma API quer; leia de SegmentationResult.mask
quando precisar dos pixels):
from tempest_fastapi_sdk.vision import (
SegmentationSchema,
Segmenter,
to_segmentation_schemas,
)
segmenter = Segmenter("models/yolov8n-seg.onnx", labels="coco")
@router.post("/segment")
async def segment(file: UploadFile) -> list[SegmentationSchema]:
results = (await segmenter.async_predict(await file.read()))[0]
return to_segmentation_schemas(results)
Inputs aceitos + execução¶
async_predict aceita os mesmos inputs do ort-vision-sdk: caminho,
bytes, numpy.ndarray ou imagem PIL — então await file.read() (bytes)
entra direto.
| Método | Quando usar |
|---|---|
predict(img) |
Síncrono — scripts, jobs offline. |
async_predict(img) |
Default em FastAPI — roda via asyncio.to_thread, não bloqueia o loop. |
ort_async_predict(img) |
run_async nativo do ORT — alta concorrência. |
Extras de aceleração
GPU: tempest-fastapi-sdk[vision] + pip install ort-vision-sdk[gpu]
(troca pra onnxruntime-gpu). Backend OpenCV de imagem:
ort-vision-sdk[opencv]. Escolha os providers no construtor
(Detector(..., providers=[...])).
Recap¶
uv add "tempest-fastapi-sdk[vision]"; importe detempest_fastapi_sdk.vision(submódulo).Detector/Classifier/Segmenter(lazy;ImportErrorclaro sem o extra).- Carregue o modelo uma vez; use
async_predictno FastAPI. - Pegue o
[0]do retorno e mapeie:to_detection_schemas,to_classification_schema,to_segmentation_schemas. - Os schemas (
DetectionSchema/ClassificationSchema/SegmentationSchema) serializam direto na resposta; máscara de pixels fica de fora do JSON.