codernotme's picture
Update app.py
7e31dfe verified
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import uvicorn
import shutil
import os
import uuid
from pathlib import Path
import logging
from detect import detect_face_shape
from recommend import get_recommendations
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="AI Frame Recommendation Service")
# Configure CORS
# For Hugging Face Spaces, we allow all origins or you can add specific patterns later
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
UPLOAD_DIR = Path("uploads")
UPLOAD_DIR.mkdir(exist_ok=True)
@app.post("/recommend")
async def recommend(file: UploadFile = File(...), source: str = "upload", consent: bool = False):
try:
# Save uploaded file temporarily
file_ext = file.filename.split(".")[-1] if file.filename else "jpg"
filename = f"{uuid.uuid4()}.{file_ext}"
file_path = UPLOAD_DIR / filename
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
logger.info(f"Processing image: {file_path}")
# Analyze face shape
try:
# Returns dict: {'Oval': 0.6, 'Square': 0.4, ...}
face_shape_probs = detect_face_shape(str(file_path))
# Get top result (keys are sorted descending in detect.py)
top_shape = list(face_shape_probs.keys())[0]
top_confidence = face_shape_probs[top_shape]
logger.info(f"Detected face shape: {top_shape} ({top_confidence})")
except Exception as e:
logger.error(f"Error analyzing face: {str(e)}")
# Clean up file if analysis fails
if not consent:
try:
os.remove(file_path)
except: pass
raise HTTPException(status_code=400, detail=f"Could not analyze face: {str(e)}")
# Get recommendations using top shape
recommendations = get_recommendations(top_shape)
# Format response
response = {
"face_shape": top_shape,
"confidence": top_confidence,
"shape_confidence": top_confidence,
"shape_probabilities": face_shape_probs,
"frame_types": [
{"label": rec, "confidence": 0.9} for rec in recommendations.get("glasses", [])
],
"frame_size": {"label": "Medium", "confidence": 0.8}, # Placeholder
"bridge_type": {"label": "Regular", "confidence": 0.8}, # Placeholder
"materials": [{"label": "Acetate", "confidence": 0.7}], # Placeholder
"measurements": {
"face_width": 140, # Placeholder
"temple_length": 145 # Placeholder
},
"ml_predicted_frame": recommendations.get("glasses", [])[0] if recommendations.get("glasses") else None,
"ml_confidence": 0.85,
"image_id": str(filename) if consent else None
}
# Clean up if no consent to save
if not consent:
os.remove(file_path)
return JSONResponse(content=response, headers={"Cache-Control": "no-store"})
except Exception as e:
logger.error(f"Server error: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
def health_check():
return {"status": "ok"}
if __name__ == "__main__":
# Use PORT environment variable if available (for Railway/Spaces)
port = int(os.environ.get("PORT", 7860))
uvicorn.run("app:app", host="0.0.0.0", port=port, reload=True)