Polysort / app.py
vennsa's picture
Upload 6 files
105944b verified
Raw History Blame Contribute Delete
5.87 kB
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from ultralytics import YOLO
import numpy as np
from PIL import Image
import io
import json
import os
import logging
import threading
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
app = FastAPI(title="Plastic Detection API", version="1.0.0")
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Global model state
model = None
model_loading = False
model_ready = False
def preload_model():
"""Load model in background thread"""
global model, model_loading, model_ready
if model is None and not model_loading:
model_loading = True
logger.info("๐Ÿ”„ Pre-loading YOLO model in background...")
try:
model_path = "runs/plastic_detection/weights/best.pt"
if not os.path.exists(model_path):
logger.warning(f"Model not found: {model_path}, using yolov8n.pt")
model_path = "yolov8n.pt"
model = YOLO(model_path)
model_ready = True
logger.info(f"โœ… Model loaded: {model_path}")
except Exception as e:
logger.error(f"โŒ Failed to load model: {e}")
model_loading = False
def get_model():
"""Get model, load if needed"""
global model, model_loading, model_ready
if model is None:
if not model_loading:
preload_model()
# Wait for model to load (with timeout)
timeout = 120 # 2 minutes
waited = 0
while model is None and waited < timeout:
import time
time.sleep(0.5)
waited += 0.5
if model is None:
raise RuntimeError("Model failed to load")
return model
@app.on_event("startup")
async def startup_event():
"""Pre-load model on startup"""
logger.info("๐Ÿš€ Starting Plastic Detection API...")
# Load model in background thread
thread = threading.Thread(target=preload_model, daemon=True)
thread.start()
@app.get("/")
async def root():
"""Health check endpoint"""
return {
"message": "Plastic Detection API is running",
"status": "ok",
"model_ready": model_ready
}
@app.post("/predict")
@app.post("/Predict")
async def predict(file: UploadFile = File(None)):
"""
Predict plastic type in an image
Args:
file: Image file (multipart form upload)
Returns:
JSON with predictions
"""
logger.info(f"๐Ÿ“ฅ Prediction request received, model_ready={model_ready}")
try:
if not file:
raise HTTPException(status_code=400, detail="No file provided")
model_instance = get_model()
# Read image file
contents = await file.read()
image = Image.open(io.BytesIO(contents))
logger.info(f"โœ… Image loaded: {image.size}")
# Run inference
logger.info("๐Ÿ”ฎ Running YOLO inference...")
results = model_instance.predict(source=image, conf=0.25, verbose=False)
# Extract predictions
predictions = []
if results and len(results) > 0:
result = results[0]
class_names = result.names
if result.boxes is not None and len(result.boxes) > 0:
for box in result.boxes:
pred = {
"class": class_names[int(box.cls)],
"class_id": int(box.cls),
"confidence": float(box.conf),
"bbox": {
"x1": float(box.xyxy[0][0]),
"y1": float(box.xyxy[0][1]),
"x2": float(box.xyxy[0][2]),
"y2": float(box.xyxy[0][3]),
}
}
predictions.append(pred)
logger.info(f"โœ… Found {len(predictions)} detections")
return {
"success": True,
"detections": predictions,
"count": len(predictions),
"model": "YOLOv8 Best",
"filename": file.filename
}
except Exception as e:
logger.error(f"โŒ Error: {str(e)}", exc_info=True)
raise HTTPException(status_code=400, detail=f"Error processing image: {str(e)}")
@app.get("/classes")
async def get_classes():
"""Get all detectable classes"""
logger.info("Classes request")
try:
model_instance = get_model()
classes = model_instance.names
logger.info(f"Returning {len(classes)} classes")
return {
"classes": classes,
"total": len(classes)
}
except Exception as e:
logger.error(f"Error: {str(e)}", exc_info=True)
raise HTTPException(status_code=400, detail=f"Error: {str(e)}")
@app.get("/stats")
async def get_stats():
"""Get backend statistics"""
return {
"status": "running",
"model_ready": model_ready,
"model_loading": model_loading,
"available_models": ["best.pt", "yolov8n.pt"]
}
if __name__ == "__main__":
print("๐Ÿš€ Starting Plastic Detection API...")
print("๐ŸŒ Available at: http://0.0.0.0:8000")
print("๐Ÿ“– Docs: http://localhost:8000/docs")
print("โณ Model loads on first request...")
uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")