gk2291 commited on
Commit
d7e28cf
Β·
verified Β·
1 Parent(s): a46abe2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -171
app.py CHANGED
@@ -1,211 +1,134 @@
1
  import gradio as gr
2
  import spaces
3
  from PIL import Image
4
- import numpy as np
5
  from gradio_client import Client, handle_file
6
  import logging
7
- import os
8
  import tempfile
 
9
 
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
13
- # Try multiple VTON models
14
- VTON_MODELS = [
15
- ("yisol/IDM-VTON", "IDM-VTON"),
16
- ("levihsu/OOTDiffusion", "OOTDiffusion"),
17
- ]
18
 
19
- active_client = None
20
- active_model = None
21
-
22
- def init_vton_client():
23
- """Try to connect to available VTON models"""
24
- global active_client, active_model
25
-
26
- for model_id, model_name in VTON_MODELS:
27
- try:
28
- logger.info(f"Trying to connect to {model_name}...")
29
- active_client = Client(model_id)
30
- active_model = model_name
31
- logger.info(f"βœ… Connected to {model_name}")
32
- return True
33
- except Exception as e:
34
- logger.warning(f"Could not connect to {model_name}: {e}")
35
- continue
36
-
37
- logger.error("❌ Could not connect to any VTON model")
38
- return False
39
 
40
- # Initialize
41
- init_vton_client()
42
 
43
  @spaces.GPU(duration=180)
44
- def virtual_tryon(person_image, garment_image, garment_desc, progress=gr.Progress()):
45
- """
46
- Virtual try-on using IDM-VTON or OOTDiffusion
47
- """
48
- global active_client, active_model
49
 
50
  try:
51
- if person_image is None or garment_image is None:
52
- raise gr.Error("❌ Please upload both person and garment images!")
53
 
54
- if active_client is None:
55
- logger.info("Reconnecting to VTON service...")
56
- if not init_vton_client():
57
- raise gr.Error("❌ Virtual Try-On service unavailable. Try again in a moment.")
58
 
59
- logger.info(f"🎯 Using {active_model} for virtual try-on")
60
- progress(0.1, desc="πŸ“Έ Preparing images...")
61
 
62
- # Prepare images
63
  person_img = person_image.convert('RGB')
64
  garment_img = garment_image.convert('RGB')
65
 
66
- # Save to temp files
67
- with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as f:
68
- person_img.save(f.name, 'JPEG', quality=95)
69
- person_path = f.name
70
 
71
- with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as f:
72
- garment_img.save(f.name, 'JPEG', quality=95)
73
- garment_path = f.name
74
 
