VTO / app /api /routers /v1 /tryon.py
Akshajzclap's picture
Upload 89 files
bd55a25 verified
from fastapi import APIRouter, File, UploadFile, HTTPException, Form
from fastapi.responses import StreamingResponse
from app.services.image_generation_service import generate_image_from_files_and_prompt
from app.services.prompt_factory import LIFESTYLE_TRYON_PROMPT_TEMPLATE
from app.schemas.common_schemas import ModelSchema, CameraSchema,GarmentType
import io
from typing import List, Annotated
router = APIRouter()
@router.post("/tryon", tags=["TRY-ON"])
async def tryon(
garment_type: Annotated[str, Form()],
model_gender: Annotated[str, Form()],
model_age_range: Annotated[str, Form()],
model_body_shape: Annotated[str, Form()],
model_race_ethnicity: Annotated[str, Form()],
model_pose: Annotated[str, Form()],
camera_view_angle: Annotated[str, Form()],
camera_distance_meters: Annotated[float, Form()],
camera_focal_length_mm: Annotated[float, Form()],
camera_aperture_f_number: Annotated[float, Form()],
camera_lighting_condition: Annotated[str, Form()],
camera_background: Annotated[str, Form()],
garment_images: List[UploadFile] = File(..., description="Exactly one image of the garments (e.g., dress /bag/shoes).", min_items=1, max_items=1),
):
"""
Generate a try-on image based on garment images, model characteristics, and camera settings.
"""
try:
garment=GarmentType(garment_type=garment_type)
model_attributes = ModelSchema(
gender=model_gender,
age_range=model_age_range,
body_shape=model_body_shape,
race_ethnicity=model_race_ethnicity,
pose=model_pose
)
camera_settings = CameraSchema(
view_angle=camera_view_angle,
distance_meters=camera_distance_meters,
focal_length_mm=camera_focal_length_mm,
aperture_f_number=camera_aperture_f_number,
lighting_condition=camera_lighting_condition,
background=camera_background
)
except Exception as e:
raise HTTPException(status_code=400, detail=f"Invalid model or camera attributes: {e}")
# Validation for exactly two files (FastAPI also handles min_items/max_items for File, but explicit check is fine)
if not garment_images or len(garment_images) != 1:
raise HTTPException(status_code=400, detail="Exactly one garment images are required for outfit match.")
image_bytes_list = []
for image_file in garment_images: # No need to check if garment_images is None here
if not image_file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail=f"Invalid file type for garment: {image_file.filename}. Must be an image.")
content = await image_file.read()
image_bytes_list.append((content, image_file.filename)) # Store as tuple (bytes, filename)
prompt=LIFESTYLE_TRYON_PROMPT_TEMPLATE.format(
garment_type=garment.garment_type,
model_gender=model_attributes.gender,
model_age_range=model_attributes.age_range,
model_race_ethnicity=model_attributes.race_ethnicity,
model_body_shape=model_attributes.body_shape,
model_pose=model_attributes.pose,
camera_view_angle=camera_settings.view_angle,
camera_distance_meters=camera_settings.distance_meters,
camera_focal_length_mm=camera_settings.focal_length_mm,
camera_aperture_f_number=camera_settings.aperture_f_number,
camera_lighting_condition=camera_settings.lighting_condition,
camera_background=camera_settings.background
)
generated_image_data = await generate_image_from_files_and_prompt(
image_files=image_bytes_list,
prompt=prompt
)
if generated_image_data:
return StreamingResponse(io.BytesIO(generated_image_data), media_type="image/png")
else:
raise HTTPException(status_code=500, detail="Failed to generate image. Check service logs.")