IcecreamDetect / api_server.py
ibsocr1's picture
Upload 13 files
f2798b7 verified
Raw History Blame Contribute Delete
3.29 kB
import json
import os
import shutil
import threading
import uuid
from pathlib import Path
from typing import Optional
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import FileResponse
from pydantic import BaseModel
from paddle_backend import train, training_status, paddle_status, predict, model_ready
ROOT = Path(__file__).resolve().parent
if os.getenv('DATA_DIR'):
BASE = Path(os.environ['DATA_DIR'])
elif Path('/data').exists() and os.access('/data', os.W_OK):
BASE = Path('/data') / 'icecream_counter'
else:
BASE = Path('./data')
DATASET = BASE / 'generated_dataset'
MODEL = BASE / 'model'
CLASSES = BASE / 'classes.txt'
API_OUTPUT = BASE / 'api_outputs'
API_OUTPUT.mkdir(parents=True, exist_ok=True)
app = FastAPI(title='Ice Cream RT-DETR API', version='1.0.0')
class TrainRequest(BaseModel):
epochs: int = 30
batch_size: int = 2
learning_rate: float = 1e-5
@app.get('/dataset/status')
def dataset_status():
return {
'train_annotations': (DATASET / 'train' / 'annotations.json').exists(),
'val_annotations': (DATASET / 'val' / 'annotations.json').exists(),
'classes': [x.strip() for x in CLASSES.read_text(encoding='utf-8').splitlines() if x.strip()] if CLASSES.exists() else [],
}
@app.get('/health')
def health():
p = paddle_status()
return {
'ok': True,
'engine': 'PaddleDetection RT-DETR',
'huggingface': False,
'transformers': False,
'torch_training': False,
'paddle': p,
'model_ready': model_ready(MODEL)
}
@app.get('/train/status')
def status():
return training_status()
@app.post('/train')
def start_train(req: TrainRequest):
s = training_status()
if s['running']:
return s
if not (DATASET / 'train' / 'annotations.json').exists():
raise HTTPException(400, 'Training dataset is not prepared.')
if not CLASSES.exists():
raise HTTPException(400, 'classes.txt is missing.')
def job():
train(DATASET, CLASSES, MODEL, max(1, req.epochs), max(1, req.batch_size), req.learning_rate)
threading.Thread(target=job, daemon=True).start()
return {'running': True, 'message': 'training started', 'engine': 'PaddleDetection RT-DETR'}
@app.post('/predict')
async def predict_image(file: UploadFile = File(...), threshold: float = 0.35):
if not model_ready(MODEL):
raise HTTPException(409, 'No trained model yet.')
job_id = uuid.uuid4().hex
job_dir = API_OUTPUT / job_id
job_dir.mkdir(parents=True, exist_ok=True)
suffix = Path(file.filename or 'image.jpg').suffix or '.jpg'
src = job_dir / ('input' + suffix)
src.write_bytes(await file.read())
try:
result = predict(src, MODEL, max(0.0, min(1.0, threshold)), CLASSES, job_dir)
result['job_id'] = job_id
return result
except Exception as e:
shutil.rmtree(job_dir, ignore_errors=True)
raise HTTPException(500, str(e))
@app.get('/predict/{job_id}/image')
def predicted_image(job_id: str):
d = API_OUTPUT / job_id
for p in d.glob('*'):
if p.suffix.lower() in {'.jpg','.jpeg','.png'} and p.name != 'input.jpg':
return FileResponse(p)
raise HTTPException(404, 'Prediction image not found.')