File size: 3,827 Bytes
a5a6a2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e31dfe
a5a6a2e
 
 
 
 
 
 
 
 
 
b4b54e2
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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)