StableVITON Deployer commited on
Commit
8495b3a
·
0 Parent(s):

Initial backend deployment

Browse files
Files changed (7) hide show
  1. .gitignore +42 -0
  2. Dockerfile +47 -0
  3. README.md +65 -0
  4. inference_wrapper.py +201 -0
  5. main.py +309 -0
  6. requirements.txt +26 -0
  7. setup.bat +42 -0
.gitignore ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ env/
26
+ ENV/
27
+
28
+ # Environment Variables
29
+ .env
30
+
31
+ # IDEs
32
+ .vscode/
33
+ .idea/
34
+ *.swp
35
+
36
+ # Model Cache (Local)
37
+ models/
38
+ model_cache/
39
+ *.safetensors
40
+ *.ckpt
41
+ *.bin
42
+ *.pth
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for Hugging Face Spaces (GPU Support)
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ # libgl1/libglib2.0-0 needed for OpenCV
6
+ RUN apt-get update && apt-get install -y \
7
+ git \
8
+ wget \
9
+ libgl1 \
10
+ libglib2.0-0 \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Set up a new user named "user" with user ID 1000
14
+ RUN useradd -m -u 1000 user
15
+
16
+ # Switch to the "user" user
17
+ USER user
18
+
19
+ # Set home to the user's home directory
20
+ ENV HOME=/home/user \
21
+ PATH=/home/user/.local/bin:$PATH
22
+
23
+ # Set working directory to the user's home directory
24
+ WORKDIR $HOME/app
25
+
26
+ # Copy requirements.txt and install dependencies
27
+ # Doing this before copying the rest of the code cache-busts only if requirements change
28
+ COPY --chown=user requirements.txt $HOME/app/requirements.txt
29
+ RUN pip install --no-cache-dir --upgrade pip && \
30
+ pip install --no-cache-dir -r requirements.txt
31
+
32
+ # Copy the rest of the application code
33
+ COPY --chown=user . $HOME/app
34
+
35
+ # Set environment variables for Hugging Face Cache
36
+ ENV HF_HOME=$HOME/app/model_cache
37
+ ENV TRANSFORMERS_CACHE=$HOME/app/model_cache
38
+ ENV TORCH_HOME=$HOME/app/model_cache
39
+
40
+ # Create cache directory
41
+ RUN mkdir -p $HF_HOME
42
+
43
+ # Expose the port
44
+ EXPOSE 7860
45
+
46
+ # Start the application
47
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: StableVITON Virtual Try-On
3
+ emoji: 👕
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ app_port: 7860
10
+ ---
11
+
12
+ # StableVITON Virtual Try-On Backend
13
+
14
+ AI-powered virtual try-on service using StableVITON and FastAPI.
15
+
16
+ ## Features
17
+
18
+ - Virtual try-on inference via REST API
19
+ - Support for JPEG/PNG images
20
+ - Automatic image preprocessing and validation
21
+ - GPU-optimized inference (CUDA support)
22
+ - Single-request processing to prevent OOM
23
+ - Comprehensive error handling
24
+
25
+ ## API Documentation
26
+
27
+ ### POST /tryon
28
+
29
+ Perform virtual try-on inference.
30
+
31
+ **Request:**
32
+ - `person_image`: Full-body photo (multipart/form-data)
33
+ - `garment_image`: Garment image (multipart/form-data)
34
+
35
+ **Response:**
36
+ ```json
37
+ {
38
+ "success": true,
39
+ "result_image": "data:image/png;base64,...",
40
+ "processing_time": 23.4,
41
+ "model_version": "stablevton-v1"
42
+ }
43
+ ```
44
+
45
+ ### GET /health
46
+
47
+ Health check endpoint.
48
+
49
+ ## Local Development
50
+
51
+ ```bash
52
+ # Install dependencies
53
+ pip install -r requirements.txt
54
+
55
+ # Run server
56
+ python main.py
57
+ ```
58
+
59
+ ## Deployment
60
+
61
+ This project is designed to run on Hugging Face Spaces with Docker SDK.
62
+
63
+ 1. Create a Space with Docker SDK
64
+ 2. Select GPU hardware (T4 small is sufficient)
65
+ 3. Push the code to the Space
inference_wrapper.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ StableVITON Inference Wrapper
3
+ Clean abstraction layer for virtual try-on inference
4
+ """
5
+
6
+ import torch
7
+ from PIL import Image
8
+ import numpy as np
9
+ from typing import Tuple, Optional
10
+ import gc
11
+ from diffusers import StableDiffusionPipeline, DDIMScheduler
12
+ from transformers import CLIPTextModel, CLIPTokenizer
13
+ import logging
14
+
15
+ logging.basicConfig(level=logging.INFO)
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class StableVITONInference:
20
+ """
21
+ Wrapper for StableVITON model inference.
22
+ Handles model loading, preprocessing, inference, and cleanup.
23
+ """
24
+ def __init__(
25
+ self,
26
+ model_path: str = "yisol/IDM-VTON",
27
+ device: str = "auto",
28
+ resolution: int = 768,
29
+ num_inference_steps: int = 30,
30
+ guidance_scale: float = 2.5,
31
+ seed: int = 42
32
+ ):
33
+ """
34
+ Initialize StableVITON inference wrapper.
35
+
36
+ Args:
37
+ model_path: Path to model or Hugging Face model ID
38
+ device: Device to run inference on ("cuda", "cpu", or "auto")
39
+ resolution: Output resolution (default: 768)
40
+ num_inference_steps: Number of diffusion steps (default: 30)
41
+ guidance_scale: Guidance scale (default: 2.5)
42
+ seed: Random seed (default: 42)
43
+ """
44
+ self.model_path = model_path
45
+ self.resolution = resolution
46
+ self.num_inference_steps = num_inference_steps
47
+ self.guidance_scale = guidance_scale
48
+ self.seed = seed
49
+
50
+ # Determine device
51
+ if device == "auto":
52
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
53
+ else:
54
+ self.device = device
55
+
56
+ logger.info(f"Initializing StableVITON on device: {self.device}")
57
+
58
+ self.generator = torch.Generator(device=self.device).manual_seed(self.seed)
59
+ self.pipe = None
60
+
61
+ # Load model immediately
62
+ self._load_model()
63
+
64
+ def _load_model(self):
65
+ """Load the model pipeline"""
66
+ try:
67
+ logger.info(f"Loading model from {self.model_path}...")
68
+
69
+ # Use float16 for GPU to save memory, float32 for CPU
70
+ torch_dtype = torch.float16 if self.device == "cuda" else torch.float32
71
+
72
+ self.pipe = AutoPipelineForInpainting.from_pretrained(
73
+ self.model_path,
74
+ torch_dtype=torch_dtype,
75
+ variant="fp16" if self.device == "cuda" else None,
76
+ use_safetensors=True,
77
+ low_cpu_mem_usage=True,
78
+ )
79
+
80
+ if self.device == "cuda":
81
+ self.pipe.to(self.device)
82
+ # Enable memory efficient attention if available
83
+ try:
84
+ self.pipe.enable_xformers_memory_efficient_attention()
85
+ logger.info("Enabled xformers memory efficient attention")
86
+ except Exception as e:
87
+ logger.warning(f"Could not enable xformers: {e}")
88
+
89
+ logger.info("Model loaded successfully")
90
+
91
+ except Exception as e:
92
+ logger.error(f"Failed to load model: {e}")
93
+ orig_width, orig_height = image.size
94
+
95
+ # Calculate aspect ratio preserving resize
96
+ if orig_width > orig_height:
97
+ new_width = self.resolution
98
+ new_height = int((orig_height / orig_width) * self.resolution)
99
+ else:
100
+ new_height = self.resolution
101
+ new_width = int((orig_width / orig_height) * self.resolution)
102
+
103
+ # Resize
104
+ image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
105
+
106
+ # Pad to square if needed
107
+ if new_width != self.resolution or new_height != self.resolution:
108
+ padded = Image.new("RGB", (self.resolution, self.resolution), (255, 255, 255))
109
+ offset_x = (self.resolution - new_width) // 2
110
+ offset_y = (self.resolution - new_height) // 2
111
+ padded.paste(image, (offset_x, offset_y))
112
+ image = padded
113
+
114
+ return image
115
+
116
+ def tryon(
117
+ self,
118
+ person_image: Image.Image,
119
+ garment_image: Image.Image,
120
+ **kwargs
121
+ ) -> Image.Image:
122
+ """
123
+ Perform virtual try-on inference.
124
+
125
+ Args:
126
+ person_image: PIL Image of the person
127
+ garment_image: PIL Image of the garment
128
+ **kwargs: Additional parameters for inference
129
+
130
+ Returns:
131
+ PIL Image of the person wearing the garment
132
+ """
133
+ try:
134
+ logger.info("Starting try-on inference")
135
+
136
+ # Preprocess images
137
+ person_processed = self.preprocess_image(person_image, is_garment=False)
138
+ garment_processed = self.preprocess_image(garment_image, is_garment=True)
139
+
140
+ logger.info(f"Images preprocessed to {self.resolution}x{self.resolution}")
141
+
142
+ # TODO: Replace with actual StableVITON inference
143
+ # This is a placeholder - actual implementation will use StableVITON's specific pipeline
144
+
145
+ # Placeholder: For now, we'll use a simple prompt-based approach
146
+ # In reality, StableVITON uses the garment image directly as conditioning
147
+ prompt = "high quality photo of a person wearing clothes, detailed, realistic"
148
+
149
+ with torch.no_grad():
150
+ # This is a simplified placeholder
151
+ # Actual StableVITON will pass both images through the pipeline differently
152
+ result = self.pipe(
153
+ prompt=prompt,
154
+ num_inference_steps=self.num_inference_steps,
155
+ guidance_scale=self.guidance_scale,
156
+ **kwargs
157
+ ).images[0]
158
+
159
+ logger.info("Inference completed successfully")
160
+
161
+ return result
162
+
163
+ except Exception as e:
164
+ logger.error(f"Inference failed: {e}")
165
+ raise
166
+
167
+ def cleanup(self):
168
+ """
169
+ Clean up GPU memory after inference.
170
+ """
171
+ if self.device == "cuda":
172
+ torch.cuda.empty_cache()
173
+ gc.collect()
174
+ logger.info("GPU memory cleaned up")
175
+
176
+ def __del__(self):
177
+ """Destructor to ensure cleanup."""
178
+ self.cleanup()
179
+
180
+
181
+ # Example usage
182
+ if __name__ == "__main__":
183
+ # Test the inference wrapper
184
+ print("Testing StableVITON Inference Wrapper")
185
+
186
+ # Create dummy images for testing
187
+ person_img = Image.new("RGB", (512, 768), color=(200, 200, 200))
188
+ garment_img = Image.new("RGB", (512, 512), color=(100, 150, 200))
189
+
190
+ # Initialize wrapper
191
+ wrapper = StableVITONInference(
192
+ device="cpu", # Use CPU for testing
193
+ resolution=512,
194
+ num_inference_steps=20
195
+ )
196
+
197
+ # Run inference
198
+ result = wrapper.tryon(person_img, garment_img)
199
+
200
+ print(f"Result image size: {result.size}")
201
+ print("Test completed successfully")
main.py ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FastAPI Backend for StableVITON Virtual Try-On
3
+ Provides REST API endpoint for virtual try-on inference
4
+ """
5
+
6
+ from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import JSONResponse
9
+ from pydantic import BaseModel
10
+ from PIL import Image
11
+ import io
12
+ import base64
13
+ import os
14
+ import time
15
+ import asyncio
16
+ from typing import Optional
17
+ import logging
18
+ from queue import Queue
19
+ from threading import Lock
20
+
21
+ from inference_wrapper import StableVITONInference
22
+
23
+ # Configure logging
24
+ logging.basicConfig(
25
+ level=logging.INFO,
26
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
27
+ )
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Initialize FastAPI app
31
+ app = FastAPI(
32
+ title="StableVITON Virtual Try-On API",
33
+ description="AI-powered virtual try-on service using StableVITON",
34
+ version="1.0.0"
35
+ )
36
+
37
+ # CORS configuration - allow all origins for demo (restrict in production)
38
+ app.add_middleware(
39
+ CORSMiddleware,
40
+ allow_origins=["*"], # Change to specific domains in production
41
+ allow_credentials=True,
42
+ allow_methods=["*"],
43
+ allow_headers=["*"],
44
+ )
45
+
46
+ # Global model instance (loaded once at startup)
47
+ model: Optional[StableVITONInference] = None
48
+
49
+ # Request queue for single-request processing
50
+ request_queue = Queue()
51
+ processing_lock = Lock()
52
+
53
+ # Configuration
54
+ MAX_IMAGE_SIZE = 10 * 1024 * 1024 # 10MB
55
+ ALLOWED_EXTENSIONS = {"image/jpeg", "image/png", "image/jpg"}
56
+ REQUEST_TIMEOUT = 90 # seconds
57
+
58
+
59
+ class TryOnResponse(BaseModel):
60
+ """Response model for try-on endpoint"""
61
+ success: bool
62
+ result_image: Optional[str] = None
63
+ processing_time: Optional[float] = None
64
+ model_version: str = "stablevton-v1"
65
+ error: Optional[str] = None
66
+ error_code: Optional[str] = None
67
+
68
+
69
+ @app.on_event("startup")
70
+ async def startup_event():
71
+ """Load model on startup"""
72
+ global model
73
+ try:
74
+ logger.info("Loading StableVITON model...")
75
+
76
+ model = StableVITONInference(
77
+ model_path=os.getenv("MODEL_PATH", "yisol/IDM-VTON"),
78
+ device=os.getenv("DEVICE", "auto"),
79
+ resolution=int(os.getenv("RESOLUTION", "768")),
80
+ num_inference_steps=int(os.getenv("NUM_INFERENCE_STEPS", "30")),
81
+ guidance_scale=float(os.getenv("GUIDANCE_SCALE", "2.5"))
82
+ )
83
+
84
+ logger.info("Model loaded successfully")
85
+
86
+ # Warmup inference
87
+ logger.info("Running warmup inference...")
88
+ dummy_person = Image.new("RGB", (512, 768), color=(200, 200, 200))
89
+ dummy_garment = Image.new("RGB", (512, 512), color=(100, 150, 200))
90
+ model.tryon(dummy_person, dummy_garment)
91
+ model.cleanup()
92
+ logger.info("Warmup completed")
93
+
94
+ except Exception as e:
95
+ logger.error(f"Failed to load model: {e}")
96
+ raise
97
+
98
+
99
+ @app.on_event("shutdown")
100
+ async def shutdown_event():
101
+ """Cleanup on shutdown"""
102
+ global model
103
+ if model:
104
+ model.cleanup()
105
+ logger.info("Model cleaned up")
106
+
107
+
108
+ def validate_image(file: UploadFile) -> None:
109
+ """
110
+ Validate uploaded image file.
111
+
112
+ Args:
113
+ file: Uploaded file
114
+
115
+ Raises:
116
+ HTTPException: If validation fails
117
+ """
118
+ # Check content type
119
+ if file.content_type not in ALLOWED_EXTENSIONS:
120
+ raise HTTPException(
121
+ status_code=400,
122
+ detail=f"Invalid file type. Allowed: {ALLOWED_EXTENSIONS}"
123
+ )
124
+
125
+ # Check file size (this is approximate, actual size checked during read)
126
+ if hasattr(file, 'size') and file.size > MAX_IMAGE_SIZE:
127
+ raise HTTPException(
128
+ status_code=400,
129
+ detail=f"File too large. Maximum size: {MAX_IMAGE_SIZE / (1024*1024)}MB"
130
+ )
131
+
132
+
133
+ def image_to_base64(image: Image.Image) -> str:
134
+ """
135
+ Convert PIL Image to base64 string.
136
+
137
+ Args:
138
+ image: PIL Image
139
+
140
+ Returns:
141
+ Base64 encoded string with data URI prefix
142
+ """
143
+ buffered = io.BytesIO()
144
+ image.save(buffered, format="PNG")
145
+ img_bytes = buffered.getvalue()
146
+ img_base64 = base64.b64encode(img_bytes).decode('utf-8')
147
+ return f"data:image/png;base64,{img_base64}"
148
+
149
+
150
+ async def read_image_from_upload(file: UploadFile) -> Image.Image:
151
+ """
152
+ Read PIL Image from uploaded file.
153
+
154
+ Args:
155
+ file: Uploaded file
156
+
157
+ Returns:
158
+ PIL Image
159
+
160
+ Raises:
161
+ HTTPException: If image cannot be read
162
+ """
163
+ try:
164
+ contents = await file.read()
165
+
166
+ # Check actual size
167
+ if len(contents) > MAX_IMAGE_SIZE:
168
+ raise HTTPException(
169
+ status_code=400,
170
+ detail=f"File too large. Maximum size: {MAX_IMAGE_SIZE / (1024*1024)}MB"
171
+ )
172
+
173
+ image = Image.open(io.BytesIO(contents))
174
+
175
+ # Validate dimensions
176
+ width, height = image.size
177
+ if width < 256 or height < 256:
178
+ raise HTTPException(
179
+ status_code=400,
180
+ detail="Image too small. Minimum dimensions: 256x256"
181
+ )
182
+ if width > 2048 or height > 2048:
183
+ raise HTTPException(
184
+ status_code=400,
185
+ detail="Image too large. Maximum dimensions: 2048x2048"
186
+ )
187
+
188
+ return image
189
+
190
+ except HTTPException:
191
+ raise
192
+ except Exception as e:
193
+ logger.error(f"Failed to read image: {e}")
194
+ raise HTTPException(
195
+ status_code=400,
196
+ detail=f"Invalid image file: {str(e)}"
197
+ )
198
+
199
+
200
+ @app.get("/")
201
+ async def root():
202
+ """Root endpoint"""
203
+ return {
204
+ "message": "StableVITON Virtual Try-On API",
205
+ "version": "1.0.0",
206
+ "endpoints": {
207
+ "/tryon": "POST - Virtual try-on inference",
208
+ "/health": "GET - Health check"
209
+ }
210
+ }
211
+
212
+
213
+ @app.get("/health")
214
+ async def health_check():
215
+ """Health check endpoint"""
216
+ return {
217
+ "status": "healthy",
218
+ "model_loaded": model is not None,
219
+ "timestamp": time.time()
220
+ }
221
+
222
+
223
+ @app.post("/tryon", response_model=TryOnResponse)
224
+ async def virtual_tryon(
225
+ person_image: UploadFile = File(..., description="Full-body photo of person"),
226
+ garment_image: UploadFile = File(..., description="Garment image")
227
+ ):
228
+ """
229
+ Perform virtual try-on inference.
230
+
231
+ Args:
232
+ person_image: Full-body photo of the person
233
+ garment_image: Image of the garment to try on
234
+
235
+ Returns:
236
+ TryOnResponse with result image or error
237
+ """
238
+ start_time = time.time()
239
+
240
+ try:
241
+ # Check if model is loaded
242
+ if model is None:
243
+ raise HTTPException(
244
+ status_code=503,
245
+ detail="Model not loaded. Please try again later."
246
+ )
247
+
248
+ # Validate files
249
+ validate_image(person_image)
250
+ validate_image(garment_image)
251
+
252
+ logger.info(f"Processing try-on request: {person_image.filename}, {garment_image.filename}")
253
+
254
+ # Read images
255
+ person_img = await read_image_from_upload(person_image)
256
+ garment_img = await read_image_from_upload(garment_image)
257
+
258
+ # Acquire processing lock (single request at a time)
259
+ acquired = processing_lock.acquire(blocking=False)
260
+ if not acquired:
261
+ raise HTTPException(
262
+ status_code=503,
263
+ detail="Server is busy processing another request. Please try again in a moment."
264
+ )
265
+
266
+ try:
267
+ # Run inference
268
+ logger.info("Running inference...")
269
+ result_image = model.tryon(person_img, garment_img)
270
+
271
+ # Convert to base64
272
+ result_base64 = image_to_base64(result_image)
273
+
274
+ # Cleanup
275
+ model.cleanup()
276
+
277
+ processing_time = time.time() - start_time
278
+ logger.info(f"Inference completed in {processing_time:.2f}s")
279
+
280
+ return TryOnResponse(
281
+ success=True,
282
+ result_image=result_base64,
283
+ processing_time=processing_time
284
+ )
285
+
286
+ finally:
287
+ processing_lock.release()
288
+
289
+ except HTTPException:
290
+ raise
291
+ except Exception as e:
292
+ logger.error(f"Inference failed: {e}", exc_info=True)
293
+ return TryOnResponse(
294
+ success=False,
295
+ error=str(e),
296
+ error_code="INFERENCE_FAILED"
297
+ )
298
+
299
+
300
+ if __name__ == "__main__":
301
+ import uvicorn
302
+
303
+ uvicorn.run(
304
+ "main:app",
305
+ host="0.0.0.0",
306
+ port=7860,
307
+ reload=False, # Set to True for development
308
+ log_level="info"
309
+ )
requirements.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # StableVITON Virtual Try-On - Hugging Face Spaces Requirements
2
+
3
+ # Core Machine Learning (Latest compatible versions)
4
+ torch>=2.2.0
5
+ torchvision>=0.17.0
6
+ diffusers>=0.27.0
7
+ transformers>=4.39.0
8
+ accelerate>=0.28.0
9
+ xformers>=0.0.25 # Memory efficient attention
10
+
11
+ # Image Processing
12
+ Pillow>=10.2.0
13
+ opencv-python-headless>=4.9.0 # Headless for server capability
14
+ numpy>=1.26.0
15
+
16
+ # API and Web
17
+ fastapi>=0.110.0
18
+ uvicorn[standard]>=0.29.0
19
+ python-multipart>=0.0.9
20
+ pydantic>=2.6.0
21
+ requests>=2.31.0
22
+ aiofiles>=23.2.0
23
+ python-dotenv>=1.0.1
24
+
25
+ # Integration
26
+ huggingface-hub>=0.22.0
setup.bat ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ REM Quick Setup Script for Backend
3
+ REM Run this from C:\antigravity-workspace\backend
4
+
5
+ echo StableVITON Backend Setup
6
+ echo =========================
7
+ echo.
8
+
9
+ REM Check if we're in the right directory
10
+ if not exist "main.py" (
11
+ echo ERROR: Please run this script from C:\antigravity-workspace\backend
12
+ exit /b 1
13
+ )
14
+
15
+ REM Create virtual environment
16
+ echo Creating virtual environment...
17
+ python -m venv venv
18
+
19
+ REM Activate virtual environment
20
+ echo Activating virtual environment...
21
+ call venv\Scripts\activate.bat
22
+
23
+ REM Upgrade pip
24
+ echo Upgrading pip...
25
+ python -m pip install --upgrade pip
26
+
27
+ REM Install dependencies
28
+ echo Installing dependencies (this may take a few minutes)...
29
+ pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
30
+ pip install fastapi uvicorn[standard] python-multipart pydantic
31
+ pip install diffusers transformers accelerate
32
+ pip install Pillow opencv-python numpy
33
+ pip install huggingface-hub python-dotenv requests aiofiles
34
+
35
+ echo.
36
+ echo Setup complete!
37
+ echo.
38
+ echo To run the server:
39
+ echo 1. Activate venv: venv\Scripts\activate.bat
40
+ echo 2. Run server: python main.py
41
+ echo.
42
+ echo Note: First run will download the AI model (~4GB)