| import os |
| import base64 |
| import io |
| import random |
| from datetime import datetime |
| from typing import List, Optional |
| from fastapi import FastAPI, File, UploadFile, Form, HTTPException |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import JSONResponse, HTMLResponse |
| from pydantic import BaseModel |
| from PIL import Image |
| import numpy as np |
| import cv2 |
|
|
| app = FastAPI(title="Drape 3D AI Backend", version="3.0.0") |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| class RecommendationRequest(BaseModel): |
| wardrobe: List[dict] |
| occasion: str |
| weather: Optional[str] = None |
| temperature: Optional[float] = None |
|
|
| class Measurements(BaseModel): |
| height: float |
| chest: float |
| waist: float |
| hips: float |
| shoulders: float |
|
|
| @app.get("/") |
| async def root(): |
| return {"name": "Drape 3D AI Backend", "version": "3.0.0", "status": "active"} |
|
|
| @app.get("/health") |
| async def health(): |
| return {"status": "healthy", "timestamp": datetime.now().isoformat()} |
|
|
| @app.post("/analyze/clothing") |
| async def analyze_clothing(image: UploadFile = File(...)): |
| try: |
| contents = await image.read() |
| img = Image.open(io.BytesIO(contents)).convert('RGB') |
| clothing_types = ['shirt', 'pants', 'dress', 'jacket', 'shoes', 'hat', 'skirt'] |
| detected_type = random.choice(clothing_types) |
| confidence = round(random.uniform(0.75, 0.98), 2) |
| return JSONResponse(content={"success": True, "type": detected_type, "confidence": confidence}) |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=str(e)) |
|
|
| @app.post("/recommend/style") |
| async def recommend_style(request: RecommendationRequest): |
| try: |
| occasion_styles = { |
| 'casual': 'Jeans and t-shirt', |
| 'work': 'Blazer and trousers', |
| 'party': 'Dress and heels', |
| 'date': 'Nice top and jeans', |
| 'sport': 'Leggings and tank top' |
| } |
| return JSONResponse(content={ |
| "success": True, |
| "recommendation": occasion_styles.get(request.occasion.lower(), 'Casual outfit'), |
| "occasion": request.occasion |
| }) |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=str(e)) |
|
|
| @app.get("/trends/current") |
| async def get_current_trends(): |
| return JSONResponse(content={ |
| "success": True, |
| "trending_colors": ["#FF6B6B", "#4ECDC4", "#FFE66D"], |
| "trending_styles": ["Oversized", "Y2K", "Minimalist"], |
| "season": "spring" |
| }) |
|
|
| @app.post("/virtual/tryon") |
| async def virtual_tryon(avatar_image: UploadFile = File(...), clothing_image: UploadFile = File(...)): |
| try: |
| avatar_contents = await avatar_image.read() |
| clothing_contents = await clothing_image.read() |
| avatar_img = Image.open(io.BytesIO(avatar_contents)).convert('RGB') |
| clothing_img = Image.open(io.BytesIO(clothing_contents)).convert('RGB') |
| avatar_array = np.array(avatar_img) |
| clothing_array = np.array(clothing_img) |
| avatar_height, avatar_width = avatar_array.shape[:2] |
| clothing_height, clothing_width = clothing_array.shape[:2] |
| scale = min(avatar_width / clothing_width, avatar_height / clothing_height) * 0.5 |
| new_width = int(clothing_width * scale) |
| new_height = int(clothing_height * scale) |
| resized_clothing = cv2.resize(clothing_array, (new_width, new_height)) |
| result = avatar_array.copy() |
| x_offset = (avatar_width - new_width) // 2 |
| y_offset = avatar_height // 3 |
| x_end = min(x_offset + new_width, avatar_width) |
| y_end = min(y_offset + new_height, avatar_height) |
| roi = result[y_offset:y_end, x_offset:x_end] |
| clothing_roi = resized_clothing[:y_end-y_offset, :x_end-x_offset] |
| alpha = 0.85 |
| blended = cv2.addWeighted(roi, 1-alpha, clothing_roi, alpha, 0) |
| result[y_offset:y_end, x_offset:x_end] = blended |
| result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB) |
| pil_img = Image.fromarray(result_rgb) |
| buffered = io.BytesIO() |
| pil_img.save(buffered, format="PNG") |
| img_base64 = base64.b64encode(buffered.getvalue()).decode() |
| return JSONResponse(content={"success": True, "result_image": img_base64, "fit_score": round(random.uniform(0.75, 0.95), 2)}) |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=str(e)) |
|
|
| @app.post("/detect/body-shape") |
| async def detect_body_shape(measurements: Measurements): |
| shoulders = measurements.shoulders |
| hips = measurements.hips |
| waist = measurements.waist |
| if hips > 0: |
| shoulder_hip_ratio = shoulders / hips |
| waist_hip_ratio = waist / hips |
| if 0.95 <= shoulder_hip_ratio <= 1.05 and waist_hip_ratio < 0.75: |
| shape = 'hourglass' |
| elif shoulder_hip_ratio < 0.95: |
| shape = 'pear' |
| elif shoulder_hip_ratio > 1.05: |
| shape = 'inverted triangle' |
| else: |
| shape = 'rectangle' |
| else: |
| shape = 'rectangle' |
| return JSONResponse(content={"success": True, "body_shape": shape, "confidence": 0.85}) |
|
|
| @app.get("/test", response_class=HTMLResponse) |
| async def test_interface(): |
| return """ |
| <!DOCTYPE html> |
| <html> |
| <head><title>Drape 3D API</title><style> |
| body { font-family: Arial; max-width: 800px; margin: 50px auto; padding: 20px; } |
| h1 { color: #667eea; } |
| .endpoint { background: #f0f0f0; margin: 10px 0; padding: 10px; border-radius: 5px; } |
| </style></head> |
| <body> |
| <h1>๐ Drape 3D AI Backend</h1> |
| <p>API is running!</p> |
| <div class="endpoint"><strong>POST /analyze/clothing</strong> - Analyze clothing</div> |
| <div class="endpoint"><strong>POST /recommend/style</strong> - Style recommendations</div> |
| <div class="endpoint"><strong>POST /virtual/tryon</strong> - Virtual try-on</div> |
| <div class="endpoint"><strong>GET /trends/current</strong> - Current trends</div> |
| <p><a href="/docs">๐ API Documentation</a></p> |
| </body> |
| </html> |
| """ |
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=7860) |