Spaces:
Sleeping
Sleeping
| import os | |
| import uuid | |
| import tempfile | |
| from pathlib import Path | |
| from fastapi import FastAPI, UploadFile, File, Form, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from src.model.pipeline import pipeline | |
| ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp", ".bmp"} | |
| MAX_FILE_SIZE_MB = 5 | |
| MAX_N_CIRCLES = 500 | |
| MIN_N_CIRCLES = 1 | |
| app = FastAPI(title="Fourier Epicycle API") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=False, | |
| allow_methods=["POST", "GET"], | |
| allow_headers=["*"], | |
| ) | |
| async def get_fourier_data( | |
| file: UploadFile = File(...), | |
| n_circles: int = Form(...) | |
| ): | |
| # Validate n_circles | |
| if not (MIN_N_CIRCLES <= n_circles <= MAX_N_CIRCLES): | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"n_circles must be between {MIN_N_CIRCLES} and {MAX_N_CIRCLES}" | |
| ) | |
| # Validate file extension | |
| original_ext = Path(file.filename).suffix.lower() if file.filename else "" | |
| if original_ext not in ALLOWED_EXTENSIONS: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"Unsupported file type. Allowed: {', '.join(ALLOWED_EXTENSIONS)}" | |
| ) | |
| # Read and validate file size | |
| contents = await file.read() | |
| if len(contents) > MAX_FILE_SIZE_MB * 1024 * 1024: | |
| raise HTTPException( | |
| status_code=413, | |
| detail=f"File too large. Maximum size is {MAX_FILE_SIZE_MB} MB" | |
| ) | |
| # Write to a safe temp file with a random name | |
| tmp_dir = tempfile.gettempdir() | |
| temp_path = os.path.join(tmp_dir, f"{uuid.uuid4().hex}{original_ext}") | |
| try: | |
| with open(temp_path, "wb") as f: | |
| f.write(contents) | |
| result = pipeline(image_path=temp_path, n_circles=n_circles) | |
| circles = [ | |
| [ | |
| float(result['magnitude'][i]), | |
| float(result['frequencies'][i]), | |
| float(result['phases'][i]) | |
| ] | |
| for i in range(len(result['frequencies'])) | |
| ] | |
| return {"circles": circles, "success": True} | |
| except HTTPException: | |
| raise | |
| except Exception: | |
| return JSONResponse( | |
| status_code=500, | |
| content={"error": "Image processing failed. Try a different image or fewer circles.", "success": False} | |
| ) | |
| finally: | |
| if os.path.exists(temp_path): | |
| os.remove(temp_path) | |
| # Serve the frontend — must be mounted AFTER API routes | |
| app.mount("/", StaticFiles(directory=".", html=True), name="static") | |