# from fastapi import APIRouter, UploadFile, File, Form # from fastapi.responses import StreamingResponse, JSONResponse # import io # import traceback # from shadow_generator import initialize_once, generate_shadow_rgba # router = APIRouter() # # initialize on import / startup # initialize_once() # @router.get("/health") # async def health(): # return {"ok": True} # @router.get("/params") # async def params_info(): # return { # "angle_deg": "float. 0 = right, 90 = down. default 135.0", # "distance": "float px offset of shadow. default 40.0", # "softness": "float blur radius. default 25.0", # "opacity": "float 0..1. default 0.7", # "color": "hex color for shadow. default '#000000'", # "spread": "float px. positive expands alpha, negative contracts. default 0.0", # "shadow_type": "string. 'drop' or 'contact'. default 'drop'", # } # @router.post("/generate-shadow") # async def generate_shadow_endpoint( # image: UploadFile = File(..., description="RGBA PNG with transparent background"), # angle_deg: float = Form(135.0), # distance: float = Form(40.0), # softness: float = Form(25.0), # opacity: float = Form(0.7), # color: str = Form("#000000"), # spread: float = Form(0.0), # shadow_type: str = Form("drop"), # use_ssn: bool = Form(True) # ): # """ # Returns: PNG (RGBA) bytes stream containing object + generated shadow. # """ # try: # data = await image.read() # params = { # "angle_deg": angle_deg, # "distance": distance, # "softness": softness, # "opacity": opacity, # "color": color, # "spread": spread, # "shadow_type": shadow_type, # "prefer_ssn": use_ssn # } # out_bytes = generate_shadow_rgba(data, params=params) # return StreamingResponse(io.BytesIO(out_bytes), media_type="image/png") # except Exception as exc: # traceback.print_exc() # return JSONResponse(status_code=500, content={"error": str(exc)}) # from fastapi import APIRouter, UploadFile, File, Form # from fastapi.responses import StreamingResponse, JSONResponse # import io # import traceback # from shadow_generator import load_ssn_once, generate_shadow_rgba # router = APIRouter() # # Lazy initialization placeholder # _ssn_wrapper = None # def get_ssn_wrapper(): # global _ssn_wrapper # if _ssn_wrapper is None: # _ssn_wrapper = load_ssn_once() # return _ssn_wrapper # @router.get("/health") # async def health(): # return {"ok": True} # @router.get("/params") # async def params_info(): # return { # "angle_deg": "float. 0 = right, 90 = down. default 135.0", # "distance": "float px offset of shadow. default 40.0", # "softness": "float blur radius. default 25.0", # "opacity": "float 0..1. default 0.7", # "color": "hex color for shadow. default '#000000'", # "spread": "float px. positive expands alpha, negative contracts. default 0.0", # "shadow_type": "string. 'drop' or 'contact'. default 'drop'", # } # @router.post("/generate-shadow") # async def generate_shadow_endpoint( # image: UploadFile = File(..., description="RGBA PNG with transparent background"), # angle_deg: float = Form(135.0), # distance: float = Form(40.0), # softness: float = Form(25.0), # opacity: float = Form(0.7), # color: str = Form("#000000"), # spread: float = Form(0.0), # shadow_type: str = Form("drop"), # use_ssn: bool = Form(True) # ): # try: # data = await image.read() # params = { # "angle_deg": angle_deg, # "distance": distance, # "softness": softness, # "opacity": opacity, # "color": color, # "spread": spread, # "shadow_type": shadow_type, # "prefer_ssn": use_ssn # } # # Lazy load SSN model # ssn = get_ssn_wrapper() # if not ssn.available(): # return JSONResponse(status_code=503, content={"error": "SSN model is still loading. Try again shortly."}) # out_bytes = generate_shadow_rgba(data, params=params) # return StreamingResponse(io.BytesIO(out_bytes), media_type="image/png") # except Exception as exc: # traceback.print_exc() # return JSONResponse(status_code=500, content={"error": str(exc)}) from fastapi import APIRouter, File, UploadFile, Form from fastapi.responses import Response, JSONResponse import shadow_generator from typing import Optional import io router = APIRouter() @router.post("/generate-shadow") async def generate_shadow_route( file: UploadFile = File(...), style: Optional[str] = Form("cast"), # NEW: allow drop / cast / soft / bent softness: Optional[float] = Form(28.0), opacity: Optional[float] = Form(0.7), color: Optional[str] = Form("#000000"), direction: Optional[float] = Form(0.0), distance: Optional[float] = Form(80.0), # NEW: needed for cast shadows ): """ Generate a shadowed image from input. Styles: 'drop', 'cast', 'angled', 'soft' """ image_bytes = await file.read() params = { "style": style, "softness": softness, "opacity": opacity, "color": color, "direction": direction, "distance": distance, } try: output_image_bytes = shadow_generator.generate_shadow_rgba(image_bytes, params) return Response(content=output_image_bytes, media_type="image/png") except Exception as e: return JSONResponse(status_code=500, content={"error": str(e)}) @router.on_event("startup") async def startup_event(): """ Initialize model once on server start. """ shadow_generator.initialize_once()