tejani commited on
Commit
6519940
·
verified ·
1 Parent(s): e914f08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -54
app.py CHANGED
@@ -1,61 +1,123 @@
1
- from fastapi import FastAPI, HTTPException
2
- from pydantic import BaseModel
3
  from gradio_client import Client, handle_file
4
- from typing import Optional, List
5
- import uvicorn
6
-
7
- # Initialize FastAPI app
8
- app = FastAPI(title="Change Clothes AI API")
9
-
10
- # Initialize Gradio client
11
- try:
12
- gradio_client = Client("jallenjia/Change-Clothes-AI")
13
- except Exception as e:
14
- raise Exception(f"Failed to initialize Gradio client: {str(e)}")
15
-
16
- # Define request body model using Pydantic
17
- class TryOnRequest(BaseModel):
18
- background_url: str # URL for background image
19
- garm_img_url: str # URL for garment image
20
- garment_des: str = "Hello!!" # Garment description
21
- is_checked: bool = True # Enable virtual try-on
22
- is_checked_crop: bool = False # Crop image option
23
- denoise_steps: int = 30 # Denoising steps
24
- seed: int = -1 # Random seed
25
- category: str = "upper_body" # Garment category
26
- layers: List[str] = [] # Optional layers
27
- composite: Optional[str] = None # Optional composite image
28
-
29
- # Define the /tryon endpoint
30
- @app.post("/tryon")
31
- async def tryon(request: TryOnRequest):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  try:
33
- # Prepare the dictionary for the Gradio client
34
- input_dict = {
35
- "background": handle_file(request.background_url),
36
- "layers": request.layers,
37
- "composite": request.composite
38
- }
39
-
40
- # Call the Gradio client's predict function
41
- result = gradio_client.predict(
42
- dict=input_dict,
43
- garm_img=handle_file(request.garm_img_url),
44
- garment_des=request.garment_des,
45
- is_checked=request.is_checked,
46
- is_checked_crop=request.is_checked_crop,
47
- denoise_steps=request.denoise_steps,
48
- seed=request.seed,
49
- category=request.category,
50
- api_name="/tryon"
51
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # Return the result
54
- return {"result": result}
55
 
 
 
56
  except Exception as e:
57
- raise HTTPException(status_code=500, detail=f"Error processing request: {str(e)}")
 
58
 
59
- # Run the FastAPI server
60
- if __name__ == "__main__":
61
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
 
2
  from gradio_client import Client, handle_file
3
+ from fastapi.responses import JSONResponse
4
+ import tempfile
5
+ import os
6
+ import uuid
7
+ import asyncio
8
+ from contextlib import asynccontextmanager
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ import logging
11
+
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ app = FastAPI()
17
+
18
+ # Gradio API URL
19
+ GRADIO_API_URL = "jallenjia/Change-Clothes-AI"
20
+
21
+ # Thread pool for Gradio API calls (to avoid blocking async loop)
22
+ executor = ThreadPoolExecutor(max_workers=None) # Changed from max_workers=10 to max_workers=None to remove limit
23
+
24
+ # Context manager for temporary files
25
+ @asynccontextmanager
26
+ async def temp_file_manager(file_content: bytes, suffix: str = ".png"):
27
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, prefix=f"tryon_{uuid.uuid4()}_")
28
+ try:
29
+ temp_file.write(file_content)
30
+ temp_file.close()
31
+ yield temp_file.name
32
+ finally:
33
+ try:
34
+ if os.path.exists(temp_file.name):
35
+ os.unlink(temp_file.name)
36
+ except Exception as e:
37
+ logger.error(f"Failed to delete temp file {temp_file.name}: {e}")
38
+
39
+ # Run Gradio API call in thread pool to avoid blocking
40
+ async def run_gradio_predict(
41
+ background_path: str,
42
+ garm_img_path: str,
43
+ garment_des: str,
44
+ is_checked: bool,
45
+ is_checked_crop: bool,
46
+ denoise_steps: int,
47
+ seed: int,
48
+ category: str
49
+ ):
50
+ loop = asyncio.get_event_loop()
51
  try:
52
+ client = Client(GRADIO_API_URL)
53
+ result = await loop.run_in_executor(
54
+ executor,
55
+ lambda: client.predict(
56
+ dict={
57
+ "background": handle_file(background_path),
58
+ "layers": [],
59
+ "composite": None
60
+ },
61
+ garm_img=handle_file(garm_img_path),
62
+ garment_des=garment_des,
63
+ is_checked=is_checked,
64
+ is_checked_crop=is_checked_crop,
65
+ denoise_steps=denoise_steps,
66
+ seed=seed,
67
+ category=category,
68
+ api_name="/tryon"
69
+ )
70
  )
71
+ return result
72
+ except Exception as e:
73
+ logger.error(f"Gradio API error: {e}")
74
+ raise HTTPException(status_code=500, detail=f"Gradio API error: {str(e)}")
75
+
76
+ @app.post("/tryon")
77
+ async def tryon(
78
+ background: UploadFile = File(...),
79
+ garm_img: UploadFile = File(...),
80
+ garment_des: str = Form("navy blue polo shirt"),
81
+ is_checked: bool = Form(True),
82
+ is_checked_crop: bool = Form(False),
83
+ denoise_steps: int = Form(30),
84
+ seed: int = Form(42),
85
+ category: str = Form("upper_body")
86
+ ):
87
+ try:
88
+ # Validate file types
89
+ if not background.content_type.startswith("image/") or not garm_img.content_type.startswith("image/"):
90
+ raise HTTPException(status_code=400, detail="Only image files are allowed")
91
+
92
+ # Read file contents
93
+ background_content = await background.read()
94
+ garm_img_content = await garm_img.read()
95
+
96
+ # Create temporary files with unique names
97
+ async with temp_file_manager(background_content, ".png") as background_path:
98
+ async with temp_file_manager(garm_img_content, ".png") as garm_img_path:
99
+ # Call Gradio API in thread pool
100
+ result = await run_gradio_predict(
101
+ background_path=background_path,
102
+ garm_img_path=garm_img_path,
103
+ garment_des=garment_des,
104
+ is_checked=is_checked,
105
+ is_checked_crop=is_checked_crop,
106
+ denoise_steps=denoise_steps,
107
+ seed=seed,
108
+ category=category
109
+ )
110
 
111
+ return JSONResponse(content={"result": str(result)})
 
112
 
113
+ except HTTPException as e:
114
+ raise e
115
  except Exception as e:
116
+ logger.error(f"Request failed: {e}")
117
+ raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
118
 
119
+ # Shutdown thread pool gracefully
120
+ @app.on_event("shutdown")
121
+ def shutdown_event():
122
+ executor.shutdown(wait=True)
123
+ logger.info("Thread pool shut down")