update from asus 106

This commit is contained in:
asus committed 2026-08-05 15:56:11 +07:00
1 parent 6637fb1302
commit 8285400254
28 files changed
+3215 -459

No files matched your search

+121 -6
View File
@@ -1,13 +1,14 @@
"""Batch, frame and auto-annotation routes (REQ-020…034)."""
import json
import os
import shutil
import tempfile
from typing import Optional
from fastapi import APIRouter, HTTPException, Response
from fastapi import APIRouter, File, Form, HTTPException, Response, UploadFile
from fastapi.responses import FileResponse
from pydantic import BaseModel
from backend import autolabel, dataset, library
from backend import autolabel, dataset, library, projects
from backend import batches as batch_store
from backend import review as review_store
from backend.api.common import project_or_404, thumbnail
@@ -27,10 +28,12 @@ class AutolabelRequest(BaseModel):
engines: Optional[list[str]] = None
class_ids: Optional[list[int]] = None
engine_classes: Optional[dict[str, list[str]]] = None
target_class_names: Optional[list[str]] = None
threshold: float = autolabel.DEFAULT_THRESHOLD
iou_threshold: float = autolabel.DEFAULT_IOU
min_box_frac: float = 0.0
resume: bool = False
append: bool = False
@router.post("/api/projects/{project_id}/batches")
@@ -90,11 +93,115 @@ def start_autolabel(batch_id: int, request: AutolabelRequest) -> dict:
try:
engine_list = request.engines if (request.engines and len(request.engines) > 0) else [request.engine]
return autolabel.start(batch_id, request.threshold, request.iou_threshold,
request.min_box_frac, resume=request.resume,
request.min_box_frac, resume=request.resume, append=request.append,
engines=engine_list, class_ids=request.class_ids,
engine_classes=request.engine_classes)
engine_classes=request.engine_classes,
target_class_names=request.target_class_names)
except batch_store.BatchError as exc:
raise HTTPException(400, str(exc))
@router.post("/api/batches/inspect-model")
async def inspect_model(file: UploadFile = File(...)) -> dict:
if not (file.filename or "").endswith(".pt"):
raise HTTPException(400, "Model must be a .pt file")
with tempfile.NamedTemporaryFile(suffix=".pt", delete=False) as staged:
shutil.copyfileobj(file.file, staged)
staged_path = staged.name
await file.close()
try:
classes = projects.read_model_classes(staged_path)
return {"filename": file.filename, "classes": classes, "staged_path": staged_path}
except Exception as exc:
if os.path.exists(staged_path):
os.unlink(staged_path)
raise HTTPException(400, f"Could not inspect model: {exc}")
@router.post("/api/batches/{batch_id}/autolabel-with-model")
async def autolabel_with_model(
batch_id: int,
file: UploadFile = File(...),
threshold: float = Form(0.35),
iou_threshold: float = Form(0.8),
selected_classes: str = Form("[]"),
append: bool = Form(True),
) -> dict:
if not (file.filename or "").endswith(".pt"):
raise HTTPException(400, "Model must be a .pt file")
with tempfile.NamedTemporaryFile(suffix=".pt", delete=False) as staged:
shutil.copyfileobj(file.file, staged)
staged_path = staged.name
await file.close()
try:
target_classes = json.loads(selected_classes) if selected_classes else None
return autolabel.start(
batch_id,
threshold=threshold,
iou_threshold=iou_threshold,
append=append,
custom_model_path=staged_path,
target_class_names=target_classes,
)
except Exception as exc:
if os.path.exists(staged_path):
os.unlink(staged_path)
raise HTTPException(400, f"Auto-annotation failed to start: {exc}")
@router.post("/api/sam3/playground-test")
async def sam3_playground_test(
file: UploadFile = File(...),
prompts: str = Form(...),
threshold: float = Form(0.35),
iou_threshold: float = Form(0.8),
) -> dict:
from PIL import Image
from backend import labeling
from backend.sam3_engine import get_engine
try:
image = Image.open(file.file).convert("RGB")
except Exception as exc:
raise HTTPException(400, f"Could not read image: {exc}")
width, height = image.size
prompt_list = [p.strip() for p in prompts.split(",") if p.strip()]
if not prompt_list:
raise HTTPException(400, "At least one text prompt is required")
try:
engine = get_engine()
raw_dets = engine.detect(image, prompt_list, threshold)
kept_dets = labeling.deduplicate(raw_dets, iou_threshold=iou_threshold)
except Exception as exc:
raise HTTPException(500, f"SAM3 inference failed: {exc}")
results = []
for det in kept_dets:
norm_box = [
det.box[0] / width,
det.box[1] / height,
det.box[2] / width,
det.box[3] / height,
]
polys = []
if det.mask is not None:
raw_polys = review_store.mask_to_polygons(det.mask)
polys = [[[float(pt[0]), float(pt[1])] for pt in poly] for poly in raw_polys]
results.append({
"class_id": det.class_id,
"prompt": det.class_name,
"score": round(float(det.score), 4),
"box": [round(v, 5) for v in norm_box],
"polygons": polys,
})
return {
"width": width,
"height": height,
"detections": results,
}
@@ -150,3 +257,11 @@ def clear_batch_class_annotations(batch_id: int, class_id: int) -> dict:
deleted = review_store.clear_batch_class_annotations(batch_id, class_id)
return {"deleted": deleted}
@router.post("/api/batches/{batch_id}/reset-auto-annotations")
def reset_batch_auto_annotations(batch_id: int) -> dict:
if batch_store.get(batch_id) is None:
raise HTTPException(404, "No such batch")
deleted = review_store.clear_batch_auto_annotations(batch_id)
return {"deleted": deleted}