File size: 23,910 Bytes
4c6e6b6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
import gradio as gr
import requests
import os
import json
import base64
from PIL import Image, ImageDraw, ImageFont
import io
import numpy as np
import cv2
from typing import List, Tuple, Optional
import logging
from datetime import datetime
import tempfile
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SalesforceConnector:
"""Handle Salesforce API connections and data retrieval"""
def __init__(self):
self.access_token = None
self.instance_url = None
self.session = requests.Session()
def authenticate(self, client_id: str, client_secret: str, username: str, password: str, security_token: str = ""):
"""Authenticate with Salesforce using OAuth 2.0"""
try:
auth_url = "https://login.salesforce.com/services/oauth2/token"
auth_data = {
'grant_type': 'password',
'client_id': client_id,
'client_secret': client_secret,
'username': username,
'password': password + security_token
}
response = self.session.post(auth_url, data=auth_data)
response.raise_for_status()
auth_result = response.json()
self.access_token = auth_result['access_token']
self.instance_url = auth_result['instance_url']
self.session.headers.update({
'Authorization': f'Bearer {self.access_token}',
'Content-Type': 'application/json'
})
logger.info("Successfully authenticated with Salesforce")
return True
except requests.exceptions.RequestException as e:
logger.error(f"Salesforce authentication failed: {e}")
return False
def fetch_dress_catalog(self) -> List[dict]:
"""Fetch dress catalog from Salesforce"""
try:
# Query for dress products (adjust SOQL query based on your Salesforce schema)
query = """
SELECT Id, Name, Description, Product_Image_URL__c, Category__c, Price__c, Size__c, Color__c
FROM Product2
WHERE Category__c = 'Dress' OR Category__c = 'Clothing'
ORDER BY Name
LIMIT 50
"""
query_url = f"{self.instance_url}/services/data/v58.0/query"
params = {'q': query}
response = self.session.get(query_url, params=params)
response.raise_for_status()
result = response.json()
dresses = result.get('records', [])
logger.info(f"Fetched {len(dresses)} dresses from Salesforce")
return dresses
except requests.exceptions.RequestException as e:
logger.error(f"Failed to fetch dress catalog: {e}")
return self._get_sample_dresses()
def _get_sample_dresses(self) -> List[dict]:
"""Return sample dress data for demo purposes"""
return [
{
"Id": "dress_001",
"Name": "Elegant Evening Dress",
"Description": "Beautiful black evening dress perfect for special occasions",
"Product_Image_URL__c": "https://images.pexels.com/photos/1926769/pexels-photo-1926769.jpeg",
"Category__c": "Dress",
"Price__c": 299.99,
"Size__c": "M",
"Color__c": "Black"
},
{
"Id": "dress_002",
"Name": "Summer Floral Dress",
"Description": "Light and airy floral dress for summer occasions",
"Product_Image_URL__c": "https://images.pexels.com/photos/1926769/pexels-photo-1926769.jpeg",
"Category__c": "Dress",
"Price__c": 149.99,
"Size__c": "S",
"Color__c": "Floral"
},
{
"Id": "dress_003",
"Name": "Business Casual Dress",
"Description": "Professional dress suitable for office wear",
"Product_Image_URL__c": "https://images.pexels.com/photos/1926769/pexels-photo-1926769.jpeg",
"Category__c": "Dress",
"Price__c": 199.99,
"Size__c": "L",
"Color__c": "Navy"
}
]
class VirtualTryOnEngine:
"""Handle virtual try-on processing"""
def __init__(self):
self.model_loaded = False
def process_tryon(self, person_image: Image.Image, dress_image: Image.Image, dress_info: dict) -> Tuple[Image.Image, str]:
"""Process virtual try-on between person and dress images"""
try:
# For demo purposes, we'll create a simple overlay effect
# In production, you would use a proper virtual try-on model
result_image = self._create_demo_tryon(person_image, dress_image, dress_info)
confidence_score = "85%"
return result_image, confidence_score
except Exception as e:
logger.error(f"Try-on processing failed: {e}")
return person_image, "Error"
def _create_demo_tryon(self, person_image: Image.Image, dress_image: Image.Image, dress_info: dict) -> Image.Image:
"""Create a demo try-on result by overlaying dress info"""
# Resize images to standard size
person_img = person_image.convert("RGB")
person_img = person_img.resize((512, 768))
# Create a copy for the result
result_img = person_img.copy()
draw = ImageDraw.Draw(result_img)
# Add dress information overlay
try:
font = ImageFont.load_default()
except:
font = None
# Add semi-transparent overlay
overlay = Image.new('RGBA', result_img.size, (0, 0, 0, 128))
result_img = Image.alpha_composite(result_img.convert('RGBA'), overlay).convert('RGB')
# Add dress information text
dress_name = dress_info.get('Name', 'Selected Dress')
price = dress_info.get('Price__c', 0)
color = dress_info.get('Color__c', 'N/A')
size = dress_info.get('Size__c', 'N/A')
text_lines = [
f"Virtual Try-On Result",
f"Dress: {dress_name}",
f"Price: ${price}",
f"Color: {color}",
f"Size: {size}",
f"Confidence: 85%"
]
y_offset = 20
for line in text_lines:
draw.text((20, y_offset), line, fill="white", font=font)
y_offset += 25
return result_img
class VirtualTryOnApp:
"""Main application class"""
def __init__(self):
self.sf_connector = SalesforceConnector()
self.tryon_engine = VirtualTryOnEngine()
self.dress_catalog = []
self.selected_dress = None
def initialize_salesforce(self, client_id: str, client_secret: str, username: str, password: str, security_token: str = ""):
"""Initialize Salesforce connection"""
success = self.sf_connector.authenticate(client_id, client_secret, username, password, security_token)
if success:
self.dress_catalog = self.sf_connector.fetch_dress_catalog()
return "β
Successfully connected to Salesforce and loaded dress catalog!", self._get_dress_options()
else:
# Load sample data for demo
self.dress_catalog = self.sf_connector._get_sample_dresses()
return "β οΈ Using sample data (Salesforce connection failed)", self._get_dress_options()
def _get_dress_options(self) -> List[str]:
"""Get dress options for dropdown"""
return [f"{dress['Name']} - ${dress['Price__c']} ({dress['Color__c']})" for dress in self.dress_catalog]
def select_dress(self, dress_selection: str) -> Tuple[str, str]:
"""Handle dress selection"""
if not dress_selection or not self.dress_catalog:
return "Please select a dress", ""
# Find selected dress
dress_name = dress_selection.split(' - ')[0]
self.selected_dress = next((dress for dress in self.dress_catalog if dress['Name'] == dress_name), None)
if self.selected_dress:
dress_info = f"""
**Selected Dress Details:**
- **Name:** {self.selected_dress['Name']}
- **Description:** {self.selected_dress['Description']}
- **Price:** ${self.selected_dress['Price__c']}
- **Color:** {self.selected_dress['Color__c']}
- **Size:** {self.selected_dress['Size__c']}
"""
image_url = self.selected_dress.get('Product_Image_URL__c', '')
return dress_info, image_url
return "Dress not found", ""
def process_virtual_tryon(self, person_image: Image.Image) -> Tuple[Image.Image, str]:
"""Process the virtual try-on"""
if person_image is None:
return None, "Please upload a person image"
if self.selected_dress is None:
return person_image, "Please select a dress first"
try:
# Load dress image from URL
dress_url = self.selected_dress.get('Product_Image_URL__c', '')
if dress_url:
dress_response = requests.get(dress_url, timeout=10)
dress_image = Image.open(io.BytesIO(dress_response.content))
else:
# Create placeholder dress image
dress_image = Image.new('RGB', (300, 400), color='lightblue')
# Process virtual try-on
result_image, confidence = self.tryon_engine.process_tryon(person_image, dress_image, self.selected_dress)
status_message = f"β
Virtual try-on completed! Confidence: {confidence}"
return result_image, status_message
except Exception as e:
logger.error(f"Virtual try-on failed: {e}")
return person_image, f"β Try-on failed: {str(e)}"
# Initialize the app
app = VirtualTryOnApp()
def setup_salesforce_connection(client_id, client_secret, username, password, security_token):
"""Setup Salesforce connection and return status and dress options"""
status, dress_options = app.initialize_salesforce(client_id, client_secret, username, password, security_token)
return status, gr.Dropdown(choices=dress_options, value=None)
def handle_dress_selection(dress_selection):
"""Handle dress selection and return dress info and image"""
dress_info, image_url = app.select_dress(dress_selection)
# Try to load dress image
dress_image = None
if image_url:
try:
response = requests.get(image_url, timeout=10)
dress_image = Image.open(io.BytesIO(response.content))
except:
dress_image = None
return dress_info, dress_image
def process_tryon(person_image):
"""Process virtual try-on"""
return app.process_virtual_tryon(person_image)
# Custom CSS for better styling
custom_css = """
.gradio-container {
font-family: 'Inter', sans-serif;
max-width: 1200px !important;
margin: 0 auto;
}
.header-text {
text-align: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 2.5rem;
font-weight: bold;
margin-bottom: 1rem;
}
.section-header {
background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
padding: 1rem;
border-radius: 8px;
color: white;
font-weight: bold;
margin: 1rem 0;
}
.status-success {
background-color: #d4edda;
color: #155724;
padding: 1rem;
border-radius: 8px;
border: 1px solid #c3e6cb;
}
.status-error {
background-color: #f8d7da;
color: #721c24;
padding: 1rem;
border-radius: 8px;
border: 1px solid #f5c6cb;
}
.image-container {
border: 2px solid #e1e5e9;
border-radius: 12px;
padding: 1rem;
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.tryon-results {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 2rem;
border-radius: 12px;
margin-top: 1rem;
}
"""
# Create the Gradio interface
with gr.Blocks(css=custom_css, title="Virtual Try-On for Clothing") as demo:
# Header
gr.HTML("""
<div class="header-text">
π Virtual Try-On for Clothing
</div>
<p style="text-align: center; font-size: 1.2rem; color: #666; margin-bottom: 2rem;">
Experience the future of fashion with AI-powered virtual try-on technology
</p>
""")
# Salesforce Configuration Section
with gr.Accordion("π§ Salesforce Configuration", open=True):
gr.HTML('<div class="section-header">Connect to Salesforce to access dress catalog</div>')
with gr.Row():
with gr.Column(scale=2):
sf_client_id = gr.Textbox(
label="Client ID",
placeholder="Enter your Salesforce Client ID",
type="password"
)
sf_client_secret = gr.Textbox(
label="Client Secret",
placeholder="Enter your Salesforce Client Secret",
type="password"
)
with gr.Column(scale=2):
sf_username = gr.Textbox(
label="Username",
placeholder="Enter your Salesforce username"
)
sf_password = gr.Textbox(
label="Password",
placeholder="Enter your Salesforce password",
type="password"
)
sf_security_token = gr.Textbox(
label="Security Token (Optional)",
placeholder="Enter security token if required"
)
sf_connect_btn = gr.Button("π Connect to Salesforce", variant="primary", size="lg")
sf_status = gr.Textbox(label="Connection Status", interactive=False)
# Main Application Interface
with gr.Row():
# Left Column - Input Section
with gr.Column(scale=1):
gr.HTML('<div class="section-header">π· Upload Your Photo</div>')
person_image = gr.Image(
label="Upload your photo",
type="pil",
height=400,
elem_classes=["image-container"]
)
gr.HTML('<div class="section-header">π Select Dress</div>')
dress_dropdown = gr.Dropdown(
label="Choose a dress from catalog",
choices=[],
interactive=True
)
dress_info = gr.Markdown(
label="Dress Information",
value="Select a dress to see details"
)
dress_image = gr.Image(
label="Selected Dress",
type="pil",
height=300,
elem_classes=["image-container"]
)
# Right Column - Results Section
with gr.Column(scale=1):
gr.HTML('<div class="section-header">β¨ Virtual Try-On Results</div>')
tryon_btn = gr.Button(
"π Generate Virtual Try-On",
variant="primary",
size="lg",
elem_classes=["tryon-button"]
)
result_image = gr.Image(
label="Try-On Result",
type="pil",
height=500,
elem_classes=["image-container", "tryon-results"]
)
result_status = gr.Textbox(
label="Processing Status",
interactive=False
)
# Action buttons
with gr.Row():
download_btn = gr.DownloadButton(
"πΎ Download Result",
variant="secondary"
)
share_btn = gr.Button(
"π€ Share Result",
variant="secondary"
)
# Advanced Options
with gr.Accordion("βοΈ Advanced Options", open=False):
with gr.Row():
fit_adjustment = gr.Slider(
label="Fit Adjustment",
minimum=-10,
maximum=10,
value=0,
step=1
)
brightness_adjustment = gr.Slider(
label="Brightness",
minimum=-50,
maximum=50,
value=0,
step=5
)
pose_adjustment = gr.Dropdown(
label="Pose Adjustment",
choices=["Natural", "Straighten", "Slight Turn"],
value="Natural"
)
# Footer Information
gr.HTML("""
<div style="margin-top: 3rem; padding: 2rem; background: #f8f9fa; border-radius: 12px; text-align: center;">
<h3>π Features</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin-top: 1rem;">
<div>
<strong>π Salesforce Integration</strong><br>
Direct access to your dress catalog
</div>
<div>
<strong>π€ AI-Powered Try-On</strong><br>
Advanced virtual try-on technology
</div>
<div>
<strong>π± Responsive Design</strong><br>
Works on all devices
</div>
<div>
<strong>πΎ Export Options</strong><br>
Save and share your results
</div>
</div>
<p style="margin-top: 1.5rem; color: #666;">
Upload your photo, select a dress from your Salesforce catalog, and see how it looks on you instantly!
</p>
</div>
""")
# Event handlers
sf_connect_btn.click(
fn=setup_salesforce_connection,
inputs=[sf_client_id, sf_client_secret, sf_username, sf_password, sf_security_token],
outputs=[sf_status, dress_dropdown]
)
dress_dropdown.change(
fn=handle_dress_selection,
inputs=[dress_dropdown],
outputs=[dress_info, dress_image]
)
tryon_btn.click(
fn=process_tryon,
inputs=[person_image],
outputs=[result_image, result_status]
)
# Initialize with sample data on load
demo.load(
fn=lambda: app.initialize_salesforce("", "", "", "", ""),
outputs=[sf_status, dress_dropdown]
)
# Additional utility functions
def create_sample_person_image():
"""Create a sample person image for testing"""
img = Image.new('RGB', (512, 768), color='lightgray')
draw = ImageDraw.Draw(img)
# Draw a simple person silhouette
draw.ellipse([206, 100, 306, 200], fill='darkgray') # Head
draw.rectangle([231, 200, 281, 400], fill='darkgray') # Body
draw.rectangle([206, 400, 231, 600], fill='darkgray') # Left leg
draw.rectangle([281, 400, 306, 600], fill='darkgray') # Right leg
draw.rectangle([181, 220, 206, 350], fill='darkgray') # Left arm
draw.rectangle([306, 220, 331, 350], fill='darkgray') # Right arm
return img
def validate_image_input(image):
"""Validate uploaded image"""
if image is None:
return False, "No image uploaded"
# Check image size
if image.size[0] < 100 or image.size[1] < 100:
return False, "Image too small (minimum 100x100 pixels)"
# Check image format
if image.format not in ['JPEG', 'PNG', 'JPG']:
return False, "Unsupported image format (use JPEG or PNG)"
return True, "Image is valid"
def enhance_image_quality(image: Image.Image) -> Image.Image:
"""Enhance image quality for better try-on results"""
try:
# Convert to RGB if necessary
if image.mode != 'RGB':
image = image.convert('RGB')
# Resize to optimal dimensions
max_size = 1024
if max(image.size) > max_size:
ratio = max_size / max(image.size)
new_size = tuple(int(dim * ratio) for dim in image.size)
image = image.resize(new_size, Image.Resampling.LANCZOS)
# Enhance contrast and sharpness
from PIL import ImageEnhance
enhancer = ImageEnhance.Contrast(image)
image = enhancer.enhance(1.1)
enhancer = ImageEnhance.Sharpness(image)
image = enhancer.enhance(1.1)
return image
except Exception as e:
logger.error(f"Image enhancement failed: {e}")
return image
# Error handling and logging
def log_user_interaction(action: str, details: dict = None):
"""Log user interactions for analytics"""
timestamp = datetime.now().isoformat()
log_entry = {
'timestamp': timestamp,
'action': action,
'details': details or {}
}
logger.info(f"User interaction: {json.dumps(log_entry)}")
# Performance monitoring
def measure_processing_time(func):
"""Decorator to measure processing time"""
def wrapper(*args, **kwargs):
start_time = datetime.now()
result = func(*args, **kwargs)
end_time = datetime.now()
processing_time = (end_time - start_time).total_seconds()
logger.info(f"Processing time for {func.__name__}: {processing_time:.2f} seconds")
return result
return wrapper
# Security utilities
def sanitize_user_input(input_text: str) -> str:
"""Sanitize user input to prevent security issues"""
if not input_text:
return ""
# Remove potentially dangerous characters
dangerous_chars = ['<', '>', '"', "'", '&', ';']
for char in dangerous_chars:
input_text = input_text.replace(char, '')
return input_text.strip()
def validate_salesforce_credentials(client_id: str, client_secret: str, username: str, password: str) -> bool:
"""Validate Salesforce credentials format"""
if not all([client_id, client_secret, username, password]):
return False
# Basic format validation
if len(client_id) < 10 or len(client_secret) < 10:
return False
if '@' not in username:
return False
return True
# Configuration management
class AppConfig:
"""Application configuration management"""
def __init__(self):
self.max_image_size = 10 * 1024 * 1024 # 10MB
self.supported_formats = ['JPEG', 'PNG', 'JPG']
self.max_processing_time = 30 # seconds
self.cache_duration = 3600 # 1 hour
def get_huggingface_config(self):
"""Get configuration for Hugging Face deployment"""
return {
'title': 'Virtual Try-On for Clothing',
'description': 'AI-powered virtual try-on application with Salesforce integration',
'tags': ['fashion', 'ai', 'virtual-try-on', 'salesforce'],
'requirements': 'requirements.txt'
}
# Initialize configuration
config = AppConfig()
# Main execution
if __name__ == "__main__":
# Launch the Gradio app
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
debug=True,
show_error=True,
favicon_path=None,
ssl_verify=False
) |