alamx commited on
Commit
6b135d2
·
verified ·
1 Parent(s): 99f2171

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +372 -0
  2. requirements.txt +6 -0
  3. templates/result.html +64 -0
  4. templates/upload.html +132 -0
app.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import os
2
+ # import requests
3
+ # import base64
4
+ # import cv2
5
+ # import numpy as np
6
+ # from flask import Flask, request, send_from_directory
7
+ # from twilio.twiml.messaging_response import MessagingResponse
8
+ # from twilio.rest import Client
9
+ # from gradio_client import Client as GradioClient, file
10
+ # import shutil
11
+ # from dotenv import load_dotenv
12
+
13
+ # # Load environment variables from .env file
14
+ # load_dotenv()
15
+
16
+ # app = Flask(__name__)
17
+
18
+ # @app.route('/', methods=['GET'])
19
+ # def index():
20
+ # return "This is the virtual try-on chatbot API.", 200
21
+
22
+ # # In-memory storage for tracking sessions
23
+ # user_sessions = {}
24
+
25
+ # # Twilio credentials loaded from .env file
26
+ # TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
27
+ # TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
28
+ # client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
29
+
30
+ # # Gradio Client for Nymbo Virtual Try-On API
31
+ # gradio_client = GradioClient("Nymbo/Virtual-Try-On")
32
+
33
+ # # Ngrok URL loaded from .env file
34
+ # NGROK_URL = os.getenv("NGROK_URL")
35
+
36
+ # # Webhook route to handle POST requests from Twilio
37
+ # @app.route('/webhook', methods=['POST'])
38
+ # def webhook():
39
+ # sender_number = request.form.get('From') # User's WhatsApp number
40
+ # media_url = request.form.get('MediaUrl0') # URL of the media if image is sent
41
+
42
+ # # Log the media URL
43
+ # print(f"Received media URL: {media_url}")
44
+
45
+ # # Create a response object for Twilio
46
+ # resp = MessagingResponse()
47
+
48
+ # # If no image is received, inform the user
49
+ # if media_url is None:
50
+ # resp.message("We didn't receive an image. Please try sending your image again.")
51
+ # return str(resp)
52
+
53
+ # # Step 1: Check if person image is uploaded
54
+ # if sender_number not in user_sessions:
55
+ # user_sessions[sender_number] = {}
56
+ # if media_url:
57
+ # user_sessions[sender_number]['person_image'] = media_url
58
+ # resp.message("Great! Now please send the image of the garment you want to try on.")
59
+ # else:
60
+ # resp.message("Please send your image to begin the virtual try-on process.")
61
+ # # Step 2: Check if garment image is uploaded
62
+ # elif 'person_image' in user_sessions[sender_number] and 'garment_image' not in user_sessions[sender_number]:
63
+ # if media_url:
64
+ # user_sessions[sender_number]['garment_image'] = media_url
65
+ # # Now both images are collected, send them to the Gradio API for virtual try-on
66
+ # try_on_image_url = send_to_gradio(user_sessions[sender_number]['person_image'], media_url)
67
+ # if try_on_image_url:
68
+ # # Send the image as a WhatsApp media message
69
+ # send_media_message(sender_number, try_on_image_url)
70
+ # resp.message("Here is your virtual try-on result!")
71
+ # else:
72
+ # resp.message("Sorry, something went wrong with the try-on process.")
73
+ # # Clear session after completion
74
+ # del user_sessions[sender_number]
75
+ # else:
76
+ # resp.message("Please send the garment image to complete the process.")
77
+ # else:
78
+ # # If both images have already been received, start the process again
79
+ # resp.message("Please send your image to begin the virtual try-on process.")
80
+
81
+ # return str(resp)
82
+
83
+ # # Function to interact with the Gradio API
84
+ # def send_to_gradio(person_image_url, garment_image_url):
85
+ # # Download both images from Twilio
86
+ # person_image_path = download_image(person_image_url, 'person_image.jpg')
87
+ # garment_image_path = download_image(garment_image_url, 'garment_image.jpg')
88
+
89
+ # if person_image_path is None or garment_image_path is None:
90
+ # print("Error: One of the images could not be downloaded.")
91
+ # return None
92
+
93
+ # try:
94
+ # # Interact with the Gradio API using the client
95
+ # result = gradio_client.predict(
96
+ # dict={"background": file(person_image_path), "layers": [], "composite": None},
97
+ # garm_img=file(garment_image_path),
98
+ # garment_des="A cool description of the garment",
99
+ # is_checked=True,
100
+ # is_checked_crop=False,
101
+ # denoise_steps=30,
102
+ # seed=42,
103
+ # api_name="/tryon"
104
+ # )
105
+
106
+ # # Log the result for debugging
107
+ # print(f"API result: {result}")
108
+
109
+ # # Check if the result is returned correctly
110
+ # if result and len(result) > 0:
111
+ # try_on_image_path = result[0] # First item in result is the output image path
112
+ # print(f"Generated try-on image path: {try_on_image_path}")
113
+
114
+ # # Ensure the static directory exists
115
+ # static_dir = 'static'
116
+ # if not os.path.exists(static_dir):
117
+ # os.makedirs(static_dir)
118
+ # print(f"Created directory: {static_dir}")
119
+
120
+ # # Make sure the path exists
121
+ # if os.path.exists(try_on_image_path):
122
+ # # Convert the image to PNG format and save it
123
+ # img = cv2.imread(try_on_image_path)
124
+ # target_path_png = os.path.join(static_dir, 'result.png')
125
+ # cv2.imwrite(target_path_png, img)
126
+ # print(f"Image saved to: {target_path_png}")
127
+
128
+ # # Return the public URL for the image as PNG
129
+ # return f"{NGROK_URL}/static/result.png"
130
+ # else:
131
+ # print(f"Image not found at: {try_on_image_path}")
132
+ # return None
133
+
134
+ # print("No image returned from the API.")
135
+ # return None
136
+
137
+ # except Exception as e:
138
+ # print(f"Error interacting with Gradio API: {e}")
139
+ # return None
140
+
141
+ # # Helper function to send media message via Twilio
142
+ # def send_media_message(to_number, media_url):
143
+ # message = client.messages.create(
144
+ # from_='whatsapp:+14155238886', # Twilio sandbox number
145
+ # body="Here is your virtual try-on result:",
146
+ # media_url=[media_url], # Public URL of the media
147
+ # to=to_number
148
+ # )
149
+ # print(f"Sent media message to {to_number}. Message SID: {message.sid}")
150
+
151
+ # # Helper function to download an image from Twilio using the Twilio API
152
+ # def download_image(media_url, filename):
153
+ # try:
154
+ # # Extract Message SID and Media SID from the URL
155
+ # message_sid = media_url.split('/')[-3]
156
+ # media_sid = media_url.split('/')[-1]
157
+
158
+ # # Log the message and media SIDs
159
+ # print(f"Message SID: {message_sid}, Media SID: {media_sid}")
160
+
161
+ # # Use Twilio client to fetch the media resource
162
+ # media = client.api.accounts(TWILIO_ACCOUNT_SID).messages(message_sid).media(media_sid).fetch()
163
+
164
+ # # Construct the actual media URL
165
+ # media_uri = media.uri.replace('.json', '')
166
+ # image_url = f"https://api.twilio.com{media_uri}"
167
+
168
+ # # Log the full URL being used for the image download
169
+ # print(f"Downloading image from: {image_url}")
170
+
171
+ # # Download the image with proper authorization (using Basic Auth)
172
+ # response = requests.get(image_url, auth=(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN))
173
+
174
+ # if response.status_code == 200:
175
+ # # Save the image locally
176
+ # with open(filename, 'wb') as f:
177
+ # f.write(response.content)
178
+ # print(f"Image downloaded successfully as {filename}.")
179
+ # return filename
180
+ # else:
181
+ # print(f"Failed to download image: {response.status_code}")
182
+ # return None
183
+ # except Exception as err:
184
+ # print(f"Error downloading image from Twilio: {err}")
185
+ # return None
186
+
187
+ # # Ensure Flask serves static files properly
188
+ # @app.route('/static/<path:filename>')
189
+ # def serve_static_file(filename):
190
+ # file_path = os.path.join('static', filename)
191
+ # # Check if the file exists and serve with the correct Content-Type
192
+ # if os.path.exists(file_path):
193
+ # return send_from_directory('static', filename, mimetype='image/png')
194
+ # else:
195
+ # print(f"File not found: {filename}")
196
+ # return "File not found", 404
197
+
198
+ # if __name__ == '__main__':
199
+ # app.run(port=8080)
200
+
201
+
202
+ """
203
+ Virtual Try-On WhatsApp Bot using Twilio and Gradio API
204
+
205
+ This Flask application provides:
206
+ 1. A WhatsApp bot interface via Twilio that guides users through virtual try-on process
207
+ 2. An HTML interface for uploading images and viewing results
208
+ 3. Integration with Nymbo/Virtual-Try-On Gradio API
209
+
210
+ Key Features:
211
+ - WhatsApp conversation flow to collect person and garment images
212
+ - Image processing via Gradio API
213
+ - Result display in WhatsApp and web interface
214
+ - Session management for multiple users
215
+ """
216
+
217
+ import os
218
+ import requests
219
+ import base64
220
+ import cv2
221
+ import numpy as np
222
+ from flask import Flask, request, send_from_directory, render_template
223
+ from twilio.twiml.messaging_response import MessagingResponse
224
+ from twilio.rest import Client
225
+ from gradio_client import Client as GradioClient, file
226
+ import shutil
227
+ from dotenv import load_dotenv
228
+
229
+ # Load environment variables from .env file
230
+ load_dotenv()
231
+
232
+ app = Flask(__name__)
233
+
234
+ # Configure upload folder
235
+ UPLOAD_FOLDER = 'static/uploads'
236
+ if not os.path.exists(UPLOAD_FOLDER):
237
+ os.makedirs(UPLOAD_FOLDER)
238
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
239
+
240
+ # Twilio credentials loaded from .env file
241
+ TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
242
+ TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
243
+ client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
244
+
245
+ # Gradio Client for Nymbo Virtual Try-On API
246
+ gradio_client = GradioClient("alamx/Try-On-uetp")
247
+
248
+ # Ngrok URL loaded from .env file
249
+ NGROK_URL = os.getenv("NGROK_URL")
250
+
251
+ # In-memory storage for tracking sessions
252
+ user_sessions = {}
253
+
254
+ @app.route('/', methods=['GET'])
255
+ def index():
256
+ """Render the main upload interface"""
257
+ return render_template('upload.html')
258
+
259
+ @app.route('/upload', methods=['POST'])
260
+ def upload_images():
261
+ """Handle image uploads from web interface"""
262
+ if 'person_image' not in request.files or 'garment_image' not in request.files:
263
+ return "Both person and garment images are required", 400
264
+
265
+ person_img = request.files['person_image']
266
+ garment_img = request.files['garment_image']
267
+
268
+ if person_img.filename == '' or garment_img.filename == '':
269
+ return "No selected file", 400
270
+
271
+ # Save uploaded files
272
+ person_path = os.path.join(app.config['UPLOAD_FOLDER'], 'person_upload.jpg')
273
+ garment_path = os.path.join(app.config['UPLOAD_FOLDER'], 'garment_upload.jpg')
274
+ person_img.save(person_path)
275
+ garment_img.save(garment_path)
276
+
277
+ # Process images through Gradio API
278
+ result_url = send_to_gradio(person_path, garment_path)
279
+
280
+ if result_url:
281
+ # Return the result page with the try-on image
282
+ return render_template('result.html', result_image=result_url)
283
+ else:
284
+ return "Error processing images", 500
285
+
286
+ def send_to_gradio(person_image_path, garment_image_path):
287
+ """
288
+ Send images to YOUR Gradio API for virtual try-on processing
289
+
290
+ Args:
291
+ person_image_path (str): Path to the person's image
292
+ garment_image_path (str): Path to the garment image
293
+
294
+ Returns:
295
+ str: URL of the generated try-on image or None if failed
296
+ """
297
+ try:
298
+ # Check if your Space expects different parameters
299
+ result = gradio_client.predict(
300
+ person_image=file(person_image_path), # Parameter name might differ
301
+ garment_image=file(garment_image_path), # Parameter name might differ
302
+ api_name="/predict" # Might be "/run" or "/predict" in your Space
303
+ )
304
+
305
+ # Rest of your function remains the same...
306
+ print(f"API result: {result}")
307
+
308
+ if result and len(result) > 0:
309
+ try_on_image_path = result[0] # Might need adjustment based on output
310
+ print(f"Generated try-on image path: {try_on_image_path}")
311
+
312
+ static_dir = 'static'
313
+ if not os.path.exists(static_dir):
314
+ os.makedirs(static_dir)
315
+
316
+ if os.path.exists(try_on_image_path):
317
+ img = cv2.imread(try_on_image_path)
318
+ target_path_png = os.path.join(static_dir, 'result.png')
319
+ cv2.imwrite(target_path_png, img)
320
+ return f"{NGROK_URL}/static/result.png"
321
+
322
+ return None
323
+
324
+ except Exception as e:
325
+ print(f"Error interacting with Gradio API: {e}")
326
+ return None
327
+
328
+ # Helper function to send media message via Twilio
329
+ def send_media_message(to_number, media_url):
330
+ message = client.messages.create(
331
+ from_='whatsapp:+14155238886', # Twilio sandbox number
332
+ body="Here is your virtual try-on result:",
333
+ media_url=[media_url], # Public URL of the media
334
+ to=to_number
335
+ )
336
+ print(f"Sent media message to {to_number}. Message SID: {message.sid}")
337
+
338
+ # Helper function to download an image from Twilio using the Twilio API
339
+ def download_image(media_url, filename):
340
+ try:
341
+ # Extract Message SID and Media SID from the URL
342
+ message_sid = media_url.split('/')[-3]
343
+ media_sid = media_url.split('/')[-1]
344
+
345
+ # Log the message and media SIDs
346
+ print(f"Message SID: {message_sid}, Media SID: {media_sid}")
347
+
348
+ # Use Twilio client to fetch the media resource
349
+ media = client.api.accounts(TWILIO_ACCOUNT_SID).messages(message_sid).media(media_sid).fetch()
350
+
351
+ # Construct the actual media URL
352
+ media_uri = media.uri.replace('.json', '')
353
+ image_url = f"https://api.twilio.com{media_uri}"
354
+
355
+ # Log the full URL being used for the image download
356
+ print(f"Downloading image from: {image_url}")
357
+
358
+ # Download the image with proper authorization (using Basic Auth)
359
+ response = requests.get(image_url, auth=(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN))
360
+
361
+ if response.status_code == 200:
362
+ # Save the image locally
363
+ with open(filename, 'wb') as f:
364
+ f.write(response.content)
365
+ print(f"Image downloaded successfully as {filename}.")
366
+ return filename
367
+ else:
368
+ print(f"Failed to download image: {response.status_code}")
369
+ return None
370
+ except Exception as err:
371
+ print(f"Error downloading image from Twilio: {err}")
372
+ return None
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Flask==3.0.3
2
+ twilio==8.2.0
3
+ requests==2.31.0
4
+ numpy==1.25.2
5
+ gradio-client==0.1.4
6
+ python-dotenv==1.0.0
templates/result.html ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Try-On Result</title>
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ max-width: 800px;
12
+ margin: 0 auto;
13
+ padding: 20px;
14
+ background-color: #f5f5f5;
15
+ text-align: center;
16
+ }
17
+
18
+ .result-container {
19
+ background-color: white;
20
+ padding: 30px;
21
+ border-radius: 10px;
22
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
23
+ }
24
+
25
+ h1 {
26
+ color: #333;
27
+ }
28
+
29
+ .result-img {
30
+ max-width: 100%;
31
+ max-height: 500px;
32
+ margin: 20px 0;
33
+ border: 1px solid #ddd;
34
+ }
35
+
36
+ .btn {
37
+ background-color: #4CAF50;
38
+ color: white;
39
+ padding: 12px 20px;
40
+ border: none;
41
+ border-radius: 4px;
42
+ cursor: pointer;
43
+ font-size: 16px;
44
+ text-decoration: none;
45
+ display: inline-block;
46
+ margin-top: 20px;
47
+ }
48
+
49
+ .btn:hover {
50
+ background-color: #45a049;
51
+ }
52
+ </style>
53
+ </head>
54
+
55
+ <body>
56
+ <div class="result-container">
57
+ <h1>Your Virtual Try-On Result</h1>
58
+ <img src="{{ result_image }}" alt="Virtual try-on result" class="result-img">
59
+ <br>
60
+ <a href="/" class="btn">Try Another</a>
61
+ </div>
62
+ </body>
63
+
64
+ </html>
templates/upload.html ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Virtual Try-On</title>
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ max-width: 800px;
12
+ margin: 0 auto;
13
+ padding: 20px;
14
+ background-color: #f5f5f5;
15
+ }
16
+
17
+ .upload-container {
18
+ background-color: white;
19
+ padding: 30px;
20
+ border-radius: 10px;
21
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
22
+ }
23
+
24
+ h1 {
25
+ color: #333;
26
+ text-align: center;
27
+ }
28
+
29
+ .form-group {
30
+ margin-bottom: 20px;
31
+ }
32
+
33
+ label {
34
+ display: block;
35
+ margin-bottom: 8px;
36
+ font-weight: bold;
37
+ }
38
+
39
+ input[type="file"] {
40
+ width: 100%;
41
+ padding: 10px;
42
+ border: 1px solid #ddd;
43
+ border-radius: 4px;
44
+ }
45
+
46
+ button {
47
+ background-color: #4CAF50;
48
+ color: white;
49
+ padding: 12px 20px;
50
+ border: none;
51
+ border-radius: 4px;
52
+ cursor: pointer;
53
+ font-size: 16px;
54
+ width: 100%;
55
+ }
56
+
57
+ button:hover {
58
+ background-color: #45a049;
59
+ }
60
+
61
+ .preview-container {
62
+ display: flex;
63
+ justify-content: space-between;
64
+ margin-top: 20px;
65
+ }
66
+
67
+ .preview-box {
68
+ width: 48%;
69
+ text-align: center;
70
+ }
71
+
72
+ .preview-img {
73
+ max-width: 100%;
74
+ max-height: 300px;
75
+ border: 1px dashed #ccc;
76
+ margin-top: 10px;
77
+ }
78
+ </style>
79
+ </head>
80
+
81
+ <body>
82
+ <div class="upload-container">
83
+ <h1>Virtual Try-On</h1>
84
+ <form action="/upload" method="POST" enctype="multipart/form-data" id="uploadForm">
85
+ <div class="form-group">
86
+ <label for="person_image">Upload Your Photo:</label>
87
+ <input type="file" id="person_image" name="person_image" accept="image/*" required>
88
+ </div>
89
+
90
+ <div class="form-group">
91
+ <label for="garment_image">Upload Garment Photo:</label>
92
+ <input type="file" id="garment_image" name="garment_image" accept="image/*" required>
93
+ </div>
94
+
95
+ <button type="submit">Try It On!</button>
96
+ </form>
97
+
98
+ <div class="preview-container">
99
+ <div class="preview-box">
100
+ <h3>Your Photo</h3>
101
+ <img id="personPreview" class="preview-img" src="#" alt="Your photo preview" style="display: none;">
102
+ </div>
103
+ <div class="preview-box">
104
+ <h3>Garment Photo</h3>
105
+ <img id="garmentPreview" class="preview-img" src="#" alt="Garment preview" style="display: none;">
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <script>
111
+ // Preview images before upload
112
+ document.getElementById('person_image').addEventListener('change', function (e) {
113
+ const preview = document.getElementById('personPreview');
114
+ const file = e.target.files[0];
115
+ if (file) {
116
+ preview.style.display = 'block';
117
+ preview.src = URL.createObjectURL(file);
118
+ }
119
+ });
120
+
121
+ document.getElementById('garment_image').addEventListener('change', function (e) {
122
+ const preview = document.getElementById('garmentPreview');
123
+ const file = e.target.files[0];
124
+ if (file) {
125
+ preview.style.display = 'block';
126
+ preview.src = URL.createObjectURL(file);
127
+ }
128
+ });
129
+ </script>
130
+ </body>
131
+
132
+ </html>