75
- progress(0.3, desc=f"πŸš€ Processing with {active_model} (60-90s)...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
  try:
78
- # Call IDM-VTON with correct parameters
79
- result = active_client.predict(
80
- handle_file(person_path), # vton_img
81
- handle_file(garment_path), # garm_img
82
- garment_desc or "a garment", # n_cloth
83
- True, # is_checked
84
- True, # is_checked_crop
85
- 30, # denoise_steps
86
- 42, # seed
87
- api_name="/tryon"
88
- )
89
-
90
- progress(0.9, desc="πŸ“₯ Getting result...")
91
-
92
- # Handle result
93
- if isinstance(result, (list, tuple)):
94
- result_path = result[0]
95
- else:
96
- result_path = result
97
-
98
- result_image = Image.open(result_path)
99
-
100
- # Cleanup
101
- try:
102
- os.unlink(person_path)
103
- os.unlink(garment_path)
104
- except:
105
- pass
106
-
107
- progress(1.0, desc="βœ… Done!")
108
- logger.info("βœ… Try-on completed!")
109
-
110
- return result_image
111
-
112
- except Exception as e:
113
- # Cleanup
114
- try:
115
- os.unlink(person_path)
116
- os.unlink(garment_path)
117
- except:
118
- pass
119
-
120
- logger.error(f"API call failed: {e}")
121
- raise gr.Error(f"⚠️ Try-on failed: {str(e)}\n\nThe service may be busy. Try again in 1-2 minutes.")
122
-
123
- except gr.Error:
124
- raise
125
  except Exception as e:
126
  logger.error(f"Error: {e}")
127
- import traceback
128
- traceback.print_exc()
129
  raise gr.Error(f"Failed: {str(e)}")
130
 
131
- css = """
132
- #container {max-width: 1400px; margin: auto;}
133
- .info-box {
134
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
135
- color: white;
136
- border-radius: 12px;
137
- padding: 25px;
138
- margin: 20px 0;
139
- }
140
- footer {display: none !important;}
141
- """
142
-
143
- with gr.Blocks(css=css, title="Mirro Virtual Try-On") as demo:
144
-
145
- gr.Markdown("""
146
- # πŸ‘— Mirro Virtual Try-On API
147
- ### πŸ†“ FREE - Realistic Virtual Try-On
148
 
149
- Real AI-powered garment transfer (not overlay) using IDM-VTON.
150
- """)
151
 
152
- if active_client:
153
- gr.HTML(f"""
154
- <div class="info-box">
155
- <h3>βœ… Connected to {active_model}</h3>
156
- <p>🎨 Production-quality realistic results</p>
157
- <p>⏱️ Processing: 60-90 seconds per try-on</p>
158
- <p>πŸ’° Cost: FREE (shared resources)</p>
159
- </div>
160
- """)
161
  else:
162
- gr.Markdown("⚠️ **Connecting...** Try generating in a moment.")
163
 
164
  with gr.Row():
165
  with gr.Column():
166
  gr.Markdown("### πŸ“Έ Upload Images")
167
-
168
  person_input = gr.Image(
169
- label="πŸ‘€ Person Photo",
170
- type="pil",
171
- sources=["upload", "webcam", "clipboard"],
172
- height=400
173
  )
174
-
175
  garment_input = gr.Image(
176
- label="πŸ‘” Garment Photo",
177
- type="pil",
178
- sources=["upload", "webcam", "clipboard"],
179
- height=400
180
- )
181
-
182
- garment_desc = gr.Textbox(
183
- label="Garment Description (Optional)",
184
- placeholder="e.g., blue shirt, red dress",
185
- value="a garment"
186
  )
187
-
188
- with gr.Row():
189
- clear_btn = gr.ClearButton([person_input, garment_input, garment_desc])
190
- submit_btn = gr.Button("✨ Generate Try-On (FREE)", variant="primary", scale=2)
191
 
192
  with gr.Column():
193
  gr.Markdown("### 🎯 Result")
194
- output_image = gr.Image(label="Virtual Try-On Result", type="pil", height=850)
195
 
196
- with gr.Accordion("πŸ’‘ Tips", open=False):
197
  gr.Markdown("""
198
- ### For Best Results:
199
- - Person facing forward, arms slightly away from body
200
- - Clear, well-lit photos
201
- - Plain backgrounds preferred
202
- - Processing takes 60-90 seconds
203
-
204
- ### What You Get:
205
- - βœ… Realistic garment transfer (NOT overlay)
206
- - βœ… Natural fabric draping and wrinkles
207
- - βœ… Preserves person's face and pose
208
- - βœ… Works with selfies or album photos
 
 
 
209
  """)
210
 
211
  with gr.Accordion("πŸ“± iOS API Integration", open=False):
@@ -215,41 +138,56 @@ with gr.Blocks(css=css, title="Mirro Virtual Try-On") as demo:
215
  POST https://gk2291-mirro-app-server.hf.space/api/predict
216
  ```
217
 
218
- ### Request Format:
219
  ```json
220
  {
221
  "data": [
222
  "data:image/jpeg;base64,<person_base64>",
223
- "data:image/jpeg;base64,<garment_base64>",
224
- "a garment"
225
  ]
226
  }
227
  ```
228
 
 
 
 
 
 
 
 
229
  ### Swift Example:
230
  ```swift
 
 
 
 
 
231
  let payload: [String: Any] = [
232
- "data": [personBase64, garmentBase64, "a garment"]
233
  ]
234
- // Send to /api/predict endpoint
 
 
 
 
235
  ```
236
  """)
237
 
238
- submit_btn.click(
239
  fn=virtual_tryon,
240
- inputs=[person_input, garment_input, garment_desc],
241
  outputs=output_image,
242
  api_name="predict"
243
  )
244
 
245
  gr.Markdown("""
246
  ---
247
- <div style="text-align: center; padding: 20px; color: #666;">
248
  <p>πŸ†“ 100% FREE | ⚑ Powered by IDM-VTON | 🎨 Production Quality</p>
249
  <p style="font-size: 0.9em;">Perfect for iOS App MVP</p>
250
  </div>
251
  """)
252
 
253
  if __name__ == "__main__":
254
- demo.queue(max_size=30)
255
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
  import spaces
3
  from PIL import Image
 
4
  from gradio_client import Client, handle_file
5
  import logging
 
6
  import tempfile
7
+ import os
8
 
9
  logging.basicConfig(level=logging.INFO)
10
  logger = logging.getLogger(__name__)
11
 
12
+ vton_client = None
 
 
 
 
13
 
14
+ def init_client():
15
+ global vton_client
16
+ try:
17
+ logger.info("Connecting to IDM-VTON...")
18
+ vton_client = Client("yisol/IDM-VTON")
19
+ logger.info("βœ… Connected!")
20
+ return True
21
+ except Exception as e:
22
+ logger.error(f"Failed: {e}")
23
+ return False
 
 
 
 
 
 
 
 
 
 
24
 
25
+ model_ready = init_client()
 
26
 
27
  @spaces.GPU(duration=180)
28
+ def virtual_tryon(person_image, garment_image, progress=gr.Progress()):
29
+ global vton_client
 
 
 
30
 
31
  try:
32
+ if not person_image or not garment_image:
33
+ raise gr.Error("Please upload both images!")
34
 
35
+ if not vton_client:
36
+ if not init_client():
37
+ raise gr.Error("Service unavailable. Try again.")
 
38
 
39
+ logger.info("Starting try-on...")
40
+ progress(0.2, desc="Preparing...")
41
 
 
42
  person_img = person_image.convert('RGB')
43
  garment_img = garment_image.convert('RGB')
44
 
45
+ person_tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
46
+ garment_tmp = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
 
 
47
 
48
+ person_img.save(person_tmp.name, 'JPEG', quality=95)
49
+ garment_img.save(garment_tmp.name, 'JPEG', quality=95)
 
50
 
51
+ person_tmp.close()
52
+ garment_tmp.close()
53
+
54
+ progress(0.5, desc="Processing (60-90s)...")
55
+
56
+ result = vton_client.predict(
57
+ handle_file(person_tmp.name),
58
+ handle_file(garment_tmp.name),
59
+ "a garment",
60
+ True,
61
+ True,
62
+ 30,
63
+ 42,
64
+ api_name="/tryon"
65
+ )
66
+
67
+ progress(0.9, desc="Getting result...")
68
+
69
+ result_path = result[0] if isinstance(result, (tuple, list)) else result
70
+ result_image = Image.open(result_path)
71
 
72
  try:
73
+ os.unlink(person_tmp.name)
74
+ os.unlink(garment_tmp.name)
75
+ except:
76
+ pass
77
+
78
+ progress(1.0, desc="Done!")
79
+ logger.info("βœ… Complete!")
80
+
81
+ return result_image
82
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  except Exception as e:
84
  logger.error(f"Error: {e}")
 
 
85
  raise gr.Error(f"Failed: {str(e)}")
86
 
87
+ # UI with Gradio 5.x syntax
88
+ with gr.Blocks(title="Mirro Virtual Try-On", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ gr.Markdown("# πŸ‘— Mirro Virtual Try-On\n### πŸ†“ Free AI-Powered Virtual Try-On")
 
91
 
92
+ if model_ready:
93
+ gr.Markdown("🟒 **Ready!** Upload images to start.")
 
 
 
 
 
 
 
94
  else:
95
+ gr.Markdown("🟑 **Connecting...** Try in a moment.")
96
 
97
  with gr.Row():
98
  with gr.Column():
99
  gr.Markdown("### πŸ“Έ Upload Images")
 
100
  person_input = gr.Image(
101
+ label="πŸ‘€ Person Photo",
102
+ type="pil",
103
+ sources=["upload", "webcam"]
 
104
  )
 
105
  garment_input = gr.Image(
106
+ label="πŸ‘” Garment Photo",
107
+ type="pil",
108
+ sources=["upload", "webcam"]
 
 
 
 
 
 
 
109
  )
110
+ btn = gr.Button("✨ Generate Try-On", variant="primary")
 
 
 
111
 
112
  with gr.Column():
113
  gr.Markdown("### 🎯 Result")
114
+ output_image = gr.Image(label="Virtual Try-On Result", type="pil")
115
 
116
+ with gr.Accordion("πŸ’‘ Tips for Best Results", open=False):
117
  gr.Markdown("""
118
+ ### Person Photo:
119
+ - Face forward, arms slightly away from body
120
+ - Clear, well-lit photo
121
+ - Plain background preferred
122
+
123
+ ### Garment Photo:
124
+ - Clear photo of garment (flat lay or on hanger)
125
+ - Full garment visible
126
+ - Good lighting
127
+
128
+ ### Processing:
129
+ - Takes 60-90 seconds
130
+ - Realistic results (not overlay)
131
+ - Preserves face and pose
132
  """)
133
 
134
  with gr.Accordion("πŸ“± iOS API Integration", open=False):
 
138
  POST https://gk2291-mirro-app-server.hf.space/api/predict
139
  ```
140
 
141
+ ### Request Format (JSON):
142
  ```json
143
  {
144
  "data": [
145
  "data:image/jpeg;base64,<person_base64>",
146
+ "data:image/jpeg;base64,<garment_base64>"
 
147
  ]
148
  }
149
  ```
150
 
151
+ ### Response Format (JSON):
152
+ ```json
153
+ {
154
+ "data": ["data:image/png;base64,<result_base64>"]
155
+ }
156
+ ```
157
+
158
  ### Swift Example:
159
  ```swift
160
+ let url = URL(string: "https://gk2291-mirro-app-server.hf.space/api/predict")!
161
+ var request = URLRequest(url: url)
162
+ request.httpMethod = "POST"
163
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
164
+
165
  let payload: [String: Any] = [
166
+ "data": [personBase64, garmentBase64]
167
  ]
168
+ request.httpBody = try? JSONSerialization.data(withJSONObject: payload)
169
+
170
+ URLSession.shared.dataTask(with: request) { data, response, error in
171
+ // Handle response
172
+ }.resume()
173
  ```
174
  """)
175
 
176
+ btn.click(
177
  fn=virtual_tryon,
178
+ inputs=[person_input, garment_input],
179
  outputs=output_image,
180
  api_name="predict"
181
  )
182
 
183
  gr.Markdown("""
184
  ---
185
+ <div style="text-align: center; color: #666; padding: 20px;">
186
  <p>πŸ†“ 100% FREE | ⚑ Powered by IDM-VTON | 🎨 Production Quality</p>
187
  <p style="font-size: 0.9em;">Perfect for iOS App MVP</p>
188
  </div>
189
  """)
190
 
191
  if __name__ == "__main__":
192
+ demo.queue(max_size=20)
193
  demo.launch(server_name="0.0.0.0", server_port=7860)